OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::TreeSupport3D Namespace Reference

Classes

class  TreeModelVolumes
class  RichInterfacePlacer
struct  SupportElementInfluenceAreas
struct  SupportElementMerging
struct  AreaIncreaseSettings
struct  SupportElementStateBits
struct  SupportElementState
struct  SupportElement
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...
class  InterfacePlacer

Typedefs

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

Enumerations

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

Functions

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 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.
LineStatus get_avoidance_status (const Point &p, coord_t radius, LayerIndex layer_idx, const TreeModelVolumes &volumes, const TreeSupportSettings &config)
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, LineInformations > split_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< SupportElementState > 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)
 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 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.
static SupportElementMerging * 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 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.
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, bool has_root, 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)
static void generate_support_areas (Print &print, TreeSupport *tree_support, const BuildVolume &build_volume, const std::vector< size_t > &print_object_ids, std::function< void()> throw_on_cancel)
 Create the areas that need support.
static void recover_pending_branch_roofs (InterfacePlacer &interface_placer, const std::vector< const SupportElement * > &branch_path, const LayerIndex layer_begin, std::vector< Polygons > &slices)
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)
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)
double tiny_area_threshold ()
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)
double first_object_support_layer_z (const SlicingParameters &slicing_params)
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)
SupportGeneratorLayer & layer_initialize (SupportGeneratorLayer &layer_new, const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
SupportGeneratorLayer & layer_allocate_unguarded (SupportGeneratorLayerStorage &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, const TreeSupportSettings &config, size_t layer_idx)
SupportGeneratorLayer & layer_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 bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true

Typedef Documentation

◆ LayerIndex

◆ LineInformation

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

◆ LineInformations

◆ SupportElements

Enumeration Type Documentation

◆ InterfacePreference

Enumerator
InterfaceAreaOverwritesSupport 
SupportAreaOverwritesInterface 
InterfaceLinesOverwriteSupport 
SupportLinesOverwriteInterface 
Nothing 
46{
52};
@ SupportLinesOverwriteInterface
Definition TreeSupportCommon.hpp:50
@ SupportAreaOverwritesInterface
Definition TreeSupportCommon.hpp:48
@ InterfaceAreaOverwritesSupport
Definition TreeSupportCommon.hpp:47
@ Nothing
Definition TreeSupportCommon.hpp:51
@ InterfaceLinesOverwriteSupport
Definition TreeSupportCommon.hpp:49

◆ LineStatus

Enumerator
INVALID 
TO_MODEL 
TO_MODEL_GRACIOUS 
TO_MODEL_GRACIOUS_SAFE 
TO_BP 
TO_BP_SAFE 
796{
797 INVALID,
798 TO_MODEL,
801 TO_BP,
803};
@ TO_BP
Definition TreeSupportCommon.hpp:801
@ TO_BP_SAFE
Definition TreeSupportCommon.hpp:802
@ TO_MODEL_GRACIOUS_SAFE
Definition TreeSupportCommon.hpp:800
@ TO_MODEL
Definition TreeSupportCommon.hpp:798
@ TO_MODEL_GRACIOUS
Definition TreeSupportCommon.hpp:799
@ INVALID
Definition Model.hpp:356

Function Documentation

◆ calculateMachineBorderCollision()

Polygons Slic3r::TreeSupport3D::calculateMachineBorderCollision ( Polygon machine_border)
static
55{
56 // Put a border of 1m around the print volume so that we don't collide.
57#if 1
58 //FIXME just returning no border will let tree support legs collide with print bed boundary
59 return {};
60#else
61 //FIXME offsetting by 1000mm easily overflows int32_tr coordinate.
62 Polygons out = offset(machine_border, scaled<float>(1000.), jtMiter, 1.2);
63 machine_border.reverse(); // Makes the polygon negative so that we subtract the actual volume from the collision area.
64 out.emplace_back(std::move(machine_border));
65 return out;
66#endif
67}
void reverse()
Definition MultiPoint.hpp:43
@ jtMiter
Definition ClipperUtils.hpp:21
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

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

Referenced by Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes().

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

◆ check_self_intersections() [1/2]

void Slic3r::TreeSupport3D::check_self_intersections ( const ExPolygon & expoly,
const std::string_view message )
inlinestatic
153{
154#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
155 check_self_intersections(to_polygons(expoly), message);
156#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
157}
static void check_self_intersections(const Polygons &polygons, const std::string_view message)
Definition TreeSupport3D.cpp:145
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:307

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

Here is the call graph for this function:

◆ check_self_intersections() [2/2]

void Slic3r::TreeSupport3D::check_self_intersections ( const Polygons & polygons,
const std::string_view message )
inlinestatic
146{
147#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
148 if (!intersecting_edges(polygons).empty())
149 ::MessageBoxA(nullptr, (std::string("TreeSupport infill self intersections: ") + std::string(message)).c_str(), "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
150#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
151}
std::vector< std::pair< EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge > > intersecting_edges(const Polygons &polygons)
Definition EdgeGrid.cpp:1581
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

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()

LineInformations Slic3r::TreeSupport3D::convert_lines_to_internal ( const TreeModelVolumes & volumes,
const TreeSupportSettings & config,
const Polylines & polylines,
LayerIndex layer_idx )
staticnodiscard

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.
432{
433 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
434
435 LineInformations result;
436 // Also checks if the position is valid, if it is NOT, it deletes that point
437 for (const Polyline &line : polylines) {
438 LineInformation res_line;
439 for (Point p : line) {
440 if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p))
441 res_line.emplace_back(p, LineStatus::TO_BP_SAFE);
442 else if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p))
443 res_line.emplace_back(p, LineStatus::TO_BP);
444 else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p))
445 res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS_SAFE);
446 else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p))
447 res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS);
448 else if (config.support_rests_on_model && ! contains(volumes.getCollision(config.getRadius(0), layer_idx, min_xy_dist), p))
449 res_line.emplace_back(p, LineStatus::TO_MODEL);
450 else if (!res_line.empty()) {
451 result.emplace_back(res_line);
452 res_line.clear();
453 }
454 }
455 if (!res_line.empty()) {
456 result.emplace_back(res_line);
457 res_line.clear();
458 }
459 }
460
461 validate_range(result);
462 return result;
463}
Definition Point.hpp:199
Definition Polyline.hpp:25
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:382
@ FastSafe
Definition TreeModelVolumes.hpp:84
@ Fast
Definition TreeModelVolumes.hpp:85
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:346
bool contains(const std::vector< T > &vector, const Point &point)
Definition Geometry.cpp:55
std::vector< std::pair< Point, LineStatus > > LineInformation
Definition TreeSupport3D.cpp:99
std::vector< LineInformation > LineInformations
Definition TreeSupport3D.cpp:100
static void validate_range(const Point &pt)
Definition TreeSupport3D.cpp:103
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:563
bool support_rests_on_model
True if the branches may connect to the model.
Definition TreeSupportCommon.hpp:432
coord_t xy_distance
How far should support be from the model.
Definition TreeSupportCommon.hpp:436
coord_t xy_min_distance
minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
Definition TreeSupportCommon.hpp:453

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

Referenced by convert_lines_to_internal(), and sample_overhang_area().

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

◆ create_layer_pathing()

void Slic3r::TreeSupport3D::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
2491{
2492#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2493 const double data_size_inverse = 1 / double(move_bounds.size());
2494 double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES;
2495#endif // SLIC3R_TREESUPPORTS_PROGRESS
2496
2497 auto dur_inc = std::chrono::duration_values<std::chrono::nanoseconds>::zero();
2498 auto dur_total = std::chrono::duration_values<std::chrono::nanoseconds>::zero();
2499
2500 LayerIndex last_merge_layer_idx = move_bounds.size();
2501 bool new_element = false;
2502 const auto _tiny_area_threshold = tiny_area_threshold();
2503
2504 // 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.
2505 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);
2506 size_t merge_every_x_layers = 1;
2507 // Calculate the influence areas for each layer below (Top down)
2508 // This is done by first increasing the influence area by the allowed movement distance, and merging them with other influence areas if possible
2509 for (int layer_idx = int(move_bounds.size()) - 1; layer_idx > 0; -- layer_idx)
2510 if (SupportElements &prev_layer = move_bounds[layer_idx]; ! prev_layer.empty()) {
2511 // 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.
2512 bool had_new_element = new_element;
2513 const bool merge_this_layer = had_new_element || size_t(last_merge_layer_idx - layer_idx) >= merge_every_x_layers;
2514 if (had_new_element)
2515 merge_every_x_layers = 1;
2516 const auto ta = std::chrono::high_resolution_clock::now();
2517
2518 // ### Increase the influence areas by the allowed movement distance
2519 std::vector<SupportElementMerging> influence_areas;
2520 influence_areas.reserve(prev_layer.size());
2521 for (int32_t element_idx = 0; element_idx < int32_t(prev_layer.size()); ++ element_idx) {
2522 SupportElement &el = prev_layer[element_idx];
2523 assert(!el.influence_area.empty());
2525 parents.emplace_back(element_idx);
2526 influence_areas.push_back({ el.state, parents });
2527 }
2528 increase_areas_one_layer(volumes, config, influence_areas, layer_idx, prev_layer, merge_this_layer, throw_on_cancel);
2529
2530 // Place already fully constructed elements to the output, remove them from influence_areas.
2531 SupportElements &this_layer = move_bounds[layer_idx - 1];
2532 influence_areas.erase(std::remove_if(influence_areas.begin(), influence_areas.end(),
2533 [&this_layer, &_tiny_area_threshold, layer_idx](SupportElementMerging &elem) {
2534 if (elem.areas.influence_areas.empty())
2535 // This area was removed completely due to collisions.
2536 return true;
2537 if (elem.areas.to_bp_areas.empty() && elem.areas.to_model_areas.empty()) {
2538 if (area(elem.areas.influence_areas) < _tiny_area_threshold) {
2539 BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area bypass on layer " << layer_idx - 1;
2540 tree_supports_show_error("Insert error of area after bypassing merge.\n"sv, true);
2541 }
2542 // Move the area to output.
2543 this_layer.emplace_back(elem.state, std::move(elem.parents), std::move(elem.areas.influence_areas));
2544 return true;
2545 }
2546 // Keep the area.
2547 return false;
2548 }),
2549 influence_areas.end());
2550
2551 dur_inc += std::chrono::high_resolution_clock::now() - ta;
2552 new_element = ! move_bounds[layer_idx - 1].empty();
2553 if (merge_this_layer) {
2554 bool reduced_by_merging = false;
2555 if (size_t count_before_merge = influence_areas.size(); count_before_merge > 1) {
2556 // ### Calculate which influence areas overlap, and merge them into a new influence area (simplified: an intersection of influence areas that have such an intersection)
2557 merge_influence_areas(volumes, config, layer_idx, influence_areas, throw_on_cancel);
2558 reduced_by_merging = count_before_merge > influence_areas.size();
2559 }
2560 last_merge_layer_idx = layer_idx;
2561 if (! reduced_by_merging && ! had_new_element)
2562 merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1);
2563 }
2564
2565 dur_total += std::chrono::high_resolution_clock::now() - ta;
2566
2567 // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again.
2568 for (SupportElementMerging &elem : influence_areas)
2569 if (! elem.areas.influence_areas.empty()) {
2570 Polygons new_area = safe_union(elem.areas.influence_areas);
2571 if (area(new_area) < _tiny_area_threshold) {
2572 BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.state.to_buildplate;
2573 tree_supports_show_error("Insert error of area after merge.\n"sv, true);
2574 }
2575 this_layer.emplace_back(elem.state, std::move(elem.parents), std::move(new_area));
2576 }
2577
2578 #ifdef SLIC3R_TREESUPPORTS_PROGRESS
2579 progress_total += data_size_inverse * TREE_PROGRESS_AREA_CALC;
2580 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
2581 #endif
2582 throw_on_cancel();
2583 }
2584
2585 BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 <<
2586 " ms merging areas: " << (dur_total - dur_inc).count() / 1000000 << " ms";
2587}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
int64_t coord_t
Definition libslic3r.h:48
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 TreeSupport3D.cpp:796
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.
Definition TreeSupport3D.cpp:1828
double tiny_area_threshold()
Definition TreeSupportCommon.hpp:609
int LayerIndex
Definition TreeSupportCommon.hpp:43
void tree_supports_show_error(std::string_view message, bool critical)
Definition TreeSupportCommon.hpp:611
std::deque< SupportElement > SupportElements
Definition TreeSupport3D.hpp:308
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 TreeSupport3D.cpp:2398
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
Definition TreeSupport3D.cpp:1789
Definition TreeSupport3D.hpp:279
Polygons influence_area
The resulting influence area. Will only be set in the results of createLayerPathing,...
Definition TreeSupport3D.hpp:305
SupportElementState state
Definition TreeSupport3D.hpp:294
std::vector< int32_t > ParentIndices
Definition TreeSupport3D.hpp:280
coord_t maximum_move_distance
How far an influence area may move outward every layer at most.
Definition TreeSupportCommon.hpp:396
coord_t maximum_move_distance_slow
How far every influence area will move outward every layer if possible.
Definition TreeSupportCommon.hpp:400
coord_t layer_height
Height of a single layer.
Definition TreeSupportCommon.hpp:384

References Slic3r::area(), create_layer_pathing(), increase_areas_one_layer(), Slic3r::TreeSupport3D::SupportElement::influence_area, Slic3r::TreeSupport3D::TreeSupportSettings::layer_height, Slic3r::TreeSupport3D::TreeSupportSettings::maximum_move_distance, Slic3r::TreeSupport3D::TreeSupportSettings::maximum_move_distance_slow, merge_influence_areas(), safe_union(), Slic3r::TreeSupport3D::SupportElement::state, tiny_area_threshold(), 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()

void Slic3r::TreeSupport3D::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
2738{
2739 // Initialize points on layer 0, with a "random" point in the influence area.
2740 // 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.
2741 {
2742 SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr;
2743 if (layer_above) {
2744 for (SupportElement &elem : *layer_above)
2745 elem.state.marked = false;
2746 }
2747 for (SupportElement &init : move_bounds.front()) {
2748 init.state.result_on_layer = move_inside_if_outside(init.influence_area, init.state.next_position);
2749 // Also set the parent nodes, as these will be required for the first iteration of the loop below and mark the parent nodes.
2750 set_points_on_areas(init, layer_above);
2751 }
2752 }
2753
2754 throw_on_cancel();
2755
2756 for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
2757 auto &layer = move_bounds[layer_idx];
2758 auto *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
2759 if (layer_above)
2760 for (SupportElement &elem : *layer_above)
2761 elem.state.marked = false;
2762 for (SupportElement &elem : layer) {
2763 assert(! elem.state.deleted);
2764 assert(elem.state.layer_idx == layer_idx);
2765 // check if the resulting center point is not yet set
2766 if (! elem.state.result_on_layer_is_set()) {
2767 if (elem.state.to_buildplate || (elem.state.distance_to_top < config.min_dtt_to_model && ! elem.state.supports_roof)) {
2768 if (elem.state.to_buildplate) {
2769 BOOST_LOG_TRIVIAL(warning) << "Uninitialized Influence area targeting " << elem.state.target_position.x() << "," << elem.state.target_position.y()
2770 << ") "
2771 "at target_height: " << elem.state.target_height << " layer: " << layer_idx;
2772 tree_supports_show_error("Uninitialized support element! A branch could be missing or exist partially."sv, true);
2773 }
2774 // we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set
2775 elem.state.deleted = true;
2776 } else {
2777 // set the point where the branch will be placed on the model
2778 if (elem.state.to_model_gracious)
2779 set_to_model_contact_to_model_gracious(volumes, config, move_bounds, elem, throw_on_cancel);
2780 else
2782 }
2783 }
2784 if (! elem.state.deleted && ! elem.state.marked && elem.state.target_height == layer_idx)
2785 // Just a tip surface with no supporting element.
2786 elem.state.deleted = true;
2787 if (elem.state.deleted) {
2788 for (int32_t parent_idx : elem.parents)
2789 // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set.
2790 // As this branch needs to be removed => all parents result_on_layer have to be invalidated.
2791 (*layer_above)[parent_idx].state.result_on_layer_reset();
2792 }
2793 if (! elem.state.deleted) {
2794 // Element is valid now setting points in the layer above and mark the parent nodes.
2795 set_points_on_areas(elem, layer_above);
2796 }
2797 }
2798 throw_on_cancel();
2799 }
2800
2801#ifndef NDEBUG
2802 // Verify the tree connectivity including the branch slopes.
2803 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
2804 auto &layer = move_bounds[layer_idx];
2805 auto &above = move_bounds[layer_idx + 1];
2806 for (SupportElement &elem : layer)
2807 if (! elem.state.deleted) {
2808 for (int32_t iparent : elem.parents) {
2809 SupportElement &parent = above[iparent];
2810 assert(! parent.state.deleted);
2811 assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
2812 if (elem.state.result_on_layer_is_set()) {
2813 double radius_increase = support_element_radius(config, elem) - support_element_radius(config, parent);
2814 assert(radius_increase >= 0);
2815 double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
2816 //FIXME this assert fails a lot. Is it correct?
2817// assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
2818 }
2819 }
2820 }
2821 }
2822#endif // NDEBUG
2823
2824 remove_deleted_elements(move_bounds);
2825
2826#ifndef NDEBUG
2827 // Verify the tree connectivity including the branch slopes.
2828 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
2829 auto &layer = move_bounds[layer_idx];
2830 auto &above = move_bounds[layer_idx + 1];
2831 for (SupportElement &elem : layer) {
2832 assert(! elem.state.deleted);
2833 for (int32_t iparent : elem.parents) {
2834 SupportElement &parent = above[iparent];
2835 assert(! parent.state.deleted);
2836 assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
2837 if (elem.state.result_on_layer_is_set()) {
2838 double radius_increase = support_element_radius(config, elem) - support_element_radius(config, parent);
2839 assert(radius_increase >= 0);
2840 double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
2841 //FIXME this assert fails a lot. Is it correct?
2842// assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
2843 }
2844 }
2845 }
2846 }
2847#endif // NDEBUG
2848}
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 TreeSupport3D.cpp:2638
static Point move_inside_if_outside(const Polygons &polygons, Point from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
Definition TreeSupport3D.cpp:1608
static void set_to_model_contact_simple(SupportElement &elem)
Definition TreeSupport3D.cpp:2624
coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
Get the Radius, that this element will have.
Definition TreeSupport3D.hpp:263
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 TreeSupport3D.cpp:2594
static void remove_deleted_elements(std::vector< SupportElements > &move_bounds)
Definition TreeSupport3D.cpp:2686
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299
size_t min_dtt_to_model
If smaller (in layers) than that, all branches to model will be deleted.
Definition TreeSupportCommon.hpp:420

References Slic3r::cast(), create_nodes_from_area(), Slic3r::TreeSupport3D::SupportElementStateBits::deleted, Slic3r::TreeSupport3D::TreeSupportSettings::min_dtt_to_model, move_inside_if_outside(), remove_deleted_elements(), Slic3r::TreeSupport3D::SupportElementState::result_on_layer, Slic3r::TreeSupport3D::SupportElementState::result_on_layer_is_set(), set_points_on_areas(), set_to_model_contact_simple(), set_to_model_contact_to_model_gracious(), Slic3r::TreeSupport3D::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()

std::pair< int, int > Slic3r::TreeSupport3D::discretize_circle ( const Vec3f & center,
const Vec3f & normal,
const float radius,
const float eps,
std::vector< Vec3f > & pts )
static
2934{
2935 // Calculate discretization step and number of steps.
2936 float angle_step = 2. * acos(1. - eps / radius);
2937 auto nsteps = int(ceil(2 * M_PI / angle_step));
2938 angle_step = 2 * M_PI / nsteps;
2939
2940 // Prepare coordinate system for the circle plane.
2941 Vec3f x = normal.cross(Vec3f(0.f, -1.f, 0.f)).normalized();
2942 Vec3f y = normal.cross(x).normalized();
2943 assert(std::abs(x.cross(y).dot(normal) - 1.f) < EPSILON);
2944
2945 // Discretize the circle.
2946 int begin = int(pts.size());
2947 pts.reserve(pts.size() + nsteps);
2948 float angle = 0;
2949 x *= radius;
2950 y *= radius;
2951 for (int i = 0; i < nsteps; ++ i) {
2952 pts.emplace_back(center + x * cos(angle) + y * sin(angle));
2953 angle += angle_step;
2954 }
2955 return { begin, int(pts.size()) };
2956}
#define EPSILON
Definition ColorUtils.cpp:64
#define M_PI
Definition ExtrusionSimulator.cpp:31
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
Point normal(Point pt, double scale)
Definition TreeSupport.cpp:112

References Slic3r::angle(), Slic3r::center(), discretize_circle(), EPSILON, M_PI, and Slic3r::normal().

Referenced by discretize_circle(), and extrude_branch().

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

◆ ensure_maximum_distance_polyline()

Polylines Slic3r::TreeSupport3D::ensure_maximum_distance_polyline ( const Polylines & input,
double distance,
size_t min_points )
staticnodiscard

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.
600{
601 Polylines result;
602 for (Polyline part : input) {
603 if (part.empty())
604 continue;
605
606 double len = length(part.points);
607 Polyline line;
608 double current_distance = std::max(distance, scaled<double>(0.1));
609 if (len < 2 * distance && min_points <= 1)
610 {
611 // Insert the opposite point of the first one.
612 //FIXME pretty expensive
613 Polyline pl(part);
614 pl.clip_end(len / 2);
615 line.points.emplace_back(pl.points.back());
616 }
617 else
618 {
619 size_t optimal_end_index = part.size() - 1;
620
621 if (part.front() == part.back()) {
622 size_t optimal_start_index = 0;
623 // 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.
624 // 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.
625 // The other will be manually added (optimal_end_index)
626 coord_t max_dist2_between_vertecies = 0;
627 for (size_t idx = 0; idx < part.size() - 1; ++ idx) {
628 for (size_t inner_idx = 0; inner_idx < part.size() - 1; inner_idx++) {
629 if ((part[idx] - part[inner_idx]).cast<double>().squaredNorm() > max_dist2_between_vertecies) {
630 optimal_start_index = idx;
631 optimal_end_index = inner_idx;
632 max_dist2_between_vertecies = (part[idx] - part[inner_idx]).cast<double>().squaredNorm();
633 }
634 }
635 }
636 std::rotate(part.begin(), part.begin() + optimal_start_index, part.end() - 1);
637 part[part.size() - 1] = part[0]; // restore that property that this polyline ends where it started.
638 optimal_end_index = (part.size() + optimal_end_index - optimal_start_index - 1) % (part.size() - 1);
639 }
640
641 while (line.size() < min_points && current_distance >= scaled<double>(0.1))
642 {
643 line.clear();
644 Point current_point = part[0];
645 line.points.emplace_back(part[0]);
646 if (min_points > 1 || (part[0] - part[optimal_end_index]).cast<double>().norm() > current_distance)
647 line.points.emplace_back(part[optimal_end_index]);
648 size_t current_index = 0;
649 std::optional<std::pair<Point, size_t>> next_point;
650 double next_distance = current_distance;
651 // 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.
652 // 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!
653 while ((next_point = polyline_sample_next_point_at_distance(part.points, current_point, current_index, next_distance))) {
654 // 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.
655 // So this ensures that the points are actually a certain distance from each other.
656 // 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.
657 double min_distance_to_existing_point = std::numeric_limits<double>::max();
658 for (Point p : line)
659 min_distance_to_existing_point = std::min(min_distance_to_existing_point, (p - next_point->first).cast<double>().norm());
660 if (min_distance_to_existing_point >= current_distance) {
661 // viable point was found. Add to possible result.
662 line.points.emplace_back(next_point->first);
663 current_point = next_point->first;
664 current_index = next_point->second;
665 next_distance = current_distance;
666 } else {
667 if (current_point == next_point->first) {
668 // In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here...
669 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 <<
670 ") is smaller than 100";
671 tree_supports_show_error("Encountered issue while placing tips. Some tips may be missing."sv, true);
672 if (next_distance > 2 * current_distance)
673 // This case should never happen, but better safe than sorry.
674 break;
675 next_distance += current_distance;
676 continue;
677 }
678 // 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.
679 next_distance = std::max(current_distance - min_distance_to_existing_point, scaled<double>(0.1));
680 current_point = next_point->first;
681 current_index = next_point->second;
682 }
683 }
684 current_distance *= 0.9;
685 }
686 }
687 result.emplace_back(std::move(line));
688 }
689 validate_range(result);
690 return result;
691}
Points points
Definition MultiPoint.hpp:22
size_t size() const
Definition MultiPoint.hpp:50
void clear()
Definition Polyline.hpp:118
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 TreeSupport3D.cpp:545
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
double length(const Points &pts)
Definition MultiPoint.hpp:181
std::vector< Polyline > Polylines
Definition Polyline.hpp:22

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

Referenced by ensure_maximum_distance_polyline(), and 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()

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

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.
495{
496 using AvoidanceType = TreeModelVolumes::AvoidanceType;
497 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
498 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))
499 return true;
500 if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE))
501 return ! contains(
503 volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, min_xy_dist) :
504 volumes.getCollision(config.getRadius(0), current_layer - 1, min_xy_dist),
505 p.first);
506 return false;
507}
AvoidanceType
Definition TreeModelVolumes.hpp:82

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

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

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

◆ extrude_branch()

