OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::TreeSupport3D::TreeModelVolumes Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Support/TreeModelVolumes.hpp>

Collaboration diagram for Slic3r::TreeSupport3D::TreeModelVolumes:

Classes

class  LayerPolygonCache
class  RadiusLayerPolygonCache

Public Types

enum class  AvoidanceType : int8_t { Slow , FastSafe , Fast , Count }

Public Member Functions

 TreeModelVolumes ()=default
 TreeModelVolumes (const PrintObject &print_object, const BuildVolume &build_volume, coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, const std::vector< Polygons > &additional_excluded_areas={})
 TreeModelVolumes (TreeModelVolumes &&)=default
TreeModelVolumes & operator= (TreeModelVolumes &&)=default
 TreeModelVolumes (const TreeModelVolumes &)=delete
TreeModelVolumes & operator= (const TreeModelVolumes &)=delete
void clear ()
void clear_all_but_object_collision ()
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.
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 on this layer.
std::optional< std::pair< coord_t, std::reference_wrapper< const Polygons > > > get_collision_lower_bound_area (LayerIndex layer_id, coord_t max_radius) const
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.
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 layer.
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. This is an abstract representation not equal with the outline. See calculateWallRestrictions for better description.
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 value.
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.

Public Attributes

Polygon m_bed_area
std::vector< Polygons > m_belt_floor

Private Types

using RadiusLayerPair = std::pair<coord_t, LayerIndex>
 Convenience typedef for the keys to the caches.

Private Member Functions

const Polygons & getCollisionHolefree (coord_t radius, LayerIndex layer_idx) const
 Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.
coord_t ceilRadius (const coord_t radius) const
 Round radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value.
void calculateCollision (const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
 Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
void calculateCollision (const coord_t radius, const LayerIndex max_layer_idx, std::function< void()> throw_on_cancel)
void calculateCollisionHolefree (const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
 Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.
void calculateCollisionHolefree (RadiusLayerPair key)
 Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.
void calculateAvoidance (const std::vector< RadiusLayerPair > &keys, bool to_build_plate, bool to_model, std::function< void()> throw_on_cancel)
 Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.
void calculateAvoidance (RadiusLayerPair key, bool to_build_plate, bool to_model)
 Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.
void calculatePlaceables (const coord_t radius, const LayerIndex max_required_layer, std::function< void()> throw_on_cancel)
 Creates the areas where a branch of a given radius can be place on the model. Result is saved in the cache.
void calculatePlaceables (const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
 Creates the areas where a branch of a given radius can be placed on the model. Result is saved in the cache.
void calculateWallRestrictions (const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
 Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object).
void calculateWallRestrictions (RadiusLayerPair key)
 Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). These areas are at least xy_min_dist wide. When calculating it is always assumed that every wall is printed on top of another (as in has an overlap with the wall a layer below). Result is saved in the corresponding cache.
RadiusLayerPolygonCache & avoidance_cache (const AvoidanceType type, const bool to_model)
const RadiusLayerPolygonCache & avoidance_cache (const AvoidanceType type, const bool to_model) const

Private Attributes

coord_t m_max_move
 The maximum distance that the center point of a tree branch may move in consecutive layers if it has to avoid the model.
coord_t m_max_move_slow
 The maximum distance that the centre-point of a tree branch may move in consecutive layers if it does not have to avoid the model.
coord_t m_min_resolution
 The smallest maximum resolution for simplify.
bool m_precalculated = false
size_t m_current_outline_idx
 The index to access the outline corresponding with the currently processing mesh.
coord_t m_current_min_xy_dist
 The minimum required clearance between the model and the tree branches.
coord_t m_current_min_xy_dist_delta
 The difference between the minimum required clearance between the model and the tree branches and the regular one.
bool m_support_rests_on_model
 Does at least one mesh allow support to rest on a model.
coord_t m_increase_until_radius
 Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.
Polygons m_machine_border
 Polygons representing the limits of the printable area of the machine.
std::vector< std::pair< TreeSupportMeshGroupSettings, std::vector< Polygons > > > m_layer_outlines
 Storage for layer outlines and the corresponding settings of the meshes grouped by meshes with identical setting.
std::vector< Polygons > m_anti_overhang
 Storage for areas that should be avoided, like support blocker or previous generated trees.
std::vector< coord_t > m_ignorable_radii
 Radii that can be ignored by ceilRadius as they will never be requested, sorted.
coord_t m_radius_0
 Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0.
std::vector< double > m_raft_layers
RadiusLayerPolygonCache m_collision_cache
 Caches for the collision, avoidance and areas on the model where support can be placed safely at given radius and layer indices.
RadiusLayerPolygonCache m_collision_cache_holefree
RadiusLayerPolygonCache m_avoidance_cache
RadiusLayerPolygonCache m_avoidance_cache_slow
RadiusLayerPolygonCache m_avoidance_cache_to_model
RadiusLayerPolygonCache m_avoidance_cache_to_model_slow
RadiusLayerPolygonCache m_placeable_areas_cache
RadiusLayerPolygonCache m_avoidance_cache_holefree
 Caches to avoid holes smaller than the radius until which the radius is always increased, as they are free of holes. Also called safe avoidances, as they are safe regarding not running into holes.
RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model
RadiusLayerPolygonCache m_wall_restrictions_cache
 Caches to represent walls not allowed to be passed over.
RadiusLayerPolygonCache m_wall_restrictions_cache_min

Detailed Description

Member Typedef Documentation

◆ RadiusLayerPair

Convenience typedef for the keys to the caches.

Member Enumeration Documentation

◆ AvoidanceType

Enumerator
Slow 
FastSafe 
Fast 
Count 
82 {
83 Slow,
84 FastSafe,
85 Fast,
86 Count
87 };
@ Count
Definition ArcFitter.hpp:18

Constructor & Destructor Documentation

◆ TreeModelVolumes() [1/4]

Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes ( )
default

Referenced by TreeModelVolumes(), TreeModelVolumes(), avoidance_cache(), getAvoidance(), getCollision(), getCollisionHolefree(), getPlaceableAreas(), getWallRestriction(), operator=(), and operator=().

Here is the caller graph for this function:

◆ TreeModelVolumes() [2/4]

Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes ( const PrintObject & print_object,
const BuildVolume & build_volume,
coord_t max_move,
coord_t max_move_slow,
size_t current_mesh_idx,
const std::vector< Polygons > & additional_excluded_areas = {} )
explicit
76 :
77 // -2 to avoid rounding errors
78 m_max_move{ std::max<coord_t>(max_move - 2, 0) }, m_max_move_slow{ std::max<coord_t>(max_move_slow - 2, 0) },
79#ifdef SLIC3R_TREESUPPORTS_PROGRESS
80 m_progress_multiplier{ progress_multiplier }, m_progress_offset{ progress_offset },
81#endif // SLIC3R_TREESUPPORTS_PROGRESS
82 m_machine_border{ calculateMachineBorderCollision(build_volume.polygon()) }
83{
84 m_bed_area = build_volume.polygon();
85#if 0
86 std::unordered_map<size_t, size_t> mesh_to_layeroutline_idx;
87 for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); ++ mesh_idx) {
88 SliceMeshStorage mesh = storage.meshes[mesh_idx];
89 bool added = false;
90 for (size_t idx = 0; idx < m_layer_outlines.size(); ++ idx)
91 if (TreeSupport::TreeSupportSettings(m_layer_outlines[idx].first) == TreeSupport::TreeSupportSettings(mesh.settings)) {
92 added = true;
93 mesh_to_layeroutline_idx[mesh_idx] = idx;
94 }
95 if (! added) {
96 mesh_to_layeroutline_idx[mesh_idx] = m_layer_outlines.size();
97 m_layer_outlines.emplace_back(mesh.settings, std::vector<Polygons>(storage.support.supportLayers.size(), Polygons()));
98 }
99 }
100 for (size_t idx = 0; idx < m_layer_outlines.size(); ++ idx) {
101 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_layer_outlines[idx].second.size()),
102 [&](const tbb::blocked_range<size_t> &range) {
103 for (const size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
104 m_layer_outlines[idx].second[layer_idx] = union_(m_layer_outlines[idx].second[layer_idx]);
105 });
106 }
107 m_current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx];
108
109#else
110 {
111 m_anti_overhang = print_object.slice_support_blockers();
112 TreeSupportMeshGroupSettings mesh_settings(print_object);
113 const TreeSupportSettings config{ mesh_settings, print_object.slicing_parameters() };
114 m_current_min_xy_dist = config.xy_min_distance;
116 assert(m_current_min_xy_dist_delta >= 0);
117 m_increase_until_radius = config.increase_radius_until_radius;
118 m_radius_0 = config.getRadius(0);
119 m_raft_layers = config.raft_layers;
120 // Support blockers are consumed in the same index space as m_layer_outlines
121 // (object layer i lives at index num_raft_layers + i), but
122 // slice_support_blockers() returns them in object-layer space. Shift them.
123 //
124 // The belt surface is deliberately NOT a blocker. A blocker is a collision,
125 // and a branch descending onto a collision slides off it: on a belt that
126 // walks the branch down the tilted surface, ahead of the part, until it
127 // reaches the bottom layer floating in mid-air. The belt is where branches
128 // END: organic_draw_branches() clips their slices with m_belt_floor and the
129 // first clipped slice is the contact.
130 {
131 const size_t num_raft = m_raft_layers.size();
132 if (num_raft > 0 && ! m_anti_overhang.empty())
133 m_anti_overhang.insert(m_anti_overhang.begin(), num_raft, Polygons{});
134 }
136
137 m_layer_outlines.emplace_back(mesh_settings, std::vector<Polygons>{});
138 std::vector<Polygons> &outlines = m_layer_outlines.front().second;
139 size_t num_raft_layers = m_raft_layers.size();
140 size_t num_layers = print_object.layer_count() + num_raft_layers;
141 outlines.assign(num_layers, Polygons{});
142 tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
143 [&](const tbb::blocked_range<size_t> &range) {
144 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
145 outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution);
146 });
147
148 // Belt floor: pre-compute belt surface polygon per-layer for clipping.
149 // Branches grow toward the belt and their slices are clipped at the belt
150 // surface in organic_draw_branches(). The organic pipeline works in LOCAL
151 // Z (no global_z_offset), so use local z_shift and local print_z.
152 {
153 const auto &slicing_params = print_object.slicing_parameters();
154 const auto &pcfg2 = print_object.print()->config();
155 BeltFloorContext ctx;
156 ctx.init_local(slicing_params, pcfg2, print_object.belt_global_z_offset());
157 if (ctx.is_active()) {
158 m_belt_floor = ctx.compute_per_layer_floors(num_layers, [&](size_t layer_idx) -> double {
159 // Object layers: local print_z (subtract global offset).
160 if (layer_idx >= num_raft_layers)
161 return print_object.get_layer(layer_idx - num_raft_layers)->print_z
162 - print_object.belt_global_z_offset();
163 // Belt raft layers (below the object): each carries its own
164 // local print_z in m_raft_layers. The belt floor is a tilted
165 // plane, so the half-plane to clip grows as print_z drops —
166 // using 0 here clipped every below-object layer against the
167 // Z=0 belt surface, under-clipping the raft region and leaving
168 // a dense support mass below the floor.
169 return (layer_idx < m_raft_layers.size()) ? m_raft_layers[layer_idx] : 0.;
170 });
171 }
172 }
173 }
174#endif
175
177 m_min_resolution = std::numeric_limits<coord_t>::max();
178 for (auto data_pair : m_layer_outlines) {
179 m_support_rests_on_model |= ! data_pair.first.support_material_buildplate_only;
180 m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution);
181 }
182
183#if 0
184 for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) {
185 SliceMeshStorage mesh = storage.meshes[mesh_idx];
186 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_layer_outlines[mesh_to_layeroutline_idx[mesh_idx]].second.size()),
187 [&](const tbb::blocked_range<size_t> &range) {
188 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
189 if (layer_idx < mesh.layer_nr_max_filled_layer) {
190 Polygons outline = extractOutlineFromMesh(mesh, layer_idx);
191 append(m_layer_outlines[mesh_to_layeroutline_idx[mesh_idx]].second[layer_idx], outline);
192 }
193 });
194 }
195#endif
196
197 if (!additional_excluded_areas.empty()) {
198 m_anti_overhang.resize(std::max(m_anti_overhang.size(), additional_excluded_areas.size()));
199 tbb::parallel_for(tbb::blocked_range<size_t>(0, additional_excluded_areas.size()),
200 [&](const tbb::blocked_range<size_t> &range) {
201 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
202 append(m_anti_overhang[layer_idx], additional_excluded_areas[layer_idx]);
203 m_anti_overhang[layer_idx] = union_(m_anti_overhang[layer_idx]);
204 }
205 });
206 }
207}
bool m_support_rests_on_model
Does at least one mesh allow support to rest on a model.
Definition TreeModelVolumes.hpp:459
coord_t m_current_min_xy_dist_delta
The difference between the minimum required clearance between the model and the tree branches and the...
Definition TreeModelVolumes.hpp:455
size_t m_current_outline_idx
The index to access the outline corresponding with the currently processing mesh.
Definition TreeModelVolumes.hpp:447
coord_t m_current_min_xy_dist
The minimum required clearance between the model and the tree branches.
Definition TreeModelVolumes.hpp:451
Polygon m_bed_area
Definition TreeModelVolumes.hpp:178
std::vector< Polygons > m_anti_overhang
Storage for areas that should be avoided, like support blocker or previous generated trees.
Definition TreeModelVolumes.hpp:493
coord_t m_increase_until_radius
Increase radius in the resulting drawn branches, even if the avoidance does not allow it....
Definition TreeModelVolumes.hpp:479
std::vector< double > m_raft_layers
Definition TreeModelVolumes.hpp:505
coord_t m_max_move_slow
The maximum distance that the centre-point of a tree branch may move in consecutive layers if it does...
Definition TreeModelVolumes.hpp:437
coord_t m_radius_0
Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0.
Definition TreeModelVolumes.hpp:502
coord_t m_max_move
The maximum distance that the center point of a tree branch may move in consecutive layers if it has ...
Definition TreeModelVolumes.hpp:432
Polygons m_machine_border
Polygons representing the limits of the printable area of the machine.
Definition TreeModelVolumes.hpp:485
coord_t m_min_resolution
The smallest maximum resolution for simplify.
Definition TreeModelVolumes.hpp:441
std::vector< std::pair< TreeSupportMeshGroupSettings, std::vector< Polygons > > > m_layer_outlines
Storage for layer outlines and the corresponding settings of the meshes grouped by meshes with identi...
Definition TreeModelVolumes.hpp:489
std::vector< Polygons > m_belt_floor
Definition TreeModelVolumes.hpp:181
static Polygons calculateMachineBorderCollision(Polygon machine_border)
Definition TreeModelVolumes.cpp:54
auto range(Cont &&cont)
Definition libslic3r.h:405
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:307
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
Definition Polygon.cpp:685

