WIP TreeSupports: Refactored TreeModelVolumes for clarity

This commit is contained in:
Vojtech Bubnik 2022-08-16 11:54:30 +02:00
parent 85e9ae75bb
commit 42b546ae9c
5 changed files with 233 additions and 371 deletions

View File

@ -16,6 +16,7 @@
#include "Point.hpp"
#include "Print.hpp"
#include "PrintConfig.hpp"
#include "Utils.hpp"
#include <boost/log/trivial.hpp>
@ -282,21 +283,9 @@ void TreeModelVolumes::precalculate(coord_t max_layer)
BOOST_LOG_TRIVIAL(info) << "Precalculating collision took" << dur_col << " ms. Precalculating avoidance took " << dur_avo << " ms.";
}
/*!
* \brief Checks a cache for a given RadiusLayerPair and returns it if it is found
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
* \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found)
*/
std::optional<std::reference_wrapper<const Polygons>> getArea(const TreeModelVolumes::RadiusLayerPolygonCache& cache, const TreeModelVolumes::RadiusLayerPair& key)
{
const auto it = cache.find(key);
return it == cache.end() ? std::optional<std::reference_wrapper<const Polygons>>{} : std::optional<std::reference_wrapper<const Polygons>>{ it->second };
}
const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const
{
coord_t orig_radius = radius;
std::optional<std::reference_wrapper<const Polygons>> result;
if (!min_xy_dist)
radius += m_current_min_xy_dist_delta;
@ -304,11 +293,7 @@ const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_
if (orig_radius != 0)
radius = ceilRadius(radius);
RadiusLayerPair key{ radius, layer_idx };
{
std::lock_guard<std::mutex> critical_section_support_max_layer_nr(*m_critical_avoidance_cache);
result = getArea(m_collision_cache, key);
}
std::optional<std::reference_wrapper<const Polygons>> result = m_collision_cache.getArea(key);
if (result)
return result.value().get();
if (m_precalculated) {
@ -322,18 +307,15 @@ const Polygons& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_
const Polygons& TreeModelVolumes::getCollisionHolefree(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const
{
coord_t orig_radius = radius;
std::optional<std::reference_wrapper<const Polygons>> result;
if (!min_xy_dist)
radius += m_current_min_xy_dist_delta;
if (radius >= m_increase_until_radius + m_current_min_xy_dist_delta)
return getCollision(orig_radius, layer_idx, min_xy_dist);
// not needed as the radius should already be adjusted.
// radius = ceilRadius(radius);
RadiusLayerPair key{ radius, layer_idx };
{
std::lock_guard<std::mutex> critical_section_support_max_layer_nr(*m_critical_collision_cache_holefree);
result = getArea(m_collision_cache_holefree, key);
}
std::optional<std::reference_wrapper<const Polygons>> result = m_collision_cache_holefree.getArea(key);
if (result)
return result.value().get();
if (m_precalculated) {
@ -351,61 +333,44 @@ const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_
coord_t orig_radius = radius;
std::optional<std::reference_wrapper<const Polygons>> result;
if (!min_xy_dist)
radius += m_current_min_xy_dist_delta;
radius = ceilRadius(radius);
if (radius >= m_increase_until_radius + m_current_min_xy_dist_delta && type == AvoidanceType::FAST_SAFE) // no holes anymore by definition at this request
type = AvoidanceType::FAST;
if (radius >= m_increase_until_radius + m_current_min_xy_dist_delta && type == AvoidanceType::FastSafe) // no holes anymore by definition at this request
type = AvoidanceType::Fast;
const RadiusLayerPair key{ radius, layer_idx };
const RadiusLayerPolygonCache* cache_ptr = nullptr;
std::mutex* mutex_ptr;
if (!to_model && type == AvoidanceType::FAST) {
const RadiusLayerPolygonCache *cache_ptr = nullptr;
if (!to_model && type == AvoidanceType::Fast) {
cache_ptr = &m_avoidance_cache;
mutex_ptr = m_critical_avoidance_cache.get();
} else if (!to_model && type == AvoidanceType::SLOW) {
} else if (!to_model && type == AvoidanceType::Slow) {
cache_ptr = &m_avoidance_cache_slow;
mutex_ptr = m_critical_avoidance_cache_slow.get();
} else if (!to_model && type == AvoidanceType::FAST_SAFE) {
cache_ptr = &m_avoidance_cache_hole;
mutex_ptr = m_critical_avoidance_cache_holefree.get();
} else if (to_model && type == AvoidanceType::FAST) {
} else if (!to_model && type == AvoidanceType::FastSafe) {
cache_ptr = &m_avoidance_cache_holefree;
} else if (to_model && type == AvoidanceType::Fast) {
cache_ptr = &m_avoidance_cache_to_model;
mutex_ptr = m_critical_avoidance_cache_to_model.get();
} else if (to_model && type == AvoidanceType::SLOW) {
} else if (to_model && type == AvoidanceType::Slow) {
cache_ptr = &m_avoidance_cache_to_model_slow;
mutex_ptr = m_critical_avoidance_cache_to_model_slow.get();
} else if (to_model && type == AvoidanceType::FAST_SAFE) {
cache_ptr = &m_avoidance_cache_hole_to_model;
mutex_ptr = m_critical_avoidance_cache_holefree_to_model.get();
} else if (to_model && type == AvoidanceType::FastSafe) {
cache_ptr = &m_avoidance_cache_holefree_to_model;
} else {
BOOST_LOG_TRIVIAL(error) << "Invalid Avoidance Request";
TreeSupport::showError("Invalid Avoidance Request.\n", true);
}
std::optional<std::reference_wrapper<const Polygons>> result = cache_ptr->getArea(key);
if (result)
return result.value().get();
if (to_model) {
{
std::lock_guard<std::mutex> critical_section(*mutex_ptr);
result = getArea(*cache_ptr, key);
}
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Avoidance to model at radius " << key.first << " and layer " << key.second << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Avoidance(to model) requested.", false);
}
const_cast<TreeModelVolumes*>(this)->calculateAvoidanceToModel(key);
} else {
{
std::lock_guard<std::mutex> critical_section(*mutex_ptr);
result = getArea(*cache_ptr, key);
}
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Avoidance at radius " << key.first << " and layer " << key.second << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Avoidance(to buildplate) requested.", false);
@ -415,120 +380,47 @@ const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_
return getAvoidance(orig_radius, layer_idx, type, to_model, min_xy_dist); // retrive failed and correct result was calculated. Now it has to be retrived.
}
const Polygons& TreeModelVolumes::getPlaceableAreas(coord_t radius, LayerIndex layer_idx) const
const Polygons& TreeModelVolumes::getPlaceableAreas(coord_t orig_radius, LayerIndex layer_idx) const
{
std::optional<std::reference_wrapper<const Polygons>> result;
const coord_t orig_radius = radius;
radius = ceilRadius(radius);
RadiusLayerPair key{ radius, layer_idx };
{
std::lock_guard<std::mutex> critical_section(*m_critical_placeable_areas_cache);
result = getArea(m_placeable_areas_cache, key);
}
const coord_t radius = ceilRadius(orig_radius);
std::optional<std::reference_wrapper<const Polygons>> result = m_placeable_areas_cache.getArea({ radius, layer_idx });
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Placeable areas requested.", false);
}
if (radius != 0)
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(key);
if (orig_radius > 0)
const_cast<TreeModelVolumes*>(this)->calculatePlaceables({ radius, layer_idx });
else
getCollision(0, layer_idx, true);
return getPlaceableAreas(orig_radius, layer_idx);
}
const Polygons& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const
const Polygons& TreeModelVolumes::getWallRestriction(coord_t orig_radius, LayerIndex layer_idx, bool min_xy_dist) const
{
if (layer_idx == 0) // Should never be requested as there will be no going below layer 0 ..., but just to be sure some semi-sane catch. Alternative would be empty Polygon.
return getCollision(radius, layer_idx, min_xy_dist);
return getCollision(orig_radius, layer_idx, min_xy_dist);
coord_t orig_radius = radius;
min_xy_dist = min_xy_dist && m_current_min_xy_dist_delta > 0;
std::optional<std::reference_wrapper<const Polygons>> result;
radius = ceilRadius(radius);
const RadiusLayerPair key{ radius, layer_idx };
const RadiusLayerPolygonCache* cache_ptr = min_xy_dist ? &m_wall_restrictions_cache_min : &m_wall_restrictions_cache;
if (min_xy_dist) {
{
std::lock_guard<std::mutex> critical_section(*m_critical_wall_restrictions_cache_min);
result = getArea(*cache_ptr, key);
}
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Wall restricions at radius " << key.first << " and layer " << key.second << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Wall restriction of minimum xy distance requested ).", false);
}
} else {
{
std::lock_guard<std::mutex> critical_section(*m_critical_wall_restrictions_cache);
result = getArea(*cache_ptr, key);
}
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Wall restricions at radius " << key.first << " and layer " << key.second << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Wall restriction requested ).", false);
}
coord_t radius = ceilRadius(orig_radius);
std::optional<std::reference_wrapper<const Polygons>> result =
(min_xy_dist ? m_wall_restrictions_cache_min : m_wall_restrictions_cache).getArea({ radius, layer_idx });
if (result)
return result.value().get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(warning) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError(
min_xy_dist ?
"Not precalculated Wall restriction of minimum xy distance requested )." :
"Not precalculated Wall restriction requested )."
, false);
}
const_cast<TreeModelVolumes*>(this)->calculateWallRestrictions(key);
const_cast<TreeModelVolumes*>(this)->calculateWallRestrictions({ radius, layer_idx });
return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved.
}
coord_t TreeModelVolumes::ceilRadius(coord_t radius, bool min_xy_dist) const
{
if (! min_xy_dist)
radius += m_current_min_xy_dist_delta;
return ceilRadius(radius);
}
coord_t TreeModelVolumes::getRadiusNextCeil(coord_t radius, bool min_xy_dist) const
{
coord_t ceiled_radius = ceilRadius(radius, min_xy_dist);
if (! min_xy_dist)
ceiled_radius -= m_current_min_xy_dist_delta;
return ceiled_radius;
}
#if 0
Polygons TreeModelVolumes::extractOutlineFromMesh(const PrintObject &print_object, LayerIndex layer_idx) const
{
constexpr bool external_polys_only = false;
Polygons total;
// similar to SliceDataStorage.getLayerOutlines but only for one mesh instead of for everyone
if (mesh.settings.get<bool>("infill_mesh") || mesh.settings.get<bool>("anti_overhang_mesh"))
return Polygons();
const SliceLayer& layer = mesh.layers[layer_idx];
layer.getOutlines(total, external_polys_only);
if (mesh.settings.get<ESurfaceMode>("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
total = union_(total, layer.openPolyLines.offsetPolyLine(100));
coord_t resolution = mesh.settings.get<coord_t>("resolution");
return simplify(total, resolution);
}
#endif
LayerIndex TreeModelVolumes::getMaxCalculatedLayer(coord_t radius, const RadiusLayerPolygonCache& map) const
{
int max_layer = -1;
// the placeable on model areas do not exist on layer 0, as there can not be model below it. As such it may be possible that layer 1 is available, but layer 0 does not exist.
const RadiusLayerPair key_layer_1(radius, 1);
if (getArea(map, key_layer_1))
max_layer = 1;
while (map.count(RadiusLayerPair(radius, max_layer + 1)))
++ max_layer;
return max_layer;
}
void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
{
tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size()),
@ -537,11 +429,11 @@ void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
const coord_t radius = keys[i].first;
const size_t layer_idx = keys[i].second;
RadiusLayerPair key(radius, 0);
RadiusLayerPolygonCache data_outer;
RadiusLayerPolygonCache data_placeable_outer;
RadiusLayerPolygonCacheData data_outer;
RadiusLayerPolygonCacheData data_placeable_outer;
for (size_t outline_idx = 0; outline_idx < m_layer_outlines.size(); ++outline_idx) {
RadiusLayerPolygonCache data;
RadiusLayerPolygonCache data_placeable;
RadiusLayerPolygonCacheData data;
RadiusLayerPolygonCacheData data_placeable;
const coord_t layer_height = m_layer_outlines[outline_idx].first.layer_height;
const bool support_rests_on_this_model = ! m_layer_outlines[outline_idx].first.support_material_buildplate_only;
@ -554,12 +446,7 @@ void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
// avoiding this would require saving each collision for each outline_idx separately.
// 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.
// so avoiding this inaccuracy seems infeasible as it would require 2x the avoidance calculations => 0.5x the performance.
coord_t min_layer_bottom;
{
std::lock_guard<std::mutex> critical_section(*m_critical_collision_cache);
min_layer_bottom = getMaxCalculatedLayer(radius, m_collision_cache) - int(z_distance_bottom_layers);
}
coord_t min_layer_bottom = m_collision_cache.getMaxCalculatedLayer(radius) - int(z_distance_bottom_layers);
if (min_layer_bottom < 0)
min_layer_bottom = 0;
//FIXME parallel_for
@ -623,14 +510,9 @@ void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
}
#endif
{
std::lock_guard<std::mutex> critical_section(*m_critical_collision_cache);
m_collision_cache.insert(data_outer.begin(), data_outer.end());
}
if (radius == 0) {
std::lock_guard<std::mutex> critical_section(*m_critical_placeable_areas_cache);
m_placeable_areas_cache.insert(data_placeable_outer.begin(), data_placeable_outer.end());
}
m_collision_cache.insert(std::move(data_outer));
if (radius == 0)
m_placeable_areas_cache.insert(std::move(data_placeable_outer));
}
});
}
@ -644,7 +526,7 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque<RadiusLayerPair> ke
tbb::parallel_for(tbb::blocked_range<size_t>(0, max_layer + 1),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
RadiusLayerPolygonCache data;
RadiusLayerPolygonCacheData data;
for (RadiusLayerPair key : keys) {
// Logically increase the collision by m_increase_until_radius
coord_t radius = key.first;
@ -654,8 +536,7 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque<RadiusLayerPair> ke
data[RadiusLayerPair(radius, layer_idx)] = col;
}
std::lock_guard<std::mutex> critical_section(*m_critical_collision_cache_holefree);
m_collision_cache_holefree.insert(data.begin(), data.end());
m_collision_cache_holefree.insert(std::move(data));
}
});
}
@ -663,7 +544,7 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque<RadiusLayerPair> ke
void TreeModelVolumes::calculateAvoidance(std::deque<RadiusLayerPair> keys)
{
// For every RadiusLayer pair there are 3 avoidances that have to be calculate, calculated in the same paralell_for loop for better paralellisation.
const std::vector<AvoidanceType> all_types = { AvoidanceType::SLOW, AvoidanceType::FAST_SAFE, AvoidanceType::FAST };
const std::vector<AvoidanceType> all_types = { AvoidanceType::Slow, AvoidanceType::FastSafe, AvoidanceType::Fast };
tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size() * 3),
[&, keys, all_types](const tbb::blocked_range<size_t> &range) {
for (size_t iter_idx = range.begin(); iter_idx < range.end(); ++ iter_idx) {
@ -671,8 +552,8 @@ void TreeModelVolumes::calculateAvoidance(std::deque<RadiusLayerPair> keys)
{
size_t type_idx = iter_idx % all_types.size();
AvoidanceType type = all_types[type_idx];
const bool slow = type == AvoidanceType::SLOW;
const bool holefree = type == AvoidanceType::FAST_SAFE;
const bool slow = type == AvoidanceType::Slow;
const bool holefree = type == AvoidanceType::FastSafe;
coord_t radius = keys[key_idx].first;
LayerIndex max_required_layer = keys[key_idx].second;
@ -683,11 +564,7 @@ void TreeModelVolumes::calculateAvoidance(std::deque<RadiusLayerPair> keys)
const coord_t offset_speed = slow ? m_max_move_slow : m_max_move;
RadiusLayerPair key(radius, 0);
LayerIndex start_layer;
{
std::lock_guard<std::mutex> critical_section(*(slow ? m_critical_avoidance_cache_slow : holefree ? m_critical_avoidance_cache_holefree : m_critical_avoidance_cache));
start_layer = 1 + getMaxCalculatedLayer(radius, slow ? m_avoidance_cache_slow : holefree ? m_avoidance_cache_hole : m_avoidance_cache);
}
LayerIndex start_layer = 1 + (slow ? m_avoidance_cache_slow : holefree ? m_avoidance_cache_holefree : m_avoidance_cache).getMaxCalculatedLayer(radius);
if (start_layer > max_required_layer) {
BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?";
continue;
@ -718,10 +595,7 @@ void TreeModelVolumes::calculateAvoidance(std::deque<RadiusLayerPair> keys)
}
#endif
{
std::lock_guard<std::mutex> critical_section(*(slow ? m_critical_avoidance_cache_slow : holefree ? m_critical_avoidance_cache_holefree : m_critical_avoidance_cache));
(slow ? m_avoidance_cache_slow : holefree ? m_avoidance_cache_hole : m_avoidance_cache).insert(data.begin(), data.end());
}
(slow ? m_avoidance_cache_slow : holefree ? m_avoidance_cache_holefree : m_avoidance_cache).insert(std::move(data));
}
}
});
@ -737,11 +611,7 @@ void TreeModelVolumes::calculatePlaceables(std::deque<RadiusLayerPair> keys)
std::vector<std::pair<RadiusLayerPair, Polygons>> data(max_required_layer + 1, std::pair<RadiusLayerPair, Polygons>(RadiusLayerPair(radius, -1), Polygons()));
RadiusLayerPair key(radius, 0);
LayerIndex start_layer;
{
std::lock_guard<std::mutex> critical_section(*m_critical_placeable_areas_cache);
start_layer = 1 + getMaxCalculatedLayer(radius, m_placeable_areas_cache);
}
LayerIndex start_layer = 1 + m_placeable_areas_cache.getMaxCalculatedLayer(radius);
if (start_layer > max_required_layer) {
BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?";
continue;
@ -770,10 +640,7 @@ void TreeModelVolumes::calculatePlaceables(std::deque<RadiusLayerPair> keys)
}
#endif
{
std::lock_guard<std::mutex> critical_section(*m_critical_placeable_areas_cache);
m_placeable_areas_cache.insert(data.begin(), data.end());
}
m_placeable_areas_cache.insert(std::move(data));
}
});
}
@ -781,15 +648,15 @@ void TreeModelVolumes::calculatePlaceables(std::deque<RadiusLayerPair> keys)
void TreeModelVolumes::calculateAvoidanceToModel(std::deque<RadiusLayerPair> keys)
{
// For every RadiusLayer pair there are 3 avoidances that have to be calculated, calculated in the same parallel_for loop for better parallelization.
const std::vector<AvoidanceType> all_types = { AvoidanceType::SLOW, AvoidanceType::FAST_SAFE, AvoidanceType::FAST };
const std::vector<AvoidanceType> all_types = { AvoidanceType::Slow, AvoidanceType::FastSafe, AvoidanceType::Fast };
tbb::parallel_for(tbb::blocked_range<size_t>(0, keys.size() * 3),
[&, keys, all_types](const tbb::blocked_range<size_t> &range) {
for (size_t iter_idx = range.begin(); iter_idx < range.end(); ++ iter_idx) {
size_t key_idx = iter_idx / 3;
size_t type_idx = iter_idx % all_types.size();
AvoidanceType type = all_types[type_idx];
bool slow = type == AvoidanceType::SLOW;
bool holefree = type == AvoidanceType::FAST_SAFE;
bool slow = type == AvoidanceType::Slow;
bool holefree = type == AvoidanceType::FastSafe;
coord_t radius = keys[key_idx].first;
LayerIndex max_required_layer = keys[key_idx].second;
@ -802,12 +669,7 @@ void TreeModelVolumes::calculateAvoidanceToModel(std::deque<RadiusLayerPair> key
std::vector<std::pair<RadiusLayerPair, Polygons>> data(max_required_layer + 1, std::pair<RadiusLayerPair, Polygons>(RadiusLayerPair(radius, -1), Polygons()));
RadiusLayerPair key(radius, 0);
LayerIndex start_layer;
{
std::lock_guard<std::mutex> critical_section(*(slow ? m_critical_avoidance_cache_to_model_slow : holefree ? m_critical_avoidance_cache_holefree_to_model : m_critical_avoidance_cache_to_model));
start_layer = 1 + getMaxCalculatedLayer(radius, slow ? m_avoidance_cache_to_model_slow : holefree ? m_avoidance_cache_hole_to_model : m_avoidance_cache_to_model);
}
LayerIndex start_layer = 1 + (slow ? m_avoidance_cache_to_model_slow : holefree ? m_avoidance_cache_holefree_to_model : m_avoidance_cache_to_model).getMaxCalculatedLayer(radius);
if (start_layer > max_required_layer) {
BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?";
continue;
@ -836,15 +698,11 @@ void TreeModelVolumes::calculateAvoidanceToModel(std::deque<RadiusLayerPair> key
}
#endif
{
std::lock_guard<std::mutex> critical_section(*(slow ? m_critical_avoidance_cache_to_model_slow : holefree ? m_critical_avoidance_cache_holefree_to_model : m_critical_avoidance_cache_to_model));
(slow ? m_avoidance_cache_to_model_slow : holefree ? m_avoidance_cache_hole_to_model : m_avoidance_cache_to_model).insert(data.begin(), data.end());
}
(slow ? m_avoidance_cache_to_model_slow : holefree ? m_avoidance_cache_holefree_to_model : m_avoidance_cache_to_model).insert(std::move(data));
}
});
}
void TreeModelVolumes::calculateWallRestrictions(std::deque<RadiusLayerPair> keys)
{
// 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.
@ -886,15 +744,9 @@ void TreeModelVolumes::calculateWallRestrictions(std::deque<RadiusLayerPair> key
for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx) {
coord_t radius = keys[key_idx].first;
RadiusLayerPair key(radius, 0);
coord_t min_layer_bottom;
RadiusLayerPolygonCache data;
RadiusLayerPolygonCache data_min;
{
std::lock_guard<std::mutex> critical_section(*m_critical_wall_restrictions_cache);
min_layer_bottom = getMaxCalculatedLayer(radius, m_wall_restrictions_cache);
}
RadiusLayerPolygonCacheData data;
RadiusLayerPolygonCacheData data_min;
coord_t min_layer_bottom = m_wall_restrictions_cache.getMaxCalculatedLayer(radius);
if (min_layer_bottom < 1)
min_layer_bottom = 1;
@ -904,21 +756,14 @@ void TreeModelVolumes::calculateWallRestrictions(std::deque<RadiusLayerPair> key
Polygons wall_restriction = intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx_below, true)); // radius contains m_current_min_xy_dist_delta already if required
wall_restriction = polygons_simplify(wall_restriction, m_min_resolution);
data.emplace(key, wall_restriction);
if (m_current_min_xy_dist_delta > 0)
{
if (m_current_min_xy_dist_delta > 0) {
Polygons wall_restriction_min = intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx_below, true));
wall_restriction = polygons_simplify(wall_restriction_min, m_min_resolution);
data_min.emplace(key, wall_restriction_min);
}
}
{
std::lock_guard<std::mutex> critical_section(*m_critical_wall_restrictions_cache);
m_wall_restrictions_cache.insert(data.begin(), data.end());
}
{
std::lock_guard<std::mutex> critical_section(*m_critical_wall_restrictions_cache_min);
m_wall_restrictions_cache_min.insert(data_min.begin(), data_min.end());
}
m_wall_restrictions_cache.insert(std::move(data));
m_wall_restrictions_cache_min.insert(std::move(data_min));
}
});
}