std::pair< float, float > Slic3r::TreeSupport3D::extrude_branch ( const std::vector< const SupportElement * > & path,
const TreeSupportSettings & config,
const SlicingParameters & slicing_params,
const std::vector< SupportElements > & move_bounds,
bool has_root,
indexed_triangle_set & result )
static
2966{
2967 Vec3d p1, p2, p3;
2968 Vec3d v1, v2;
2969 Vec3d nprev;
2970 Vec3d ncurrent;
2971 assert(path.size() >= 2);
2972 static constexpr const float eps = 0.015f;
2973 std::pair<int, int> prev_strip;
2974 float zmin = 0;
2975 float zmax = 0;
2976
2977 for (size_t ipath = 1; ipath < path.size(); ++ ipath) {
2978 const SupportElement &prev = *path[ipath - 1];
2979 const SupportElement &current = *path[ipath];
2980 assert(prev.state.layer_idx + 1 == current.state.layer_idx);
2981 p1 = to_3d(unscaled<double>(prev .state.result_on_layer), layer_z(slicing_params, config, prev .state.layer_idx));
2982 p2 = to_3d(unscaled<double>(current.state.result_on_layer), layer_z(slicing_params, config, current.state.layer_idx));
2983 v1 = (p2 - p1).normalized();
2984 if (ipath == 1) {
2985 nprev = v1;
2986 float radius = unscaled<float>(support_element_radius(config, prev));
2987 if (has_root && prev.state.layer_idx == 0) {
2988 // Orca: Buildplate roots need a flat foot. A rounded cap can extend far
2989 // below the bed and make the first layer slice cut unrelated trunk geometry.
2990 const Vec3f normal(0.f, 0.f, 1.f);
2991 const Vec3f bottom_center(float(p1.x()), float(p1.y()), 0.f);
2992 const Vec3f top_center(float(p1.x()), float(p1.y()), float(p1.z()));
2993 int ifan = int(result.vertices.size());
2994 result.vertices.emplace_back(bottom_center);
2995 std::pair<int, int> bottom_strip = discretize_circle(bottom_center, normal, radius, eps, result.vertices);
2996 triangulate_fan<false>(result, ifan, bottom_strip.first, bottom_strip.second);
2997 prev_strip = discretize_circle(top_center, normal, radius, eps, result.vertices);
2998 triangulate_strip(result, bottom_strip.first, bottom_strip.second, prev_strip.first, prev_strip.second);
2999 zmin = 0.f;
3000 } else {
3001 // Extrude the bottom half sphere.
3002 float angle_step = 2. * acos(1. - eps / radius);
3003 auto nsteps = int(ceil(M_PI / (2. * angle_step)));
3004 angle_step = M_PI / (2. * nsteps);
3005 int ifan = int(result.vertices.size());
3006 result.vertices.emplace_back((p1 - nprev * radius).cast<float>());
3007 zmin = result.vertices.back().z();
3008 float angle = angle_step;
3009 for (int i = 1; i < nsteps; ++ i, angle += angle_step) {
3010 std::pair<int, int> strip = discretize_circle((p1 - nprev * radius * cos(angle)).cast<float>(), nprev.cast<float>(), radius * sin(angle), eps, result.vertices);
3011 if (i == 1)
3012 triangulate_fan<false>(result, ifan, strip.first, strip.second);
3013 else
3014 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
3015
3016 prev_strip = strip;
3017 }
3018 }
3019 }
3020 if (ipath + 1 == path.size()) {
3021 // End of the tube.
3022 ncurrent = v1;
3023 // Extrude the top half sphere.
3024 float radius = unscaled<float>(support_element_radius(config, current));
3025 float angle_step = 2. * acos(1. - eps / radius);
3026 auto nsteps = int(ceil(M_PI / (2. * angle_step)));
3027 angle_step = M_PI / (2. * nsteps);
3028 auto angle = float(M_PI / 2.);
3029 for (int i = 0; i < nsteps; ++ i, angle -= angle_step) {
3030 std::pair<int, int> strip = discretize_circle((p2 + ncurrent * radius * cos(angle)).cast<float>(), ncurrent.cast<float>(), radius * sin(angle), eps, result.vertices);
3031 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
3032// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
3033// its_write_obj(result, fname);
3034 prev_strip = strip;
3035 }
3036 int ifan = int(result.vertices.size());
3037 result.vertices.emplace_back((p2 + ncurrent * radius).cast<float>());
3038 zmax = result.vertices.back().z();
3039 triangulate_fan<true>(result, ifan, prev_strip.first, prev_strip.second);
3040// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
3041// its_write_obj(result, fname);
3042 } else {
3043 const SupportElement &next = *path[ipath + 1];
3044 assert(current.state.layer_idx + 1 == next.state.layer_idx);
3045 p3 = to_3d(unscaled<double>(next.state.result_on_layer), layer_z(slicing_params, config, next.state.layer_idx));
3046 v2 = (p3 - p2).normalized();
3047 ncurrent = (v1 + v2).normalized();
3048 float radius = unscaled<float>(support_element_radius(config, current));
3049 std::pair<int, int> strip = discretize_circle(p2.cast<float>(), ncurrent.cast<float>(), radius, eps, result.vertices);
3050 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
3051 prev_strip = strip;
3052// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++irun);
3053// its_write_obj(result, fname);
3054 }
3055#if 0
3056 if (circles_intersect(p1, nprev, support_element_radius(settings, prev), p2, ncurrent, support_element_radius(settings, current))) {
3057 // Cannot connect previous and current slice using a simple zig-zag triangulation,
3058 // because the two circles intersect.
3059
3060 } else {
3061 // Continue with chaining.
3062
3063 }
3064#endif
3065 }
3066
3067 return std::make_pair(zmin, zmax);
3068}
void triangulate_fan(indexed_triangle_set &its, int ifan, int ibegin, int iend)
Definition TreeSupport3D.cpp:2851
static std::pair< int, int > discretize_circle(const Vec3f &center, const Vec3f &normal, const float radius, const float eps, std::vector< Vec3f > &pts)
Definition TreeSupport3D.cpp:2933
double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
Definition TreeSupportCommon.hpp:626
static void triangulate_strip(indexed_triangle_set &its, int ibegin1, int iend1, int ibegin2, int iend2)
Definition TreeSupport3D.cpp:2867
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Eigen::Matrix< typename Derived::Scalar, 3, 1, Eigen::DontAlign > to_3d(const Eigen::MatrixBase< Derived > &pt, const typename Derived::Scalar z)
Definition Point.hpp:153
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
Point result_on_layer
The resulting center point around which a circle will be drawn later. Will be set by setPointsOnAreas...
Definition TreeSupport3D.hpp:187
LayerIndex layer_idx
The next height this support elements wants to reach.
Definition TreeSupport3D.hpp:171

References Slic3r::angle(), Slic3r::cast(), discretize_circle(), extrude_branch(), Slic3r::TreeSupport3D::SupportElementState::layer_idx, layer_z(), M_PI, Slic3r::normal(), Slic3r::TreeSupport3D::SupportElementState::result_on_layer, Slic3r::TreeSupport3D::SupportElement::state, support_element_radius(), Slic3r::to_3d(), triangulate_fan(), triangulate_strip(), and Slic3r::unscaled().

Referenced by extrude_branch().

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

◆ finalize_raft_contact()

void Slic3r::TreeSupport3D::finalize_raft_contact ( const PrintObject & print_object,
const int raft_contact_layer_idx,
SupportGeneratorLayersPtr & top_contacts,
std::vector< SupportElements > & move_bounds )
1090{
1091 if (raft_contact_layer_idx >= 0) {
1092 const size_t first_tree_layer = print_object.slicing_parameters().raft_layers() - 1;
1093 // Remove tree tips that start below the raft contact,
1094 // remove interface layers below the raft contact.
1095 for (size_t i = 0; i < first_tree_layer; ++i) {
1096 top_contacts[i] = nullptr;
1097 move_bounds[i].clear();
1098 }
1099 if (raft_contact_layer_idx >= 0 && print_object.config().raft_expansion.value > 0) {
1100 // 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.
1101 Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons;
1102 EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON));
1103 grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.)));
1104 SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer];
1105 double threshold = scaled<double>(print_object.config().raft_expansion.value) * 2.;
1106 first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(),
1107 [&grid, threshold](const SupportElement &el) {
1108 coordf_t dist;
1109 if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) {
1110 assert(std::abs(dist) < threshold + SCALED_EPSILON);
1111 // Support point is inside the expanded raft, remove it.
1112 return dist < - 0.;
1113 }
1114 return false;
1115 }), first_layer_move_bounds.end());
1116 #if 0
1117 // Remove the remaining tips from the raft: Closing operation on tip circles.
1118 if (! first_layer_move_bounds.empty()) {
1119 const double eps = 0.1;
1120 // All tips supporting this layer are expected to have the same radius.
1121 double radius = support_element_radius(config, first_layer_move_bounds.front());
1122 // Connect the tips with the following closing radius.
1123 double closing_distance = radius;
1124 Polygon circle = make_circle(radius + closing_distance, eps);
1125 Polygons circles;
1126 circles.reserve(first_layer_move_bounds.size());
1127 for (const SupportElement &el : first_layer_move_bounds) {
1128 circles.emplace_back(circle);
1129 circles.back().translate(el.state.result_on_layer);
1130 }
1131 raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance));
1132 }
1133 #endif
1134 }
1135 }
1136}
Definition EdgeGrid.hpp:104
const PrintObjectConfig & config() const
Definition Print.hpp:372
const SlicingParameters & slicing_parameters() const
Definition Print.hpp:496
#define SCALED_EPSILON
Definition libslic3r.h:109
#define scale_(val)
Definition libslic3r.h:106
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
Polygon make_circle(double radius, double error)
Definition Polygon.cpp:789
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
std::vector< ArithmeticOnly< T > > grid(const T &start, const T &stop, const T &stride)
A set of equidistant values starting from 'start' (inclusive), ending in the closest multiple of 'str...
Definition MTUtils.hpp:127
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:731
Slic3r::Polygon Polygon
Definition Emboss.cpp:101
size_t raft_layers() const
Definition Slicing.hpp:44

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

Referenced by finalize_raft_contact().

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

◆ first_object_support_layer_z()

double Slic3r::TreeSupport3D::first_object_support_layer_z ( const SlicingParameters & slicing_params)
inline
634{
635 return slicing_params.object_print_z_min + slicing_params.first_object_layer_height;
636}
coordf_t object_print_z_min
Definition Slicing.hpp:107
coordf_t first_object_layer_height
Definition Slicing.hpp:84

References Slic3r::SlicingParameters::first_object_layer_height, and Slic3r::SlicingParameters::object_print_z_min.

Referenced by layer_idx_ceil(), and layer_idx_floor().

Here is the caller graph for this function:

◆ generate_initial_areas()

void Slic3r::TreeSupport3D::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.
1301{
1302 using AvoidanceType = TreeModelVolumes::AvoidanceType;
1303 TreeSupportMeshGroupSettings mesh_group_settings(print_object);
1304
1305 // 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
1306 const size_t z_distance_delta = config.z_distance_top_layers + 1;
1307
1308 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
1309
1310#if 0
1311 if (mesh.overhang_areas.size() <= z_distance_delta)
1312 return;
1313#endif
1314
1315 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);
1316 // 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.
1317 // This calculates how far one has to move on the x-axis so that y=r-support_line_width/2.
1318 // 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.
1319 // As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains.
1320 const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ?
1321 config.min_radius / 2 :
1322 scale_(sqrt(sqr(unscale<double>(config.min_radius)) - sqr(unscale<double>(config.min_radius - config.support_line_width / 2))));
1323 // 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.
1324 //FIXME Vojtech: This is not sufficient for support enforcers to work.
1325 //FIXME There is no account for the support overhang angle.
1326 //FIXME There is no account for the width of the collision regions.
1327 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)
1328 //FIXME this is a heuristic value for support enforcers to work.
1329// + 10 * config.support_line_width;
1330 ;
1331 const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
1332 const bool roof_enabled = num_support_roof_layers > 0;
1333 const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.zero_gap_interface_top || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
1334 // 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
1335 // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
1336 // 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.
1337 // The 2*z_distance_delta is only a catch for when the support angle is very high.
1338 // Used only if not min_xy_dist.
1339 coord_t max_overhang_insert_lag = 0;
1340 if (config.z_distance_top_layers > 0) {
1341 max_overhang_insert_lag = 2 * config.z_distance_top_layers;
1342
1343 //FIXME
1344 if (mesh_group_settings.support_angle > EPSILON && mesh_group_settings.support_angle < 0.5 * M_PI - EPSILON) {
1345 //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).
1346 //used by max_overhang_insert_lag, only if not min_xy_dist.
1347 const auto max_overhang_speed = coord_t(tan(mesh_group_settings.support_angle) * config.layer_height);
1348 max_overhang_insert_lag = std::max(max_overhang_insert_lag, round_up_divide(config.xy_distance, max_overhang_speed / 2));
1349 }
1350 }
1351
1352 size_t num_support_layers;
1353 int raft_contact_layer_idx;
1354 // Layers with their overhang regions.
1355 std::vector<std::pair<size_t, const Polygons*>> raw_overhangs;
1356
1357 {
1358 const size_t num_raft_layers = config.raft_layers.size();
1359 const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
1360 num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
1361 raft_contact_layer_idx = generate_raft_contact(print_object, config, interface_placer);
1362 // Enumerate layers for which the support tips may be generated from overhangs above.
1363 raw_overhangs.reserve(num_support_layers - first_support_layer);
1364 for (size_t layer_idx = first_support_layer; layer_idx < num_support_layers; ++ layer_idx)
1365 if (const size_t overhang_idx = layer_idx + z_distance_delta; ! overhangs[overhang_idx].empty())
1366 raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
1367 }
1368
1369 RichInterfacePlacer rich_interface_placer{ interface_placer, volumes, force_tip_to_roof, num_support_layers, move_bounds };
1370
1371 tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
1372 [&volumes, &config, &raw_overhangs, &mesh_group_settings,
1373 min_xy_dist, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length,
1374 &rich_interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
1375 for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) {
1376 size_t layer_idx = raw_overhangs[raw_overhang_idx].first;
1377 const Polygons &overhang_raw = *raw_overhangs[raw_overhang_idx].second;
1378
1379 // take the least restrictive avoidance possible
1380 Polygons relevant_forbidden;
1381 {
1382 const Polygons &relevant_forbidden_raw = config.support_rests_on_model ?
1383 volumes.getCollision(config.getRadius(0), layer_idx, min_xy_dist) :
1384 volumes.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::Fast, false, min_xy_dist);
1385 // prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise.
1386 relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled<float>(0.005), jtMiter, 1.2);
1387 }
1388
1389 // 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
1390 // even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and
1391 // 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)
1392 Polygons overhang_regular;
1393 {
1394 // When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway.
1395 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);
1396 //check_self_intersections(overhang_regular, "overhang_regular1");
1397
1398 // offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang
1399 Polygons remaining_overhang = intersection(
1400 diff(mesh_group_settings.support_offset == 0 ?
1401 overhang_raw :
1402 offset(union_ex(overhang_raw), mesh_group_settings.support_offset, jtMiter, 1.2),
1403 offset(union_ex(overhang_regular), config.support_line_width * 0.5, jtMiter, 1.2)),
1404 relevant_forbidden);
1405
1406 // 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.
1407 //+config.support_line_width / 80 to avoid calculating very small (useless) offsets because of rounding errors.
1408 //FIXME likely a better approach would be to find correspondences between the full overhang and the trimmed overhang
1409 // and if there is no correspondence, project the missing points to the clipping curve.
1410 for (coord_t extra_total_offset_acc = 0; ! remaining_overhang.empty() && extra_total_offset_acc + config.support_line_width / 8 < extra_outset; ) {
1411 const coord_t offset_current_step = std::min(
1412 extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ?
1413 config.support_line_width / 8 :
1414 circle_length_to_half_linewidth_change,
1415 extra_outset - extra_total_offset_acc);
1416 extra_total_offset_acc += offset_current_step;
1417 const Polygons &raw_collision = volumes.getCollision(0, layer_idx, true);
1418 const coord_t offset_step = config.xy_min_distance + config.support_line_width;
1419 // Reducing the remaining overhang by the areas already supported.
1420 //FIXME 1.5 * extra_total_offset_acc seems to be too much, it may remove some remaining overhang without being supported at all.
1421 remaining_overhang = diff(remaining_overhang, safe_offset_inc(overhang_regular, 1.5 * extra_total_offset_acc, raw_collision, offset_step, 0, 1));
1422 // Extending the overhangs by the inflated remaining overhangs.
1423 overhang_regular = union_(overhang_regular, diff(safe_offset_inc(remaining_overhang, extra_total_offset_acc, raw_collision, offset_step, 0, 1), relevant_forbidden));
1424 //check_self_intersections(overhang_regular, "overhang_regular2");
1425 }
1426#if 0
1427 // If the xy distance overrides the z distance, some support needs to be inserted further down.
1428 //=> 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 setting description, it would not produce reasonable results. )
1429 if (! min_xy_dist) {
1430 LineInformations overhang_lines;
1431 {
1432 //Vojtech: Generate support heads at support_tree_branch_distance spacing by producing a zig-zag infill at support_tree_branch_distance spacing,
1433 // which is then resmapled
1434 // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance,
1435 // mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate
1436 // each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that
1437 // the area that is valid a layer below is to small for support roof.
1438 Polylines polylines = ensure_maximum_distance_polyline(
1439 generate_support_infill_lines(remaining_overhang, support_params, false, layer_idx, mesh_group_settings.support_tree_branch_distance),
1440 config.min_radius, 1);
1441 if (polylines.size() <= 3)
1442 // add the outer wall to ensure it is correct supported instead
1443 polylines = ensure_maximum_distance_polyline(to_polylines(remaining_overhang), connect_length, 3);
1444 for (const auto &line : polylines) {
1445 LineInformation res_line;
1446 for (Point p : line)
1447 res_line.emplace_back(p, LineStatus::INVALID);
1448 overhang_lines.emplace_back(res_line);
1449 }
1450 validate_range(overhang_lines);
1451 }
1452 for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) {
1453 // get least restricted avoidance for layer_idx-lag_ctr
1454 const Polygons &relevant_forbidden_below = config.support_rests_on_model ?
1455 volumes.getCollision(config.getRadius(0), layer_idx - lag_ctr, min_xy_dist) :
1456 volumes.getAvoidance(config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, min_xy_dist);
1457 // 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.
1458 auto evaluatePoint = [&](std::pair<Point, LineStatus> p) { return contains(relevant_forbidden_below, p.first); };
1459
1460 std::pair<LineInformations, LineInformations> split = split_lines(overhang_lines, evaluatePoint); // keep all lines that are invalid
1461 overhang_lines = split.first;
1462 // Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again.
1463 LineInformations fresh_valid_points = convert_lines_to_internal(volumes, config, convert_internal_to_lines(split.second), layer_idx - lag_ctr);
1464 validate_range(fresh_valid_points);
1465
1466 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);
1467 }
1468 }
1469#endif
1470 }
1471
1472 throw_on_cancel();
1473
1474 if (roof_enabled) {
1475 // Try to support the overhangs by dense interfaces for num_support_roof_layers, cover the bottom most interface with tree tips.
1476 static constexpr const coord_t support_roof_offset = 0;
1477 Polygons overhang_roofs = safe_offset_inc(overhang_raw, support_roof_offset, relevant_forbidden, config.min_radius * 2 + config.xy_min_distance, 0, 1);
1478 if (mesh_group_settings.minimum_support_area > 0)
1479 remove_small(overhang_roofs, mesh_group_settings.minimum_roof_area);
1480 overhang_regular = diff(overhang_regular, overhang_roofs, ApplySafetyOffset::Yes);
1481 //check_self_intersections(overhang_regular, "overhang_regular3");
1482 for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
1483 sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
1484 mesh_group_settings, rich_interface_placer);
1485 throw_on_cancel();
1486 }
1487 }
1488 // Either the roof is not enabled, then these are all the overhangs to be supported,
1489 // or roof is enabled and these are the thin overhangs at object slopes (not horizontal overhangs).
1490 if (mesh_group_settings.minimum_support_area > 0)
1491 remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
1492
1493 for (ExPolygon &support_part : union_ex(overhang_regular)) {
1494 sample_overhang_area(to_polygons(std::move(support_part)),
1495 false, layer_idx, num_support_roof_layers, connect_length,
1496 mesh_group_settings, rich_interface_placer);
1497 throw_on_cancel();
1498 }
1499 }
1500 });
1501
1502 finalize_raft_contact(print_object, raft_contact_layer_idx, interface_placer.top_contacts_mutable(), move_bounds);
1503}
size_t layer_count() const
Definition Print.hpp:454
const SupportParameters & support_parameters
Definition TreeSupportCommon.hpp:734
SupportGeneratorLayersPtr & top_contacts_mutable()
Definition TreeSupportCommon.hpp:736
Definition TreeSupport3D.cpp:898
void finalize_raft_contact(const PrintObject &print_object, const int raft_contact_layer_idx, SupportGeneratorLayersPtr &top_contacts, std::vector< SupportElements > &move_bounds)
Definition TreeSupport3D.cpp:1085
int generate_raft_contact(const PrintObject &print_object, const TreeSupportSettings &config, InterfacePlacer &interface_placer)
Definition TreeSupport3D.cpp:1064
auto range(Cont &&cont)
Definition libslic3r.h:405
INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor)
Definition Utils.hpp:463
constexpr T sqr(T x)
Definition libslic3r.h:301
T unscale(Q v)
Definition libslic3r.h:138
bool zero_gap_interface_top
Definition SupportParameters.hpp:225
Definition TreeSupportCommon.hpp:54
coord_t support_line_width
Width of a single line of support.
Definition TreeSupportCommon.hpp:380
size_t z_distance_top_layers
Amount of layers distance required the top of the support to the model.
Definition TreeSupportCommon.hpp:457
coord_t min_radius
smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
Definition TreeSupportCommon.hpp:392
std::vector< coordf_t > raft_layers
Definition TreeSupportCommon.hpp:518

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

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_raft_contact()

int Slic3r::TreeSupport3D::generate_raft_contact ( const PrintObject & print_object,
const TreeSupportSettings & config,
InterfacePlacer & interface_placer )
1068{
1069 int raft_contact_layer_idx = -1;
1070 if (print_object.has_raft() && print_object.layer_count() > 0) {
1071 // Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer.
1072 // Raft layers supporting raft contact interface will be produced by the classic raft generator.
1073 // Find the raft contact layer.
1074 raft_contact_layer_idx = int(config.raft_layers.size()) - 1;
1075 while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
1076 -- raft_contact_layer_idx;
1077 // Create the raft contact layer.
1078 const ExPolygons &lslices = print_object.get_layer(0)->lslices;
1079 double expansion = print_object.config().raft_expansion.value;
1080 interface_placer.add_roof_unguarded(expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices), raft_contact_layer_idx, 0);
1081 }
1082 return raft_contact_layer_idx;
1083}
ExPolygons lslices
Definition Layer.hpp:170
static bool has_raft(const PrintObjectConfig &config)
Definition Print.hpp:507
const Layer * get_layer(int idx) const
Definition Print.hpp:456
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
Definition TreeSupportCommon.hpp:763
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20
Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:397
coordf_t raft_contact_top_z
Definition Slicing.hpp:105

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

Referenced by generate_initial_areas(), generate_raft_contact(), and generate_support_areas().

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

◆ generate_support_areas()