References Slic3r::PrintObject::belt_global_z_offset(), Slic3r::TreeSupport3D::calculateMachineBorderCollision(), Slic3r::BeltFloorContext::compute_per_layer_floors(), Slic3r::Print::config(), Slic3r::PrintObject::get_layer(), Slic3r::TreeSupport3D::TreeSupportSettings::getRadius(), Slic3r::TreeSupport3D::TreeSupportSettings::increase_radius_until_radius, Slic3r::BeltFloorContext::init_local(), Slic3r::BeltFloorContext::is_active(), Slic3r::PrintObject::layer_count(), Slic3r::Layer::lslices, m_anti_overhang, m_bed_area, m_belt_floor, m_current_min_xy_dist, m_current_min_xy_dist_delta, m_current_outline_idx, m_increase_until_radius, m_layer_outlines, m_machine_border, m_max_move, m_max_move_slow, m_min_resolution, m_radius_0, m_raft_layers, m_support_rests_on_model, Slic3r::BuildVolume::polygon(), Slic3r::polygons_simplify(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::print(), Slic3r::Layer::print_z, Slic3r::TreeSupport3D::TreeSupportSettings::raft_layers, Slic3r::range(), Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::resolution, Slic3r::PrintObject::slice_support_blockers(), Slic3r::PrintObject::slicing_parameters(), Slic3r::to_polygons(), Slic3r::TreeSupport3D::TreeSupportSettings::xy_distance, and Slic3r::TreeSupport3D::TreeSupportSettings::xy_min_distance.

Here is the call graph for this function:

◆ TreeModelVolumes() [3/4]

Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes ( TreeModelVolumes && )
default

References TreeModelVolumes().

Here is the call graph for this function:

◆ TreeModelVolumes() [4/4]

Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes ( const TreeModelVolumes & )
delete

References TreeModelVolumes().

Here is the call graph for this function:

Member Function Documentation

◆ avoidance_cache() [1/2]

RadiusLayerPolygonCache & Slic3r::TreeSupport3D::TreeModelVolumes::avoidance_cache ( const AvoidanceType type,
const bool to_model )
inlineprivate
526 {
527 if (to_model) {
528 switch (type) {
531 case AvoidanceType::Count: assert(false);
533 }
534 } else {
535 switch (type) {
538 case AvoidanceType::Count: assert(false);
540 }
541 }
542 assert(false);
543 return m_avoidance_cache;
544 }
RadiusLayerPolygonCache m_avoidance_cache_to_model_slow
Definition TreeModelVolumes.hpp:516
RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model
Definition TreeModelVolumes.hpp:524
RadiusLayerPolygonCache m_avoidance_cache_slow
Definition TreeModelVolumes.hpp:514
RadiusLayerPolygonCache m_avoidance_cache
Definition TreeModelVolumes.hpp:513
RadiusLayerPolygonCache m_avoidance_cache_holefree
Caches to avoid holes smaller than the radius until which the radius is always increased,...
Definition TreeModelVolumes.hpp:523
RadiusLayerPolygonCache m_avoidance_cache_to_model
Definition TreeModelVolumes.hpp:515
@ FastSafe
Definition TreeModelVolumes.hpp:84
@ Fast
Definition TreeModelVolumes.hpp:85
@ Count
Definition TreeModelVolumes.hpp:86
@ Slow
Definition TreeModelVolumes.hpp:83

References Count, Fast, FastSafe, m_avoidance_cache, m_avoidance_cache_holefree, m_avoidance_cache_holefree_to_model, m_avoidance_cache_slow, m_avoidance_cache_to_model, m_avoidance_cache_to_model_slow, and Slow.

Referenced by avoidance_cache(), calculateAvoidance(), and getAvoidance().

Here is the caller graph for this function:

◆ avoidance_cache() [2/2]

const RadiusLayerPolygonCache & Slic3r::TreeSupport3D::TreeModelVolumes::avoidance_cache ( const AvoidanceType type,
const bool to_model ) const
inlineprivate
545 {
546 return const_cast<TreeModelVolumes*>(this)->avoidance_cache(type, to_model);
547 }
RadiusLayerPolygonCache & avoidance_cache(const AvoidanceType type, const bool to_model)
Definition TreeModelVolumes.hpp:526

References TreeModelVolumes(), and avoidance_cache().

Here is the call graph for this function:

◆ calculateAvoidance() [1/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateAvoidance ( const std::vector< RadiusLayerPair > & keys,
bool to_build_plate,
bool to_model,
std::function< void()> throw_on_cancel )
private

Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.

The result is a 2D area that would cause nodes of radius radius to collide with the model. Result is saved in the cache.

Parameters
keysRadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
674{
675 // For every RadiusLayer pair there are 3 avoidances that have to be calculated.
676 // Prepare tasks for parallelization.
677 struct AvoidanceTask {
678 AvoidanceType type;
679 coord_t radius;
680 LayerIndex max_required_layer;
681 bool to_model;
682 LayerIndex start_layer;
683
684 bool slow() const { return this->type == AvoidanceType::Slow; }
685 bool holefree() const { return this->type == AvoidanceType::FastSafe; }
686 };
687
688 std::vector<AvoidanceTask> avoidance_tasks;
689 avoidance_tasks.reserve((int(to_build_plate) + int(to_model)) * keys.size() * size_t(AvoidanceType::Count));
690
691 for (int iter_idx = 0; iter_idx < 2 * int(keys.size()) * int(AvoidanceType::Count); ++ iter_idx) {
692 AvoidanceTask task{
693 AvoidanceType(iter_idx % int(AvoidanceType::Count)),
694 keys[iter_idx / 6].first, // radius
695 keys[iter_idx / 6].second, // max_layer
696 ((iter_idx / 3) & 1) != 0 // to_model
697 };
698 // Ensure start_layer is at least 1 as if no avoidance was calculated yet getMaxCalculatedLayer() returns -1.
699 task.start_layer = std::max<LayerIndex>(1, 1 + avoidance_cache(task.type, task.to_model).getMaxCalculatedLayer(task.radius));
700 if (task.start_layer > task.max_required_layer) {
701 BOOST_LOG_TRIVIAL(debug) << "Calculation requested for value already calculated?";
702 continue;
703 }
704 if ((task.to_model ? to_model : to_build_plate) &&
705 (! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta))
706 avoidance_tasks.emplace_back(task);
707 }
708
709 if(throw_on_cancel)
710 throw_on_cancel();
711
712 tbb::parallel_for(tbb::blocked_range<size_t>(0, avoidance_tasks.size(), 1),
713 [this, &avoidance_tasks, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
714 for (size_t task_idx = range.begin(); task_idx < range.end(); ++ task_idx) {
715 const AvoidanceTask &task = avoidance_tasks[task_idx];
716 assert(! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta);
717 if (task.to_model)
718 // ensuring Placeableareas are calculated
719 //FIXME pass throw_on_cancel
720 getPlaceableAreas(task.radius, task.max_required_layer, throw_on_cancel);
721 // The following loop propagating avoidance regions bottom up is inherently serial.
722 const bool collision_holefree = (task.slow() || task.holefree()) && task.radius < m_increase_until_radius + m_current_min_xy_dist_delta;
723 const float max_move = task.slow() ? m_max_move_slow : m_max_move;
724 // Limiting the offset step so that unioning the shrunk latest_avoidance with the current layer collisions
725 // will not create gaps in the resulting avoidance region letting a tree support branch tunneling through an object wall.
726 float move_step = 1.9 * std::max(task.radius, m_current_min_xy_dist);
727 if (move_step < EPSILON) return;
728 int move_steps = round_up_divide<int>(max_move, move_step);
729 assert(move_steps > 0);
730 float last_move_step = max_move - (move_steps - 1) * move_step;
731 if (last_move_step < scaled<float>(0.05)) {
732 assert(move_steps > 1);
733 if (move_steps > 1) {
734 // Avoid taking a very short last step, stretch the other steps a bit instead.
735 move_step = max_move / (-- move_steps);
736 last_move_step = move_step;
737 }
738 }
739 // minDist as the delta was already added, also avoidance for layer 0 will return the collision.
740 Polygons latest_avoidance = getAvoidance(task.radius, task.start_layer - 1, task.type, task.to_model, true);
741 std::vector<std::pair<RadiusLayerPair, Polygons>> data;
742 data.reserve(task.max_required_layer + 1 - task.start_layer);
743 for (LayerIndex layer_idx = task.start_layer; layer_idx <= task.max_required_layer; ++ layer_idx) {
744 // Merge current layer collisions with shrunk last_avoidance.
745 const Polygons &current_layer_collisions = collision_holefree ? getCollisionHolefree(task.radius, layer_idx) : getCollision(task.radius, layer_idx, true);
746 // For mildly steep branch angles only one step will be taken.
747 for (int istep = 0; istep < move_steps; ++ istep)
748 latest_avoidance = union_(current_layer_collisions,
749 offset(latest_avoidance,
750 istep + 1 == move_steps ? - last_move_step : - move_step,
751 jtRound, m_min_resolution));
752 if (task.to_model)
753 latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
754 latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution);
755 data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
756 if (throw_on_cancel)
757 throw_on_cancel();
758 }
759#ifdef SLIC3R_TREESUPPORTS_PROGRESS
760 {
761 std::lock_guard<std::mutex> critical_section(*m_critical_progress);
762 if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_PRECALC_AVO) {
763 m_precalculation_progress += to_model ?
764 0.4 * TREE_PROGRESS_PRECALC_AVO / (keys.size() * 3) :
765 m_support_rests_on_model ? 0.4 : 1 * TREE_PROGRESS_PRECALC_AVO / (keys.size() * 3);
766 Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
767 }
768 }
769#endif
770 avoidance_cache(task.type, task.to_model).insert(std::move(data));
771 }
772 });
773}
LayerIndex getMaxCalculatedLayer(coord_t radius) const
Get the highest already calculated layer in the cache.
Definition TreeModelVolumes.hpp:286
AvoidanceType
Definition TreeModelVolumes.hpp:82
int64_t coord_t
Definition libslic3r.h:48
int LayerIndex
Definition TreeSupportCommon.hpp:43