View File

@ -23,7 +23,6 @@ namespace Slic3r
{
using LayerIndex = int;
using AngleRadians = double;
class BuildVolume;
class PrintObject;
@ -52,7 +51,7 @@ struct TreeSupportMeshGroupSettings {
// Support Overhang Angle
// The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.
AngleRadians support_angle { 50. * M_PI / 180. };
double support_angle { 50. * M_PI / 180. };
// Support Line Width
// Width of a single support structure line.
coord_t support_line_width { scaled<coord_t>(0.4) };
@ -92,7 +91,7 @@ struct TreeSupportMeshGroupSettings {
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end
// of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained
// in square brackets. Default is an empty list which means use the default angle 0 degrees.
std::vector<AngleRadians> support_infill_angles {};
std::vector<double> support_infill_angles {};
// Enable Support Roof
// Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support.
bool support_roof_enable { false };
@ -106,7 +105,7 @@ struct TreeSupportMeshGroupSettings {
// and when the end of the list is reached, it starts at the beginning again. The list items are separated
// by commas and the whole list is contained in square brackets. Default is an empty list which means
// use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees).
std::vector<AngleRadians> support_roof_angles {};
std::vector<double> support_roof_angles {};
// Support Roof Pattern (aka top interface)
// The pattern with which the roofs of the support are printed.
SupportMaterialInterfacePattern support_roof_pattern { smipAuto };
@ -144,12 +143,12 @@ struct TreeSupportMeshGroupSettings {
// Tree Support Maximum Branch Angle
// The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.
// minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85
AngleRadians support_tree_angle { 60. * M_PI / 180. };
double support_tree_angle { 60. * M_PI / 180. };
// Tree Support Branch Diameter Angle
// The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length.
// A bit of an angle can increase stability of the tree support.
// minimum: 0, maximum: 89.9999, maximum warning: 15
AngleRadians support_tree_branch_diameter_angle { 5. * M_PI / 180. };
double support_tree_branch_diameter_angle { 5. * M_PI / 180. };
// Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
@ -166,7 +165,7 @@ struct TreeSupportMeshGroupSettings {
// Tree Support Preferred Branch Angle
// The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.
// minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1
AngleRadians support_tree_angle_slow { 50. * M_PI / 180. };
double support_tree_angle_slow { 50. * M_PI / 180. };
// Tree Support Diameter Increase To Model
// The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate.
// Increasing this reduces print time, but increases the area of support that rests on model
@ -195,17 +194,13 @@ struct TreeSupportMeshGroupSettings {
//enum support_interface_priority { support_lines_overwrite_interface_area };
};
inline coord_t round_up_divide(const coord_t dividend, const coord_t divisor) //!< Return dividend divided by divisor rounded to the nearest integer
{
return (dividend + divisor - 1) / divisor;
}
class TreeModelVolumes
{
public:
TreeModelVolumes() = default;
explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume,
coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier, double progress_offset, const std::vector<Polygons> &additional_excluded_areas = {});
coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier,
double progress_offset, const std::vector<Polygons> &additional_excluded_areas = {});
TreeModelVolumes(TreeModelVolumes&&) = default;
TreeModelVolumes& operator=(TreeModelVolumes&&) = default;
@ -214,17 +209,16 @@ public:
enum class AvoidanceType
{
SLOW,
FAST_SAFE,
FAST
Slow,
FastSafe,
Fast
};
/*!
* \brief Precalculate avoidances and collisions up to this layer.
*
* This uses knowledge about branch angle to only calculate avoidances and collisions that could actually be needed.
* Not calling this 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!
* \brief 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!
*/
void precalculate(coord_t max_layer);
@ -239,20 +233,7 @@ public:
* \param min_xy_dist Is the minimum xy distance used.
* \return Polygons object
*/
const Polygons& getCollision(coord_t radius, LayerIndex layer_idx, bool min_xy_dist = false) const;
/*!
* \brief 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.
* \param radius The radius of the node of interest
* \param layer_idx The layer of interest
* \param min_xy_dist Is the minimum xy distance used.
* \return Polygons object
*/
const Polygons& getCollisionHolefree(coord_t radius, LayerIndex layer_idx, bool min_xy_dist = false) const;
const Polygons& getCollision(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const;
/*!
* \brief Provides the areas that have to be avoided by the tree's branches
@ -266,12 +247,12 @@ public:
*
* \param radius The radius of the node of interest
* \param layer_idx The layer of interest
* \param slow Is the propagation with the maximum move distance slow required.
* \param type Is the propagation with the maximum move distance slow required.
* \param to_model Does the avoidance allow good connections with the model.
* \param min_xy_dist is the minimum xy distance used.
* \return Polygons object
*/
const Polygons& getAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model = false, bool min_xy_dist = false) const;
const Polygons& getAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) const;
/*!
* \brief Provides the area represents all areas on the model where the branch does completely fit on the given layer.
* \param radius The radius of the node of interest
@ -296,7 +277,9 @@ public:
* \param min_xy_dist is the minimum xy distance used.
* \return The rounded radius
*/
coord_t ceilRadius(coord_t radius, bool min_xy_dist) const;
coord_t ceilRadius(coord_t radius, bool min_xy_dist) const {
return this->ceilRadius(min_xy_dist ? radius : radius + m_current_min_xy_dist_delta);
}
/*!
* \brief Round \p radius upwards to the maximum that would still round up to the same value as the provided one.
*
@ -304,16 +287,84 @@ public:
* \param min_xy_dist is the minimum xy distance used.
* \return The maximum radius, resulting in the same rounding.
*/
coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const;
coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const {
return min_xy_dist ?
this->ceilRadius(radius) :
this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta;
}
private:
/*!
* \brief Convenience typedef for the keys to the caches
*/
using RadiusLayerPair = std::pair<coord_t, LayerIndex>;
using RadiusLayerPolygonCache = std::unordered_map<RadiusLayerPair, Polygons, boost::hash<RadiusLayerPair>>;
using RadiusLayerPair = std::pair<coord_t, LayerIndex>;
using RadiusLayerPolygonCacheData = std::unordered_map<RadiusLayerPair, Polygons, boost::hash<RadiusLayerPair>>;
class RadiusLayerPolygonCache {
public:
RadiusLayerPolygonCache() = default;
RadiusLayerPolygonCache(RadiusLayerPolygonCache &&rhs) : data(std::move(rhs.data)) {}
RadiusLayerPolygonCache& operator=(RadiusLayerPolygonCache &&rhs) { data = std::move(rhs.data); return *this; }
friend std::optional<std::reference_wrapper<const Polygons>> getArea(const TreeModelVolumes::RadiusLayerPolygonCache &cache, const TreeModelVolumes::RadiusLayerPair &key);
RadiusLayerPolygonCache(const RadiusLayerPolygonCache&) = delete;
RadiusLayerPolygonCache& operator=(const RadiusLayerPolygonCache&) = delete;
void insert(RadiusLayerPolygonCacheData &&in) {
std::lock_guard<std::mutex> guard(this->mutex);
for (auto& d : in)
this->data.emplace(d.first, std::move(d.second));
}
void insert(std::vector<std::pair<RadiusLayerPair, Polygons>> && in) {
std::lock_guard<std::mutex> guard(this->mutex);
for (auto& d : in)
this->data.emplace(d.first, std::move(d.second));
}
/*!
* \brief Checks a cache for a given RadiusLayerPair and returns it if it is found
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
* \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found)
*/
std::optional<std::reference_wrapper<const Polygons>> getArea(const TreeModelVolumes::RadiusLayerPair &key) const {
std::lock_guard<std::mutex> guard(this->mutex);
const auto it = this->data.find(key);
return it == this->data.end() ?
std::optional<std::reference_wrapper<const Polygons>>{} : std::optional<std::reference_wrapper<const Polygons>>{ it->second };
}
/*!
* \brief Get the highest already calculated layer in the cache.
* \param radius The radius for which the highest already calculated layer has to be found.
* \param map The cache in which the lookup is performed.
*
* \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found)
*/
LayerIndex getMaxCalculatedLayer(coord_t radius) const {
std::lock_guard<std::mutex> guard(this->mutex);
int max_layer = -1;
// the placeable on model areas do not exist on layer 0, as there can not be model below it. As such it may be possible that layer 1 is available, but layer 0 does not exist.
if (this->data.find({ radius, 1 }) != this->data.end())
max_layer = 1;
while (this->data.count(TreeModelVolumes::RadiusLayerPair(radius, max_layer + 1)) > 0)
++ max_layer;
return max_layer;
}
private:
RadiusLayerPolygonCacheData data;
mutable std::mutex mutex;
};
/*!
* \brief 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.
* \param radius The radius of the node of interest
* \param layer_idx The layer of interest
* \param min_xy_dist Is the minimum xy distance used.
* \return Polygons object
*/
const Polygons& getCollisionHolefree(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const;
/*!
* \brief Round \p radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value
@ -322,14 +373,6 @@ private:
*/
coord_t ceilRadius(coord_t radius) const;
/*!
* \brief Extracts the relevant outline from a mesh
* \param[in] mesh The mesh which outline will be extracted
* \param layer_idx The layer which should be extracted from the mesh
* \return Polygons object representing the outline
*/
// Polygons extractOutlineFromMesh(const PrintObject &print_object, LayerIndex layer_idx) const;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
*
@ -449,15 +492,6 @@ private:
calculateWallRestrictions(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*!
* \brief Get the highest already calculated layer in the cache.
* \param radius The radius for which the highest already calculated layer has to be found.
* \param map The cache in which the lookup is performed.
*
* \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found)
*/
LayerIndex getMaxCalculatedLayer(coord_t radius, const RadiusLayerPolygonCache& map) const;
/*!
* \brief The maximum distance that the center point of a tree branch may move in consecutive layers if it has to avoid the model.
*/
@ -531,59 +565,40 @@ private:
*/
coord_t m_radius_0;
/*!
* \brief 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;
std::unique_ptr<std::mutex> m_critical_collision_cache { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_collision_cache_holefree;
std::unique_ptr<std::mutex> m_critical_collision_cache_holefree { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache;
std::unique_ptr<std::mutex> m_critical_avoidance_cache { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache_slow;
std::unique_ptr<std::mutex> m_critical_avoidance_cache_slow { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache_to_model;
std::unique_ptr<std::mutex> m_critical_avoidance_cache_to_model { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache_to_model_slow;
std::unique_ptr<std::mutex> m_critical_avoidance_cache_to_model_slow { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_placeable_areas_cache;
std::unique_ptr<std::mutex> m_critical_placeable_areas_cache { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_collision_cache;
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;
/*!
* \brief 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_hole;
std::unique_ptr<std::mutex> m_critical_avoidance_cache_holefree { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache_hole_to_model;
std::unique_ptr<std::mutex> m_critical_avoidance_cache_holefree_to_model { std::make_unique<std::mutex>() };
RadiusLayerPolygonCache m_avoidance_cache_holefree;
RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model;
/*!
* \brief Caches to represent walls not allowed to be passed over.
*/
RadiusLayerPolygonCache m_wall_restrictions_cache;
std::unique_ptr<std::mutex> m_critical_wall_restrictions_cache { std::make_unique<std::mutex>() };
// A different cache for min_xy_dist as the maximal safe distance an influence area can be increased(guaranteed overlap of two walls in consecutive layer)
// is much smaller when min_xy_dist is used. This causes the area of the wall restriction to be thinner and as such just using the min_xy_dist wall
// restriction would be slower.
RadiusLayerPolygonCache m_wall_restrictions_cache_min;
std::unique_ptr<std::mutex> m_critical_wall_restrictions_cache_min = std::make_unique<std::mutex>();
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
std::unique_ptr<std::mutex> m_critical_progress { std::make_unique<std::mutex>() };
#endif // SLIC3R_TREESUPPORTS_PROGRESS
};
Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision);
}
#endif //slic3r_TreeModelVolumes_hpp

View File

@ -525,13 +525,13 @@ void TreeSupport::generateSupportAreas(Print &print, const BuildVolume &build_vo
for (const Polyline &line : polylines) {
LineInformation res_line;
for (Point p : line) {
if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FAST_SAFE, false, !xy_overrides_z), p))
if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, !xy_overrides_z), p))
res_line.emplace_back(p, LineStatus::TO_BP_SAFE);
else if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FAST, false, !xy_overrides_z), p))
else if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, !xy_overrides_z), p))
res_line.emplace_back(p, LineStatus::TO_BP);
else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FAST_SAFE, true, !xy_overrides_z), p))
else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, !xy_overrides_z), p))
res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS_SAFE);
else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FAST, true, !xy_overrides_z), p))
else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, !xy_overrides_z), p))
res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS);
else if (config.support_rests_on_model && ! contains(volumes.getCollision(config.getRadius(0), layer_idx, !xy_overrides_z), p))
res_line.emplace_back(p, LineStatus::TO_MODEL);
@ -580,12 +580,12 @@ void TreeSupport::generateSupportAreas(Print &print, const BuildVolume &build_vo
size_t current_layer, std::pair<Point, LineStatus> &p)
{
using AvoidanceType = TreeSupport::AvoidanceType;
if (! contains(volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, false, !config.support_xy_overrides_z), p.first))
if (! contains(volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, false, !config.support_xy_overrides_z), p.first))
return true;
if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE))
return ! contains(
p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ?
volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, true, !config.support_xy_overrides_z) :
volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, !config.support_xy_overrides_z) :
volumes.getCollision(config.getRadius(0), current_layer - 1, !config.support_xy_overrides_z),
p.first);
return false;
@ -936,19 +936,6 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
return result;
}
// ensures offsets are only done in sizes with a max step size per offset while adding the collision offset after each step, this ensures that areas cannot glitch through walls defined by the collision when offsetting to fast
[[nodiscard]] Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision)
{
const size_t steps = std::abs(distance / max_safe_step_distance);
assert(int64_t(distance) * int64_t(max_safe_step_distance) >= 0);
ExPolygons ret = union_ex(me);
Polygons collision_trimmed = clip_for_diff(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON));
for (size_t i = 0; i < steps; ++ i)
ret = union_ex(union_(offset(ret, max_safe_step_distance, jt, jt == jtRound ? scaled<float>(0.01) : 1.2), collision_trimmed));
return union_(offset(ret, distance % max_safe_step_distance, jt, jt == jtRound ? scaled<float>(0.01) : 1.2), collision_trimmed);
}
/*!
* \brief Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag through over a given obstacle.
* \param me[in] Polygons object that has to be offset.
@ -1065,7 +1052,7 @@ void TreeSupport::generateInitialAreas(
Polygon base_circle;
const auto base_radius = scaled<int>(0.01);
for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; ++ i) {
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
const double angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
base_circle.points.emplace_back(coord_t(cos(angle) * base_radius), coord_t(sin(angle) * base_radius));
}
TreeSupportMeshGroupSettings mesh_group_settings(print_object);
@ -1106,9 +1093,9 @@ void TreeSupport::generateInitialAreas(
Polygons relevant_forbidden;
{
const Polygons &relevant_forbidden_raw = (mesh_config.support_rests_on_model ?
(SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, true, !xy_overrides_z) :
(SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::Fast, true, !xy_overrides_z) :
m_volumes.getCollision(mesh_config.getRadius(0), layer_idx, !xy_overrides_z)) :
m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, false, !xy_overrides_z));
m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::Fast, false, !xy_overrides_z));
// prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise.
relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled<float>(0.005), jtMiter, 1.2);
}
@ -1257,7 +1244,7 @@ void TreeSupport::generateInitialAreas(
for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) {
// get least restricted avoidance for layer_idx-lag_ctr
const Polygons &relevant_forbidden_below = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::FAST, true, !xy_overrides_z) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - lag_ctr, !xy_overrides_z)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::FAST, false, !xy_overrides_z));
const Polygons &relevant_forbidden_below = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, true, !xy_overrides_z) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - lag_ctr, !xy_overrides_z)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, !xy_overrides_z));
// 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.
auto evaluatePoint = [&](std::pair<Point, LineStatus> p) { return contains(relevant_forbidden_below, p.first); };
@ -1304,7 +1291,11 @@ void TreeSupport::generateInitialAreas(
// here the roof is handled. If roof can not be added the branches will try to not move instead
Polygons forbidden_next;
{
const Polygons &forbidden_next_raw = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::FAST, true, !xy_overrides_z) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), !xy_overrides_z)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::FAST, false, !xy_overrides_z));\
const Polygons &forbidden_next_raw = mesh_config.support_rests_on_model ?
(SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ?
m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::Fast, true, !xy_overrides_z) :
m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), !xy_overrides_z)) :
m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::Fast, false, !xy_overrides_z);
// prevent rounding errors down the line
forbidden_next = offset(union_ex(forbidden_next_raw), scaled<float>(0.005), jtMiter, 1.2);
}
@ -1314,11 +1305,11 @@ void TreeSupport::generateInitialAreas(
size_t dtt_before = dtt_roof > 0 ? dtt_roof - 1 : 0;
if (dtt_roof != 0) {
// Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
overhang_lines = convertLinesToInternal(m_volumes, m_config, ensureMaximumDistancePolyline(generateLines(last_overhang, true, layer_idx - dtt_before), connect_length, 1), layer_idx - dtt_before);
overhang_lines = convertLinesToInternal(m_volumes, m_config,
ensureMaximumDistancePolyline(generateLines(last_overhang, true, layer_idx - dtt_before), connect_length, 1), layer_idx - dtt_before);
overhang_lines = splitLines(overhang_lines,
[this, layer_idx, dtt_before](std::pair<Point, LineStatus> &p){ return evaluatePointForNextLayerFunction(m_volumes, m_config, layer_idx - dtt_before, p); }).first;
}
break;
}
added_roofs[dtt_roof] = overhang_outset;
@ -1641,7 +1632,7 @@ static void mergeHelper(
erase.emplace_back(reduced_check_iter->first);
erase.emplace_back(influence_iter->first);
Polygons merge = diff_clipped(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled<float>(0.01)), volumes.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer.
Polygons merge = diff_clipped(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled<float>(0.01)), volumes.getCollision(0, layer_idx - 1, false)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer.
reduced_aabb.erase(reduced_check_iter->first); // this invalidates reduced_check_iter
reduced_aabb.emplace(key, get_extents(merge));
@ -1692,7 +1683,7 @@ static void mergeInfluenceAreas(
// max_bucket_count is input_size/min_elements_per_bucket round down to the next 2^n.
// The rounding to 2^n is to ensure improved performance, as every iteration two buckets will be merged, halving the amount of buckets.
// If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be optimal performance-wise.
const size_t max_bucket_count = std::pow(2, std::floor(std::log(round_up_divide(input_size, min_elements_per_bucket))));
const size_t max_bucket_count = std::pow(2, std::floor(std::log(round_up_divide<int>(input_size, min_elements_per_bucket))));
int bucket_count = std::min(max_bucket_count, num_threads); // do not use more buckets than available threads.
// To achieve that every element in a bucket is already correctly merged with other elements in this bucket
@ -1946,7 +1937,7 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
};
const bool parent_moved_slow = elem.last_area_increase.increase_speed < m_config.maximum_move_distance;
const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::SLOW;
const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::Slow;
if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer && !avoidance_speed_mismatch && (elem.distance_to_top >= m_config.tip_layers || parent_moved_slow))
{
// assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster.
@ -1957,27 +1948,27 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
if (!elem.can_use_safe_radius)
{
// 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. This does not guarantee the avoidance of such holes, but ensures they are avoided if possible.
// order.emplace_back(AvoidanceType::SLOW,!increase_radius,no_error,!use_min_radius,move);
insertSetting({ AvoidanceType::SLOW, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we go through the hole
// order.emplace_back(AvoidanceType::Slow,!increase_radius,no_error,!use_min_radius,move);
insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we go through the hole
// 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. This CAN cause a branch to go though a hole it otherwise may have avoided.
if (elem.distance_to_top < round_up_divide(m_config.tip_layers, 2))
insertSetting({ AvoidanceType::FAST, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true);
insertSetting({ AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we manage to avoid the hole
insertSetting({ AvoidanceType::FAST_SAFE, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
insertSetting({ AvoidanceType::FAST, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
if (elem.distance_to_top < round_up_divide(m_config.tip_layers, size_t(2)))
insertSetting({ AvoidanceType::Fast, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true);
insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we manage to avoid the hole
insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
}
else
{
insertSetting({ AvoidanceType::SLOW, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
// 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, which looks similar to a layer shift and can reduce stability.
// as such idx have chosen to only use the user setting for radius increases as a friendly recommendation.
insertSetting({ AvoidanceType::SLOW, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a
insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a
if (elem.distance_to_top < m_config.tip_layers)
{
insertSetting({ AvoidanceType::FAST_SAFE, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
insertSetting({ AvoidanceType::FastSafe, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
}
insertSetting({ AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, !use_min_radius, move }, true); // b
insertSetting({ AvoidanceType::FAST_SAFE, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, move }, true); // b
insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
}
if (elem.use_min_xy_dist)
@ -1994,11 +1985,11 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
if (elem.to_buildplate || (elem.to_model_gracious && intersection(*parent->area, m_volumes.getPlaceableAreas(radius, layer_idx)).empty())) // error case
{
// 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
insertSetting({ AvoidanceType::FAST, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true);
insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true);
}
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.
// Only do not move when already in a no hole avoidance with the regular xy distance.
insertSetting({ AvoidanceType::SLOW, 0, increase_radius, no_error, !use_min_radius, !move }, false);
insertSetting({ AvoidanceType::Slow, 0, increase_radius, no_error, !use_min_radius, !move }, false);
Polygons inc_wo_collision;
// 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.
@ -2057,7 +2048,7 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
else
elem.result_on_layer = parent->result_on_layer;
elem.can_use_safe_radius = settings.type != AvoidanceType::FAST;
elem.can_use_safe_radius = settings.type != AvoidanceType::Fast;
if (!settings.use_min_distance)
elem.use_min_xy_dist = false;
@ -2368,7 +2359,7 @@ void TreeSupport::generateBranchAreas(
Polygon branch_circle; // Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; ++ i) {
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
const double angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
branch_circle.points.emplace_back(coord_t(cos(angle) * m_config.branch_radius), coord_t(sin(angle) * m_config.branch_radius));
}
@ -2594,7 +2585,7 @@ void TreeSupport::dropNonGraciousAreas(
Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem];
LayerIndex counter = 1;
while (area(rest_support) > tiny_area_threshold && counter < linear_data[idx].first) {
rest_support = diff_clipped(rest_support, m_volumes.getCollision(0, linear_data[idx].first - counter));
rest_support = diff_clipped(rest_support, m_volumes.getCollision(0, linear_data[idx].first - counter, false));
dropped_down_areas[idx].emplace_back(linear_data[idx].first - counter, rest_support);
counter++;
}

View File

@ -15,6 +15,7 @@
#include <boost/functional/hash.hpp> // For combining hashes
#include "BoundingBox.hpp"
#include "Utils.hpp"
#define TREE_SUPPORT_SHOW_ERRORS
@ -95,7 +96,7 @@ public:
struct AreaIncreaseSettings
{
AvoidanceType type { AvoidanceType::FAST };
AvoidanceType type { AvoidanceType::Fast };
coord_t increase_speed { 0 };
bool increase_radius { false };
bool no_error { false };
@ -116,7 +117,7 @@ public:
target_height(target_height), target_position(target_position), next_position(target_position), next_height(target_height), effective_radius_height(distance_to_top),
to_buildplate(to_buildplate), distance_to_top(distance_to_top), area(nullptr), result_on_layer(target_position), increased_to_model_radius(0), to_model_gracious(to_model_gracious),
elephant_foot_increases(0), use_min_xy_dist(use_min_xy_dist), supports_roof(supports_roof), dont_move_until(dont_move_until), can_use_safe_radius(can_use_safe_radius),
last_area_increase(AreaIncreaseSettings{ AvoidanceType::FAST, 0, false, false, false, false }), missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), skip_ovalisation(skip_ovalisation)
last_area_increase(AreaIncreaseSettings{ AvoidanceType::Fast, 0, false, false, false, false }), missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), skip_ovalisation(skip_ovalisation)
{
}
@ -176,16 +177,19 @@ public:
}
// ONLY to be called in merge as it assumes a few assurances made by it.
explicit SupportElement(const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position, coord_t increased_to_model_radius, const TreeSupportSettings& config) : next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius), use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof), dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius), missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false)
explicit SupportElement(
const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position,
coord_t increased_to_model_radius, const TreeSupportSettings& config) :
next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius),
use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof),
dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius),
missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false)
{
if (first.target_height > second.target_height)
{
if (first.target_height > second.target_height) {
target_height = first.target_height;
target_position = first.target_position;
}
else
{
} else {
target_height = second.target_height;
target_position = second.target_position;
}
@ -199,8 +203,7 @@ public:
AddParents(second.parents);
elephant_foot_increases = 0;
if (config.diameter_scale_bp_radius > 0)
{
if (config.diameter_scale_bp_radius > 0) {
coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(*this));
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
@ -406,7 +409,7 @@ public:
xy_distance = std::max(xy_distance, xy_min_distance);
// (logic) from getInterfaceAngles in FFFGcodeWriter.
auto getInterfaceAngles = [&](std::vector<AngleRadians>& angles, SupportMaterialInterfacePattern pattern) {
auto getInterfaceAngles = [&](std::vector<double>& angles, SupportMaterialInterfacePattern pattern) {
if (angles.empty())
{
if (pattern == SupportMaterialInterfacePattern::smipConcentric)
@ -543,11 +546,11 @@ public:
/*!
* \brief User specified angles for the support infill.
*/
std::vector<AngleRadians> support_infill_angles;
std::vector<double> support_infill_angles;
/*!
* \brief User specified angles for the support roof infill.
*/
std::vector<AngleRadians> support_roof_angles;
std::vector<double> support_roof_angles;
/*!
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
*/
@ -613,7 +616,7 @@ public:
||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
settings.get<AngleRadians>("wall_transition_angle") == other.settings.get<AngleRadians>("wall_transition_angle") &&
settings.get<double>("wall_transition_angle") == other.settings.get<double>("wall_transition_angle") &&
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&

View File

@ -192,6 +192,14 @@ inline INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
return idx;
}
// Return dividend divided by divisor rounded to the nearest integer
template<typename INDEX_TYPE>
inline INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor)
{
return (dividend + divisor - 1) / divisor;
}
template<typename CONTAINER_TYPE>
inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{