void Slic3r::TreeSupport3D::generate_support_areas ( Print & print,
TreeSupport * tree_support,
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.
3450{
3451 // Settings with the indexes of meshes that use these settings.
3452 std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> grouped_meshes = group_meshes(print, print_object_ids);
3453 if (grouped_meshes.empty())
3454 return;
3455
3456 size_t counter = 0;
3457
3458 // 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.
3459 for (std::pair<TreeSupportSettings, std::vector<size_t>> &processing : grouped_meshes)
3460 {
3461 // process each combination of meshes
3462 // 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.
3463 //FIXME this is a copy
3464 // Contains config settings to avoid loading them in every function. This was done to improve readability of the code.
3465 const TreeSupportSettings &config = processing.first;
3466 BOOST_LOG_TRIVIAL(info) << "Processing support tree mesh group " << counter + 1 << " of " << grouped_meshes.size() << " containing " << grouped_meshes[counter].second.size() << " meshes.";
3467 auto t_start = std::chrono::high_resolution_clock::now();
3468#if 0
3469 std::vector<Polygons> exclude(num_support_layers);
3470 // get all already existing support areas and exclude them
3471 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_support_layers),
3472 [&](const tbb::blocked_range<size_t> &range) {
3473 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
3474 Polygons exlude_at_layer;
3475 append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_bottom);
3476 append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_roof);
3477 for (auto part : storage.support.supportLayers[layer_idx].support_infill_parts)
3478 append(exlude_at_layer, part.outline);
3479 exclude[layer_idx] = union_(exlude_at_layer);
3480 }
3481 });
3482#endif
3483#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3484 m_progress_multiplier = 1.0 / double(grouped_meshes.size());
3485 m_progress_offset = counter == 0 ? 0 : TREE_PROGRESS_TOTAL * (double(counter) * m_progress_multiplier);
3486#endif // SLIC3R_TREESUPPORT_PROGRESS
3487 PrintObject &print_object = *print.get_object(processing.second.front());
3488 const size_t num_raft_layers_for_exclusions = config.raft_layers.size();
3489 std::vector<std::pair<coordf_t, coordf_t>> tree_layer_z_ranges;
3490 tree_layer_z_ranges.reserve(num_raft_layers_for_exclusions + print_object.layer_count());
3491 coordf_t previous_z = 0.;
3492 for (const coordf_t print_z : config.raft_layers) {
3493 tree_layer_z_ranges.emplace_back(previous_z, print_z);
3494 previous_z = print_z;
3495 }
3496 for (const Layer *layer : print_object.layers()) {
3497 tree_layer_z_ranges.emplace_back(layer->print_z - layer->height, layer->print_z);
3498 }
3499 // A tree is one continuous structure while "current filament" may
3500 // alternate between base and interface nozzles from layer to layer.
3501 // Planning against both masks prevents a locally valid layer from
3502 // forcing a branch through another possible nozzle's exclusion volume.
3503 std::vector<Polygons> exclusion_areas =
3504 support_exclusion_areas_for_layers(print_object, tree_layer_z_ranges, false);
3505 const bool tree_uses_interface_material = !config.raft_layers.empty() ||
3506 print_object.config().support_interface_top_layers.value > 0 ||
3507 number_of_support_interface_bottom_layers(print_object.config()) > 0;
3508 if (tree_uses_interface_material) {
3509 const std::vector<Polygons> interface_exclusion_areas =
3510 support_exclusion_areas_for_layers(print_object, tree_layer_z_ranges, true);
3511 for (size_t layer_idx = 0; layer_idx < exclusion_areas.size(); ++layer_idx) {
3512 append(exclusion_areas[layer_idx], interface_exclusion_areas[layer_idx]);
3513 exclusion_areas[layer_idx] = union_(exclusion_areas[layer_idx]);
3514 }
3515 }
3516
3517 // Generator for model collision, avoidance and internal guide volumes.
3518 TreeModelVolumes volumes{ print_object, build_volume, config.maximum_move_distance, config.maximum_move_distance_slow, processing.second.front(),
3519#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3520 m_progress_multiplier, m_progress_offset,
3521#endif // SLIC3R_TREESUPPORTS_PROGRESS
3522 exclusion_areas };
3523
3524 //FIXME generating overhangs just for the first mesh of the group.
3525 assert(processing.second.size() == 1);
3526
3527#if 1
3528 // use smart overhang detection
3529 std::vector<Polygons> overhangs;
3530 tree_support->detect_overhangs();
3531 const int num_raft_layers = int(config.raft_layers.size());
3532 const int num_layers = int(print_object.layer_count()) + num_raft_layers;
3533 overhangs.resize(num_layers);
3534 for (size_t i = 0; i < print_object.layer_count(); i++) {
3535 for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) {
3536 Polygons polys = to_polygons(expoly);
3537 if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { polys = offset(polys, scale_(0.2));
3538 }
3539 append(overhangs[i + num_raft_layers], polys);
3540 }
3541 }
3542 // add vertical enforcer points
3543 std::vector<float> zs = zs_from_layers(print_object.layers());
3545 for (auto &pt_and_normal :tree_support->m_vertical_enforcer_points) {
3546 auto pt = pt_and_normal.first;
3547 auto normal = pt_and_normal.second; // normal seems useless
3548 auto iter = std::lower_bound(zs.begin(), zs.end(), pt.z());
3549 if (iter != zs.end()) {
3550 size_t layer_nr = iter - zs.begin();
3551 if (layer_nr > 0 && layer_nr < print_object.layer_count()) {
3552 Polygon circle = base_circle;
3553 circle.translate(to_2d(pt).cast<coord_t>());
3554 overhangs[layer_nr + num_raft_layers].emplace_back(std::move(circle));
3555 }
3556 }
3557 }
3558#else
3559 std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
3560#endif
3561 // ### Precalculate avoidances, collision etc.
3562 size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
3563 bool has_support = num_support_layers > 0;
3564 bool has_raft = config.raft_layers.size() > 0;
3565 num_support_layers = std::max(num_support_layers, config.raft_layers.size());
3566
3567 if (num_support_layers == 0)
3568 continue;
3569
3570 SupportParameters support_params(print_object);
3571 support_params.with_sheath = true;
3572// Don't override the support density of tree supports, as the support density is used for raft.
3573// The trees will have the density zeroed in tree_supports_generate_paths()
3574// support_params.support_density = 0;
3575
3576
3577 SupportGeneratorLayerStorage layer_storage;
3578 SupportGeneratorLayersPtr top_contacts;
3579 SupportGeneratorLayersPtr bottom_contacts;
3580 SupportGeneratorLayersPtr interface_layers;
3581 SupportGeneratorLayersPtr base_interface_layers;
3582 SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr);
3583 if (support_params.has_top_contacts || has_raft)
3584 top_contacts.assign(num_support_layers, nullptr);
3585 if (support_params.has_bottom_contacts)
3586 bottom_contacts.assign(num_support_layers, nullptr);
3587 if (support_params.has_interfaces() || has_raft)
3588 interface_layers.assign(num_support_layers, nullptr);
3589 if (support_params.has_base_interfaces() || has_raft)
3590 base_interface_layers.assign(num_support_layers, nullptr);
3591
3592 auto remove_undefined_layers = [&bottom_contacts, &top_contacts, &interface_layers, &base_interface_layers, &intermediate_layers]() {
3593 auto doit = [](SupportGeneratorLayersPtr& layers) {
3594 layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
3595 };
3596 doit(bottom_contacts);
3597 doit(top_contacts);
3598 doit(interface_layers);
3599 doit(base_interface_layers);
3600 doit(intermediate_layers);
3601 };
3602
3603 InterfacePlacer interface_placer{
3604 print_object.slicing_parameters(), support_params, config,
3605 // Outputs
3606 layer_storage, top_contacts, interface_layers, base_interface_layers };
3607
3608 if (has_support) {
3609 auto t_precalc = std::chrono::high_resolution_clock::now();
3610
3611 // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas
3612 std::vector<SupportElements> move_bounds(num_support_layers);
3613
3614 // ### Place tips of the support tree
3615 for (size_t mesh_idx : processing.second)
3616 generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs,
3617 move_bounds, interface_placer, throw_on_cancel);
3618 auto t_gen = std::chrono::high_resolution_clock::now();
3619
3620
3621 // ### Propagate the influence areas downwards. This is an inherently serial operation.
3622 print.set_status(60, _L("Generating support"));
3623 create_layer_pathing(volumes, config, move_bounds, throw_on_cancel);
3624 auto t_path = std::chrono::high_resolution_clock::now();
3625
3626 // ### Set a point in each influence area
3627 create_nodes_from_area(volumes, config, move_bounds, throw_on_cancel);
3628 auto t_place = std::chrono::high_resolution_clock::now();
3629
3630 // ### draw these points as circles
3631 // this new function give correct result when raft is also enabled
3633 *print.get_object(processing.second.front()), volumes, config, move_bounds,
3634 bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
3635 throw_on_cancel);
3636
3637 //tree_support->move_bounds_to_contact_nodes(move_bounds, print_object, config);
3638
3639 remove_undefined_layers();
3640
3641 std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params,
3642 bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
3643
3644 auto t_draw = std::chrono::high_resolution_clock::now();
3645 auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
3646 auto dur_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_gen - t_precalc).count();
3647 auto dur_path = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_path - t_gen).count();
3648 auto dur_place = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_place - t_path).count();
3649 auto dur_draw = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_draw - t_place).count();
3650 auto dur_total = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_draw - t_start).count();
3651 BOOST_LOG_TRIVIAL(info) <<
3652 "Total time used creating Tree support for the currently grouped meshes: " << dur_total << " ms. "
3653 "Different subtasks:\nCalculating Avoidance: " << dur_pre_gen << " ms "
3654 "Creating inital influence areas: " << dur_gen << " ms "
3655 "Influence area creation: " << dur_path << "ms "
3656 "Placement of Points in InfluenceAreas: " << dur_place << "ms "
3657 "Drawing result as support " << dur_draw << " ms";
3658 // if (config.branch_radius==2121)
3659 // 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.)";
3660
3661 move_bounds.clear();
3662 } else if (generate_raft_contact(print_object, config, interface_placer) >= 0) {
3663 remove_undefined_layers();
3664 } else
3665 // No raft.
3666 continue;
3667
3668 // Produce the support G-code.
3669 SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
3671 print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers,
3672 interface_layers, base_interface_layers);
3673 SupportGeneratorLayersPtr layers_sorted = generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
3674
3675 // BBS: This is a hack to avoid the support being generated outside the bed area. See #4769.
3676 tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) {
3677 if (layer) layer->polygons = intersection(layer->polygons, volumes.m_bed_area);
3678 });
3679
3680 // Belt floor: clip ALL organic support layers (including intermediate/base
3681 // fill) against the belt surface. The branch slices were already clipped
3682 // in organic_draw_branches(), but intermediate layers generated between
3683 // branches and the build plate need clipping too.
3684 // Compute the belt floor polygon directly from each layer's print_z
3685 // rather than mapping to a layer index (avoids index mismatch issues).
3686 {
3687 const auto &sp = print_object.slicing_parameters();
3688 const auto &pcfg = print_object.print()->config();
3689 BeltFloorContext ctx;
3690 ctx.init_local(sp, pcfg, print_object.belt_global_z_offset());
3691 if (ctx.is_active()) {
3692 tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) {
3693 if (!layer || layer->polygons.empty())
3694 return;
3695 layer->polygons = diff(layer->polygons, ctx.surface_polygon(layer->print_z));
3696 });
3697 }
3698 }
3699
3700 print.set_status(69, _L("Generating support"));
3701 generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
3702 raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
3703
3704 auto t_end = std::chrono::high_resolution_clock::now();
3705 BOOST_LOG_TRIVIAL(info) << "Total time of organic tree support: " << 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_start).count() << " ms";
3706 #if 0
3707//#ifdef SLIC3R_DEBUG
3708 {
3709 static int iRun = 0;
3710 ++ iRun;
3711 size_t layer_id = 0;
3712 for (int i = 0; i < int(layers_sorted.size());) {
3713 // Find the last layer with roughly the same print_z, find the minimum layer height of all.
3714 // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
3715 int j = i + 1;
3716 coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
3717 bool empty = layers_sorted[i]->polygons.empty();
3718 for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j)
3719 if (!layers_sorted[j]->polygons.empty())
3720 empty = false;
3721 if (!empty) {
3722 export_print_z_polygons_to_svg(
3723 debug_out_path("support-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
3724 layers_sorted.data() + i, j - i);
3725 export_print_z_polygons_and_extrusions_to_svg(
3726 debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
3727 layers_sorted.data() + i, j - i,
3728 *print_object.support_layers()[layer_id]);
3729 ++layer_id;
3730 }
3731 i = j;
3732 }
3733 }
3734#endif /* SLIC3R_DEBUG */
3735
3736 ++ counter;
3737 }
3738
3739// storage.support.generated = true;
3740}
#define _L(s)
Definition OBJ.cpp:34
void translate(double x, double y)
Definition MultiPoint.hpp:38
void set_status(int percent, const std::string &message, unsigned int flags=SlicingStatus::DEFAULT, int warning_step=-1) const
Definition PrintBase.cpp:123
PrintObject * get_object(size_t idx)
Definition Print.hpp:1135
Definition TreeSupportCommon.hpp:714
Definition TreeModelVolumes.hpp:46
@ SharpTail
Definition TreeSupport.hpp:425
void detect_overhangs(bool check_support_necessity=false)
Definition TreeSupport.cpp:739
std::vector< std::pair< Vec3f, Vec3f > > m_vertical_enforcer_points
Definition TreeSupport.hpp:427
std::map< const ExPolygon *, OverhangType > overhang_types
Definition TreeSupport.hpp:426
double coordf_t
Definition libslic3r.h:51
static std::vector< std::pair< TreeSupportSettings, std::vector< size_t > > > group_meshes(const Print &print, const std::vector< size_t > &print_object_ids)
Definition TreeSupport3D.cpp:159
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 TreeSupport3D.cpp:3769
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 TreeSupport3D.cpp:1293
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 TreeSupport3D.cpp:379
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 TreeSupport3D.cpp:2733
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 TreeSupport3D.cpp:2490
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
double coordf_t
Definition GUI_ObjectList.hpp:46
std::vector< float > zs_from_layers(const LayerContainer &layers)
Definition Layer.hpp:357
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:194
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > to_2d(const Eigen::MatrixBase< Derived > &ptN)
Definition Point.hpp:147
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:404
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:1549
std::vector< SupportGeneratorLayer * > SupportGeneratorLayersPtr
Definition SupportLayer.hpp:146
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:1467
int number_of_support_interface_bottom_layers(const PrintObjectConfig &object_config)
Definition SupportParameters.hpp:21
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:157
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION
Definition TreeSupportCommon.hpp:40
void trim_support_layers_by_exclusion_volumes(const 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:141
std::vector< Polygons > support_exclusion_areas_for_layers(const PrintObject &object, const std::vector< std::pair< coordf_t, coordf_t > > &layer_z_ranges, const bool interface_material)
Definition SupportCommon.cpp:104
This struct contains settings used in the tree support. Thanks to this most functions do not need to ...
Definition TreeSupportCommon.hpp:282

References _L, Slic3r::append(), Slic3r::cast(), create_layer_pathing(), create_nodes_from_area(), Slic3r::debug_out_path(), Slic3r::TreeSupport::detect_overhangs(), Slic3r::empty(), EPSILON, generate_initial_areas(), Slic3r::generate_interface_layers(), Slic3r::generate_raft_base(), generate_raft_contact(), generate_support_areas(), Slic3r::generate_support_layers(), Slic3r::generate_support_toolpaths(), Slic3r::Print::get_object(), group_meshes(), Slic3r::SupportParameters::has_base_interfaces(), Slic3r::SupportParameters::has_bottom_contacts, Slic3r::SupportParameters::has_interfaces(), Slic3r::SupportParameters::has_top_contacts, Slic3r::BeltFloorContext::init_local(), Slic3r::BeltFloorContext::is_active(), Slic3r::TreeSupport::m_vertical_enforcer_points, Slic3r::make_circle(), Slic3r::TreeSupport3D::TreeSupportSettings::maximum_move_distance, Slic3r::TreeSupport3D::TreeSupportSettings::maximum_move_distance_slow, Slic3r::normal(), Slic3r::number_of_support_interface_bottom_layers(), Slic3r::offset(), organic_draw_branches(), Slic3r::TreeSupport::overhang_types, precalculate(), Slic3r::TreeSupport3D::TreeSupportSettings::raft_layers, Slic3r::range(), scale_, Slic3r::PrintBase::set_status(), Slic3r::TreeSupport::SharpTail, Slic3r::support_exclusion_areas_for_layers(), Slic3r::SUPPORT_TREE_CIRCLE_RESOLUTION, Slic3r::to_2d(), Slic3r::to_polygons(), Slic3r::MultiPoint::translate(), Slic3r::trim_support_layers_by_exclusion_volumes(), Slic3r::union_(), Slic3r::SupportParameters::with_sheath, and Slic3r::zs_from_layers().

Referenced by generate_support_areas(), and Slic3r::generate_tree_support_3D().

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

◆ generate_support_infill_lines()

Polylines Slic3r::TreeSupport3D::generate_support_infill_lines ( const Polygons & polygon,
const SupportParameters & support_params,
bool roof,
LayerIndex layer_idx,
coord_t support_infill_distance )
staticnodiscard

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.
708{
709#if 0
710 Polygons gaps;
711 // 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
712
713 const EFillMethod pattern = roof ? config.roof_pattern : config.support_pattern;
714
715// const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support;
716 const bool connect_polygons = false;
717 constexpr coord_t support_roof_overlap = 0;
718 constexpr size_t infill_multiplier = 1;
719 constexpr coord_t outline_offset = 0;
720 const int support_shift = roof ? 0 : support_infill_distance / 2;
721 const size_t wall_line_count = include_walls && !roof ? config.support_wall_count : 0;
722 const Point infill_origin;
723 constexpr Polygons* perimeter_gaps = nullptr;
724 constexpr bool use_endpieces = true;
725 const bool connected_zigzags = roof ? false : config.connect_zigzags;
726 const size_t zag_skip_count = roof ? 0 : config.zag_skip_count;
727 constexpr coord_t pocket_size = 0;
728 std::vector<AngleRadians> angles = roof ? config.support_roof_angles : config.support_infill_angles;
729 std::vector<VariableWidthLines> toolpaths;
730
731 const coord_t z = config.getActualZ(layer_idx);
732 int divisor = static_cast<int>(angles.size());
733 int index = ((layer_idx % divisor) + divisor) % divisor;
734 const AngleRadians fill_angle = angles[index];
735 Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon,
736 roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier,
737 fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin,
738 perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size);
739 Polygons polygons;
740 Polygons lines;
741 roof_computation.generate(toolpaths, polygons, lines, config.settings);
742 append(lines, to_polylines(polygons));
743 return lines;
744#else
745// const bool connected_zigzags = roof ? false : config.connect_zigzags;
746// const int support_shift = roof ? 0 : support_infill_distance / 2;
747
748 const Flow &flow = roof ? support_params.support_material_interface_flow : support_params.support_material_flow;
749 std::unique_ptr<Fill> filler = std::unique_ptr<Fill>(Fill::new_from_type(roof ? support_params.interface_fill_pattern : support_params.base_fill_pattern));
750 FillParams fill_params;
751
752 filler->layer_id = layer_idx;
753 filler->spacing = flow.spacing();
754 filler->angle = roof ?
755 //fixme support_layer.interface_id() instead of layer_idx
756 (support_params.interface_angle + ((layer_idx & 1) ? float(- M_PI_4) : float(+ M_PI_4))) :
757 support_params.base_angle;
758
759 // ORCA: use top-specific interface density after separating top/bottom settings.
760 fill_params.density = float(roof ? support_params.top_interface_density : scaled<float>(filler->spacing) / (scaled<float>(filler->spacing) + float(support_infill_distance)));
761 fill_params.dont_adjust = true;
762
763 Polylines out;
764 for (ExPolygon &expoly : union_ex(polygon)) {
765 // The surface type does not matter.
766 assert(area(expoly) > 0.);
767#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
768 if (area(expoly) <= 0.)
769 ::MessageBoxA(nullptr, "TreeSupport infill negative area", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
770#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
771 assert(intersecting_edges(to_polygons(expoly)).empty());
772 check_self_intersections(expoly, "generate_support_infill_lines");
773 Surface surface(stInternal, std::move(expoly));
774 try {
775 Polylines pl = filler->fill_surface(&surface, fill_params);
776 assert(pl.empty() || get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl)));
777#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
778 if (! pl.empty() && ! get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl)))
779 ::MessageBoxA(nullptr, "TreeSupport infill failure", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
780#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
781 append(out, std::move(pl));
782 } catch (InfillFailedException &) {
783 }
784 }
785 validate_range(out);
786 return out;
787#endif
788}
Definition ExPolygon.hpp:23
static Fill * new_from_type(const InfillPattern type)
Definition FillBase.cpp:65
Definition Flow.hpp:55
float spacing() const
Definition Flow.hpp:69
Definition FillBase.hpp:45
Definition Surface.hpp:43
Polylines to_polylines(const ExPolygon &src)
Definition ExPolygon.hpp:235
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
Definition ClipperUtils.cpp:1011
@ stInternal
Definition Surface.hpp:26
Definition FillBase.hpp:60
bool dont_adjust
Definition FillBase.hpp:78
float density
Definition FillBase.hpp:66
Flow support_material_flow
Definition SupportParameters.hpp:251
float interface_angle
Definition SupportParameters.hpp:269
Flow support_material_interface_flow
Definition SupportParameters.hpp:253
coordf_t top_interface_density
Definition SupportParameters.hpp:274
InfillPattern base_fill_pattern
Definition SupportParameters.hpp:286
InfillPattern interface_fill_pattern
Definition SupportParameters.hpp:288

References Slic3r::append(), Slic3r::area(), Slic3r::SupportParameters::base_angle, Slic3r::SupportParameters::base_fill_pattern, check_self_intersections(), Slic3r::FillParams::density, Slic3r::FillParams::dont_adjust, Slic3r::empty(), Slic3r::Surface::expolygon, generate_support_infill_lines(), Slic3r::get_extents(), Slic3r::SupportParameters::interface_angle, Slic3r::SupportParameters::interface_fill_pattern, Slic3r::intersecting_edges(), Slic3r::Fill::new_from_type(), Slic3r::TreeSupport3D::TreeSupportSettings::resolution, Slic3r::TreeSupport3D::TreeSupportSettings::roof_pattern, Slic3r::scaled(), SCALED_EPSILON, Slic3r::TreeSupport3D::TreeSupportSettings::settings, Slic3r::Flow::spacing(), Slic3r::stInternal, Slic3r::TreeSupport3D::TreeSupportSettings::support_line_width, Slic3r::SupportParameters::support_material_flow, Slic3r::SupportParameters::support_material_interface_flow, Slic3r::TreeSupport3D::TreeSupportSettings::support_pattern, Slic3r::TreeSupport3D::TreeSupportSettings::support_roof_angles, Slic3r::TreeSupport3D::TreeSupportSettings::support_roof_line_width, Slic3r::TreeSupport3D::TreeSupportSettings::support_wall_count, Slic3r::to_polygons(), Slic3r::to_polylines(), Slic3r::SupportParameters::top_interface_density, Slic3r::union_ex(), and validate_range().

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

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

◆ get_avoidance_status()

LineStatus Slic3r::TreeSupport3D::get_avoidance_status ( const Point & p,
coord_t radius,
LayerIndex layer_idx,
const TreeModelVolumes & volumes,
const TreeSupportSettings & config )
nodiscard
402{
403 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
404
406
407 if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p))
409 else if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p))
410 type = LineStatus::TO_BP;
411 else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p))
413 else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p))
415 else if (config.support_rests_on_model && !contains(volumes.getCollision(radius, layer_idx, min_xy_dist), p))
417
418 return type;
419}
LineStatus
Definition TreeSupportCommon.hpp:796
@ INVALID
Definition TreeSupportCommon.hpp:797

References Slic3r::Geometry::contains(), Slic3r::TreeSupport3D::TreeModelVolumes::Fast, Slic3r::TreeSupport3D::TreeModelVolumes::FastSafe, get_avoidance_status(), Slic3r::TreeSupport3D::TreeModelVolumes::getAvoidance(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollision(), INVALID, Slic3r::TreeSupport3D::TreeSupportSettings::support_rests_on_model, TO_BP, TO_BP_SAFE, TO_MODEL, TO_MODEL_GRACIOUS, TO_MODEL_GRACIOUS_SAFE, Slic3r::TreeSupport3D::TreeSupportSettings::xy_distance, and Slic3r::TreeSupport3D::TreeSupportSettings::xy_min_distance.

Referenced by get_avoidance_status().

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

◆ getEffectiveDTT()

size_t Slic3r::TreeSupport3D::getEffectiveDTT ( const TreeSupportSettings & settings,
const SupportElementState & elem )
inlinenodiscard

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.
252{
255 elem.effective_radius_height;
256}
uint32_t effective_radius_height
The Effective distance to top of this element regarding radius increases and collision calculations.
Definition TreeSupport3D.hpp:176
uint32_t distance_to_top
The amount of layers this element is below the topmost layer of this branch.
Definition TreeSupport3D.hpp:181
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:428

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

Referenced by support_element_radius().

Here is the caller graph for this function:

◆ group_meshes()

std::vector< std::pair< TreeSupportSettings, std::vector< size_t > > > Slic3r::TreeSupport3D::group_meshes ( const Print & print,
const std::vector< size_t > & print_object_ids )
static
160{
161 std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> grouped_meshes;
162
163 // Orca: Recompute static mesh-group state for this support generation pass.
165
166 //FIXME this is ugly, it does not belong here.
167 for (size_t object_id : print_object_ids) {
168 const PrintObject &print_object = *print.get_object(object_id);
169 const PrintObjectConfig &object_config = print_object.config();
170 if (object_config.support_top_z_distance < EPSILON)
171 // || min_feature_size < scaled<coord_t>(0.1) that is the minimum line width
173 }
174
175 size_t largest_printed_mesh_idx = 0;
176
177 // Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group,
178 // as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes.
179 for (size_t object_id : print_object_ids) {
180 const PrintObject &print_object = *print.get_object(object_id);
181
182 bool found_existing_group = false;
183 TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object }, print_object.slicing_parameters() };
184 //FIXME for now only a single object per group is enabled.
185#if 0
186 for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx)
187 if (next_settings == grouped_meshes[idx].first) {
188 found_existing_group = true;
189 grouped_meshes[idx].second.emplace_back(object_id);
190 // 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.
191 grouped_meshes[idx].first.performance_interface_skip_layers = std::min(grouped_meshes[idx].first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers);
192 }
193#endif
194 if (! found_existing_group)
195 grouped_meshes.emplace_back(next_settings, std::vector<size_t>{ object_id });
196
197 // no need to do this per mesh group as adaptive layers and raft setting are not setable per mesh.
198 if (print.get_object(largest_printed_mesh_idx)->layers().back()->print_z < print_object.layers().back()->print_z)
199 largest_printed_mesh_idx = object_id;
200 }
201
202#if 0
203 {
204 std::vector<coord_t> known_z(storage.meshes[largest_printed_mesh_idx].layers.size());
205 for (size_t z = 0; z < storage.meshes[largest_printed_mesh_idx].layers.size(); z++)
206 known_z[z] = storage.meshes[largest_printed_mesh_idx].layers[z].printZ;
207 for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx)
208 grouped_meshes[idx].first.setActualZ(known_z);
209 }
210#endif
211
212 return grouped_meshes;
213}
const T * back() const
Definition Print.hpp:209
coordf_t print_z
Definition Layer.hpp:150
Definition Print.hpp:365
ConstLayerPtrsAdaptor layers() const
Definition Print.hpp:374
zaa_minimize_perimeter_height PrintObjectConfig
Definition PrintConfig.hpp:2183
static bool zero_top_z_gap
Definition TreeSupportCommon.hpp:376

References Slic3r::ConstVectorOfPtrsAdaptor< T >::back(), Slic3r::PrintObject::config(), EPSILON, Slic3r::Print::get_object(), Slic3r::PrintObject::layers(), Slic3r::Layer::print_z, Slic3r::PrintObjectConfig, Slic3r::PrintObject::slicing_parameters(), and Slic3r::TreeSupport3D::TreeSupportSettings::zero_top_z_gap.

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()