References avoidance_cache(), calculateAvoidance(), Count, FastSafe, m_current_min_xy_dist_delta, m_increase_until_radius, Slic3r::range(), and Slow.

Referenced by calculateAvoidance(), calculateAvoidance(), getAvoidance(), and precalculate().

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

◆ calculateAvoidance() [2/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateAvoidance ( RadiusLayerPair key,
bool to_build_plate,
bool to_model )
inlineprivate

Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.

The result is a 2D area that would cause nodes of radius radius to collide with the model. Result is saved in the cache.

Parameters
keyRadiusLayerPair of the requested areas. It will be calculated up to the provided layer.
391 {
392 calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model, []{});
393 }
std::pair< coord_t, LayerIndex > RadiusLayerPair
Convenience typedef for the keys to the caches.
Definition TreeModelVolumes.hpp:210
void calculateAvoidance(const std::vector< RadiusLayerPair > &keys, bool to_build_plate, bool to_model, std::function< void()> throw_on_cancel)
Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.
Definition TreeModelVolumes.cpp:673

References calculateAvoidance().

Here is the call graph for this function:

◆ calculateCollision() [1/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateCollision ( const coord_t radius,
const LayerIndex max_layer_idx,
std::function< void()> throw_on_cancel )
private
469{
470// assert(radius == this->ceilRadius(radius));
471
472 // Process the outlines from least layers to most layers so that the final union will run over the longest vector.
473 std::vector<size_t> layer_outline_indices(m_layer_outlines.size(), 0);
474 std::iota(layer_outline_indices.begin(), layer_outline_indices.end(), 0);
475 std::sort(layer_outline_indices.begin(), layer_outline_indices.end(),
476 [this](size_t i, size_t j) { return m_layer_outlines[i].second.size() < m_layer_outlines[j].second.size(); });
477
478 // Layer range for which the collisions will be calculated.
480 data.allocate(m_collision_cache.getMaxCalculatedLayer(radius) + 1, max_layer_idx + 1);
481
482 const bool calculate_placable = m_support_rests_on_model && radius == 0;
483 LayerPolygonCache data_placeable;
484 if (calculate_placable)
485 data_placeable.allocate(data.begin(), data.end());
486
487 for (size_t outline_pos = 0; outline_pos < layer_outline_indices.size(); ++outline_pos) {
488 const size_t outline_idx = layer_outline_indices[outline_pos];
489 if (const std::vector<Polygons> &outlines = m_layer_outlines[outline_idx].second; ! outlines.empty()) {
490 const TreeSupportMeshGroupSettings &settings = m_layer_outlines[outline_idx].first;
491 const coord_t layer_height = settings.layer_height;
492 const int z_distance_bottom_layers = int(round(double(settings.support_bottom_distance) / double(layer_height)));
493 const int z_distance_top_layers = int(round(double(settings.support_top_distance) / double(layer_height)));
494 const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist :
495 // technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all
496 // not currently processing meshes as this delta will be added at request time.
497 // avoiding this would require saving each collision for each outline_idx separately.
498 // and later for each avoidance... But avoidance calculation has to be for the whole scene and can NOT be done for each outline_idx separately and combined later.
499 // so avoiding this inaccuracy seems infeasible as it would require 2x the avoidance calculations => 0.5x the performance.
500 //FIXME support_xy_distance is not corrected for "soluble" flag, see TreeSupportSettings constructor.
501 settings.support_xy_distance;
502
503 // 1) Calculate offsets of collision areas in parallel.
504 LayerPolygonCache collision_areas_offsetted;
505 collision_areas_offsetted.allocate(
506 std::max<LayerIndex>(0, data.begin() - z_distance_bottom_layers),
507 std::min<LayerIndex>(outlines.size(), data.end() + z_distance_top_layers));
508 tbb::parallel_for(tbb::blocked_range<LayerIndex>(collision_areas_offsetted.begin(), collision_areas_offsetted.end()),
509 [&outlines, &machine_border = std::as_const(m_machine_border), offset_value = radius + xy_distance, &collision_areas_offsetted, &throw_on_cancel]
510 (const tbb::blocked_range<LayerIndex> &range) {
511 for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
512 Polygons collision_areas = machine_border;
513 append(collision_areas, outlines[layer_idx]);
514 // jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model.
515 // if a key does not exist when it is accessed it is added!
516 collision_areas_offsetted[layer_idx] = offset_value == 0 ?
517 union_(collision_areas) :
518 offset(union_ex(collision_areas), offset_value, jtMiter, 1.2);
519 if(throw_on_cancel)
520 throw_on_cancel();
521 }
522 });
523
524 // 2) Sum over top / bottom ranges.
525 const bool processing_last_mesh = outline_pos + 1 == layer_outline_indices.size();
526 tbb::parallel_for(tbb::blocked_range<LayerIndex>(data.begin(), data.end()),
527 [&collision_areas_offsetted, &outlines, &machine_border = m_machine_border, &anti_overhang = m_anti_overhang, radius,
528 xy_distance, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, processing_last_mesh, &throw_on_cancel]
529 (const tbb::blocked_range<LayerIndex>& range) {
530 for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
531 Polygons collisions;
532 for (int i = - z_distance_bottom_layers; i <= 0; ++ i)
533 if (int j = layer_idx + i; collision_areas_offsetted.has(j))
534 append(collisions, collision_areas_offsetted[j]);
535 for (int i = 1; i <= z_distance_top_layers; ++ i)
536 if (int j = layer_idx + i; j < int(outlines.size())) {
537 Polygons collision_areas_original = machine_border;
538 append(collision_areas_original, outlines[j]);
539
540 // If just the collision (including the xy distance) of the layers above is accumulated, it leads to the
541 // following issue:
542 // Example: assuming the z distance is 2 layer
543 // + = xy_distance
544 // - = model
545 // o = overhang of the area two layers above that should result in tips on this layer
546 //
547 // +-----+
548 // +-----+
549 // +-----+
550 // o +-----+
551 // If just the collision above is accumulated the overhang will get overwritten by the xy_distance of the
552 // layer below the overhang...
553 //
554 // This only causes issues if the overhang area is thinner than xy_distance
555 // Just accumulating areas of the model above without the xy distance is also problematic, as then support
556 // may get closer to the model (on the diagonal downwards) than the user intended. Example (s = support):
557 // +-----+
558 // +-----+
559 // +-----+
560 // s+-----+
561
562 // technically the calculation below is off by one layer, as the actual distance between plastic one layer
563 // down is 0 not layer height, as this layer is filled with said plastic. But otherwise a part of the
564 // overhang that is expected to be supported is overwritten by the remaining part of the xy distance of the
565 // layer below the to be supported area.
566 coord_t required_range_x =
567 (xy_distance - ((i - (z_distance_top_layers == 1 ? 0.5 : 0)) * xy_distance / z_distance_top_layers));
568 // the conditional -0.5 ensures that plastic can never touch on the diagonal
569 // downward when the z_distance_top_layers = 1. It is assumed to be better to
570 // not support an overhang<90 degree than to risk fusing to it.
571 append(collisions, offset(union_ex(collision_areas_original), radius + required_range_x, jtMiter, 1.2));
572 }
573 if (processing_last_mesh) {
574 if (layer_idx < int(anti_overhang.size()))
575 append(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, jtMiter, 1.2));
576 // NOTE: m_belt_floor is NOT added to collision here — branches
577 // should grow toward the belt and terminate at it, not avoid it.
578 // Belt floor clipping is done post-generation in organic_draw_branches().
579 }
580 collisions = union_(collisions);
581 auto &dst = data[layer_idx];
582 if (processing_last_mesh) {
583 if (! dst.empty())
584 collisions = union_(collisions, dst);
585 dst = polygons_simplify(collisions, min_resolution);
586 } else
587 append(dst, std::move(collisions));
588 if (throw_on_cancel)
589 throw_on_cancel();
590 }
591 });
592
593 // 3) Optionally calculate placables.
594 if (calculate_placable) {
595 // Now calculate the placable areas.
596 tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.begin()), data.end()),
597 [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
598 min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
599 (const tbb::blocked_range<LayerIndex>& range) {
600 for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
601 LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1;
602 assert(layer_idx_below >= 0);
603 const Polygons &current = collision_areas_offsetted[layer_idx];
604 const Polygons &below = outlines[layer_idx_below];
605 Polygons placable = diff(
606 // Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface.
607 offset(below, xy_distance),
608 layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
609 auto &dst = data_placeable[layer_idx];
610 if (processing_last_mesh) {
611 if (! dst.empty())
612 placable = union_(placable, dst);
613 dst = polygons_simplify(placable, min_resolution);
614 } else
615 append(dst, placable);
616 if (throw_on_cancel)
617 throw_on_cancel();
618 }
619 });
620 } else {
621 // Calculating just the collision areas.
622 }
623 }
624 }
625#ifdef SLIC3R_TREESUPPORTS_PROGRESS
626 {
627 std::lock_guard<std::mutex> critical_section(*m_critical_progress);
628 if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL) {
629 m_precalculation_progress += TREE_PROGRESS_PRECALC_COLL / keys.size();
630 Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
631 }
632 }
633#endif
634 if (throw_on_cancel)
635 throw_on_cancel();
636 m_collision_cache.insert(std::move(data), radius);
637 if (calculate_placable)
638 m_placeable_areas_cache.insert(std::move(data_placeable), radius);
639}
void allocate(LayerIndex aidx_begin, LayerIndex aidx_end)
Definition TreeModelVolumes.hpp:187
RadiusLayerPolygonCache m_placeable_areas_cache
Definition TreeModelVolumes.hpp:517
bool m_precalculated
Definition TreeModelVolumes.hpp:443
RadiusLayerPolygonCache m_collision_cache
Caches for the collision, avoidance and areas on the model where support can be placed safely at give...
Definition TreeModelVolumes.hpp:511
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat

References Slic3r::TreeSupport3D::TreeModelVolumes::LayerPolygonCache::allocate(), Slic3r::TreeSupport3D::TreeModelVolumes::LayerPolygonCache::begin(), calculateCollision(), Slic3r::TreeSupport3D::TreeModelVolumes::LayerPolygonCache::end(), Slic3r::layer_height(), Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::layer_height, m_anti_overhang, m_collision_cache, m_current_min_xy_dist, m_current_outline_idx, m_layer_outlines, m_machine_border, m_min_resolution, m_support_rests_on_model, Slic3r::range(), Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::support_bottom_distance, Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::support_top_distance, and Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::support_xy_distance.

Here is the call graph for this function:

◆ calculateCollision() [2/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateCollision ( const std::vector< RadiusLayerPair > & keys,
std::function< void()> throw_on_cancel )
private

Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.

The result is a 2D area that would cause nodes of given radius to collide with the model. Result is saved in the cache.

Parameters
keysRadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
455{
456 tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size()),
457 [&](const tbb::blocked_range<size_t> &range) {
458 for (size_t ikey = range.begin(); ikey != range.end(); ++ ikey) {
459 const LayerIndex radius = keys[ikey].first;
460 const size_t max_layer_idx = keys[ikey].second;
461 // recursive call to parallel_for.
462 calculateCollision(radius, max_layer_idx, throw_on_cancel);
463 }
464 });
465}

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

Referenced by calculateCollision(), calculateCollision(), getCollision(), and precalculate().

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

◆ calculateCollisionHolefree() [1/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateCollisionHolefree ( const std::vector< RadiusLayerPair > & keys,
std::function< void()> throw_on_cancel )
private

Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.

The result is a 2D area that would cause nodes of given radius to collide with the model or be inside a hole. Result is saved in the cache. A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall.

Parameters
keysRadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
642{
643 LayerIndex max_layer = 0;
644 for (long long unsigned int i = 0; i < keys.size(); i++)
645 max_layer = std::max(max_layer, keys[i].second);
646
647 tbb::parallel_for(tbb::blocked_range<LayerIndex>(0, max_layer + 1, keys.size()),
648 [&](const tbb::blocked_range<LayerIndex> &range) {
649 std::vector<std::pair<RadiusLayerPair, Polygons>> data;
650 data.reserve(range.size() * keys.size());
651 for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
652 for (RadiusLayerPair key : keys)
653 if (layer_idx <= key.second) {
654 // Logically increase the collision by m_increase_until_radius
655 coord_t radius = key.first;
656 assert(radius == this->ceilRadius(radius));
657 assert(radius < m_increase_until_radius + m_current_min_xy_dist_delta);
658 coord_t increase_radius_ceil = ceilRadius(m_increase_until_radius, false) - radius;
659 assert(increase_radius_ceil > 0);
660 // this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area.
661 data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify(
662 offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
663 5 - increase_radius_ceil, jtRound, m_min_resolution),
664 m_min_resolution));
665 if (throw_on_cancel)
666 throw_on_cancel();
667 }
668 }
669 m_collision_cache_holefree.insert(std::move(data));
670 });
671}
RadiusLayerPolygonCache m_collision_cache_holefree
Definition TreeModelVolumes.hpp:512

References calculateCollisionHolefree(), m_collision_cache_holefree, and Slic3r::range().

Referenced by calculateCollisionHolefree(), calculateCollisionHolefree(), getCollisionHolefree(), and precalculate().

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