void Slic3r::TreeSupport3D::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.
1840{
1841 using AvoidanceType = TreeModelVolumes::AvoidanceType;
1842
1843 tbb::parallel_for(tbb::blocked_range<size_t>(0, merging_areas.size(), 1),
1844 [&](const tbb::blocked_range<size_t> &range) {
1845 for (size_t merging_area_idx = range.begin(); merging_area_idx < range.end(); ++ merging_area_idx) {
1846 SupportElementMerging &merging_area = merging_areas[merging_area_idx];
1847 assert(merging_area.parents.size() == 1);
1848 SupportElement &parent = layer_elements[merging_area.parents.front()];
1849 SupportElementState elem = SupportElementState::propagate_down(parent.state);
1850 const Polygons &wall_restriction =
1851 // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall.
1852 volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist);
1853
1854
1855 Polygons to_bp_data, to_model_data;
1856 coord_t radius = support_element_collision_radius(config, elem);
1857
1858 // 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).
1859 // 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.
1860 // As the branch may have become larger the distance between these 2 walls is smaller than the distance of the center points.
1861 // These extra distance is added to the movement distance possible for this layer.
1862
1863 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.
1864 coord_t extra_slow_speed = 0; // Only added to the slow movement distance.
1865 const coord_t ceiled_parent_radius = volumes.ceilRadius(support_element_collision_radius(config, parent.state), parent.state.use_min_xy_dist);
1866 coord_t projected_radius_increased = config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases);
1867 coord_t projected_radius_delta = projected_radius_increased - support_element_collision_radius(config, parent.state);
1868
1869 // 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):
1870 /*
1871 * layer z+1:dddddiiiiiioooo
1872 * layer z+0:xxxxxdddddddddd
1873 * layer z-1:dddddxxxxxxxxxx
1874 * For more detailed visualisation see calculateWallRestrictions
1875 */
1876 coord_t safe_movement_distance =
1877 (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
1878 (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
1879
1880 // safe_movement_distance is used as a divisor and as the safe_offset_inc() step,
1881 // so keep it non-zero to avoid division by zero and preserve branch movement.
1882 if (safe_movement_distance == 0)
1883 safe_movement_distance = scaled<coord_t>(0.1);
1884
1885 if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) ||
1886 projected_radius_increased < config.increase_radius_until_radius)
1887 // 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
1888 extra_speed += projected_radius_delta;
1889 else
1890 // if a guaranteed radius increase is not possible, only increase the slow speed
1891 // Ensure that the slow movement distance can not become larger than the fast one.
1892 extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed));
1893
1894 if (config.layer_start_bp_radius > layer_idx &&
1895 config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) {
1896 // can guarantee elephant foot radius increase
1897 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))
1898 extra_speed += config.bp_radius_increase_per_layer;
1899 else
1900 extra_slow_speed += std::min(coord_t(config.bp_radius_increase_per_layer),
1901 config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed));
1902 }
1903
1904 const coord_t fast_speed = config.maximum_move_distance + extra_speed;
1905 const coord_t slow_speed = config.maximum_move_distance_slow + extra_speed + extra_slow_speed;
1906
1907 Polygons offset_slow, offset_fast;
1908
1909 bool add = false;
1910 bool bypass_merge = false;
1911 constexpr bool increase_radius = true, no_error = true, use_min_radius = true, move = true; // aliases for better readability
1912
1913 // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found
1914 std::vector<AreaIncreaseSettings> order;
1915 auto insertSetting = [&](AreaIncreaseSettings settings, bool back) {
1916 if (std::find(order.begin(), order.end(), settings) == order.end()) {
1917 if (back)
1918 order.emplace_back(settings);
1919 else
1920 order.insert(order.begin(), settings);
1921 }
1922 };
1923
1924 const bool parent_moved_slow = elem.last_area_increase.increase_speed < config.maximum_move_distance;
1925 const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::Slow;
1926 if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer &&
1927 !avoidance_speed_mismatch && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) {
1928 // assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster.
1929 insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
1930 increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
1931 insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
1932 !increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
1933 }
1934 // 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.
1935 if (!elem.can_use_safe_radius) {
1936 // 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.
1937 // This does not guarantee the avoidance of such holes, but ensures they are avoided if possible.
1938 // order.emplace_back(AvoidanceType::Slow,!increase_radius,no_error,!use_min_radius,move);
1939 insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we go through the hole
1940 // 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.
1941 // This CAN cause a branch to go though a hole it otherwise may have avoided.
1942 if (elem.distance_to_top < round_up_divide(config.tip_layers, size_t(2)))
1943 insertSetting({ AvoidanceType::Fast, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true);
1944 insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we manage to avoid the hole
1945 insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1946 insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1947 } else {
1948 insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
1949 // 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,
1950 // which looks similar to a layer shift and can reduce stability.
1951 // as such idx have chosen to only use the user setting for radius increases as a friendly recommendation.
1952 insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a
1953 if (elem.distance_to_top < config.tip_layers)
1954 insertSetting({ AvoidanceType::FastSafe, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
1955 insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, move }, true); // b
1956 insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1957 }
1958
1959 if (elem.use_min_xy_dist) {
1960 std::vector<AreaIncreaseSettings> new_order;
1961 // if the branch currently has to use min_xy_dist check if the configuration would also be valid
1962 // with the regular xy_distance before checking with use_min_radius (Only happens when Support Distance priority is z overrides xy )
1963 for (AreaIncreaseSettings settings : order) {
1964 new_order.emplace_back(settings);
1965 new_order.push_back({ settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, settings.move });
1966 }
1967 order = new_order;
1968 }
1969 if (elem.to_buildplate || (elem.to_model_gracious && intersection(parent.influence_area, volumes.getPlaceableAreas(radius, layer_idx, throw_on_cancel)).empty())) {
1970 // error case
1971 // 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
1972 insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true);
1973 }
1974 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.
1975 // Only do not move when already in a no hole avoidance with the regular xy distance.
1976 insertSetting({ AvoidanceType::Slow, 0, increase_radius, no_error, !use_min_radius, !move }, false);
1977
1978 Polygons inc_wo_collision;
1979 // 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.
1980 // Calculated by comparing the steps saved when calcualting idependently with the saved steps when not.
1981 bool offset_independant_faster = radius / safe_movement_distance - int(config.maximum_move_distance + extra_speed < radius + safe_movement_distance) >
1982 round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance);
1983 for (const AreaIncreaseSettings &settings : order) {
1984 if (settings.move) {
1985 if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independant_faster)) {
1986 // 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
1987 // was never made for precision in the single digit micron range.
1988 offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow,
1989 wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2);
1990 }
1991 if (offset_fast.empty() && settings.increase_speed != slow_speed) {
1992 if (offset_independant_faster)
1993 offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance,
1994 wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 1);
1995 else {
1996 const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed);
1997 offset_fast = safe_offset_inc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independant_faster ? 2 : 1);
1998 }
1999 }
2000 }
2001 std::optional<SupportElementState> result;
2002 inc_wo_collision.clear();
2003 if (!settings.no_error) {
2004 // ERROR CASE
2005 // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if it would be a line wrongly, it still actually has an area that can be increased
2006 Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled<float>(0.005), jtMiter, 1.2);
2007 Polygons base_error_area = union_(parent.influence_area, lines_offset);
2008 result = increase_single_area(volumes, config, settings, layer_idx, parent,
2009 base_error_area, to_bp_data, to_model_data, inc_wo_collision, (config.maximum_move_distance + extra_speed) * 1.5, mergelayer);
2010#ifdef TREE_SUPPORT_SHOW_ERRORS
2011 BOOST_LOG_TRIVIAL(error)
2012#else // TREE_SUPPORT_SHOW_ERRORS
2013 BOOST_LOG_TRIVIAL(warning)
2014#endif // TREE_SUPPORT_SHOW_ERRORS
2015 << "Influence area could not be increased! Data about the Influence area: "
2016 "Radius: " << radius << " at layer: " << layer_idx - 1 << " NextTarget: " << elem.layer_idx << " Distance to top: " << elem.distance_to_top <<
2017 " Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate <<
2018 " gracious " << elem.to_model_gracious << " safe " << elem.can_use_safe_radius << " until move " << elem.dont_move_until << " \n "
2019 "Parent " << &parent << ": Radius: " << support_element_collision_radius(config, parent.state) << " at layer: " << layer_idx << " NextTarget: " << parent.state.layer_idx <<
2020 " 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 <<
2021 " 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;
2022 tree_supports_show_error("Potentially lost branch!"sv, true);
2023#ifdef TREE_SUPPORTS_TRACK_LOST
2024 if (result)
2025 result->lost = true;
2026#endif // TREE_SUPPORTS_TRACK_LOST
2027 } else
2028 result = increase_single_area(volumes, config, settings, layer_idx, parent,
2029 settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer);
2030
2031 if (result) {
2032 elem = *result;
2033 radius = support_element_collision_radius(config, elem);
2034 elem.last_area_increase = settings;
2035 add = true;
2036 // do not merge if the branch should not move or the priority has to be to get farther away from the model.
2037 bypass_merge = !settings.move || (settings.use_min_distance && elem.distance_to_top < config.tip_layers);
2038 if (settings.move)
2039 elem.dont_move_until = 0;
2040 else
2041 elem.result_on_layer = parent.state.result_on_layer;
2042
2043 elem.can_use_safe_radius = settings.type != AvoidanceType::Fast;
2044
2045 if (!settings.use_min_distance)
2046 elem.use_min_xy_dist = false;
2047 if (!settings.no_error)
2048#ifdef TREE_SUPPORT_SHOW_ERRORS
2049 BOOST_LOG_TRIVIAL(error)
2050#else // TREE_SUPPORT_SHOW_ERRORS
2051 BOOST_LOG_TRIVIAL(info)
2052#endif // TREE_SUPPORT_SHOW_ERRORS
2053 << "Trying to keep area by moving faster than intended: Success";
2054 break;
2055 } else if (!settings.no_error)
2056 BOOST_LOG_TRIVIAL(warning) << "Trying to keep area by moving faster than intended: FAILURE! WRONG BRANCHES LIKLY!";
2057 }
2058
2059 if (add) {
2060 // Union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be.
2061 assert(! inc_wo_collision.empty() || ! to_bp_data.empty() || ! to_model_data.empty());
2062 Polygons max_influence_area = safe_union(
2063 diff_clipped(inc_wo_collision, volumes.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)),
2064 safe_union(to_bp_data, to_model_data));
2065 merging_area.state = elem;
2066 assert(!max_influence_area.empty());
2067 merging_area.set_bbox(get_extents(max_influence_area));
2068 merging_area.areas.influence_areas = std::move(max_influence_area);
2069 if (! bypass_merge) {
2070 if (elem.to_buildplate)
2071 merging_area.areas.to_bp_areas = std::move(to_bp_data);
2072 if (config.support_rests_on_model)
2073 merging_area.areas.to_model_areas = std::move(to_model_data);
2074 }
2075 } else {
2076 // If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it.
2077 // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set.
2078 // A point can be set on the top most tip layer (maybe more if it should not move for a few layers).
2079 parent.state.result_on_layer_reset();
2080 parent.state.to_model_gracious = false;
2081#ifdef TREE_SUPPORTS_TRACK_LOST
2082 parent.state.verylost = true;
2083#endif // TREE_SUPPORTS_TRACK_LOST
2084 }
2085
2086 throw_on_cancel();
2087 }
2088 }, tbb::simple_partitioner());
2089}

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()

std::optional< SupportElementState > Slic3r::TreeSupport3D::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 )
staticnodiscard

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.
1647{
1649 Polygons check_layer_data;
1650 if (settings.increase_radius)
1651 current_elem.effective_radius_height += 1;
1652 coord_t radius = support_element_collision_radius(config, current_elem);
1653
1654 const auto _tiny_area_threshold = tiny_area_threshold();
1655 if (settings.move) {
1656 increased = relevant_offset;
1657 if (overspeed > 0) {
1658 coord_t safe_movement_distance =
1659 (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
1660 (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
1661 // Orca:
1662 // safe_movement_distance is used as the safe_offset_inc() step, so keep it non-zero
1663 // to preserve branch movement with zero-clearance support settings.
1664 if (safe_movement_distance == 0)
1665 safe_movement_distance = scaled<coord_t>(0.1);
1666 // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later.
1667 // The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later.
1668 increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist),
1669 safe_movement_distance, safe_movement_distance + radius, 1);
1670 }
1671 if (settings.no_error && settings.move)
1672 // as jtRound has to be used for offsets this simplify is VERY important for performance.
1673 polygons_simplify(increased, scaled<float>(0.025));
1674 } else
1675 // if no movement is done the areas keep parent area as no move == offset(0)
1676 increased = parent.influence_area;
1677
1678 if (mergelayer || current_elem.to_buildplate) {
1679 to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance)));
1680 if (! current_elem.to_buildplate && area(to_bp_data) > _tiny_area_threshold) {
1681 // mostly happening in the tip, but with merges one should check every time, just to be sure.
1682 current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it.
1683 BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " <<
1684 current_elem.target_height << " with radius " << radius;
1685 }
1686 }
1687 if (config.support_rests_on_model) {
1688 if (mergelayer || current_elem.to_model_gracious)
1689 to_model_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance)));
1690
1691 if (!current_elem.to_model_gracious) {
1692 if (mergelayer && area(to_model_data) >= _tiny_area_threshold) {
1693 current_elem.to_model_gracious = true;
1694 BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " <<
1695 current_elem.target_height << " with radius " << radius;
1696 } else
1697 // Cannot route to gracious areas. Push the tree away from object and route it down anyways.
1698 to_model_data = safe_union(diff_clipped(increased, volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)));
1699 }
1700 }
1701
1702 check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data;
1703
1704 if (settings.increase_radius && area(check_layer_data) > _tiny_area_threshold) {
1705 auto validWithRadius = [&](coord_t next_radius) {
1706 if (volumes.ceilRadius(next_radius, settings.use_min_distance) <= volumes.ceilRadius(radius, settings.use_min_distance))
1707 return true;
1708
1709 Polygons to_bp_data_2;
1710 if (current_elem.to_buildplate)
1711 // regular union as output will not be used later => this area should always be a subset of the safe_union one (i think)
1712 to_bp_data_2 = diff_clipped(increased, volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance));
1713 Polygons to_model_data_2;
1714 if (config.support_rests_on_model && !current_elem.to_buildplate)
1715 to_model_data_2 = diff_clipped(increased,
1716 current_elem.to_model_gracious ?
1717 volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
1718 volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance));
1719 Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2;
1720 return area(check_layer_data_2) > _tiny_area_threshold;
1721 };
1722 coord_t ceil_radius_before = volumes.ceilRadius(radius, settings.use_min_distance);
1723
1724 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)) {
1725 coord_t target_radius = std::min(support_element_radius(config, current_elem), config.increase_radius_until_radius);
1726 coord_t current_ceil_radius = volumes.getRadiusNextCeil(radius, settings.use_min_distance);
1727
1728 while (current_ceil_radius < target_radius && validWithRadius(volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance)))
1729 current_ceil_radius = volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance);
1730 size_t resulting_eff_dtt = current_elem.effective_radius_height;
1731 while (resulting_eff_dtt + 1 < current_elem.distance_to_top &&
1732 config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius &&
1733 config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= support_element_radius(config, current_elem))
1734 ++ resulting_eff_dtt;
1735 current_elem.effective_radius_height = resulting_eff_dtt;
1736 }
1737 radius = support_element_collision_radius(config, current_elem);
1738
1739 const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0);
1740 // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
1741 // which could cause the radius to become bigger than precalculated.
1742 double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - support_element_radius(config, current_elem)) / foot_radius_increase);
1743//FIXME
1744 bool increase_bp_foot = planned_foot_increase > 0 && current_elem.to_buildplate;
1745// bool increase_bp_foot = false;
1746
1747 if (increase_bp_foot && support_element_radius(config, current_elem) >= config.branch_radius && support_element_radius(config, current_elem) >= config.increase_radius_until_radius)
1748 if (validWithRadius(config.getRadius(current_elem.effective_radius_height, current_elem.elephant_foot_increases + planned_foot_increase))) {
1749 current_elem.elephant_foot_increases += planned_foot_increase;
1750 radius = support_element_collision_radius(config, current_elem);
1751 }
1752
1753 if (ceil_radius_before != volumes.ceilRadius(radius, settings.use_min_distance)) {
1754 if (current_elem.to_buildplate)
1755 to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance)));
1756 if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer))
1757 to_model_data = safe_union(diff_clipped(increased,
1758 current_elem.to_model_gracious ?
1759 volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
1760 volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)
1761 ));
1762 check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data;
1763 if (area(check_layer_data) < _tiny_area_threshold) {
1764 BOOST_LOG_TRIVIAL(debug) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " <<
1765 volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance);
1766 tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true);
1767 }
1768 }
1769 }
1770
1771 return area(check_layer_data) > _tiny_area_threshold ? std::optional<SupportElementState>(current_elem) : std::optional<SupportElementState>();
1772}
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:427
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:157
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:171
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 TreeSupport3D.hpp:273
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 TreeSupport3D.cpp:839
Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:735
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
Definition Polygon.cpp:685
bool move
Definition TreeSupport3D.hpp:73
bool increase_radius
Definition TreeSupport3D.hpp:70
TreeModelVolumes::AvoidanceType type
Definition TreeSupport3D.hpp:69
bool no_error
Definition TreeSupport3D.hpp:71
bool use_min_distance
Definition TreeSupport3D.hpp:72
bool to_buildplate
The element trys to reach the buildplate.
Definition TreeSupport3D.hpp:107
bool to_model_gracious
Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
Definition TreeSupport3D.hpp:112
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 TreeSupport3D.hpp:117
Definition TreeSupport3D.hpp:148
static SupportElementState propagate_down(const SupportElementState &src)
Definition TreeSupport3D.hpp:227
double elephant_foot_increases
Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
Definition TreeSupport3D.hpp:198
LayerIndex target_height
The layer this support elements wants reach.
Definition TreeSupport3D.hpp:156
double branch_radius_increase_per_layer
How much a branch radius increases with each layer to guarantee the prescribed tree widening.
Definition TreeSupportCommon.hpp:412
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:461
coord_t increase_radius_until_radius
Increase radius in the resulting drawn branches, even if the avoidance does not allow it....
Definition TreeSupportCommon.hpp:424
coord_t branch_radius
Radius of a branch when it has left the tip.
Definition TreeSupportCommon.hpp:388
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:449
coord_t min_feature_size
Definition TreeSupportCommon.hpp:515
coord_t recommendedMinRadius(LayerIndex layer_idx) const
Get the Radius an element should at least have at a given layer.
Definition TreeSupportCommon.hpp:577

References Slic3r::area(), Slic3r::TreeSupport3D::TreeSupportSettings::bp_radius_increase_per_layer, Slic3r::TreeSupport3D::TreeSupportSettings::branch_radius, Slic3r::TreeSupport3D::TreeSupportSettings::branch_radius_increase_per_layer, Slic3r::TreeSupport3D::TreeModelVolumes::ceilRadius(), Slic3r::diff_clipped(), Slic3r::TreeSupport3D::SupportElementState::distance_to_top, Slic3r::TreeSupport3D::SupportElementState::effective_radius_height, Slic3r::TreeSupport3D::SupportElementState::elephant_foot_increases, Slic3r::TreeSupport3D::TreeModelVolumes::getAvoidance(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollision(), Slic3r::TreeSupport3D::TreeSupportSettings::getRadius(), Slic3r::TreeSupport3D::TreeModelVolumes::getRadiusNextCeil(), Slic3r::TreeSupport3D::TreeModelVolumes::getWallRestriction(), Slic3r::TreeSupport3D::AreaIncreaseSettings::increase_radius, Slic3r::TreeSupport3D::TreeSupportSettings::increase_radius_until_radius, increase_single_area(), Slic3r::TreeSupport3D::SupportElement::influence_area, Slic3r::TreeSupport3D::TreeSupportSettings::min_feature_size, Slic3r::TreeSupport3D::AreaIncreaseSettings::move, Slic3r::TreeSupport3D::AreaIncreaseSettings::no_error, Slic3r::polygons_simplify(), Slic3r::TreeSupport3D::SupportElementState::propagate_down(), Slic3r::TreeSupport3D::TreeSupportSettings::recommendedMinRadius(), safe_offset_inc(), safe_union(), Slic3r::scaled(), Slic3r::TreeSupport3D::SupportElement::state, support_element_collision_radius(), support_element_radius(), Slic3r::TreeSupport3D::TreeSupportSettings::support_rests_on_model, Slic3r::TreeSupport3D::SupportElementState::target_height, tiny_area_threshold(), Slic3r::TreeSupport3D::SupportElementStateBits::to_buildplate, Slic3r::TreeSupport3D::SupportElementStateBits::to_model_gracious, tree_supports_show_error(), Slic3r::TreeSupport3D::AreaIncreaseSettings::type, Slic3r::TreeSupport3D::AreaIncreaseSettings::use_min_distance, Slic3r::TreeSupport3D::SupportElementStateBits::use_min_xy_dist, Slic3r::TreeSupport3D::TreeSupportSettings::xy_distance, Slic3r::TreeSupport3D::TreeSupportSettings::xy_min_distance, Slic3r::TreeSupport3D::TreeSupportSettings::z_distance_bottom_layers, and Slic3r::TreeSupport3D::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::TreeSupport3D::layer_allocate ( SupportGeneratorLayerStorage & layer_storage,
SupporLayerType layer_type,
const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
size_t layer_idx )
inline
708{
709 SupportGeneratorLayer &layer = layer_storage.allocate(layer_type);
710 return layer_initialize(layer, slicing_params, config, layer_idx);
711}
SupportGeneratorLayer & allocate(SupporLayerType layer_type)
Definition SupportLayer.hpp:130
Definition SupportLayer.hpp:45
SupportGeneratorLayer & layer_initialize(SupportGeneratorLayer &layer_new, const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
Definition TreeSupportCommon.hpp:663

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

Here is the call graph for this function:

◆ layer_allocate_unguarded()

SupportGeneratorLayer & Slic3r::TreeSupport3D::layer_allocate_unguarded ( SupportGeneratorLayerStorage & layer_storage,
SupporLayerType layer_type,
const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
size_t layer_idx )
inline
697{
698 SupportGeneratorLayer &layer = layer_storage.allocate_unguarded(layer_type);
699 return layer_initialize(layer, slicing_params, config, layer_idx);
700}
SupportGeneratorLayer & allocate_unguarded(SupporLayerType layer_type)
Definition SupportLayer.hpp:124

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

Referenced by Slic3r::TreeSupport3D::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::TreeSupport3D::layer_idx_ceil ( const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
const double z )
inline
642{
643 const double first_object_z = first_object_support_layer_z(slicing_params);
644 if (!config.raft_layers.empty() && z < first_object_z - EPSILON) {
645 auto it = std::lower_bound(config.raft_layers.begin(), config.raft_layers.end(), z - EPSILON);
646 return LayerIndex(it == config.raft_layers.end() ? config.raft_layers.size() : std::distance(config.raft_layers.begin(), it));
647 }
648 return LayerIndex(config.raft_layers.size()) +
649 std::max<LayerIndex>(0, LayerIndex(std::ceil((z - first_object_z) / slicing_params.layer_height)));
650}
double first_object_support_layer_z(const SlicingParameters &slicing_params)
Definition TreeSupportCommon.hpp:633
coordf_t layer_height
Definition Slicing.hpp:70

References EPSILON, first_object_support_layer_z(), Slic3r::SlicingParameters::layer_height, and Slic3r::TreeSupport3D::TreeSupportSettings::raft_layers.

Referenced by organic_smooth_branches_avoid_collisions().

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

◆ layer_idx_floor()

LayerIndex Slic3r::TreeSupport3D::layer_idx_floor ( const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
const double z )
inline
653{
654 const double first_object_z = first_object_support_layer_z(slicing_params);
655 if (!config.raft_layers.empty() && z < first_object_z - EPSILON) {
656 auto it = std::upper_bound(config.raft_layers.begin(), config.raft_layers.end(), z + EPSILON);
657 return LayerIndex(it == config.raft_layers.begin() ? 0 : std::distance(config.raft_layers.begin(), it) - 1);
658 }
659 return LayerIndex(config.raft_layers.size()) +
660 std::max<LayerIndex>(0, LayerIndex(std::floor((z - first_object_z) / slicing_params.layer_height)));
661}

References EPSILON, first_object_support_layer_z(), Slic3r::SlicingParameters::layer_height, and Slic3r::TreeSupport3D::TreeSupportSettings::raft_layers.

Referenced by organic_smooth_branches_avoid_collisions().

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

◆ layer_initialize()

SupportGeneratorLayer & Slic3r::TreeSupport3D::layer_initialize ( SupportGeneratorLayer & layer_new,
const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
const size_t layer_idx )
inline
668{
669 layer_new.print_z = layer_z(slicing_params, config, layer_idx);
670 // Layer 0 has no layer below it, so its bottom is the build plate at z = 0 --
671 // true for a flat bed, false for a belt, whose virtual support layers extend
672 // below zero. Taking 0 there made the bottom-most belt layer's height come out
673 // as its own (negative) print_z, which reached Flow::with_height() and threw
674 // FlowErrorNegativeFlow, so tree support could not slice any belt model whose
675 // branches reached down that far.
676 //
677 // Only the negative case is corrected. An earlier version used
678 // min(0, print_z - layer_height), which also fires whenever the initial layer
679 // is THINNER than the regular layer height -- e.g. 0.2 over 0.3, both
680 // independently configurable -- and silently changed flat-bed support layer
681 // heights. Keying on the sign leaves every non-negative print_z on exactly
682 // the previous value of 0.
683 layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
684 if (layer_idx == 0 && layer_new.print_z < 0.)
685 layer_new.bottom_z = layer_new.print_z - slicing_params.layer_height;
686 layer_new.height = layer_new.print_z - layer_new.bottom_z;
687 return layer_new;
688}
coordf_t print_z
Definition SupportLayer.hpp:95
coordf_t height
Definition SupportLayer.hpp:100
coordf_t bottom_z
Definition SupportLayer.hpp:98

References Slic3r::SupportGeneratorLayer::bottom_z, Slic3r::SupportGeneratorLayer::height, Slic3r::SlicingParameters::layer_height, layer_z(), and Slic3r::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::TreeSupport3D::layer_z ( const SlicingParameters & slicing_params,
const TreeSupportSettings & config,
const size_t layer_idx )
inline
627{
628 return layer_idx >= config.raft_layers.size() ?
629 slicing_params.object_print_z_min + slicing_params.first_object_layer_height + (layer_idx - config.raft_layers.size()) * slicing_params.layer_height :
630 config.raft_layers[layer_idx];
631}

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

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

Here is the caller graph for this function:

◆ merge_influence_areas()

void Slic3r::TreeSupport3D::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.
2404{
2405 const size_t input_size = influence_areas.size();
2406 if (input_size == 0)
2407 return;
2408
2409 // Merging by divide & conquer.
2410 // The majority of time is consumed by Clipper polygon operations, intersection is accelerated by bounding boxes.
2411 // Sorting input into an AABB tree helps to perform most of the intersections at first iterations,
2412 // thus reducing computation when merging larger subtrees.
2413 // The actual merge logic is found in merge_influence_areas_two_sets.
2414
2415 // Build an AABB tree over the influence areas.
2416 //FIXME A full tree does not need to be built, the lowest level branches will be always bucketed.
2417 // However the additional time consumed is negligible.
2419 // Sort influence_areas in place.
2420 tree.build_modify_input(influence_areas);
2421
2422 throw_on_cancel();
2423
2424 // Prepare the initial buckets as ranges of influence areas. The initial buckets contain power of 2 influence areas to follow
2425 // the branching of the AABB tree.
2426 // Vectors of ranges of influence areas, following the branching of the AABB tree:
2427 std::vector<std::pair<SupportElementMerging*, SupportElementMerging*>> buckets;
2428 // Initial number of buckets for 1st round of merging.
2429 size_t num_buckets_initial;
2430 {
2431 // How many buckets per first merge iteration?
2432 // Fixed at 4: merging is not associative, so sizing buckets off max_concurrency() made
2433 // results depend on the core count of the slicing machine.
2434 const size_t bucket_size = 4;
2435 num_buckets_initial = (input_size + 2) / 4;
2436 // Fill in the buckets.
2437 SupportElementMerging *it = influence_areas.data();
2438 // Reserve one more bucket to keep a single influence area which will not be merged in the first iteration.
2439 buckets.reserve(num_buckets_initial + 1);
2440 for (size_t i = 0; i < num_buckets_initial; ++ i, it += bucket_size)
2441 buckets.emplace_back(std::make_pair(it, it + bucket_size));
2442 SupportElementMerging *it_end = influence_areas.data() + influence_areas.size();
2443 if (buckets.back().second >= it_end) {
2444 // Last bucket is less than size 4, but bigger than size 1.
2445 buckets.back().second = std::min(buckets.back().second, it_end);
2446 } else {
2447 // Last bucket is size 1, it will not be merged in the first iteration.
2448 assert(it + 1 == it_end);
2449 buckets.emplace_back(std::make_pair(it, it_end));
2450 }
2451 }
2452
2453 // 1st merge iteration, merge one with each other.
2454 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_buckets_initial),
2455 [&](const tbb::blocked_range<size_t> &range) {
2456 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
2457 // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets
2458 buckets[idx].second = merge_influence_areas_leaves(volumes, config, layer_idx, buckets[idx].first, buckets[idx].second);
2459 throw_on_cancel();
2460 }
2461 });
2462
2463 // Further merge iterations, merging one AABB subtree with another one, hopefully minimizing intersections between the elements
2464 // of each of the subtree.
2465 while (buckets.size() > 1) {
2466 tbb::parallel_for(tbb::blocked_range<size_t>(0, buckets.size() / 2),
2467 [&](const tbb::blocked_range<size_t> &range) {
2468 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
2469 const size_t bucket_pair_idx = idx * 2;
2470 // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets
2471 buckets[bucket_pair_idx].second = merge_influence_areas_two_sets(volumes, config, layer_idx,
2472 buckets[bucket_pair_idx].first, buckets[bucket_pair_idx].second,
2473 buckets[bucket_pair_idx + 1].first, buckets[bucket_pair_idx + 1].second);
2474 throw_on_cancel();
2475 }
2476 });
2477 // Remove odd buckets, which were merged into even buckets.
2478 size_t new_size = (buckets.size() + 1) / 2;
2479 for (size_t i = 1; i < new_size; ++ i)
2480 buckets[i] = std::move(buckets[i * 2]);
2481 buckets.erase(buckets.begin() + new_size, buckets.end());
2482 }
2483}
Definition AABBTreeIndirect.hpp:45
void build_modify_input(std::vector< SourceNode > &input)
Definition AABBTreeIndirect.hpp:98
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 TreeSupport3D.cpp:2318

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()

SupportElementMerging * Slic3r::TreeSupport3D::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.
2321{
2322 // Merging at the lowest level of the AABB tree. Checking one against each other, O(n^2).
2323 assert(dst_begin < dst_end);
2324 for (SupportElementMerging *i = dst_begin; i + 1 < dst_end;) {
2325 for (SupportElementMerging *j = i + 1; j != dst_end;)
2326 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *i, *j)) {
2327 // i was merged with j, j is empty.
2328 if (j != -- dst_end)
2329 *j = std::move(*dst_end);
2330 goto merged;
2331 } else
2332 ++ j;
2333 // not merged
2334 ++ i;
2335 merged:
2336 ;
2337 }
2338 return dst_end;
2339}
static bool merge_influence_areas_two_elements(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging &dst, SupportElementMerging &src)
Definition TreeSupport3D.cpp:2140

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()

bool Slic3r::TreeSupport3D::merge_influence_areas_two_elements ( const TreeModelVolumes & volumes,
const TreeSupportSettings & config,
const LayerIndex layer_idx,
SupportElementMerging & dst,
SupportElementMerging & src )
static
2143{
2144 // Don't merge gracious with a non gracious area as bad placement could negatively impact reliability of the whole subtree.
2145 const bool merging_gracious_and_non_gracious = dst.state.to_model_gracious != src.state.to_model_gracious;
2146 // Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased
2147 // by the delta to the larger it is engulfed by it already. But because a different collision
2148 // may be removed from the in draw_area() generated circles, this assumption could be wrong.
2149 const bool merging_min_and_regular_xy = dst.state.use_min_xy_dist != src.state.use_min_xy_dist;
2150
2151 if (merging_gracious_and_non_gracious || merging_min_and_regular_xy)
2152 return false;
2153
2154 const bool dst_radius_bigger = support_element_collision_radius(config, dst.state) > support_element_collision_radius(config, src.state);
2155 const SupportElementMerging &smaller_rad = dst_radius_bigger ? src : dst;
2156 const SupportElementMerging &bigger_rad = dst_radius_bigger ? dst : src;
2157 const coord_t real_radius_delta = std::abs(support_element_radius(config, bigger_rad.state) - support_element_radius(config, smaller_rad.state));
2158 {
2159 // Testing intersection of bounding boxes.
2160 // Expand the smaller radius branch bounding box to match the lambda intersect_small_with_bigger() below.
2161 // Because the lambda intersect_small_with_bigger() applies a rounded offset, a snug offset of the bounding box
2162 // is sufficient. On the other side, if a mitered offset was used by the lambda,
2163 // the bounding box expansion would have to account for the mitered extension of the sharp corners.
2164 Eigen::AlignedBox<coord_t, 2> smaller_bbox = smaller_rad.bbox();
2165 smaller_bbox.min() -= Point{ real_radius_delta, real_radius_delta };
2166 smaller_bbox.max() += Point{ real_radius_delta, real_radius_delta };
2167 if (! smaller_bbox.intersects(bigger_rad.bbox()))
2168 return false;
2169 }
2170
2171 // Accumulator of a radius increase of a "to model" branch by merging in a "to build plate" branch.
2172 coord_t increased_to_model_radius = 0;
2173 const bool merging_to_bp = dst.state.to_buildplate && src.state.to_buildplate;
2174 if (! merging_to_bp) {
2175 // Get the real radius increase as the user does not care for the collision model.
2176 if (dst.state.to_buildplate != src.state.to_buildplate) {
2177 // Merging a "to build plate" branch with a "to model" branch.
2178 // Don't allow merging a thick "to build plate" branch into a thinner "to model" branch.
2179 const coord_t rdst = support_element_radius(config, dst.state);
2180 const coord_t rsrc = support_element_radius(config, src.state);
2181 if (dst.state.to_buildplate) {
2182 if (rsrc < rdst)
2183 increased_to_model_radius = src.state.increased_to_model_radius + rdst - rsrc;
2184 } else {
2185 if (rsrc > rdst)
2186 increased_to_model_radius = dst.state.increased_to_model_radius + rsrc - rdst;
2187 }
2188 if (increased_to_model_radius > config.max_to_model_radius_increase)
2189 return false;
2190 }
2191 // if a merge could place a stable branch on unstable ground, would be increasing the radius further
2192 // than allowed to when merging to model and to_bp trees or would merge to model before it is known
2193 // they will even been drawn the merge is skipped
2194 if (! dst.state.supports_roof && ! src.state.supports_roof &&
2195 std::max(src.state.distance_to_top, dst.state.distance_to_top) < config.min_dtt_to_model)
2196 return false;
2197 }
2198
2199 // Area of the bigger radius is used to ensure correct placement regarding the relevant avoidance,
2200 // so if that would change an invalid area may be created.
2201 if (! bigger_rad.state.can_use_safe_radius && smaller_rad.state.can_use_safe_radius)
2202 return false;
2203
2204 // the bigger radius is used to verify that the area is still valid after the increase with the delta.
2205 // If there were a point where the big influence area could be valid with can_use_safe_radius
2206 // the element would already be can_use_safe_radius.
2207 // the smaller radius, which gets increased by delta may reach into the area where use_min_xy_dist is no longer required.
2208 const bool use_min_radius = bigger_rad.state.use_min_xy_dist && smaller_rad.state.use_min_xy_dist;
2209
2210 // The idea is that the influence area with the smaller collision radius is increased by the radius difference.
2211 // 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.
2212 // Because of this a merge may happen even if the influence areas (that represent possible center points of branches) do not intersect yet.
2213 // Remember that collision radius <= real radius as otherwise this assumption would be false.
2214 const coord_t smaller_collision_radius = support_element_collision_radius(config, smaller_rad.state);
2215 const Polygons &collision = volumes.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius);
2216 auto intersect_small_with_bigger = [real_radius_delta, smaller_collision_radius, &collision, &config](const Polygons &small, const Polygons &bigger) {
2217 return intersection(
2219 small, real_radius_delta, collision,
2220 // -3 avoids possible rounding errors
2221 2 * (config.xy_distance + smaller_collision_radius - 3), 0, 0),
2222 bigger);
2223 };
2224//#define TREES_MERGE_RATHER_LATER
2226#ifdef TREES_MERGE_RATHER_LATER
2228#else
2229 intersect_small_with_bigger(
2230#endif
2231 merging_to_bp ? smaller_rad.areas.to_bp_areas : smaller_rad.areas.to_model_areas,
2232 merging_to_bp ? bigger_rad.areas.to_bp_areas : bigger_rad.areas.to_model_areas);
2233
2234 const auto _tiny_area_threshold = tiny_area_threshold();
2235 // 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)
2236 // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper).
2237 if (area(intersect) <= _tiny_area_threshold)
2238 return false;
2239
2240 // While 0.025 was guessed as enough, i did not have reason to change it.
2241 if (area(offset(intersect, scaled<float>(-0.025), jtMiter, 1.2)) <= _tiny_area_threshold)
2242 return false;
2243
2244#ifdef TREES_MERGE_RATHER_LATER
2245 intersect =
2246 intersect_small_with_bigger(
2247 merging_to_bp ? smaller_rad.areas.to_bp_areas : smaller_rad.areas.to_model_areas,
2248 merging_to_bp ? bigger_rad.areas.to_bp_areas : bigger_rad.areas.to_model_areas);
2249#endif
2250
2251 // Do the actual merge now that the branches are confirmed to be able to intersect.
2252 // calculate which point is closest to the point of the last merge (or tip center if no merge above it has happened)
2253 // used at the end to estimate where to best place the branch on the bottom most layer
2254 // could be replaced with a random point inside the new area
2255 Point new_pos = move_inside_if_outside(intersect, dst.state.next_position);
2256
2257 SupportElementState new_state = merge_support_element_states(dst.state, src.state, new_pos, layer_idx - 1, config);
2258 new_state.increased_to_model_radius = increased_to_model_radius == 0 ?
2259 // increased_to_model_radius was not set yet. Propagate maximum.
2261 increased_to_model_radius;
2262
2263 // Rather unioning with "intersect" due to some rounding errors.
2264 Polygons influence_areas = safe_union(
2265 intersect_small_with_bigger(smaller_rad.areas.influence_areas, bigger_rad.areas.influence_areas),
2266 intersect);
2267
2268 Polygons to_model_areas;
2269 if (merging_to_bp && config.support_rests_on_model)
2270 to_model_areas = new_state.to_model_gracious ?
2271 // Rather unioning with "intersect" due to some rounding errors.
2272 safe_union(
2273 intersect_small_with_bigger(smaller_rad.areas.to_model_areas, bigger_rad.areas.to_model_areas),
2274 intersect) :
2275 influence_areas;
2276
2277 dst.parents.insert(dst.parents.end(), src.parents.begin(), src.parents.end());
2278 dst.state = new_state;
2279 dst.areas.influence_areas = std::move(influence_areas);
2280 dst.areas.to_bp_areas.clear();
2281 dst.areas.to_model_areas.clear();
2282 if (merging_to_bp) {
2283 dst.areas.to_bp_areas = std::move(intersect);
2284 if (config.support_rests_on_model)
2285 dst.areas.to_model_areas = std::move(to_model_areas);
2286 } else
2287 dst.areas.to_model_areas = std::move(intersect);
2288 // Update the bounding box.
2289 BoundingBox bbox(get_extents(dst.areas.influence_areas));
2290 bbox.merge(get_extents(dst.areas.to_bp_areas));
2291 bbox.merge(get_extents(dst.areas.to_model_areas));
2292 dst.set_bbox(bbox);
2293 // Clear the source data.
2294 src.areas.clear();
2295 src.parents.clear();
2296 return true;
2297}
void intersect(CGALMesh &A, CGALMesh &B)
Definition MeshBoolean.cpp:477
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 TreeSupport3D.cpp:2091
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
void clear()
Definition TreeSupport3D.cpp:1782
Polygons influence_areas
Definition TreeSupport3D.cpp:1776
Polygons to_bp_areas
Definition TreeSupport3D.cpp:1778
Polygons to_model_areas
Definition TreeSupport3D.cpp:1780
SupportElementInfluenceAreas areas
Definition TreeSupport3D.cpp:1796
void set_bbox(const BoundingBox &abbox)
Definition TreeSupport3D.cpp:1802
const Eigen::AlignedBox< coord_t, 2 > & bbox() const
Definition TreeSupport3D.cpp:1800
SupportElementState state
Definition TreeSupport3D.cpp:1790
SupportElement::ParentIndices parents
All elements in the layer above the current one that are supported by this element.
Definition TreeSupport3D.cpp:1794
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 TreeSupport3D.hpp:127
bool supports_roof
True if this Element or any parent (element above) provides support to a support roof.
Definition TreeSupport3D.hpp:122
coord_t increased_to_model_radius
The amount of extra radius we got from merging branches that could have reached the buildplate,...
Definition TreeSupport3D.hpp:193
Point next_position
The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding d...
Definition TreeSupport3D.hpp:166
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:416

References Slic3r::area(), Slic3r::TreeSupport3D::SupportElementMerging::areas, Slic3r::TreeSupport3D::SupportElementMerging::bbox(), Slic3r::TreeSupport3D::SupportElementStateBits::can_use_safe_radius, Slic3r::TreeSupport3D::SupportElementInfluenceAreas::clear(), Slic3r::TreeSupport3D::SupportElementState::distance_to_top, Slic3r::get_extents(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollision(), Slic3r::TreeSupport3D::SupportElementState::increased_to_model_radius, Slic3r::TreeSupport3D::SupportElementInfluenceAreas::influence_areas, Slic3r::intersection(), Slic3r::jtMiter, Slic3r::TreeSupport3D::TreeSupportSettings::max_to_model_radius_increase, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), merge_influence_areas_two_elements(), merge_support_element_states(), Slic3r::TreeSupport3D::TreeSupportSettings::min_dtt_to_model, move_inside_if_outside(), Slic3r::TreeSupport3D::SupportElementState::next_position, Slic3r::offset(), Slic3r::TreeSupport3D::SupportElementMerging::parents, safe_offset_inc(), safe_union(), Slic3r::scaled(), Slic3r::TreeSupport3D::SupportElementMerging::set_bbox(), Slic3r::TreeSupport3D::SupportElementMerging::state, support_element_collision_radius(), support_element_radius(), Slic3r::TreeSupport3D::TreeSupportSettings::support_rests_on_model, Slic3r::TreeSupport3D::SupportElementStateBits::supports_roof, tiny_area_threshold(), Slic3r::TreeSupport3D::SupportElementInfluenceAreas::to_bp_areas, Slic3r::TreeSupport3D::SupportElementStateBits::to_buildplate, Slic3r::TreeSupport3D::SupportElementInfluenceAreas::to_model_areas, Slic3r::TreeSupport3D::SupportElementStateBits::to_model_gracious, Slic3r::TreeSupport3D::SupportElementStateBits::use_min_xy_dist, and Slic3r::TreeSupport3D::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()

SupportElementMerging * Slic3r::TreeSupport3D::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
2345{
2346 // Merging src into dst.
2347 // Areas of src should not overlap with areas of another elements of src.
2348 // Areas of dst should not overlap with areas of another elements of dst.
2349 // The memory from dst_begin to src_end is reserved for the merging operation,
2350 // src follows dst.
2351 assert(src_begin < src_end);
2352 assert(dst_begin < dst_end);
2353 assert(dst_end <= src_begin);
2354 for (SupportElementMerging *src = src_begin; src != src_end; ++ src) {
2355 SupportElementMerging *dst = dst_begin;
2356 SupportElementMerging *merged = nullptr;
2357 for (; dst != dst_end; ++ dst)
2358 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *dst, *src)) {
2359 merged = dst ++;
2360 if (src != src_begin)
2361 // Compactify src.
2362 *src = std::move(*src_begin);
2363 ++ src_begin;
2364 break;
2365 }
2366 for (; dst != dst_end;)
2367 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *merged, *dst)) {
2368 // Compactify dst.
2369 if (dst != -- dst_end)
2370 *dst = std::move(*dst_end);
2371 } else
2372 ++ dst;
2373 }
2374 // Compactify src elements that were not merged with dst to the end of dst.
2375 assert(dst_end <= src_begin);
2376 if (dst_end == src_begin)
2377 dst_end = src_end;
2378 else
2379 while (src_begin != src_end)
2380 *dst_end ++ = std::move(*src_begin ++);
2381
2382 return dst_end;
2383}

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()

SupportElementState Slic3r::TreeSupport3D::merge_support_element_states ( const SupportElementState & first,
const SupportElementState & second,
const Point & next_position,
const coord_t layer_idx,
const TreeSupportSettings & config )
staticnodiscard
2094{
2096 out.next_position = next_position;
2097 out.layer_idx = layer_idx;
2098 out.use_min_xy_dist = first.use_min_xy_dist || second.use_min_xy_dist;
2099 out.supports_roof = first.supports_roof || second.supports_roof;
2100 out.dont_move_until = std::max(first.dont_move_until, second.dont_move_until);
2102 // Preserve the deepest outstanding roof recovery request across merged sub-branches.
2104 std::max(first.missing_roof_layers, second.missing_roof_layers),
2105 std::max(first.roof_recovery_dtt, second.roof_recovery_dtt));
2106 out.skip_ovalisation = false;
2107 if (first.target_height > second.target_height) {
2108 out.target_height = first.target_height;
2109 out.target_position = first.target_position;
2110 } else {
2111 out.target_height = second.target_height;
2112 out.target_position = second.target_position;
2113 }
2115 out.distance_to_top = std::max(first.distance_to_top, second.distance_to_top);
2116
2117 out.to_buildplate = first.to_buildplate && second.to_buildplate;
2118 out.to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious
2119
2121 if (config.bp_radius_increase_per_layer > 0) {
2122 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));
2123 // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
2124 // the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
2125 out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer);
2126 }
2127
2128 // 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.
2129 out.last_area_increase = {
2130 std::min(first.last_area_increase.type, second.last_area_increase.type),
2136
2137 return out;
2138}
coord_t increase_speed
Definition TreeSupport3D.hpp:67
bool skip_ovalisation
Skip the ovalisation to parent and children when generating the final circles.
Definition TreeSupport3D.hpp:132
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 TreeSupport3D.hpp:203
uint32_t roof_recovery_dtt
Contact/interface depth that this node should recover when missing_roof_layers > 0.
Definition TreeSupport3D.hpp:218
uint32_t missing_roof_layers
Number of pending roof/contact recovery slices from this node downward, including this node.
Definition TreeSupport3D.hpp:213
void set_pending_roof_recovery(uint32_t pending_layers, uint32_t recovery_depth)
Definition TreeSupport3D.hpp:220
Point target_position
The position this support elements wants to support on layer=target_height.
Definition TreeSupport3D.hpp:161
AreaIncreaseSettings last_area_increase
Settings used to increase the influence area to its current state.
Definition TreeSupport3D.hpp:208

References Slic3r::TreeSupport3D::TreeSupportSettings::bp_radius_increase_per_layer, Slic3r::TreeSupport3D::TreeSupportSettings::branch_radius_increase_per_layer, Slic3r::TreeSupport3D::SupportElementStateBits::can_use_safe_radius, Slic3r::TreeSupport3D::SupportElementState::distance_to_top, Slic3r::TreeSupport3D::SupportElementState::dont_move_until, Slic3r::TreeSupport3D::SupportElementState::effective_radius_height, Slic3r::TreeSupport3D::SupportElementState::elephant_foot_increases, Slic3r::TreeSupport3D::AreaIncreaseSettings::increase_radius, Slic3r::TreeSupport3D::AreaIncreaseSettings::increase_speed, Slic3r::TreeSupport3D::SupportElementState::last_area_increase, Slic3r::TreeSupport3D::SupportElementState::layer_idx, merge_support_element_states(), Slic3r::TreeSupport3D::SupportElementState::missing_roof_layers, Slic3r::TreeSupport3D::AreaIncreaseSettings::move, Slic3r::TreeSupport3D::SupportElementState::next_position, Slic3r::TreeSupport3D::AreaIncreaseSettings::no_error, Slic3r::TreeSupport3D::SupportElementState::roof_recovery_dtt, Slic3r::TreeSupport3D::SupportElementState::set_pending_roof_recovery(), Slic3r::TreeSupport3D::SupportElementStateBits::skip_ovalisation, support_element_collision_radius(), Slic3r::TreeSupport3D::SupportElementStateBits::supports_roof, Slic3r::TreeSupport3D::SupportElementState::target_height, Slic3r::TreeSupport3D::SupportElementState::target_position, Slic3r::TreeSupport3D::SupportElementStateBits::to_buildplate, Slic3r::TreeSupport3D::SupportElementStateBits::to_model_gracious, Slic3r::TreeSupport3D::AreaIncreaseSettings::type, Slic3r::TreeSupport3D::AreaIncreaseSettings::use_min_distance, and Slic3r::TreeSupport3D::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()