◆ calculateCollisionHolefree() [2/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateCollisionHolefree ( RadiusLayerPair key)
inlineprivate

Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.

The result is a 2D area that would cause nodes of given radius to collide with the model or be inside a hole. Result is saved in the cache. A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall.

Parameters
keyRadiusLayerPairs the requested areas. The radius will be calculated up to the provided layer.
370 {
371 calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
372 }
void calculateCollisionHolefree(const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
Creates the areas that have to be avoided by the tree's branches to prevent collision with the model ...
Definition TreeModelVolumes.cpp:641

References calculateCollisionHolefree().

Here is the call graph for this function:

◆ calculatePlaceables() [1/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculatePlaceables ( const coord_t radius,
const LayerIndex max_required_layer,
std::function< void()> throw_on_cancel )
private

Creates the areas where a branch of a given radius can be place on the model. Result is saved in the cache.

Parameters
keyRadiusLayerPair of the requested areas. It will be calculated up to the provided layer.
786{
787 LayerIndex start_layer = 1 + m_placeable_areas_cache.getMaxCalculatedLayer(radius);
788 if (start_layer > max_required_layer) {
789 BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?";
790 return;
791 }
792
793 std::vector<Polygons> data(max_required_layer + 1 - start_layer, Polygons{});
794
795 if (start_layer == 0)
796 data[0] = diff(m_machine_border, getCollision(radius, 0, true));
797
798 tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(1, start_layer), max_required_layer + 1),
799 [this, &data, radius, start_layer, &throw_on_cancel](const tbb::blocked_range<LayerIndex>& range) {
800 for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
801 data[layer_idx - start_layer] = offset(
802 union_ex(getPlaceableAreas(0, layer_idx, throw_on_cancel)),
803 // As a placeable area is calculated by (collision of the layer below) - (collision of the current layer) and the collision is offset by xy_distance,
804 // it can happen that a small line is considered a flat area to place something onto, even though it is mostly
805 // xy_distance that cant support it. Making the area smaller by xy_distance fixes this.
807 jtMiter, 1.2);
808 if(throw_on_cancel)
809 throw_on_cancel();
810 }
811 });
812#ifdef SLIC3R_TREESUPPORTS_PROGRESS
813 {
814 std::lock_guard<std::mutex> critical_section(*m_critical_progress);
815 if (m_precalculated && m_precalculation_progress < TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_PRECALC_AVO) {
816 m_precalculation_progress += 0.2 * TREE_PROGRESS_PRECALC_AVO / (keys.size());
817 Progress::messageProgress(Progress::Stage::SUPPORT, m_precalculation_progress * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
818 }
819 }
820#endif
821 m_placeable_areas_cache.insert(std::move(data), start_layer, radius);
822}
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
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
@ jtMiter
Definition ClipperUtils.hpp:21
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
Definition ClipperUtils.cpp:1011
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:731

References calculatePlaceables(), Slic3r::diff(), getCollision(), getPlaceableAreas(), Slic3r::jtMiter, m_current_min_xy_dist, m_current_min_xy_dist_delta, m_machine_border, m_placeable_areas_cache, m_precalculated, Slic3r::offset(), Slic3r::range(), and Slic3r::union_ex().

Referenced by calculatePlaceables(), calculatePlaceables(), getPlaceableAreas(), and precalculate().

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

◆ calculatePlaceables() [2/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculatePlaceables ( const std::vector< RadiusLayerPair > & keys,
std::function< void()> throw_on_cancel )
private

Creates the areas where a branch of a given radius can be placed on the model. Result is saved in the cache.

Parameters
keysRadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
777{
778 tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size()),
779 [&, keys](const tbb::blocked_range<size_t>& range) {
780 for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx)
781 this->calculatePlaceables(keys[key_idx].first, keys[key_idx].second, throw_on_cancel);
782 });
783}

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

Here is the call graph for this function:

◆ calculateWallRestrictions() [1/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateWallRestrictions ( const std::vector< RadiusLayerPair > & keys,
std::function< void()> throw_on_cancel )
private

Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object).

These areas are at least xy_min_dist wide. When calculating it is always assumed that every wall is printed on top of another (as in has an overlap with the wall a layer below). Result is saved in the corresponding cache.

Parameters
keysRadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
825{
826 // Wall restrictions are mainly important when they represent actual walls that are printed, and not "just" the configured z_distance, because technically valid placement is no excuse for moving through a wall.
827 // As they exist to prevent accidentially moving though a wall at high speed between layers like thie (x = wall,i = influence area,o= empty space,d = blocked area because of z distance) Assume maximum movement distance is two characters and maximum safe movement distance of one character
828
829 /* Potential issue addressed by the wall restrictions: Influence area may lag through a wall
830 * layer z+1:iiiiiiiiiiioooo
831 * layer z+0:xxxxxiiiiiiiooo
832 * layer z-1:ooooixxxxxxxxxx
833 */
834
835 // The radius for the upper collission has to be 0 as otherwise one may not enter areas that may be forbidden on layer_idx but not one below (c = not an influence area even though it should ):
836 /*
837 * layer z+1:xxxxxiiiiiioo
838 * layer z+0:dddddiiiiiiio
839 * layer z-1:dddocdddddddd
840 */
841 // Also there can not just the collision of the lower layer be used because if it were:
842 /*
843 * layer z+1:dddddiiiiiiiiiio
844 * layer z+0:xxxxxddddddddddc
845 * layer z-1:dddddxxxxxxxxxxc
846 */
847 // Or of the upper layer be used because if it were:
848 /*
849 * layer z+1:dddddiiiiiiiiiio
850 * layer z+0:xxxxcddddddddddc
851 * layer z-1:ddddcxxxxxxxxxxc
852 */
853
854 // And just offseting with maximum movement distance (and not in multiple steps) could cause:
855 /*
856 * layer z: oxiiiiiiiiioo
857 * layer z-1: ixiiiiiiiiiii
858 */
859
860 tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size()),
861 [&, keys](const tbb::blocked_range<size_t> &range) {
862 for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx) {
863 const coord_t radius = keys[key_idx].first;
864 const LayerIndex max_required_layer = keys[key_idx].second;
865 const coord_t min_layer_bottom = std::max(1, m_wall_restrictions_cache.getMaxCalculatedLayer(radius));
866 const size_t buffer_size = max_required_layer + 1 - min_layer_bottom;
867 std::vector<Polygons> data(buffer_size, Polygons{});
868 std::vector<Polygons> data_min;
869 if (m_current_min_xy_dist_delta > 0)
870 data_min.assign(buffer_size, Polygons{});
871 tbb::parallel_for(tbb::blocked_range<LayerIndex>(min_layer_bottom, max_required_layer + 1),
872 [this, &data, &data_min, radius, min_layer_bottom, &throw_on_cancel](const tbb::blocked_range<LayerIndex> &range) {
873 for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
874 data[layer_idx - min_layer_bottom] = polygons_simplify(
875 // radius contains m_current_min_xy_dist_delta already if required
876 intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)),
877 m_min_resolution);
878 if (! data_min.empty())
879 data_min[layer_idx - min_layer_bottom] =
880 polygons_simplify(
881 intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
882 m_min_resolution);
883 if (throw_on_cancel)
884 throw_on_cancel();
885 }
886 });
887 m_wall_restrictions_cache.insert(std::move(data), min_layer_bottom, radius);
888 if (! data_min.empty())
889 m_wall_restrictions_cache_min.insert(std::move(data_min), min_layer_bottom, radius);
890 }
891 });
892}

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

Referenced by calculateWallRestrictions(), calculateWallRestrictions(), getWallRestriction(), and precalculate().

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

◆ calculateWallRestrictions() [2/2]

void Slic3r::TreeSupport3D::TreeModelVolumes::calculateWallRestrictions ( RadiusLayerPair key)
inlineprivate

Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). These areas are at least xy_min_dist wide. When calculating it is always assumed that every wall is printed on top of another (as in has an overlap with the wall a layer below). Result is saved in the corresponding cache.

Parameters
keyRadiusLayerPair of the requested area. It well be will be calculated up to the provided layer.
425 {
426 calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
427 }
void calculateWallRestrictions(const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
Creates the areas that can not be passed when expanding an area downwards. As such these areas are an...
Definition TreeModelVolumes.cpp:824

References calculateWallRestrictions().

Here is the call graph for this function:

◆ ceilRadius() [1/2]

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::ceilRadius ( const coord_t radius) const
private

Round radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value.

Parameters
radiusThe radius of the node of interest
895{
896 if (radius == 0)
897 return 0;
898
899 coord_t out = m_radius_0;
900 if (radius > m_radius_0) {
901 // generate SUPPORT_TREE_PRE_EXPONENTIAL_STEPS of radiis before starting to exponentially increase them.
903 auto ignore = [this](coord_t r) { return std::binary_search(m_ignorable_radii.begin(), m_ignorable_radii.end(), r); };
904 if (initial_radius_delta > SUPPORT_TREE_COLLISION_RESOLUTION) {
905 const int num_steps = round_up_divide(initial_radius_delta, SUPPORT_TREE_EXPONENTIAL_THRESHOLD);
906 const int stepsize = initial_radius_delta / num_steps;
907 out += stepsize;
908 for (auto step = 0; step < num_steps; ++ step) {
909 if (out >= radius && ! ignore(out))
910 return out;
911 out += stepsize;
912 }
913 } else
915 while (out < radius || ignore(out)) {
918 }
919 }
920 return out;
921}
#define SUPPORT_TREE_COLLISION_RESOLUTION
Definition TreeModelVolumes.hpp:42
#define SUPPORT_TREE_EXPONENTIAL_THRESHOLD
Definition TreeModelVolumes.hpp:41
std::vector< coord_t > m_ignorable_radii
Radii that can be ignored by ceilRadius as they will never be requested, sorted.
Definition TreeModelVolumes.hpp:497
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR
Definition TreeModelVolumes.hpp:40
INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor)
Definition Utils.hpp:463
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References ceilRadius(), m_ignorable_radii, m_radius_0, Slic3r::round_up_divide(), SUPPORT_TREE_COLLISION_RESOLUTION, Slic3r::TreeSupport3D::SUPPORT_TREE_EXPONENTIAL_FACTOR, and SUPPORT_TREE_EXPONENTIAL_THRESHOLD.