unsigned int Slic3r::TreeSupport3D::move_inside ( const Polygons & polygons,
Point & from,
int distance = 0,
int64_t maxDist2 = std::numeric_limits<int64_t>::max() )
static
1506{
1507 Point ret = from;
1508 double bestDist2 = std::numeric_limits<double>::max();
1509 auto bestPoly = static_cast<unsigned int>(-1);
1510 bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
1511 for (unsigned int poly_idx = 0; poly_idx < polygons.size(); ++ poly_idx) {
1512 const Polygon &poly = polygons[poly_idx];
1513 if (poly.size() < 2)
1514 continue;
1515 Point p0 = poly[poly.size() - 2];
1516 Point p1 = poly.back();
1517 // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop
1518 // to avoid integer rounding edge cases
1519 bool projected_p_beyond_prev_segment = (p1 - p0).cast<int64_t>().dot((from - p0).cast<int64_t>()) >= (p1 - p0).cast<int64_t>().squaredNorm();
1520 for (const Point& p2 : poly) {
1521 // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A));
1522 // = A + (B-A) * ((B-A) dot (P-A)) / VSize2(B-A);
1523 // X = P projected on AB
1524 const Point& a = p1;
1525 const Point& b = p2;
1526 const Point& p = from;
1527 Vec2i64 ab = (b - a).cast<int64_t>();
1528 Vec2i64 ap = (p - a).cast<int64_t>();
1529 int64_t ab_length2 = ab.squaredNorm();
1530 if (ab_length2 <= 0) { //A = B, i.e. the input polygon had two adjacent points on top of each other.
1531 p1 = p2; //Skip only one of the points.
1532 continue;
1533 }
1534 int64_t dot_prod = ab.dot(ap);
1535 if (dot_prod <= 0) { // x is projected to before ab
1536 if (projected_p_beyond_prev_segment) {
1537 // case which looks like: > .
1538 projected_p_beyond_prev_segment = false;
1539 Point& x = p1;
1540
1541 auto dist2 = (x - p).cast<int64_t>().squaredNorm();
1542 if (dist2 < bestDist2) {
1543 bestDist2 = dist2;
1544 bestPoly = poly_idx;
1545 if (distance == 0)
1546 ret = x;
1547 else {
1548 Vec2d abd = ab.cast<double>();
1549 Vec2d p1p2 = (p1 - p0).cast<double>();
1550 double lab = abd.norm();
1551 double lp1p2 = p1p2.norm();
1552 // inward direction irrespective of sign of [distance]
1553 Vec2d inward_dir = perp(abd * (scaled<double>(10.0) / lab) + p1p2 * (scaled<double>(10.0) / lp1p2));
1554 // MM2INT(10.0) to retain precision for the eventual normalization
1555 ret = x + (inward_dir * (distance / inward_dir.norm())).cast<coord_t>();
1556 is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast<double>()) * distance >= 0;
1557 }
1558 }
1559 } else {
1560 projected_p_beyond_prev_segment = false;
1561 p0 = p1;
1562 p1 = p2;
1563 continue;
1564 }
1565 } else if (dot_prod >= ab_length2) {
1566 // x is projected to beyond ab
1567 projected_p_beyond_prev_segment = true;
1568 p0 = p1;
1569 p1 = p2;
1570 continue;
1571 } else {
1572 // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
1573 projected_p_beyond_prev_segment = false;
1574 Point x = a + (ab.cast<double>() * (double(dot_prod) / double(ab_length2))).cast<coord_t>();
1575 auto dist2 = (p - x).cast<int64_t>().squaredNorm();
1576 if (dist2 < bestDist2) {
1577 bestDist2 = dist2;
1578 bestPoly = poly_idx;
1579 if (distance == 0)
1580 ret = x;
1581 else {
1582 Vec2d abd = ab.cast<double>();
1583 Vec2d inward_dir = perp(abd * (distance / abd.norm())); // inward or outward depending on the sign of [distance]
1584 ret = x + inward_dir.cast<coord_t>();
1585 is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast<double>()) >= 0;
1586 }
1587 }
1588 }
1589 p0 = p1;
1590 p1 = p2;
1591 }
1592 }
1593 // 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
1594 if (is_already_on_correct_side_of_boundary) {
1595 if (bestDist2 < distance * distance)
1596 from = ret;
1597 else {
1598 // from = from; // original point stays unaltered. It is already inside by enough distance
1599 }
1600 return bestPoly;
1601 } else if (bestDist2 < maxDist2) {
1602 from = ret;
1603 return bestPoly;
1604 }
1605 return -1;
1606}
Definition Polygon.hpp:31
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > perp(const Eigen::MatrixBase< Derived > &v)
Definition Point.hpp:130
Eigen::Matrix< int64_t, 2, 1, Eigen::DontAlign > Vec2i64
Definition Point.hpp:58
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67

References Slic3r::cast(), move_inside(), Slic3r::perp(), and Slic3r::scaled().

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()

Point Slic3r::TreeSupport3D::move_inside_if_outside ( const Polygons & polygons,
Point from,
int distance = 0,
int64_t maxDist2 = std::numeric_limits<int64_t>::max() )
static
1609{
1610 if (! contains(polygons, from))
1611 move_inside(polygons, from);
1612 return from;
1613}
static unsigned int move_inside(const Polygons &polygons, Point &from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
Definition TreeSupport3D.cpp:1505

References Slic3r::Geometry::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::TreeSupport3D::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 )
3785{
3786 // All SupportElements are put into a layer independent storage to improve parallelization.
3787 std::vector<std::pair<SupportElement*, int>> elements_with_link_down;
3788 std::vector<size_t> linear_data_layers;
3789 {
3790 std::vector<std::pair<SupportElement*, int>> map_downwards_old;
3791 std::vector<std::pair<SupportElement*, int>> map_downwards_new;
3792 linear_data_layers.emplace_back(0);
3793 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
3794 SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
3795 map_downwards_new.clear();
3796 std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto& l, auto& r) { return l.first < r.first; });
3797 SupportElements &layer = move_bounds[layer_idx];
3798 for (size_t elem_idx = 0; elem_idx < layer.size(); ++ elem_idx) {
3799 SupportElement &elem = layer[elem_idx];
3800 int child = -1;
3801 if (layer_idx > 0) {
3802 auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), &elem, [](auto& l, const SupportElement* r) { return l.first < r; });
3803 if (it != map_downwards_old.end() && it->first == &elem) {
3804 child = it->second;
3805 // Only one link points to a node above from below.
3806 assert(!(++it != map_downwards_old.end() && it->first == &elem));
3807 }
3808#ifndef NDEBUG
3809 {
3810 const SupportElement *pchild = child == -1 ? nullptr : &move_bounds[layer_idx - 1][child];
3811 assert(pchild ? pchild->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
3812 }
3813#endif // NDEBUG
3814 }
3815 for (int32_t parent_idx : elem.parents) {
3816 SupportElement &parent = (*layer_above)[parent_idx];
3817 if (parent.state.result_on_layer_is_set())
3818 map_downwards_new.emplace_back(&parent, elem_idx);
3819 }
3820
3821 elements_with_link_down.push_back({ &elem, int(child) });
3822 }
3823 std::swap(map_downwards_old, map_downwards_new);
3824 linear_data_layers.emplace_back(elements_with_link_down.size());
3825 }
3826 }
3827
3828 throw_on_cancel();
3829
3830 organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers, throw_on_cancel);
3831
3832 // 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.
3834
3835 // Unmark all nodes.
3836 for (SupportElements &elements : move_bounds)
3837 for (SupportElement &element : elements)
3838 element.state.marked = false;
3839
3840 // Traverse all nodes, generate tubes.
3841 // Traversal stack with nodes and their current parent
3842
3843 struct Branch {
3844 std::vector<const SupportElement*> path;
3845 bool has_root{ false };
3846 bool has_tip { false };
3847 };
3848
3849 struct Slice {
3850 Polygons polygons;
3851 Polygons bottom_contacts;
3852 size_t num_branches{ 0 };
3853 };
3854
3855 struct Tree {
3856 std::vector<Branch> branches;
3857
3858 std::vector<Slice> slices;
3859 LayerIndex first_layer_id{ -1 };
3860 };
3861
3862 std::vector<Tree> trees;
3863
3864 struct TreeVisitor {
3865 static void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Tree &out) {
3866 assert(! start_element.state.marked && ! start_element.parents.empty());
3867 // Collect elements up to a bifurcation above.
3868 start_element.state.marked = true;
3869 // For each branch bifurcating from this point:
3870 //SupportElements &layer = move_bounds[start_element.state.layer_idx];
3871 SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
3872 bool root = out.branches.empty();
3873 for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
3874 Branch branch;
3875 branch.path.emplace_back(&start_element);
3876 // Traverse each branch until it branches again.
3877 SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
3878 assert(! first_parent.state.marked);
3879 assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx);
3880 branch.path.emplace_back(&first_parent);
3881 if (first_parent.parents.size() < 2)
3882 first_parent.state.marked = true;
3883 SupportElement *next_branch = nullptr;
3884 if (first_parent.parents.size() == 1) {
3885 for (SupportElement *parent = &first_parent;;) {
3886 assert(parent->state.marked);
3887 SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
3888 assert(! next_parent.state.marked);
3889 assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx);
3890 branch.path.emplace_back(&next_parent);
3891 if (next_parent.parents.size() > 1) {
3892 // Branching point was reached.
3893 next_branch = &next_parent;
3894 break;
3895 }
3896 next_parent.state.marked = true;
3897 if (next_parent.parents.size() == 0)
3898 // Tip is reached.
3899 break;
3900 parent = &next_parent;
3901 }
3902 } else if (first_parent.parents.size() > 1)
3903 // Branching point was reached.
3904 next_branch = &first_parent;
3905 assert(branch.path.size() >= 2);
3906 assert(next_branch == nullptr || ! next_branch->state.marked);
3907 branch.has_root = root;
3908 branch.has_tip = ! next_branch;
3909 out.branches.emplace_back(std::move(branch));
3910 if (next_branch)
3911 visit_recursive(move_bounds, *next_branch, out);
3912 }
3913 }
3914 };
3915
3916 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
3917// int ielement;
3918 for (SupportElement& start_element : move_bounds[layer_idx]) {
3919 if (!start_element.state.marked && !start_element.parents.empty()) {
3920#if 0
3921 int found = 0;
3922 if (layer_idx > 0) {
3923 for (auto& el : move_bounds[layer_idx - 1]) {
3924 for (auto iparent : el.parents)
3925 if (iparent == ielement)
3926 ++found;
3927 }
3928 if (found != 0)
3929 printf("Found: %d\n", found);
3930 }
3931#endif
3932 trees.push_back({});
3933 TreeVisitor::visit_recursive(move_bounds, start_element, trees.back());
3934 assert(!trees.back().branches.empty());
3935 //FIXME debugging
3936#if 0
3937 if (start_element.state.lost) {
3938 }
3939 else if (start_element.state.verylost) {
3940 } else
3941 trees.pop_back();
3942#endif
3943 }
3944// ++ ielement;
3945 }
3946 }
3947
3948 const SlicingParameters &slicing_params = print_object.slicing_parameters();
3949 MeshSlicingParams mesh_slicing_params;
3950 mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
3951
3952 tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
3953 [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
3954 indexed_triangle_set partial_mesh;
3955 std::vector<float> slice_z;
3956 std::vector<Polygons> bottom_contacts;
3957 for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
3958 Tree &tree = trees[tree_id];
3959 for (const Branch &branch : tree.branches) {
3960 // Triangulate the tube.
3961 partial_mesh.clear();
3962 std::pair<float, float> zspan = extrude_branch(branch.path, config, slicing_params, move_bounds, branch.has_root, partial_mesh);
3963 LayerIndex layer_begin = branch.has_root ?
3964 branch.path.front()->state.layer_idx :
3965 std::min(branch.path.front()->state.layer_idx, layer_idx_ceil(slicing_params, config, zspan.first));
3966 LayerIndex layer_end = (branch.has_tip ?
3967 branch.path.back()->state.layer_idx :
3968 std::max(branch.path.back()->state.layer_idx, layer_idx_floor(slicing_params, config, zspan.second))) + 1;
3969 slice_z.clear();
3970 for (LayerIndex layer_idx = layer_begin; layer_idx < layer_end; ++ layer_idx) {
3971 const double print_z = layer_z(slicing_params, config, layer_idx);
3972 const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
3973 slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
3974 }
3975
3976 std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
3977
3978 // ORCA: guard against empty slices from meshing.
3979 if (slices.empty())
3980 continue;
3981
3982 bottom_contacts.clear();
3983 // ORCA: trim tiny fragments to reduce degenerate polygon booleans.
3984 const double tiny_area = tiny_area_threshold();
3985 //FIXME parallelize?
3986 for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i) {
3987 // ORCA: safety offset when trimming collision/bed to improve robustness.
3988 slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true), ApplySafetyOffset::Yes); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
3989 slices[i] = intersection(slices[i], volumes.m_bed_area, ApplySafetyOffset::Yes);
3990 // Belt floor: clip branch slices against the belt surface plane.
3991 LayerIndex belt_idx = layer_begin + i;
3992 if (belt_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[belt_idx].empty())
3993 slices[i] = diff(slices[i], volumes.m_belt_floor[belt_idx]);
3994 remove_small(slices[i], tiny_area);
3995 }
3996
3997 size_t num_empty = 0;
3998
3999 if (slices.front().empty()) {
4000 // Some of the initial layers are empty.
4001 num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
4002 }
4003
4004 // ORCA: trim leading empty slices to keep layer indices aligned.
4005 if (num_empty >= slices.size())
4006 continue;
4007
4008 if (num_empty > 0) {
4009 slices.erase(slices.begin(), slices.begin() + num_empty);
4010 layer_begin += LayerIndex(num_empty);
4011 }
4012
4013 // ORCA: use the trimmed front slice as the contact reference.
4014 Polygons slice_front_contact = slices.front();
4015
4016 if (branch.has_root) {
4017 if (branch.path.front()->state.to_model_gracious) {
4018 if (config.settings.support_floor_layers > 0) {
4019 // If bottom Z gap is non-zero, keep bottom contacts even when not touching the model.
4020 Polygons contacts;
4021
4022 // ORCA: non-zero bottom Z should not be clipped by placeable areas.
4023 if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
4024 contacts = slice_front_contact;
4025 else {
4026 Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
4027 contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes);
4028 }
4029
4030 remove_small(contacts, tiny_area);
4031
4032 // ORCA: ensure bottom contacts exist if clipping removed them.
4033 if (contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty())
4034 contacts = slice_front_contact;
4035 if (!contacts.empty())
4036 bottom_contacts.emplace_back(std::move(contacts));
4037 }
4038 } else if (layer_begin > 0 && (volumes.m_belt_floor.empty() || num_empty == 0)) {
4039 // Belt-floor clipping makes initial slices empty often; without this
4040 // gate, "verylost" branches propagate rest_support down to layer 0 and
4041 // OOM on tall belt prints.
4042 // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
4043 struct BottomExtraSlice {
4044 Polygons polygons;
4045 double area;
4046 };
4047 std::vector<BottomExtraSlice> bottom_extra_slices;
4048 Polygons rest_support;
4049 coord_t bottom_radius = support_element_radius(config, *branch.path.front());
4050 // Belt printer (GeneratorOnly belt floor): the tilted belt surface is the
4051 // build surface, so a branch should terminate ON the belt with a thin tip,
4052 // not stamp its full footprint straight down to Z=0 and weld neighbouring
4053 // branches into a solid floor slab. m_belt_floor is only populated in that
4054 // mode, so it doubles as the gate (no effect on other printer types).
4055 const bool belt_mode = !volumes.m_belt_floor.empty();
4056 // Don't propagate further than 1.5 * bottom radius.
4057 //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height;
4058 LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height;
4059 LayerIndex layer_bottommost = (branch.path.front()->state.verylost && !belt_mode) ?
4060 // If the tree bottom is hanging in the air, bring it down to some surface.
4061 0 :
4062 // In belt mode never force-drop to Z=0 (the belt clip below handles
4063 // termination); otherwise the "verylost" branch welds into the slab.
4064 //FIXME the "verylost" branches should stop when crossing another support.
4065 std::max(0, layer_begin - layers_propagate_max);
4066 double support_area_min_radius = M_PI * sqr(double(config.branch_radius));
4067 double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius);
4068 // Only propagate until the rest area is smaller than this threshold.
4069 //double support_area_min = 0.1 * support_area_min_radius;
4070 for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
4071 LayerIndex collision_layer = (layer_idx == layer_begin - 1) ? layer_begin : layer_idx;
4072 Polygons collision = volumes.getCollision(0, collision_layer, false);
4073 rest_support = diff_clipped(rest_support.empty() ? slice_front_contact : rest_support, collision, ApplySafetyOffset::Yes);
4074 // Belt floor: clip propagated support at belt surface.
4075 bool belt_cut = false;
4076 if (layer_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[layer_idx].empty()) {
4077 double area_before = area(rest_support);
4078 rest_support = diff(rest_support, volumes.m_belt_floor[layer_idx]);
4079 // The belt counts as "reached" only when it actually removes part
4080 // of this branch's footprint. The belt half-plane is non-empty at
4081 // every near-belt layer, so testing non-emptiness alone would
4082 // terminate a laterally-distant branch ~1 layer above true contact,
4083 // leaving a gap. Require a real area reduction instead.
4084 belt_cut = belt_mode && area(rest_support) < area_before - tiny_area;
4085 }
4086 remove_small(rest_support, tiny_area);
4087 double rest_support_area = area(rest_support);
4088 if (rest_support_area < support_area_stop)
4089 // Don't propagate a fraction of the tree contact surface.
4090 break;
4091 bottom_extra_slices.push_back({ rest_support, rest_support_area });
4092 // Belt mode: once the belt surface actually starts cutting this branch
4093 // it has reached the belt — keep this last (belt-clipped) slice as the
4094 // contact and stop, rather than stamping the footprint further down.
4095 if (belt_cut)
4096 break;
4097 }
4098 // Now remove those bottom slices that are not supported at all.
4099#if 0
4100 while (! bottom_extra_slices.empty()) {
4101 Polygons this_bottom_contacts = intersection_clipped(
4102 bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
4103 if (area(this_bottom_contacts) < support_area_min)
4104 bottom_extra_slices.pop_back();
4105 else {
4106 // At least a fraction of the tree bottom is considered to be supported.
4107 if (config.settings.support_floor_layers > 0)
4108 // Turn this fraction of the tree bottom into a contact layer.
4109 bottom_contacts.emplace_back(std::move(this_bottom_contacts));
4110 break;
4111 }
4112 }
4113#endif
4114 // Belt mode: no solid support-floor pad under these branches — it is what
4115 // welds neighbouring belt-terminating branches into the dense Z=0 slab.
4116 // They simply taper out onto the tilted belt as distributed thin contacts.
4117 if (!belt_mode && config.settings.support_floor_layers > 0) {
4118 Polygons contacts;
4119 if (!bottom_extra_slices.empty()) {
4120 const int contact_idx = int(bottom_extra_slices.size()) - 1; // Use the lowest contact slice as the footprint.
4121
4122 // ORCA: non-zero bottom Z should not be clipped by placeable areas.
4123 if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
4124 contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, Polygons{volumes.m_bed_area}, ApplySafetyOffset::Yes);
4125 else {
4126 Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
4127 contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, placeable, ApplySafetyOffset::Yes);
4128 }
4129 } else {
4130 // Fallback: use the current contact slice when no propagation happened.
4131 if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0)
4132 contacts = slice_front_contact;
4133 else {
4134 Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {});
4135 contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes);
4136 }
4137 }
4138
4139 remove_small(contacts, tiny_area);
4140
4141 if (!contacts.empty())
4142 bottom_contacts.emplace_back(std::move(contacts));
4143
4144 // ORCA: ensure bottom contacts exist if clipping removed them.
4145 if (bottom_contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty())
4146 bottom_contacts.emplace_back(slice_front_contact);
4147 }
4148 layer_begin -= LayerIndex(bottom_extra_slices.size());
4149 slices.insert(slices.begin(), bottom_extra_slices.size(), {});
4150 auto it_dst = slices.begin();
4151 for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
4152 *it_dst ++ = std::move(it_src->polygons);
4153 }
4154
4155 // ORCA: retain bottom contacts even when no placeable areas intersect.
4156 // Skipped in belt mode (m_belt_floor populated) so we don't re-introduce a
4157 // solid floor pad for branches that terminate on the tilted belt surface.
4158 if (volumes.m_belt_floor.empty() &&
4159 branch.has_root && config.support_rests_on_model && branch.path.front()->state.layer_idx > 0 &&
4160 config.settings.support_floor_layers > 0 && config.z_distance_bottom_layers > 0 &&
4161 bottom_contacts.empty() && !slice_front_contact.empty())
4162 bottom_contacts.emplace_back(slice_front_contact);
4163
4164 }
4165 // ORCA: bottom contacts provide the footprint; interface layers are built later.
4166
4167 recover_pending_branch_roofs(interface_placer, branch.path, layer_begin, slices);
4168
4169 while (! slices.empty() && slices.back().empty()) {
4170 slices.pop_back();
4171 }
4172
4173 // ORCA: recompute layer_end after trimming trailing empty slices.
4174 layer_end = layer_begin + LayerIndex(slices.size());
4175
4176 if (layer_begin < layer_end) {
4177 LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin);
4178 LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end);
4179 size_t new_size = size_t(new_end - new_begin);
4180 if (tree.first_layer_id == -1) {
4181 } else if (tree.slices.capacity() < new_size) {
4182 std::vector<Slice> new_slices;
4183 new_slices.reserve(new_size);
4184 if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
4185 new_slices.insert(new_slices.end(), dif, {});
4186 append(new_slices, std::move(tree.slices));
4187 tree.slices.swap(new_slices);
4188 } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
4189 tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
4190 tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
4191 for (LayerIndex i = layer_begin; i != layer_end; ++ i) {
4192 int j = i - layer_begin;
4193 Polygons &src = slices[j];
4194 bool has_bottom_contacts = j < int(bottom_contacts.size()) && !bottom_contacts[j].empty();
4195
4196 // ORCA: preserve bottom contacts even if base polygons are empty.
4197 if (!src.empty() || has_bottom_contacts) {
4198 Slice &dst = tree.slices[i - new_begin];
4199 if (++ dst.num_branches > 1) {
4200 if (!src.empty())
4201 append(dst.polygons, std::move(src));
4202 if (has_bottom_contacts)
4203 append(dst.bottom_contacts, std::move(bottom_contacts[j]));
4204 } else {
4205 if (!src.empty())
4206 dst.polygons = std::move(src);
4207 if (has_bottom_contacts)
4208 dst.bottom_contacts = std::move(bottom_contacts[j]);
4209 }
4210 }
4211 }
4212
4213 tree.first_layer_id = new_begin;
4214 }
4215 }
4216 }
4217 }, tbb::simple_partitioner());
4218
4219 tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
4220 [&trees, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
4221 for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
4222 Tree &tree = trees[tree_id];
4223 for (Slice &slice : tree.slices)
4224 if (slice.num_branches > 1) {
4225 // ORCA: avoid union_ on empty containers.
4226 if (!slice.polygons.empty())
4227 slice.polygons = union_(slice.polygons);
4228 if (!slice.bottom_contacts.empty())
4229 slice.bottom_contacts = union_(slice.bottom_contacts);
4230
4231 slice.num_branches = 1;
4232 }
4233
4234 throw_on_cancel();
4235 }
4236 }, tbb::simple_partitioner());
4237
4238 size_t num_layers = 0;
4239 for (Tree &tree : trees)
4240 if (tree.first_layer_id >= 0)
4241 num_layers = std::max(num_layers, size_t(tree.first_layer_id + tree.slices.size()));
4242
4243 std::vector<Slice> slices(num_layers, Slice{});
4244 for (Tree &tree : trees)
4245 if (tree.first_layer_id >= 0) {
4246 for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i) {
4247 Slice &src = tree.slices[i - tree.first_layer_id];
4248 bool has_bottom_contacts = !src.bottom_contacts.empty();
4249
4250 // ORCA: preserve bottom contacts even if base polygons are empty.
4251 if (!src.polygons.empty() || has_bottom_contacts) {
4252 Slice &dst = slices[i];
4253
4254 if (++ dst.num_branches > 1) {
4255 if (!src.polygons.empty())
4256 append(dst.polygons, std::move(src.polygons));
4257 if (has_bottom_contacts)
4258 append(dst.bottom_contacts, std::move(src.bottom_contacts));
4259 } else {
4260 if (!src.polygons.empty())
4261 dst.polygons = std::move(src.polygons);
4262 if (has_bottom_contacts)
4263 dst.bottom_contacts = std::move(src.bottom_contacts);
4264 }
4265 }
4266 }
4267 }
4268
4269 tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
4270 [&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
4271 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
4272 Slice &slice = slices[layer_idx];
4273 assert(intermediate_layers[layer_idx] == nullptr);
4274 // ORCA: avoid union_ on empty inputs.
4275 Polygons base_layer_polygons = slice.polygons.empty() ? Polygons{} :
4276 (slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons));
4277 Polygons bottom_contact_polygons = slice.bottom_contacts.empty() ? Polygons{} :
4278 (slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts));
4279
4280 if (! base_layer_polygons.empty()) {
4281 // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
4282 base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
4283 //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
4284 // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
4285 base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)));
4286 }
4287
4288 // Subtract top contact layer polygons from support base.
4289 SupportGeneratorLayer *top_contact_layer = top_contacts.empty() ? nullptr : top_contacts[layer_idx];
4290 if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) {
4291 base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons);
4292 if (! bottom_contact_polygons.empty())
4293 //FIXME it may be better to clip bottom contacts with top contacts first after they are propagated to produce interface layers.
4294 bottom_contact_polygons = diff(bottom_contact_polygons, top_contact_layer->polygons);
4295 }
4296 if (! bottom_contact_polygons.empty()) {
4297 base_layer_polygons = diff(base_layer_polygons, bottom_contact_polygons);
4298 SupportGeneratorLayer *bottom_contact_layer = bottom_contacts[layer_idx] = &layer_allocate(
4299 layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
4300 bottom_contact_layer->polygons = std::move(bottom_contact_polygons);
4301 }
4302 if (! base_layer_polygons.empty()) {
4303 SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(
4304 layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
4305 base_layer->polygons = union_(base_layer_polygons);
4306 }
4307
4308 throw_on_cancel();
4309 }
4310 }, tbb::simple_partitioner());
4311}
void clear_all_but_object_collision()
Definition TreeModelVolumes.hpp:67
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 TreeSupport3D.cpp:3074
CGAL::AABB_tree< Traits > Tree
Definition CutSurface.cpp:2808
@ Positive
Definition TriangleMeshSlicer.hpp:28
ParentIndices parents
All elements in the layer above the current one that are supported by this element.
Definition TreeSupport3D.hpp:299

References Slic3r::TreeSupport3D::TreeModelVolumes::clear_all_but_object_collision(), Slic3r::TreeSupport3D::SupportElementState::layer_idx, Slic3r::TreeSupport3D::SupportElementStateBits::marked, Slic3r::MeshSlicingParams::mode, organic_draw_branches(), organic_smooth_branches_avoid_collisions(), Slic3r::TreeSupport3D::SupportElement::parents, Slic3r::MeshSlicingParams::Positive, Slic3r::range(), Slic3r::TreeSupport3D::SupportElementState::result_on_layer_is_set(), Slic3r::PrintObject::slicing_parameters(), Slic3r::TreeSupport3D::SupportElement::state, and Slic3r::TreeSupport3D::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()