Here is the call graph for this function:

◆ ceilRadius() [2/2]

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::ceilRadius ( const coord_t radius,
const bool min_xy_dist ) const
inline

Round radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value.

It also adds the difference between the minimum xy distance and the regular one.

Parameters
radiusThe radius of the node of interest
min_xy_distis the minimum xy distance used.
Returns
The rounded radius
157 {
158 assert(radius >= 0);
159 return min_xy_dist ?
160 this->ceilRadius(radius) :
161 // special case as if a radius 0 is requested it could be to ensure correct xy distance. As such it is beneficial if the collision is as close to the configured values as possible.
163 }
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

References ceilRadius(), and m_current_min_xy_dist_delta.

Referenced by ceilRadius(), ceilRadius(), getAvoidance(), getCollision(), getCollisionHolefree(), getPlaceableAreas(), getRadiusNextCeil(), getWallRestriction(), Slic3r::TreeSupport3D::increase_single_area(), and precalculate().

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

◆ clear()

void Slic3r::TreeSupport3D::TreeModelVolumes::clear ( )
inline
62 {
64 m_collision_cache.clear();
66 }
void clear_all_but_object_collision()
Definition TreeModelVolumes.hpp:67

References clear_all_but_object_collision(), m_collision_cache, and m_placeable_areas_cache.

Here is the call graph for this function:

◆ clear_all_but_object_collision()

void Slic3r::TreeSupport3D::TreeModelVolumes::clear_all_but_object_collision ( )
inline
67 {
68 //m_collision_cache.clear_all_but_radius0();
70 m_avoidance_cache.clear();
74 m_placeable_areas_cache.clear_all_but_radius0();
79 }
RadiusLayerPolygonCache m_wall_restrictions_cache_min
Definition TreeModelVolumes.hpp:557
RadiusLayerPolygonCache m_wall_restrictions_cache
Caches to represent walls not allowed to be passed over.
Definition TreeModelVolumes.hpp:552

References m_avoidance_cache, m_avoidance_cache_holefree, m_avoidance_cache_holefree_to_model, m_avoidance_cache_slow, m_avoidance_cache_to_model, m_avoidance_cache_to_model_slow, m_collision_cache_holefree, m_placeable_areas_cache, m_wall_restrictions_cache, and m_wall_restrictions_cache_min.

Referenced by clear(), and Slic3r::TreeSupport3D::organic_draw_branches().

Here is the caller graph for this function:

◆ get_collision_lower_bound_area()

std::optional< std::pair< coord_t, std::reference_wrapper< const Polygons > > > Slic3r::TreeSupport3D::TreeModelVolumes::get_collision_lower_bound_area ( LayerIndex layer_id,
coord_t max_radius ) const
363{
364 return m_collision_cache.get_lower_bound_area({ max_radius, layer_id });
365}

References get_collision_lower_bound_area(), and m_collision_cache.

Referenced by get_collision_lower_bound_area(), and Slic3r::TreeSupport3D::organic_smooth_branches_avoid_collisions().

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

◆ getAvoidance()

const Polygons & Slic3r::TreeSupport3D::TreeModelVolumes::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.

The result is a 2D area that would cause nodes of radius radius to collide with the model or be unable to reach the build platform.

The input collision areas are inset by the maximum move distance and propagated upwards.

Parameters
radiusThe radius of the node of interest
layer_idxThe layer of interest
typeIs the propagation with the maximum move distance slow required.
to_modelDoes the avoidance allow good connections with the model.
min_xy_distis the minimum xy distance used.
Returns
Polygons object
383{
384 if (layer_idx == 0) // What on the layer directly above buildplate do i have to avoid to reach the buildplate ...
385 return getCollision(orig_radius, layer_idx, min_xy_dist);
386
387 const coord_t radius = this->ceilRadius(orig_radius, min_xy_dist);
389 // no holes anymore by definition at this request
390 type = AvoidanceType::Fast;
391
392 if (std::optional<std::reference_wrapper<const Polygons>> result =
393 this->avoidance_cache(type, to_model).getArea({ radius, layer_idx });
394 result)
395 return (*result).get();
396
397 if (m_precalculated) {
398 if (to_model) {
399 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance to model at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
400 tree_supports_show_error("Not precalculated Avoidance(to model) requested."sv, false);
401 } else {
402 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
403 tree_supports_show_error("Not precalculated Avoidance(to buildplate) requested."sv, false);
404 }
405 }
406 const_cast<TreeModelVolumes*>(this)->calculateAvoidance({ radius, layer_idx }, ! to_model, to_model);
407 // Retrive failed and correct result was calculated. Now it has to be retrived.
408 return getAvoidance(orig_radius, layer_idx, type, to_model, min_xy_dist);
409}
#define error_level_not_in_cache
Definition TreeModelVolumes.cpp:51
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
void tree_supports_show_error(std::string_view message, bool critical)
Definition TreeSupportCommon.hpp:611

References TreeModelVolumes(), avoidance_cache(), calculateAvoidance(), ceilRadius(), error_level_not_in_cache, Fast, FastSafe, getAvoidance(), getCollision(), m_current_min_xy_dist_delta, m_increase_until_radius, m_precalculated, and Slic3r::TreeSupport3D::tree_supports_show_error().

Referenced by Slic3r::TreeSupport3D::convert_lines_to_internal(), Slic3r::TreeSupport3D::evaluate_point_for_next_layer_function(), Slic3r::TreeSupport3D::get_avoidance_status(), getAvoidance(), Slic3r::TreeSupport3D::increase_single_area(), and Slic3r::TreeSupport3D::sample_overhang_area().

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

◆ getCollision()

const Polygons & Slic3r::TreeSupport3D::TreeModelVolumes::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 on this layer.

The result is a 2D area that would cause nodes of radius radius to collide with the model.

Parameters
radiusThe radius of the node of interest
layer_idxThe layer of interest
min_xy_distIs the minimum xy distance used.
Returns
Polygons object
347{
348 const coord_t radius = this->ceilRadius(orig_radius, min_xy_dist);
349 if (std::optional<std::reference_wrapper<const Polygons>> result = m_collision_cache.getArea({ radius, layer_idx }); result)
350 return (*result).get();
351 if (m_precalculated) {
352 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
353 tree_supports_show_error("Not precalculated Collision requested."sv, false);
354 }
355 const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx, []{});
356 return getCollision(orig_radius, layer_idx, min_xy_dist);
357}
void calculateCollision(const std::vector< RadiusLayerPair > &keys, std::function< void()> throw_on_cancel)
Creates the areas that have to be avoided by the tree's branches to prevent collision with the model ...
Definition TreeModelVolumes.cpp:454

References TreeModelVolumes(), calculateCollision(), ceilRadius(), error_level_not_in_cache, getCollision(), m_collision_cache, m_precalculated, and Slic3r::TreeSupport3D::tree_supports_show_error().

Referenced by calculatePlaceables(), Slic3r::TreeSupport3D::convert_lines_to_internal(), Slic3r::TreeSupport3D::evaluate_point_for_next_layer_function(), Slic3r::TreeSupport3D::get_avoidance_status(), getAvoidance(), getCollision(), getPlaceableAreas(), getWallRestriction(), Slic3r::TreeSupport3D::increase_single_area(), Slic3r::TreeSupport3D::merge_influence_areas_two_elements(), and Slic3r::TreeSupport3D::sample_overhang_area().

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

◆ getCollisionHolefree()

const Polygons & Slic3r::TreeSupport3D::TreeModelVolumes::getCollisionHolefree ( coord_t radius,
LayerIndex layer_idx ) const
private

Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.

The result is a 2D area that would cause nodes of given radius to collide with the model or be inside a hole. A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. minimum xy distance is always used.

Parameters
radiusThe radius of the node of interest
layer_idxThe layer of interest
min_xy_distIs the minimum xy distance used.
Returns
Polygons object
369{
370 assert(radius == this->ceilRadius(radius));
372 if (std::optional<std::reference_wrapper<const Polygons>> result = m_collision_cache_holefree.getArea({ radius, layer_idx }); result)
373 return (*result).get();
374 if (m_precalculated) {
375 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision holefree at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
376 tree_supports_show_error("Not precalculated Holefree Collision requested."sv, false);
377 }
378 const_cast<TreeModelVolumes*>(this)->calculateCollisionHolefree({ radius, layer_idx });
379 return getCollisionHolefree(radius, layer_idx);
380}
const Polygons & getCollisionHolefree(coord_t radius, LayerIndex layer_idx) const
Provides the areas that have to be avoided by the tree's branches to prevent collision with the model...
Definition TreeModelVolumes.cpp:368

References TreeModelVolumes(), calculateCollisionHolefree(), ceilRadius(), error_level_not_in_cache, getCollisionHolefree(), m_collision_cache_holefree, m_current_min_xy_dist_delta, m_increase_until_radius, m_precalculated, and Slic3r::TreeSupport3D::tree_supports_show_error().

Referenced by getCollisionHolefree().

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

◆ getPlaceableAreas()

const Polygons & Slic3r::TreeSupport3D::TreeModelVolumes::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 layer.