void Slic3r::TreeSupport3D::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
3082{
3083 struct LayerCollisionCache {
3084 coord_t min_element_radius{ std::numeric_limits<coord_t>::max() };
3085 bool min_element_radius_known() const { return this->min_element_radius != std::numeric_limits<coord_t>::max(); }
3086 coord_t collision_radius{ 0 };
3087 std::vector<Linef> lines;
3088 AABBTreeIndirect::Tree<2, double> aabbtree_lines;
3089 bool empty() const { return this->lines.empty(); }
3090 };
3091 std::vector<LayerCollisionCache> layer_collision_cache;
3092 layer_collision_cache.reserve(1024);
3093 const SlicingParameters &slicing_params = print_object.slicing_parameters();
3094 for (const std::pair<SupportElement*, int>& element : elements_with_link_down) {
3095 LayerIndex layer_idx = element.first->state.layer_idx;
3096 if (size_t num_layers = layer_idx + 1; num_layers > layer_collision_cache.size()) {
3097 if (num_layers > layer_collision_cache.capacity())
3098 reserve_power_of_2(layer_collision_cache, num_layers);
3099 layer_collision_cache.resize(num_layers, {});
3100 }
3101 auto& l = layer_collision_cache[layer_idx];
3102 l.min_element_radius = std::min(l.min_element_radius, support_element_radius(config, *element.first));
3103 }
3104
3105 throw_on_cancel();
3106
3107 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_collision_cache.size()); ++layer_idx)
3108 if (LayerCollisionCache& l = layer_collision_cache[layer_idx]; !l.min_element_radius_known())
3109 l.min_element_radius = 0;
3110 else {
3111 //FIXME
3112 l.min_element_radius = 0;
3113 std::optional<std::pair<coord_t, std::reference_wrapper<const Polygons>>> res = volumes.get_collision_lower_bound_area(layer_idx, l.min_element_radius);
3114 assert(res.has_value());
3115 l.collision_radius = res->first;
3116 Lines alines = to_lines(res->second.get());
3117 l.lines.reserve(alines.size());
3118 for (const Line &line : alines)
3119 l.lines.push_back({ unscaled<double>(line.a), unscaled<double>(line.b) });
3120 l.aabbtree_lines = AABBTreeLines::build_aabb_tree_over_indexed_lines(l.lines);
3121 throw_on_cancel();
3122 }
3123
3124 struct CollisionSphere {
3125 const SupportElement& element;
3126 int element_below_id;
3127 const bool locked;
3128 float radius;
3129 // Current position, when nudged away from the collision.
3130 Vec3f position;
3131 // Previous position, for Laplacian smoothing.
3132 Vec3f prev_position;
3133 //
3134 Vec3f last_collision;
3135 double last_collision_depth;
3136 // Minimum Z for which the sphere collision will be evaluated.
3137 // Limited by the minimum sloping angle and by the bottom of the tree.
3138 float min_z{ -std::numeric_limits<float>::max() };
3139 // Maximum Z for which the sphere collision will be evaluated.
3140 // Limited by the minimum sloping angle and by the tip of the current branch.
3141 float max_z{ std::numeric_limits<float>::max() };
3142 uint32_t layer_begin;
3143 uint32_t layer_end;
3144 };
3145
3146 std::vector<CollisionSphere> collision_spheres;
3147 collision_spheres.reserve(elements_with_link_down.size());
3148 for (const std::pair<SupportElement*, int> &element_with_link : elements_with_link_down) {
3149 const SupportElement &element = *element_with_link.first;
3150 const int link_down = element_with_link.second;
3151 collision_spheres.push_back({
3152 element,
3153 link_down,
3154 // locked
3155 element.parents.empty() || (link_down == -1 && element.state.layer_idx > 0),
3156 unscaled<float>(support_element_radius(config, element)),
3157 // 3D position
3158 to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx)))
3159 });
3160 // Update min_z coordinate to min_z of the tree below.
3161 CollisionSphere &collision_sphere = collision_spheres.back();
3162 if (link_down != -1) {
3163 const size_t offset_below = linear_data_layers[element.state.layer_idx - 1];
3164 collision_sphere.min_z = collision_spheres[offset_below + link_down].min_z;
3165 } else
3166 collision_sphere.min_z = collision_sphere.position.z();
3167 }
3168 // Update max_z by propagating max_z from the tips of the branches.
3169 for (int collision_sphere_id = int(collision_spheres.size()) - 1; collision_sphere_id >= 0; -- collision_sphere_id) {
3170 CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id];
3171 if (collision_sphere.element.parents.empty())
3172 // Tip
3173 collision_sphere.max_z = collision_sphere.position.z();
3174 else {
3175 // Below tip
3176 const size_t offset_above = linear_data_layers[collision_sphere.element.state.layer_idx + 1];
3177 for (auto iparent : collision_sphere.element.parents) {
3178 float parent_z = collision_spheres[offset_above + iparent].max_z;
3179// collision_sphere.max_z = collision_sphere.max_z == std::numeric_limits<float>::max() ? parent_z : std::max(collision_sphere.max_z, parent_z);
3180 collision_sphere.max_z = std::min(collision_sphere.max_z, parent_z);
3181 }
3182 }
3183 }
3184 // Update min_z / max_z to limit the search Z span of a given sphere for collision detection.
3185 for (CollisionSphere &collision_sphere : collision_spheres) {
3186 //FIXME limit the collision span by the tree slope.
3187 collision_sphere.min_z = std::max(collision_sphere.min_z, collision_sphere.position.z() - collision_sphere.radius);
3188 collision_sphere.max_z = std::min(collision_sphere.max_z, collision_sphere.position.z() + collision_sphere.radius);
3189 collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, config, collision_sphere.min_z));
3190 assert(collision_sphere.layer_begin < layer_collision_cache.size());
3191 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);
3192 }
3193
3194 throw_on_cancel();
3195
3196 static constexpr const double collision_extra_gap = 0.1;
3197 static constexpr const double max_nudge_collision_avoidance = 0.5;
3198 static constexpr const double max_nudge_smoothing = 0.2;
3199 static constexpr const size_t num_iter = 100; // 1000;
3200
3201 // Orca:
3202 // Collision and Laplacian smoothing run iteratively; keep each candidate reachable from linked upper/lower layers to avoid accumulated drift.
3203 auto limit_candidate_to_linked_layers = [&collision_spheres, &linear_data_layers, &config](const size_t collision_sphere_id, Vec2d candidate) {
3204 auto constrain_to_anchor = [](Vec2d candidate, const Vec2d &current_pos, const Vec2d &anchor, double allowed_shift) {
3205 const Vec2d delta = candidate - anchor;
3206 const double candidate_dist = delta.norm();
3207 const double current_dist = (current_pos - anchor).norm();
3208 allowed_shift = std::max(allowed_shift, current_dist);
3209 return candidate_dist > allowed_shift && candidate_dist > EPSILON ?
3210 anchor + delta * (allowed_shift / candidate_dist) :
3211 candidate;
3212 };
3213
3214 const CollisionSphere &sphere = collision_spheres[collision_sphere_id];
3215 const LayerIndex layer_idx = sphere.element.state.layer_idx;
3216 const Vec2d current_pos = to_2d(sphere.position).cast<double>();
3217 const double current_radius = double(support_element_radius(config, sphere.element));
3218 const double maximum_move_distance_slow = double(config.maximum_move_distance_slow);
3219
3220 if (sphere.element_below_id != -1 && layer_idx > 0) {
3221 const size_t lower_id = linear_data_layers[layer_idx - 1] + size_t(sphere.element_below_id);
3222 if (lower_id < collision_spheres.size()) {
3223 const CollisionSphere &lower = collision_spheres[lower_id];
3224 const double lower_radius = double(support_element_radius(config, lower.element));
3225 const double allowed_shift = unscaled<double>(std::max(0., lower_radius - current_radius) + maximum_move_distance_slow);
3226 candidate = constrain_to_anchor(candidate, current_pos, to_2d(lower.prev_position).cast<double>(), allowed_shift);
3227 }
3228 }
3229
3230 const LayerIndex upper_layer_idx = layer_idx + 1;
3231 if (!sphere.element.parents.empty() && upper_layer_idx < LayerIndex(linear_data_layers.size())) {
3232 const size_t upper_offset = linear_data_layers[upper_layer_idx];
3233 for (int32_t parent_idx : sphere.element.parents) {
3234 const size_t upper_id = upper_offset + size_t(parent_idx);
3235 if (upper_id >= collision_spheres.size())
3236 continue;
3237 const CollisionSphere &upper = collision_spheres[upper_id];
3238 const double upper_radius = double(support_element_radius(config, upper.element));
3239 const double allowed_shift = unscaled<double>(std::max(0., current_radius - upper_radius) + maximum_move_distance_slow);
3240 candidate = constrain_to_anchor(candidate, current_pos, to_2d(upper.prev_position).cast<double>(), allowed_shift);
3241 }
3242 }
3243
3244 return candidate;
3245 };
3246
3247 for (size_t iter = 0; iter < num_iter; ++ iter) {
3248 // Back up prev position before Laplacian smoothing.
3249 for (CollisionSphere &collision_sphere : collision_spheres)
3250 collision_sphere.prev_position = collision_sphere.position;
3251 std::atomic<size_t> num_moved{ 0 };
3252 tbb::parallel_for(tbb::blocked_range<size_t>(0, collision_spheres.size()),
3253 [&collision_spheres, &layer_collision_cache, &slicing_params, &config, &linear_data_layers, &num_moved, &throw_on_cancel, &limit_candidate_to_linked_layers](const tbb::blocked_range<size_t> range) {
3254 for (size_t collision_sphere_id = range.begin(); collision_sphere_id < range.end(); ++ collision_sphere_id)
3255 if (CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id]; ! collision_sphere.locked) {
3256 // Calculate collision of multiple 2D layers against a collision sphere.
3257 collision_sphere.last_collision_depth = - std::numeric_limits<double>::max();
3258 for (uint32_t layer_id = collision_sphere.layer_begin; layer_id != collision_sphere.layer_end; ++ layer_id) {
3259 double dz = (layer_id - collision_sphere.element.state.layer_idx) * slicing_params.layer_height;
3260 if (double r2 = sqr(collision_sphere.radius) - sqr(dz); r2 > 0) {
3261 if (const LayerCollisionCache &layer_collision_cache_item = layer_collision_cache[layer_id]; ! layer_collision_cache_item.empty()) {
3262 size_t hit_idx_out;
3263 Vec2d hit_point_out;
3264 if (double dist = sqrt(AABBTreeLines::squared_distance_to_indexed_lines(
3265 layer_collision_cache_item.lines, layer_collision_cache_item.aabbtree_lines, Vec2d(to_2d(collision_sphere.position).cast<double>()),
3266 hit_idx_out, hit_point_out, r2)); dist >= 0.) {
3267 double collision_depth = sqrt(r2) - dist;
3268 if (collision_depth > collision_sphere.last_collision_depth) {
3269 collision_sphere.last_collision_depth = collision_depth;
3270 collision_sphere.last_collision = to_3d(hit_point_out.cast<float>(), float(layer_z(slicing_params, config, layer_id)));
3271 }
3272 }
3273 }
3274 }
3275 }
3276 if (collision_sphere.last_collision_depth > 0) {
3277 // Collision detected to be removed.
3278 // Nudge the circle center away from the collision.
3279 if (collision_sphere.last_collision_depth > EPSILON)
3280 // a little bit of hysteresis to detect end of
3281 ++ num_moved;
3282 // Limit collision-avoidance nudge per iteration.
3283 double nudge_dist = std::min(std::max(0., collision_sphere.last_collision_depth + collision_extra_gap), max_nudge_collision_avoidance);
3284 Vec2d nudge_vector = (to_2d(collision_sphere.position) - to_2d(collision_sphere.last_collision)).cast<double>().normalized() * nudge_dist;
3285 Vec2d candidate = to_2d(collision_sphere.position).cast<double>() + nudge_vector * nudge_dist;
3286 candidate = limit_candidate_to_linked_layers(collision_sphere_id, candidate);
3287 collision_sphere.position.head<2>() = candidate.cast<float>();
3288 }
3289 // Laplacian smoothing
3290 Vec2d avg{ 0, 0 };
3291 //const SupportElements &above = move_bounds[collision_sphere.element.state.layer_idx + 1];
3292 const size_t offset_above = linear_data_layers[collision_sphere.element.state.layer_idx + 1];
3293 double weight = 0.;
3294 for (auto iparent : collision_sphere.element.parents) {
3295 double w = collision_sphere.radius;
3296 avg += w * to_2d(collision_spheres[offset_above + iparent].prev_position.cast<double>());
3297 weight += w;
3298 }
3299 if (collision_sphere.element_below_id != -1) {
3300 const size_t offset_below = linear_data_layers[collision_sphere.element.state.layer_idx - 1];
3301 const double w = weight; // support_element_radius(config, move_bounds[element.state.layer_idx - 1][below]);
3302 avg += w * to_2d(collision_spheres[offset_below + collision_sphere.element_below_id].prev_position.cast<double>());
3303 weight += w;
3304 }
3305 avg /= weight;
3306 static constexpr const double smoothing_factor = 0.5;
3307 Vec2d old_pos = to_2d(collision_sphere.position).cast<double>();
3308 Vec2d new_pos = (1. - smoothing_factor) * old_pos + smoothing_factor * avg;
3309 Vec2d shift = new_pos - old_pos;
3310 double nudge_dist_max = shift.norm();
3311 // Limit Laplacian smoothing nudge per iteration.
3312 double nudge_dist = std::min(std::max(0., nudge_dist_max), max_nudge_smoothing);
3313 if (nudge_dist > 0.) {
3314 Vec2d candidate = old_pos + shift * (nudge_dist / nudge_dist_max);
3315 candidate = limit_candidate_to_linked_layers(collision_sphere_id, candidate);
3316 collision_sphere.position.head<2>() = candidate.cast<float>();
3317 }
3318
3319 throw_on_cancel();
3320 }
3321 });
3322#if 0
3323 std::vector<double> stat;
3324 for (CollisionSphere& collision_sphere : collision_spheres)
3325 if (!collision_sphere.locked)
3326 stat.emplace_back(collision_sphere.last_collision_depth);
3327 std::sort(stat.begin(), stat.end());
3328 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]);
3329#endif
3330 if (num_moved == 0)
3331 break;
3332 }
3333
3334 for (size_t i = 0; i < collision_spheres.size(); ++ i)
3335 elements_with_link_down[i].first->state.result_on_layer = scaled<coord_t>(to_2d(collision_spheres[i].position));
3336}
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:362
AABBTreeIndirect::Tree< LineType::Dim, typename LineType::Scalar > build_aabb_tree_over_indexed_lines(const std::vector< LineType > &lines)
Definition AABBTreeLines.hpp:172
LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
Definition TreeSupportCommon.hpp:641
LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
Definition TreeSupportCommon.hpp:652
long num_iter(const std::array< size_t, N > &idx, size_t gridsz)
Definition BruteforceOptimizer.hpp:18
indexed_triangle_set sphere(double rho, Portion portion, double fa)
Definition SupportTreeMesher.cpp:14
std::vector< Line > Lines
Definition Line.hpp:20
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:124
void reserve_power_of_2(VectorType &vector, size_t n)
Definition Utils.hpp:430

References Slic3r::AABBTreeLines::build_aabb_tree_over_indexed_lines(), Slic3r::empty(), EPSILON, Slic3r::TreeSupport3D::TreeModelVolumes::get_collision_lower_bound_area(), Slic3r::TreeSupport3D::SupportElementState::layer_idx, layer_idx_ceil(), layer_idx_floor(), layer_z(), Slic3r::TreeSupport3D::TreeSupportSettings::maximum_move_distance_slow, organic_smooth_branches_avoid_collisions(), Slic3r::TreeSupport3D::SupportElement::parents, Slic3r::range(), Slic3r::reserve_power_of_2(), Slic3r::TreeSupport3D::SupportElementState::result_on_layer, Slic3r::PrintObject::slicing_parameters(), Slic3r::TreeSupport3D::SupportElement::state, support_element_radius(), Slic3r::to_2d(), Slic3r::to_3d(), Slic3r::to_lines(), and Slic3r::unscaled().

Referenced by organic_draw_branches(), and organic_smooth_branches_avoid_collisions().

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

◆ polyline_sample_next_point_at_distance()

std::optional< std::pair< Point, size_t > > Slic3r::TreeSupport3D::polyline_sample_next_point_at_distance ( const Points & polyline,
const Point & start_pt,
size_t start_idx,
double dist )
static
546{
547 const double dist2 = sqr(dist);
548 const auto dist2i = int64_t(dist2);
549 const auto eps = scaled<double>(0.01);
550
551 for (size_t i = start_idx + 1; i < polyline.size(); ++ i) {
552 const Point p1 = polyline[i];
553 if ((p1 - start_pt).cast<int64_t>().squaredNorm() >= dist2i) {
554 // The end point is outside the circle with center "start_pt" and radius "dist".
555 const Point p0 = polyline[i - 1];
556 Vec2d v = (p1 - p0).cast<double>();
557 double l2v = v.squaredNorm();
558 if (l2v < sqr(eps)) {
559 // Very short segment.
560 Point c = (p0 + p1) / 2;
561 if (std::abs((start_pt - c).cast<double>().norm() - dist) < eps)
562 return std::pair<Point, size_t>{ c, i - 1 };
563 else
564 continue;
565 }
566 Vec2d p0f = (start_pt - p0).cast<double>();
567 // Foot point of start_pt into v.
568 Vec2d foot_pt = v * (p0f.dot(v) / l2v);
569 // Vector from foot point of "start_pt" to "start_pt".
570 Vec2d xf = p0f - foot_pt;
571 // Squared distance of "start_pt" from the ray (p0, p1).
572 double l2_from_line = xf.squaredNorm();
573 // Squared distance of an intersection point of a circle with center at the foot point.
574 if (double l2_intersection = dist2 - l2_from_line;
575 l2_intersection > - SCALED_EPSILON) {
576 // The ray (p0, p1) touches or intersects a circle centered at "start_pt" with radius "dist".
577 // Distance of the circle intersection point from the foot point.
578 l2_intersection = std::max(l2_intersection, 0.);
579 if ((v - foot_pt).cast<double>().squaredNorm() >= l2_intersection) {
580 // Intersection of the circle with the segment (p0, p1) is on the right side (close to p1) from the foot point.
581 Point p = p0 + (foot_pt + v * sqrt(l2_intersection / l2v)).cast<coord_t>();
583 return std::pair<Point, size_t>{ p, i - 1 };
584 }
585 }
586 }
587 }
588 return {};
589}
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
T foot_pt(const T &line_pt, const T &line_dir, const T &pt)
Definition Geometry.hpp:186

References Slic3r::cast(), Slic3r::Geometry::dist(), Slic3r::Geometry::foot_pt(), polyline_sample_next_point_at_distance(), Slic3r::scaled(), SCALED_EPSILON, Slic3r::sqr(), and validate_range().

Referenced by ensure_maximum_distance_polyline(), and polyline_sample_next_point_at_distance().

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

◆ precalculate()

LayerIndex Slic3r::TreeSupport3D::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 )
staticnodiscard

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
380{
381 // calculate top most layer that is relevant for support
382 LayerIndex max_layer = 0;
383 for (size_t object_id : object_ids) {
384 const PrintObject &print_object = *print.get_object(object_id);
385 const int num_raft_layers = int(config.raft_layers.size());
386 const int num_layers = int(print_object.layer_count()) + num_raft_layers;
387 int max_support_layer_id = 0;
388 for (int layer_id = std::max<int>(num_raft_layers, 1); layer_id < num_layers; ++ layer_id)
389 if (! overhangs[layer_id].empty())
390 max_support_layer_id = layer_id;
391 max_layer = std::max(max_support_layer_id - int(config.z_distance_top_layers), 0);
392 }
393 if (max_layer > 0)
394 // The actual precalculation happens in TreeModelVolumes.
395 volumes.precalculate(*print.get_object(object_ids.front()), max_layer, throw_on_cancel);
396 return max_layer;
397}
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:209

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

Referenced by generate_support_areas(), and precalculate().

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

◆ recover_pending_branch_roofs()

void Slic3r::TreeSupport3D::recover_pending_branch_roofs ( InterfacePlacer & interface_placer,
const std::vector< const SupportElement * > & branch_path,
const LayerIndex layer_begin,
std::vector< Polygons > & slices )
static
3747{
3748 if (! interface_placer.support_parameters.has_top_contacts)
3749 return;
3750
3751 for (auto it = branch_path.rbegin(); it != branch_path.rend(); ++ it) {
3752 const SupportElement &el = **it;
3754 break;
3755
3756 const LayerIndex slice_idx = el.state.layer_idx - layer_begin;
3757 if (slice_idx < 0 || slice_idx >= LayerIndex(slices.size()))
3758 continue;
3759 if (slices[size_t(slice_idx)].empty())
3760 continue;
3762 continue;
3763
3764 interface_placer.add_roof(std::move(slices[size_t(slice_idx)]), el.state.layer_idx, el.state.roof_recovery_dtt);
3765 }
3766}
void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
Definition TreeSupportCommon.hpp:750
size_t num_top_interface_layers
Definition SupportParameters.hpp:233
bool has_top_contacts
Definition SupportParameters.hpp:229
bool has_pending_roof_recovery() const
Definition TreeSupport3D.hpp:243

References Slic3r::TreeSupport3D::InterfacePlacer::add_roof(), Slic3r::empty(), Slic3r::TreeSupport3D::SupportElementState::has_pending_roof_recovery(), Slic3r::SupportParameters::has_top_contacts, Slic3r::TreeSupport3D::SupportElementState::layer_idx, Slic3r::SupportParameters::num_top_interface_layers, recover_pending_branch_roofs(), Slic3r::TreeSupport3D::SupportElementState::roof_recovery_dtt, Slic3r::TreeSupport3D::SupportElement::state, and Slic3r::TreeSupport3D::InterfacePlacer::support_parameters.

Referenced by recover_pending_branch_roofs().

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

◆ remove_deleted_elements()

void Slic3r::TreeSupport3D::remove_deleted_elements ( std::vector< SupportElements > & move_bounds)
static
2687{
2688 std::vector<int32_t> map_parents;
2689 std::vector<int32_t> map_current;
2690 for (LayerIndex layer_idx = LayerIndex(move_bounds.size()) - 1; layer_idx >= 0; -- layer_idx) {
2691 SupportElements &layer = move_bounds[layer_idx];
2692 map_current.clear();
2693 for (int32_t i = 0; i < int32_t(layer.size());) {
2694 SupportElement &element = layer[i];
2695 if (element.state.deleted) {
2696 if (map_current.empty()) {
2697 // Initialize with identity map.
2698 map_current.assign(layer.size(), 0);
2699 std::iota(map_current.begin(), map_current.end(), 0);
2700 }
2701 // Delete all "deleted" elements from the end of the layer vector.
2702 while (i < int32_t(layer.size()) && layer.back().state.deleted) {
2703 layer.pop_back();
2704 // Mark as deleted in the map.
2705 map_current[layer.size()] = -1;
2706 }
2707 assert(i == layer.size() || i + 1 < layer.size());
2708 if (i + 1 < int32_t(layer.size())) {
2709 element = std::move(layer.back());
2710 layer.pop_back();
2711 // Mark the current element as deleted.
2712 map_current[i] = -1;
2713 // Mark the moved element as moved to index i.
2714 map_current[layer.size()] = i;
2715 }
2716 } else {
2717 // Current element is not deleted. Update its parent indices.
2718 if (! map_parents.empty())
2719 for (int32_t &parent_idx : element.parents)
2720 parent_idx = map_parents[parent_idx];
2721 ++ i;
2722 }
2723 }
2724 std::swap(map_current, map_parents);
2725 }
2726}
bool deleted
Definition TreeSupport3D.hpp:141

References Slic3r::TreeSupport3D::SupportElementStateBits::deleted, Slic3r::TreeSupport3D::SupportElement::parents, remove_deleted_elements(), and Slic3r::TreeSupport3D::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()