Parameters
radiusThe radius of the node of interest
layer_idxThe layer of interest
Returns
Polygons object
412{
413 const coord_t radius = ceilRadius(orig_radius);
414 if (std::optional<std::reference_wrapper<const Polygons>> result = m_placeable_areas_cache.getArea({ radius, layer_idx }); result)
415 return (*result).get();
416 if (m_precalculated) {
417 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
418 tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false);
419 }
420 if (orig_radius == 0)
421 // Placable areas for radius 0 are calculated in the general collision code.
422 return this->getCollision(0, layer_idx, true);
423 const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
424 return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel);
425}
void calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer, std::function< void()> throw_on_cancel)
Creates the areas where a branch of a given radius can be place on the model. Result is saved in the ...
Definition TreeModelVolumes.cpp:785
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45

References TreeModelVolumes(), calculatePlaceables(), ceilRadius(), error_level_not_in_cache, Slic3r::format(), getCollision(), getPlaceableAreas(), m_placeable_areas_cache, m_precalculated, and Slic3r::TreeSupport3D::tree_supports_show_error().

Referenced by calculatePlaceables(), getPlaceableAreas(), and Slic3r::TreeSupport3D::set_to_model_contact_to_model_gracious().

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

◆ getRadiusNextCeil()

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::getRadiusNextCeil ( coord_t radius,
bool min_xy_dist ) const
inline

Round radius upwards to the maximum that would still round up to the same value as the provided one.

Parameters
radiusThe radius of the node of interest
min_xy_distis the minimum xy distance used.
Returns
The maximum radius, resulting in the same rounding.
171 {
172 assert(radius > 0);
173 return min_xy_dist ?
174 this->ceilRadius(radius) :
176 }

References ceilRadius(), and m_current_min_xy_dist_delta.

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

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

◆ getWallRestriction()

const Polygons & Slic3r::TreeSupport3D::TreeModelVolumes::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. This is an abstract representation not equal with the outline. See calculateWallRestrictions for better description.

Parameters
radiusThe radius of the node of interest.
layer_idxThe layer of interest.
min_xy_distis the minimum xy distance used.
Returns
Polygons object
428{
429 assert(layer_idx > 0);
430 if (layer_idx == 0)
431 // Should never be requested as there will be no going below layer 0 ...,
432 // but just to be sure some semi-sane catch. Alternative would be empty Polygon.
433 return getCollision(orig_radius, layer_idx, min_xy_dist);
434
435 min_xy_dist &= m_current_min_xy_dist_delta > 0;
436
437 const coord_t radius = ceilRadius(orig_radius);
438 if (std::optional<std::reference_wrapper<const Polygons>> result =
439 (min_xy_dist ? m_wall_restrictions_cache_min : m_wall_restrictions_cache).getArea({ radius, layer_idx });
440 result)
441 return (*result).get();
442 if (m_precalculated) {
443 BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
445 min_xy_dist ?
446 "Not precalculated Wall restriction of minimum xy distance requested )." :
447 "Not precalculated Wall restriction requested )."sv
448 , false);
449 }
450 const_cast<TreeModelVolumes*>(this)->calculateWallRestrictions({ radius, layer_idx });
451 return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved.
452}
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

References TreeModelVolumes(), calculateWallRestrictions(), ceilRadius(), error_level_not_in_cache, getCollision(), getWallRestriction(), m_current_min_xy_dist_delta, m_precalculated, m_wall_restrictions_cache, m_wall_restrictions_cache_min, and Slic3r::TreeSupport3D::tree_supports_show_error().

Referenced by getWallRestriction(), and Slic3r::TreeSupport3D::increase_single_area().

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

◆ operator=() [1/2]

TreeModelVolumes & Slic3r::TreeSupport3D::TreeModelVolumes::operator= ( const TreeModelVolumes & )
delete

References TreeModelVolumes().

Here is the call graph for this function:

◆ operator=() [2/2]

TreeModelVolumes & Slic3r::TreeSupport3D::TreeModelVolumes::operator= ( TreeModelVolumes && )
default

References TreeModelVolumes().

Here is the call graph for this function:

◆ precalculate()

void Slic3r::TreeSupport3D::TreeModelVolumes::precalculate ( const PrintObject & print_object,
const coord_t max_layer,
std::function< void()> throw_on_cancel )

Precalculate avoidances and collisions up to max_layer.

Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed. Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores!

210{
211 auto t_start = std::chrono::high_resolution_clock::now();
212 m_precalculated = true;
213
214 // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant.
215 // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex
216 // like inital layer diameter are only done in once.
217 TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first, print_object.slicing_parameters());
218
219 {
220 // calculate which radius each layer in the tip may have.
221 std::vector<coord_t> possible_tip_radiis;
222 for (size_t distance_to_top = 0; distance_to_top <= config.tip_layers; ++ distance_to_top) {
223 possible_tip_radiis.emplace_back(ceilRadius(config.getRadius(distance_to_top)));
224 possible_tip_radiis.emplace_back(ceilRadius(config.getRadius(distance_to_top) + m_current_min_xy_dist_delta));
225 }
226 sort_remove_duplicates(possible_tip_radiis);
227 // It theoretically may happen in the tip, that the radius can change so much in-between 2 layers,
228 // that a ceil step is skipped (as in there is a radius r so that ceilRadius(radius(dtt))<ceilRadius(r)<ceilRadius(radius(dtt+1))).
229 // As such a radius will not reasonable happen in the tree and it will most likely not be requested,
230 // there is no need to calculate them. So just skip these.
231 for (coord_t radius_eval = m_radius_0; radius_eval <= config.branch_radius; radius_eval = ceilRadius(radius_eval + 1))
232 if (! std::binary_search(possible_tip_radiis.begin(), possible_tip_radiis.end(), radius_eval))
233 m_ignorable_radii.emplace_back(radius_eval);
234 }
235
236 if (throw_on_cancel)
237 throw_on_cancel();
238
239 // it may seem that the required avoidance can be of a smaller radius when going to model (no initial layer diameter for to model branches)
240 // but as for every branch going towards the bp, the to model avoidance is required to check for possible merges with to model branches, this assumption is in-fact wrong.
241 std::unordered_map<coord_t, LayerIndex> radius_until_layer;
242 // while it is possible to calculate, up to which layer the avoidance should be calculated, this simulation is easier to understand, and does not need to be adjusted if something of the radius calculation is changed.
243 // Overhead with an assumed worst case of 6600 layers was about 2ms
244 for (LayerIndex distance_to_top = 0; distance_to_top <= max_layer; ++ distance_to_top) {
245 const LayerIndex current_layer = max_layer - distance_to_top;
246 auto update_radius_until_layer = [&radius_until_layer, current_layer](coord_t r) {
247 auto it = radius_until_layer.find(r);
248 if (it == radius_until_layer.end())
249 radius_until_layer.emplace_hint(it, r, current_layer);
250 else
251 assert(it->second >= current_layer);
252 };
253 // regular radius
254 update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0) + m_current_min_xy_dist_delta));
255 // the maximum radius that the radius with the min_xy_dist can achieve
256 update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0)));
257 update_radius_until_layer(ceilRadius(config.recommendedMinRadius(current_layer) + m_current_min_xy_dist_delta));
258 }
259
260 if (throw_on_cancel)
261 throw_on_cancel();
262
263 // Copy to deque to use in parallel for later.
264 std::vector<RadiusLayerPair> relevant_avoidance_radiis{ radius_until_layer.begin(), radius_until_layer.end() };
265
266 // Append additional radiis needed for collision.
267 // To calculate collision holefree for every radius, the collision of radius m_increase_until_radius will be required.
268 radius_until_layer[ceilRadius(m_increase_until_radius + m_current_min_xy_dist_delta)] = max_layer;
269 // Collision for radius 0 needs to be calculated everywhere, as it will be used to ensure valid xy_distance in drawAreas.
270 radius_until_layer[0] = max_layer;
272 radius_until_layer[m_current_min_xy_dist_delta] = max_layer;
273
274 // Now that required_avoidance_limit contains the maximum of ild and regular required radius just copy.
275 std::vector<RadiusLayerPair> relevant_collision_radiis{ radius_until_layer.begin(), radius_until_layer.end() };
276
277 // Calculate the relevant collisions
278 calculateCollision(relevant_collision_radiis, throw_on_cancel);
279
280 // calculate a separate Collisions with all holes removed. These are relevant for some avoidances that try to avoid holes (called safe)
281 std::vector<RadiusLayerPair> relevant_hole_collision_radiis;
282 for (RadiusLayerPair key : relevant_avoidance_radiis)
284 relevant_hole_collision_radiis.emplace_back(key);
285
286 // Calculate collisions without holes, built from regular collision
287 calculateCollisionHolefree(relevant_hole_collision_radiis, throw_on_cancel);
288 // Let placables be calculated from calculateAvoidance() for better parallelization.
290 calculatePlaceables(relevant_avoidance_radiis, throw_on_cancel);
291
292 auto t_coll = std::chrono::high_resolution_clock::now();
293
294 // Calculate the relevant avoidances in parallel as far as possible
295 {
296 tbb::task_group task_group;
297 task_group.run([this, relevant_avoidance_radiis, throw_on_cancel]{ calculateAvoidance(relevant_avoidance_radiis, true, m_support_rests_on_model, throw_on_cancel); });
298 task_group.run([this, relevant_avoidance_radiis, throw_on_cancel]{ calculateWallRestrictions(relevant_avoidance_radiis, throw_on_cancel); });
299 task_group.wait();
300 }
301 auto t_end = std::chrono::high_resolution_clock::now();
302 auto dur_col = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_coll - t_start).count();
303 auto dur_avo = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_coll).count();
304
305// m_precalculated = true;
306 BOOST_LOG_TRIVIAL(info) << "Precalculating collision took" << dur_col << " ms. Precalculating avoidance took " << dur_avo << " ms.";
307
308#if 0
309 // Paint caches into SVGs:
310 auto paint_cache_into_SVGs = [this](const RadiusLayerPolygonCache &cache, std::string_view name) {
311 const std::vector<std::pair<RadiusLayerPair, std::reference_wrapper<const Polygons>>> sorted = cache.sorted();
312 static constexpr const std::string_view colors[] = {
313 "red", "green", "blue", "magenta", "orange"
314 };
315 static constexpr const size_t num_colors = sizeof(colors) / sizeof(colors[0]);
316 for (size_t i = 0; i < sorted.size();) {
317 // Find range of cache items with the same layer index.
318 size_t j = i;
319 for (++ j; j < sorted.size() && sorted[i].first.second == sorted[j].first.second; ++ j) ;
320 // Collect expolygons in reverse order (largest to smallest).
321 std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> expolygons_with_attributes;
322 for (int k = int(j - 1); k >= int(i); -- k) {
323 std::string legend = format("radius-%1%", unscaled<float>(sorted[k].first.first));
324 expolygons_with_attributes.push_back({ union_ex(sorted[k].second), SVG::ExPolygonAttributes(legend, std::string(colors[(k - int(i)) % num_colors]), 1.) });
325 SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d-%s.svg", name.data(), sorted[i].first.second, legend.c_str()), { expolygons_with_attributes.back() });
326 }
327 // Render the range of per radius collision polygons into a common SVG.
328 SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d.svg", name.data(), sorted[i].first.second), expolygons_with_attributes);
329 i = j;
330 }
331 };
332 paint_cache_into_SVGs(m_collision_cache, "collision_cache");
333 paint_cache_into_SVGs(m_collision_cache_holefree, "collision_cache_holefree");
334 paint_cache_into_SVGs(m_avoidance_cache, "avoidance_cache");
335 paint_cache_into_SVGs(m_avoidance_cache_slow, "avoidance_cache_slow");
336 paint_cache_into_SVGs(m_avoidance_cache_to_model, "avoidance_cache_to_model");
337 paint_cache_into_SVGs(m_avoidance_cache_to_model_slow, "avoidance_cache_to_model_slow");
338 paint_cache_into_SVGs(m_placeable_areas_cache, "placable_areas_cache");
339 paint_cache_into_SVGs(m_avoidance_cache_holefree, "avoidance_cache_holefree");
340 paint_cache_into_SVGs(m_avoidance_cache_holefree_to_model, "avoidance_cache_holefree_to_model");
341 paint_cache_into_SVGs(m_wall_restrictions_cache, "wall_restrictions_cache");
342 paint_cache_into_SVGs(m_wall_restrictions_cache_min, "wall_restrictions_cache_min");
343#endif
344}
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer="black", std::string stroke_holes="blue", coordf_t stroke_width=0)
Definition SVG.cpp:381
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231

References Slic3r::TreeSupport3D::TreeSupportSettings::branch_radius, calculateAvoidance(), calculateCollision(), calculateCollisionHolefree(), calculatePlaceables(), calculateWallRestrictions(), ceilRadius(), Slic3r::debug_out_path(), Slic3r::SVG::export_expolygons(), Slic3r::format(), Slic3r::TreeSupport3D::TreeSupportSettings::getRadius(), m_avoidance_cache, m_avoidance_cache_holefree, m_avoidance_cache_holefree_to_model, m_avoidance_cache_slow, m_avoidance_cache_to_model, m_avoidance_cache_to_model_slow, m_collision_cache, m_collision_cache_holefree, m_current_min_xy_dist_delta, m_current_outline_idx, m_ignorable_radii, m_increase_until_radius, m_layer_outlines, m_placeable_areas_cache, m_precalculated, m_radius_0, m_support_rests_on_model, m_wall_restrictions_cache, m_wall_restrictions_cache_min, precalculate(), Slic3r::TreeSupport3D::TreeSupportSettings::recommendedMinRadius(), Slic3r::PrintObject::slicing_parameters(), Slic3r::sort_remove_duplicates(), Slic3r::TreeSupport3D::TreeModelVolumes::RadiusLayerPolygonCache::sorted(), Slic3r::TreeSupport3D::TreeSupportSettings::tip_layers, Slic3r::union_ex(), and Slic3r::unscaled().

Referenced by Slic3r::TreeSupport3D::precalculate(), and precalculate().

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

Member Data Documentation

◆ m_anti_overhang

std::vector<Polygons> Slic3r::TreeSupport3D::TreeModelVolumes::m_anti_overhang
private

Storage for areas that should be avoided, like support blocker or previous generated trees.

Referenced by TreeModelVolumes(), and calculateCollision().

◆ m_avoidance_cache

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache
private

◆ m_avoidance_cache_holefree

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache_holefree
private

Caches to avoid holes smaller than the radius until which the radius is always increased, as they are free of holes. Also called safe avoidances, as they are safe regarding not running into holes.

Referenced by avoidance_cache(), clear_all_but_object_collision(), and precalculate().

◆ m_avoidance_cache_holefree_to_model

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache_holefree_to_model
private

◆ m_avoidance_cache_slow

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache_slow
private

◆ m_avoidance_cache_to_model

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache_to_model
private

◆ m_avoidance_cache_to_model_slow

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_avoidance_cache_to_model_slow
private

◆ m_bed_area

Polygon Slic3r::TreeSupport3D::TreeModelVolumes::m_bed_area

Referenced by TreeModelVolumes().

◆ m_belt_floor

std::vector<Polygons> Slic3r::TreeSupport3D::TreeModelVolumes::m_belt_floor

Referenced by TreeModelVolumes().

◆ m_collision_cache

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_collision_cache
private

Caches for the collision, avoidance and areas on the model where support can be placed safely at given radius and layer indices.

Referenced by calculateCollision(), clear(), get_collision_lower_bound_area(), getCollision(), and precalculate().

◆ m_collision_cache_holefree

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_collision_cache_holefree
private

◆ m_current_min_xy_dist

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_current_min_xy_dist
private

The minimum required clearance between the model and the tree branches.

Referenced by TreeModelVolumes(), calculateCollision(), and calculatePlaceables().

◆ m_current_min_xy_dist_delta

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_current_min_xy_dist_delta
private

The difference between the minimum required clearance between the model and the tree branches and the regular one.

Referenced by TreeModelVolumes(), calculateAvoidance(), calculatePlaceables(), ceilRadius(), getAvoidance(), getCollisionHolefree(), getRadiusNextCeil(), getWallRestriction(), and precalculate().

◆ m_current_outline_idx

size_t Slic3r::TreeSupport3D::TreeModelVolumes::m_current_outline_idx
private

The index to access the outline corresponding with the currently processing mesh.

Referenced by TreeModelVolumes(), calculateCollision(), and precalculate().

◆ m_ignorable_radii

std::vector<coord_t> Slic3r::TreeSupport3D::TreeModelVolumes::m_ignorable_radii
private

Radii that can be ignored by ceilRadius as they will never be requested, sorted.

Referenced by ceilRadius(), and precalculate().

◆ m_increase_until_radius

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_increase_until_radius
private

Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.

Referenced by TreeModelVolumes(), calculateAvoidance(), getAvoidance(), getCollisionHolefree(), and precalculate().

◆ m_layer_outlines

std::vector<std::pair<TreeSupportMeshGroupSettings, std::vector<Polygons> > > Slic3r::TreeSupport3D::TreeModelVolumes::m_layer_outlines
private

Storage for layer outlines and the corresponding settings of the meshes grouped by meshes with identical setting.

Referenced by TreeModelVolumes(), calculateCollision(), and precalculate().

◆ m_machine_border

Polygons Slic3r::TreeSupport3D::TreeModelVolumes::m_machine_border
private

Polygons representing the limits of the printable area of the machine.

Referenced by TreeModelVolumes(), calculateCollision(), and calculatePlaceables().

◆ m_max_move

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_max_move
private

The maximum distance that the center point of a tree branch may move in consecutive layers if it has to avoid the model.

Referenced by TreeModelVolumes().

◆ m_max_move_slow

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_max_move_slow
private

The maximum distance that the centre-point of a tree branch may move in consecutive layers if it does not have to avoid the model.

Referenced by TreeModelVolumes().

◆ m_min_resolution

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_min_resolution
private

The smallest maximum resolution for simplify.

Referenced by TreeModelVolumes(), and calculateCollision().

◆ m_placeable_areas_cache

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_placeable_areas_cache
private

◆ m_precalculated

bool Slic3r::TreeSupport3D::TreeModelVolumes::m_precalculated = false
private

◆ m_radius_0

coord_t Slic3r::TreeSupport3D::TreeModelVolumes::m_radius_0
private

Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0.

Referenced by TreeModelVolumes(), ceilRadius(), and precalculate().

◆ m_raft_layers

std::vector<double> Slic3r::TreeSupport3D::TreeModelVolumes::m_raft_layers
private

Referenced by TreeModelVolumes().

◆ m_support_rests_on_model

bool Slic3r::TreeSupport3D::TreeModelVolumes::m_support_rests_on_model
private

Does at least one mesh allow support to rest on a model.

Referenced by TreeModelVolumes(), calculateCollision(), and precalculate().

◆ m_wall_restrictions_cache

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_wall_restrictions_cache
private

Caches to represent walls not allowed to be passed over.

Referenced by clear_all_but_object_collision(), getWallRestriction(), and precalculate().

◆ m_wall_restrictions_cache_min

RadiusLayerPolygonCache Slic3r::TreeSupport3D::TreeModelVolumes::m_wall_restrictions_cache_min
private

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