Polygons Slic3r::TreeSupport3D::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 )
staticnodiscard

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.
840{
841 bool do_final_difference = last_step_offset_without_check == 0;
842 Polygons ret = safe_union(me); // ensure sane input
843
844 // Trim the collision polygons with the region of interest for diff() efficiency.
845 Polygons collision_trimmed_buffer;
846 auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& {
847 if (collision_trimmed_buffer.empty() && ! collision.empty())
848 collision_trimmed_buffer = ClipperUtils::clip_clipper_polygons_with_subject_bbox(collision, get_extents(ret).inflated(std::max((coord_t)0, distance) + SCALED_EPSILON));
849 return collision_trimmed_buffer;
850 };
851
852 if (distance == 0)
853 return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret);
854 if (safe_step_size < 0 || last_step_offset_without_check < 0) {
855 BOOST_LOG_TRIVIAL(warning) << "Offset increase got invalid parameter!";
856 tree_supports_show_error("Negative offset distance... How did you manage this ?"sv, true);
857 return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret);
858 }
859
860 coord_t step_size = safe_step_size;
861 int steps = distance > last_step_offset_without_check ? (distance - last_step_offset_without_check) / step_size : 0;
862 if (distance - steps * step_size > last_step_offset_without_check) {
863 if ((steps + 1) * step_size <= distance)
864 // This will be the case when last_step_offset_without_check >= safe_step_size
865 ++ steps;
866 else
867 do_final_difference = true;
868 }
869 if (steps + (distance < last_step_offset_without_check || (distance % step_size) != 0) < int(min_amount_offset) && min_amount_offset > 1) {
870 // yes one can add a bool as the standard specifies that a result from compare operators has to be 0 or 1
871 // reduce the stepsize to ensure it is offset the required amount of times
872 step_size = distance / min_amount_offset;
873 if (step_size >= safe_step_size) {
874 // effectivly reduce last_step_offset_without_check
875 step_size = safe_step_size;
876 steps = min_amount_offset;
877 } else
878 steps = distance / step_size;
879 }
880 // offset in steps
881 for (int i = 0; i < steps; ++ i) {
882 ret = diff(offset(ret, step_size, jtRound, scaled<float>(0.01)), collision_trimmed());
883 // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound.
884 if (i % 10 == 7)
885 ret = polygons_simplify(ret, scaled<double>(0.015));
886 }
887 // offset the remainder
888 float last_offset = distance - steps * step_size;
889 if (last_offset > SCALED_EPSILON)
890 ret = offset(ret, distance - steps * step_size, jtRound, scaled<float>(0.01));
891 ret = polygons_simplify(ret, scaled<double>(0.015));
892
893 if (do_final_difference)
894 ret = diff(ret, collision_trimmed());
895 return union_(ret);
896}
Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox)
Definition ClipperUtils.cpp:132
float distance(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:19
@ jtRound
Definition ClipperUtils.hpp:21

References Slic3r::ClipperUtils::clip_clipper_polygons_with_subject_bbox(), Slic3r::diff(), Slic3r::get_extents(), Slic3r::jtRound, Slic3r::offset(), Slic3r::polygons_simplify(), safe_offset_inc(), safe_union(), Slic3r::scaled(), SCALED_EPSILON, tree_supports_show_error(), and Slic3r::union_().

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

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

◆ safe_union()

Polygons Slic3r::TreeSupport3D::safe_union ( const Polygons first,
const Polygons second = {} )
staticnodiscard

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
796 {})
797{
798 // unionPolygons can slowly remove Polygons under certain circumstances, because of rounding issues (Polygons that have a thin area).
799 // 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
800 // 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:
801 /*
802 Polygons example;
803 Polygon exampleInner;
804 exampleInner.add(Point(120410,83599));//A
805 exampleInner.add(Point(120384,83643));//B
806 exampleInner.add(Point(120399,83618));//C
807 exampleInner.add(Point(120414,83591));//D
808 exampleInner.add(Point(120423,83570));//E
809 exampleInner.add(Point(120419,83580));//F
810 example.add(exampleInner);
811 for(int i=0;i<10;i++){
812 log("Iteration %d Example area: %f\n",i,area(example));
813 example=example.unionPolygons();
814 }
815*/
816
817 Polygons result;
818 if (! first.empty() || ! second.empty()) {
819 result = union_(first, second);
820 if (result.empty()) {
821 BOOST_LOG_TRIVIAL(debug) << "Caught an area destroying union, enlarging areas a bit.";
822 // 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.
823 result = union_(offset(to_polylines(first), scaled<float>(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled<float>(0.002), jtMiter, 1.2));
824 }
825 }
826
827 return result;
828}

References safe_union().

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

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

◆ sample_overhang_area()

void Slic3r::TreeSupport3D::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 )
1162{
1163 // 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
1164 // 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
1165 // made to change it again if there are actual issues encountered regarding supporting roofs.
1166 // 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,
1167 // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
1168 // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
1169 // for each pattern harms maintainability as it very well could be >100 LOC
1170 auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
1171 return generate_support_infill_lines(area, interface_placer.support_parameters, true, layer_idx, mesh_group_settings.support_roof_line_distance);
1172 };
1173
1174 LineInformations overhang_lines;
1175 // Track how many top contact / interface layers were already generated.
1176 size_t dtt_roof = 0;
1177 size_t layer_generation_dtt = 0;
1178
1179 if (large_horizontal_roof) {
1180 assert(num_support_roof_layers > 0);
1181 // 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).
1182 // To catch these cases the added roofs are saved to be evaluated later.
1183 std::vector<Polygons> added_roofs(num_support_roof_layers);
1184 Polygons last_overhang = overhang_area;
1185 for (dtt_roof = 0; dtt_roof < num_support_roof_layers && layer_idx - dtt_roof >= 1; ++ dtt_roof) {
1186 // here the roof is handled. If roof can not be added the branches will try to not move instead
1187 Polygons forbidden_next;
1188 {
1189 const bool min_xy_dist = interface_placer.config.xy_distance > interface_placer.config.xy_min_distance;
1190 const Polygons &forbidden_next_raw = interface_placer.config.support_rests_on_model ?
1191 interface_placer.volumes.getCollision(interface_placer.config.getRadius(0), layer_idx - (dtt_roof + 1), min_xy_dist) :
1192 interface_placer.volumes.getAvoidance(interface_placer.config.getRadius(0), layer_idx - (dtt_roof + 1), TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist);
1193 // prevent rounding errors down the line
1194 //FIXME maybe use SafetyOffset::Yes at the following diff() instead?
1195 forbidden_next = offset(union_ex(forbidden_next_raw), scaled<float>(0.005), jtMiter, 1.2);
1196 }
1197 Polygons overhang_area_next = diff(overhang_area, forbidden_next);
1198 if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) {
1199 // Next layer down the roof area would be to small so we have to insert our roof support here.
1200 if (dtt_roof > 0) {
1201 size_t dtt_before = dtt_roof - 1;
1202 // Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
1203 overhang_lines = split_lines(
1204 convert_lines_to_internal(interface_placer.volumes, interface_placer.config,
1205 ensure_maximum_distance_polyline(generate_roof_lines(last_overhang, layer_idx - dtt_before), connect_length, 1), layer_idx - dtt_before),
1206 [&interface_placer, layer_idx, dtt_before](const std::pair<Point, LineStatus> &p)
1207 { return evaluate_point_for_next_layer_function(interface_placer.volumes, interface_placer.config, layer_idx - dtt_before, p); })
1208 .first;
1209 }
1210 break;
1211 }
1212 added_roofs[dtt_roof] = overhang_area;
1213 last_overhang = std::move(overhang_area);
1214 overhang_area = std::move(overhang_area_next);
1215 }
1216
1217 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;
1218 // if the roof should be valid, check that the area does generate lines. This is NOT guaranteed.
1219 if (overhang_lines.empty() && dtt_roof != 0 && generate_roof_lines(overhang_area, layer_idx - layer_generation_dtt).empty())
1220 for (size_t idx = 0; idx < dtt_roof; idx++) {
1221 // check for every roof area that it has resulting lines. Remember idx 1 means the 2. layer of roof => higher idx == lower layer
1222 if (generate_roof_lines(added_roofs[idx], layer_idx - idx).empty()) {
1223 dtt_roof = idx;
1224 layer_generation_dtt = std::max(dtt_roof, size_t(1)) - 1;
1225 break;
1226 }
1227 }
1228 added_roofs.erase(added_roofs.begin() + dtt_roof, added_roofs.end());
1229 interface_placer.add_roofs(std::move(added_roofs), layer_idx);
1230 }
1231
1232 if (overhang_lines.empty()) {
1233 // 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,
1234 // but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour.
1235 // 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
1236 bool supports_roof = dtt_roof > 0;
1237 bool continuous_tips = !supports_roof && large_horizontal_roof;
1239 generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt,
1240 supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance),
1241 continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1);
1242 size_t point_count = 0;
1243 for (const Polyline &poly : polylines)
1244 point_count += poly.size();
1245 const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(3), coord_t(total_length(overhang_area) / connect_length)));
1246 if (point_count <= min_support_points) {
1247 // 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 support line width.
1248 // 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
1249 // 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 degrees so there is a fallback,
1250 // as some support is better than none.
1251 Polygons reduced_overhang_area = offset(union_ex(overhang_area), -interface_placer.config.support_line_width / 2.2, jtMiter, 1.2);
1254 ! reduced_overhang_area.empty() &&
1255 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)) ?
1256 reduced_overhang_area :
1257 overhang_area),
1258 connect_length, min_support_points);
1259 }
1260 overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof);
1261 }
1262
1263 assert(dtt_roof <= layer_idx);
1264 if (dtt_roof >= layer_idx && large_horizontal_roof)
1265 // Reached buildplate when generating contact, interface and base interface layers.
1266 interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof);
1267 else {
1268 // normal trees have to be generated
1269 const bool roof_enabled = num_support_roof_layers > 0;
1270 interface_placer.add_points_along_lines(
1271 // Sample along these lines
1272 overhang_lines,
1273 // First layer index to insert the tree tips or interfaces.
1274 layer_idx - dtt_roof,
1275 // Remaining roof tip layers.
1276 interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0,
1277 // Supports roof already? How many roof layers were already produced above these tips?
1278 dtt_roof,
1279 // Don't move until the following distance to top is reached.
1280 roof_enabled ? num_support_roof_layers - dtt_roof : 0);
1281 }
1282}
void add_roofs(std::vector< Polygons > &&new_roofs, const size_t insert_layer_idx)
Definition TreeSupportCommon.hpp:740
void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
Definition TreeSupportCommon.hpp:757
const TreeSupportSettings & config
Definition TreeSupportCommon.hpp:735
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 TreeSupport3D.cpp:923
const bool force_tip_to_roof
Definition TreeSupport3D.cpp:918
const TreeModelVolumes & volumes
Definition TreeSupport3D.cpp:916
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751
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 TreeSupport3D.cpp:703
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 TreeSupport3D.cpp:599
static std::pair< LineInformations, LineInformations > split_lines(const LineInformations &lines, EvaluatePointFn evaluatePoint)
Evaluates which points of some lines are not valid one layer below and which are. Assumes all points ...
Definition TreeSupport3D.cpp:517
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()...
Definition TreeSupport3D.cpp:492
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 TreeSupport3D.cpp:429
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:831
double total_length(const Polygons &polylines)
Definition Polygon.hpp:127
double minimum_roof_area
Definition TreeSupportCommon.hpp:180
coord_t support_tree_branch_distance
Definition TreeSupportCommon.hpp:232
coord_t support_roof_line_distance
Definition TreeSupportCommon.hpp:205

References Slic3r::TreeSupport3D::RichInterfacePlacer::add_points_along_lines(), Slic3r::TreeSupport3D::InterfacePlacer::add_roof_build_plate(), Slic3r::TreeSupport3D::InterfacePlacer::add_roofs(), Slic3r::area(), Slic3r::TreeSupport3D::InterfacePlacer::config, convert_lines_to_internal(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::empty(), ensure_maximum_distance_polyline(), evaluate_point_for_next_layer_function(), Slic3r::TreeSupport3D::TreeModelVolumes::Fast, Slic3r::TreeSupport3D::RichInterfacePlacer::force_tip_to_roof, generate_support_infill_lines(), Slic3r::TreeSupport3D::TreeModelVolumes::getAvoidance(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollision(), Slic3r::TreeSupport3D::TreeSupportSettings::getRadius(), Slic3r::jtMiter, Slic3r::TreeSupport3D::TreeSupportSettings::min_radius, Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::minimum_roof_area, Slic3r::offset(), sample_overhang_area(), Slic3r::scaled(), split_lines(), Slic3r::sqr(), Slic3r::TreeSupport3D::TreeSupportSettings::support_line_width, Slic3r::TreeSupport3D::InterfacePlacer::support_parameters, Slic3r::TreeSupport3D::TreeSupportSettings::support_rests_on_model, Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::support_roof_line_distance, Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::support_tree_branch_distance, Slic3r::to_polylines(), Slic3r::total_length(), Slic3r::union_ex(), Slic3r::TreeSupport3D::RichInterfacePlacer::volumes, Slic3r::TreeSupport3D::TreeSupportSettings::xy_distance, and Slic3r::TreeSupport3D::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()

void Slic3r::TreeSupport3D::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.
2595{
2596 assert(!elem.state.deleted);
2597 assert(layer_above != nullptr || elem.parents.empty());
2598
2599 // Based on the branch center point of the current layer, the point on the next (further up) layer is calculated.
2600 if (! elem.state.result_on_layer_is_set()) {
2601 BOOST_LOG_TRIVIAL(warning) << "Uninitialized support element";
2602 tree_supports_show_error("Uninitialized support element. A branch may be missing.\n"sv, true);
2603 return;
2604 }
2605
2606 if (layer_above)
2607 for (int32_t next_elem_idx : elem.parents) {
2608 assert(next_elem_idx >= 0);
2609 SupportElement &next_elem = (*layer_above)[next_elem_idx];
2610 assert(! next_elem.state.deleted);
2611 // 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.
2612 if (! next_elem.state.result_on_layer_is_set()) {
2613 // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1
2614 // it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance.
2615 // While this seems like a problem it may for example occur after merges.
2616 next_elem.state.result_on_layer = move_inside_if_outside(next_elem.influence_area, elem.state.result_on_layer);
2617 // do not call recursive because then amount of layers would be restricted by the stack size
2618 }
2619 // Mark the parent element as accessed from a valid child element.
2620 next_elem.state.marked = true;
2621 }
2622}
bool result_on_layer_is_set() const
Definition TreeSupport3D.hpp:188

References Slic3r::TreeSupport3D::SupportElementStateBits::deleted, Slic3r::TreeSupport3D::SupportElement::influence_area, Slic3r::TreeSupport3D::SupportElementStateBits::marked, move_inside_if_outside(), Slic3r::TreeSupport3D::SupportElement::parents, Slic3r::TreeSupport3D::SupportElementState::result_on_layer, Slic3r::TreeSupport3D::SupportElementState::result_on_layer_is_set(), set_points_on_areas(), Slic3r::TreeSupport3D::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()

void Slic3r::TreeSupport3D::set_to_model_contact_simple ( SupportElement & elem)
static
2625{
2627 elem.state.result_on_layer = best;
2628 BOOST_LOG_TRIVIAL(debug) << "Added NON gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << elem.state.layer_idx;
2629}

References Slic3r::TreeSupport3D::SupportElement::influence_area, Slic3r::TreeSupport3D::SupportElementState::layer_idx, move_inside_if_outside(), Slic3r::TreeSupport3D::SupportElementState::next_position, Slic3r::TreeSupport3D::SupportElementState::result_on_layer, set_to_model_contact_simple(), and Slic3r::TreeSupport3D::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()

void Slic3r::TreeSupport3D::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.
2644{
2645 SupportElement *last_successfull_layer = nullptr;
2646
2647 // 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.
2648 {
2649 SupportElement *elem = &first_elem;
2650 for (LayerIndex layer_check = elem->state.layer_idx;
2651 ! intersection(elem->influence_area, volumes.getPlaceableAreas(support_element_collision_radius(config, elem->state), layer_check, throw_on_cancel)).empty();
2652 elem = &move_bounds[++ layer_check][elem->parents.front()]) {
2653 assert(elem->state.layer_idx == layer_check);
2654 assert(! elem->state.deleted);
2655 assert(elem->state.to_model_gracious);
2656 last_successfull_layer = elem;
2657 if (elem->parents.size() != 1)
2658 // Reached merge point.
2659 break;
2660 }
2661 }
2662
2663 // Could not find valid placement, even though it should exist => error handling
2664 if (last_successfull_layer == nullptr) {
2665 BOOST_LOG_TRIVIAL(warning) << "No valid placement found for to model gracious element on layer " << first_elem.state.layer_idx;
2666 tree_supports_show_error("Could not fine valid placement on model! Just placing it down anyway. Could cause floating branches."sv, true);
2667 first_elem.state.to_model_gracious = false;
2668 set_to_model_contact_simple(first_elem);
2669 } else {
2670 // Found a gracious area above first_elem. Remove all below last_successfull_layer.
2671 {
2672 LayerIndex parent_layer_idx = first_elem.state.layer_idx;
2673 for (SupportElement *elem = &first_elem; elem != last_successfull_layer; elem = &move_bounds[++ parent_layer_idx][elem->parents.front()]) {
2674 assert(! elem->state.deleted);
2675 elem->state.deleted = true;
2676 }
2677 }
2678 // Guess a point inside the influence area, in which the branch will be placed in.
2679 const Point best = move_inside_if_outside(last_successfull_layer->influence_area, last_successfull_layer->state.next_position);
2680 last_successfull_layer->state.result_on_layer = best;
2681 BOOST_LOG_TRIVIAL(debug) << "Added gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << last_successfull_layer;
2682 }
2683}
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:411

References Slic3r::TreeSupport3D::SupportElementStateBits::deleted, Slic3r::TreeSupport3D::TreeModelVolumes::getPlaceableAreas(), Slic3r::TreeSupport3D::SupportElement::influence_area, Slic3r::intersection(), Slic3r::TreeSupport3D::SupportElementState::layer_idx, move_inside_if_outside(), Slic3r::TreeSupport3D::SupportElementState::next_position, Slic3r::TreeSupport3D::SupportElement::parents, Slic3r::TreeSupport3D::SupportElementState::result_on_layer, set_to_model_contact_simple(), set_to_model_contact_to_model_gracious(), Slic3r::TreeSupport3D::SupportElement::state, support_element_collision_radius(), Slic3r::TreeSupport3D::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:

◆ split_lines()

template<typename EvaluatePointFn>
std::pair< LineInformations, LineInformations > Slic3r::TreeSupport3D::split_lines ( const LineInformations & lines,
EvaluatePointFn evaluatePoint )
staticnodiscard

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.
518{
519 // assumes all Points on the current line are valid
520
521 LineInformations keep;
522 LineInformations set_free;
523 for (const std::vector<std::pair<Point, LineStatus>> &line : lines) {
524 bool current_keep = true;
525 LineInformation resulting_line;
526 for (const std::pair<Point, LineStatus> &me : line) {
527 if (evaluatePoint(me) != current_keep) {
528 if (! resulting_line.empty())
529 (current_keep ? &keep : &set_free)->emplace_back(std::move(resulting_line));
530 current_keep = !current_keep;
531 }
532 resulting_line.emplace_back(me);
533 }
534 if (! resulting_line.empty())
535 (current_keep ? &keep : &set_free)->emplace_back(std::move(resulting_line));
536 }
537 validate_range(keep);
538 validate_range(set_free);
539 return std::pair<std::vector<std::vector<std::pair<Point, LineStatus>>>, std::vector<std::vector<std::pair<Point, LineStatus>>>>(keep, set_free);
540}

References split_lines(), and validate_range().

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

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::TreeSupport3D::support_element_collision_radius ( const TreeSupportSettings & settings,
const SupportElement & elem )
inlinenodiscard
316{
317 return support_element_collision_radius(settings, elem.state);
318}

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

Here is the call graph for this function:

◆ support_element_collision_radius() [2/2]

coord_t Slic3r::TreeSupport3D::support_element_collision_radius ( const TreeSupportSettings & settings,
const SupportElementState & elem )
inlinenodiscard

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.
274{
276}

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

Referenced by increase_single_area(), merge_influence_areas_two_elements(), merge_support_element_states(), set_to_model_contact_to_model_gracious(), 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::TreeSupport3D::support_element_radius ( const TreeSupportSettings & settings,
const SupportElement & elem )
inlinenodiscard
311{
312 return support_element_radius(settings, elem.state);
313}

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

Here is the call graph for this function:

◆ support_element_radius() [2/2]

coord_t Slic3r::TreeSupport3D::support_element_radius ( const TreeSupportSettings & settings,
const SupportElementState & elem )
inlinenodiscard

Get the Radius, that this element will have.

Parameters
elem[in]The Element.
Returns
The radius the element has.
264{
265 return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
266}
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 TreeSupport3D.hpp:251

References Slic3r::TreeSupport3D::SupportElementState::elephant_foot_increases, getEffectiveDTT(), and Slic3r::TreeSupport3D::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(), and support_element_radius().

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

◆ tiny_area_threshold()

double Slic3r::TreeSupport3D::tiny_area_threshold ( )
inline
609{ return sqr(scaled<double>(0.001)); }

References Slic3r::scaled(), and Slic3r::sqr().

Referenced by create_layer_pathing(), 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:

◆ tree_supports_show_error()

void Slic3r::TreeSupport3D::tree_supports_show_error ( std::string_view message,
bool critical )
inline
612{ // todo Remove! ONLY FOR PUBLIC BETA!!
613 printf("Error: %s, critical: %d\n", message.data(), int(critical));
614#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
615 static bool g_showed_critical_error = false;
616 static bool g_showed_performance_warning = false;
617 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");
618 bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
619 (critical ? g_showed_critical_error : g_showed_performance_warning) = true;
620 if (show)
621 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(),
622 "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
623#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
624}

Referenced by Slic3r::TreeSupport3D::RichInterfacePlacer::add_point_as_influence_area(), create_layer_pathing(), create_nodes_from_area(), ensure_maximum_distance_polyline(), Slic3r::TreeSupport3D::TreeModelVolumes::getAvoidance(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollision(), Slic3r::TreeSupport3D::TreeModelVolumes::getCollisionHolefree(), Slic3r::TreeSupport3D::TreeModelVolumes::getPlaceableAreas(), Slic3r::TreeSupport3D::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::TreeSupport3D::triangulate_fan ( indexed_triangle_set & its,
int ifan,
int ibegin,
int iend )
2852{
2853 // at least 3 vertices, increasing order.
2854 assert(ibegin + 3 <= iend);
2855 assert(ibegin >= 0 && iend <= its.vertices.size());
2856 assert(ifan >= 0 && ifan < its.vertices.size());
2857 int num_faces = iend - ibegin;
2858 its.indices.reserve(its.indices.size() + num_faces * 3);
2859 for (int v = ibegin, u = iend - 1; v < iend; u = v ++) {
2860 if (flip_normals)
2861 its.indices.push_back({ ifan, u, v });
2862 else
2863 its.indices.push_back({ ifan, v, u });
2864 }
2865}

References triangulate_fan().

Referenced by extrude_branch(), and triangulate_fan().

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

◆ triangulate_strip()

void Slic3r::TreeSupport3D::triangulate_strip ( indexed_triangle_set & its,
int ibegin1,
int iend1,
int ibegin2,
int iend2 )
static
2868{
2869 // at least 3 vertices, increasing order.
2870 assert(ibegin1 + 3 <= iend1);
2871 assert(ibegin1 >= 0 && iend1 <= its.vertices.size());
2872 assert(ibegin2 + 3 <= iend2);
2873 assert(ibegin2 >= 0 && iend2 <= its.vertices.size());
2874 int n1 = iend1 - ibegin1;
2875 int n2 = iend2 - ibegin2;
2876 its.indices.reserve(its.indices.size() + (n1 + n2) * 3);
2877
2878 // For the first vertex of 1st strip, find the closest vertex on the 2nd strip.
2879 int istart2 = ibegin2;
2880 {
2881 const Vec3f &p1 = its.vertices[ibegin1];
2882 auto d2min = std::numeric_limits<float>::max();
2883 for (int i = ibegin2; i < iend2; ++ i) {
2884 const Vec3f &p2 = its.vertices[i];
2885 const float d2 = (p2 - p1).squaredNorm();
2886 if (d2 < d2min) {
2887 d2min = d2;
2888 istart2 = i;
2889 }
2890 }
2891 }
2892
2893 // Now triangulate the strip zig-zag fashion taking always the shortest connection if possible.
2894 for (int u = ibegin1, v = istart2; n1 > 0 || n2 > 0;) {
2895 bool take_first;
2896 int u2, v2;
2897 auto update_u2 = [&u2, u, ibegin1, iend1]() {
2898 u2 = u;
2899 if (++ u2 == iend1)
2900 u2 = ibegin1;
2901 };
2902 auto update_v2 = [&v2, v, ibegin2, iend2]() {
2903 v2 = v;
2904 if (++ v2 == iend2)
2905 v2 = ibegin2;
2906 };
2907 if (n1 == 0) {
2908 take_first = false;
2909 update_v2();
2910 } else if (n2 == 0) {
2911 take_first = true;
2912 update_u2();
2913 } else {
2914 update_u2();
2915 update_v2();
2916 float l1 = (its.vertices[u2] - its.vertices[v]).squaredNorm();
2917 float l2 = (its.vertices[v2] - its.vertices[u]).squaredNorm();
2918 take_first = l1 < l2;
2919 }
2920 if (take_first) {
2921 its.indices.push_back({ u, u2, v });
2922 -- n1;
2923 u = u2;
2924 } else {
2925 its.indices.push_back({ u, v2, v });
2926 -- n2;
2927 v = v2;
2928 }
2929 }
2930}
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:177

References Slic3r::l2(), and triangulate_strip().

Referenced by extrude_branch(), and triangulate_strip().

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

◆ validate_range() [1/7]

void Slic3r::TreeSupport3D::validate_range ( const LineInformation & lines)
inlinestatic
134{
135 for (const auto& p : lines)
136 validate_range(p.first);
137}

References validate_range().

Here is the call graph for this function:

◆ validate_range() [2/7]

void Slic3r::TreeSupport3D::validate_range ( const LineInformations & lines)
inlinestatic
140{
141 for (const LineInformation &l : lines)
143}

References validate_range().

Here is the call graph for this function:

◆ validate_range() [3/7]

void Slic3r::TreeSupport3D::validate_range ( const MultiPoint & mp)
inlinestatic
117{
119}

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

Here is the call graph for this function:

◆ validate_range() [4/7]

void Slic3r::TreeSupport3D::validate_range ( const Point & pt)
inlinestatic
104{
105 static constexpr const int32_t hi = 65536 * 16384;
106 if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi)
107 throw RuntimeError("Coordinate outside allowed range");
108}

Referenced by Slic3r::TreeSupport3D::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]

void Slic3r::TreeSupport3D::validate_range ( const Points & points)
inlinestatic
111{
112 for (const Point &p : points)
114}

References validate_range().

Here is the call graph for this function:

◆ validate_range() [6/7]

void Slic3r::TreeSupport3D::validate_range ( const Polygons & polygons)
inlinestatic
122{
123 for (const Polygon &p : polygons)
125}

References validate_range().

Here is the call graph for this function:

◆ validate_range() [7/7]

void Slic3r::TreeSupport3D::validate_range ( const Polylines & polylines)
inlinestatic
128{
129 for (const Polyline &p : polylines)
131}

References validate_range().

Here is the call graph for this function:

Variable Documentation

◆ SUPPORT_TREE_AVOID_SUPPORT_BLOCKER

const bool Slic3r::TreeSupport3D::SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true
staticconstexpr

◆ SUPPORT_TREE_EXPONENTIAL_FACTOR

const double Slic3r::TreeSupport3D::SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5
staticconstexpr