ENH: fixed crash issue of lightning infill of tree support

Use clipper2 and remove colinear points.
Later on we can try to set lightning infill as the default infill pattern of tree support.

jira: none
Change-Id: Id545b15d778071cec6e56d212ab68db1ab90ad2a
This commit is contained in:
Arthur 2025-02-13 17:52:06 +08:00 committed by lane.wei
parent 26881072a4
commit 2debc31c4d
8 changed files with 131 additions and 46 deletions

View File

@ -79,6 +79,14 @@ static ExPolygons PolyTreeToExPolygons(Clipper2Lib::PolyTree64 &&polytree)
return retval; return retval;
} }
void SimplifyPolyTree(const Clipper2Lib::PolyPath64 &polytree, double epsilon, Clipper2Lib::PolyPath64 &result)
{
for (const auto &child : polytree) {
Clipper2Lib::PolyPath64 *newchild = result.AddChild(Clipper2Lib::SimplifyPath(child->Polygon(), epsilon));
SimplifyPolyTree(*child, epsilon, *newchild);
}
}
Clipper2Lib::Paths64 Slic3rPolygons_to_Paths64(const Polygons &in) Clipper2Lib::Paths64 Slic3rPolygons_to_Paths64(const Polygons &in)
{ {
Clipper2Lib::Paths64 out; Clipper2Lib::Paths64 out;
@ -132,7 +140,7 @@ Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip) Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); } { return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); }
ExPolygons union_ex2(const Polygons& polygons) ExPolygons union_ex_2(const Polygons& polygons)
{ {
Clipper2Lib::Clipper64 c; Clipper2Lib::Clipper64 c;
c.AddSubject(Slic3rPolygons_to_Paths64(polygons)); c.AddSubject(Slic3rPolygons_to_Paths64(polygons));
@ -147,7 +155,7 @@ ExPolygons union_ex2(const Polygons& polygons)
return results; return results;
} }
ExPolygons union_ex2(const ExPolygons &expolygons) ExPolygons union_ex_2(const ExPolygons &expolygons)
{ {
Clipper2Lib::Clipper64 c; Clipper2Lib::Clipper64 c;
c.AddSubject(Slic3rExPolygons_to_Paths64(expolygons)); c.AddSubject(Slic3rExPolygons_to_Paths64(expolygons));
@ -161,4 +169,43 @@ ExPolygons union_ex2(const ExPolygons &expolygons)
return results; return results;
} }
}
// 对 ExPolygons 进行偏移
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta)
{
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
Clipper2Lib::ClipperOffset offsetter;
offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
Clipper2Lib::PolyPath64 polytree;
offsetter.Execute(delta, polytree);
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
return results;
}
ExPolygons offset2_ex_2(const ExPolygons& expolygons, double delta1, double delta2)
{
// 1st offset
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
Clipper2Lib::ClipperOffset offsetter;
offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
Clipper2Lib::PolyPath64 polytree;
offsetter.Execute(delta1, polytree);
// simplify the result
Clipper2Lib::PolyPath64 polytree2;
SimplifyPolyTree(polytree, SCALED_EPSILON, polytree2);
// 2nd offset
offsetter.Clear();
offsetter.AddPaths(Clipper2Lib::PolyTreeToPaths64(polytree2), Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
polytree.Clear();
offsetter.Execute(delta2, polytree);
// convert back to expolygons
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
return results;
}
} // namespace Slic3r

View File

@ -8,8 +8,10 @@ namespace Slic3r {
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
ExPolygons union_ex2(const Polygons &expolygons); ExPolygons union_ex_2(const Polygons &expolygons);
ExPolygons union_ex2(const ExPolygons &expolygons); ExPolygons union_ex_2(const ExPolygons &expolygons);
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta);
ExPolygons offset2_ex_2(const ExPolygons &expolygons, double delta1, double delta2);
} }
#endif #endif

View File

@ -413,6 +413,18 @@ Lines ExPolygon::lines() const
return lines; return lines;
} }
bool ExPolygon::remove_colinear_points() {
bool removed = this->contour.remove_colinear_points();
if (contour.size() < 3) {
contour.points.clear();
holes.clear();
return true;
}
for (Polygon &hole : this->holes)
removed |= hole.remove_colinear_points();
return removed;
}
// Do expolygons match? If they match, they must have the same topology, // Do expolygons match? If they match, they must have the same topology,
// however their contours may be rotated. // however their contours may be rotated.
bool expolygons_match(const ExPolygon &l, const ExPolygon &r) bool expolygons_match(const ExPolygon &l, const ExPolygon &r)

View File

@ -76,6 +76,8 @@ public:
{ Polylines out; this->medial_axis(min_width, max_width, &out); return out; } { Polylines out; this->medial_axis(min_width, max_width, &out); return out; }
Lines lines() const; Lines lines() const;
bool remove_colinear_points();
// Number of contours (outer contour with holes). // Number of contours (outer contour with holes).
size_t num_contours() const { return this->holes.size() + 1; } size_t num_contours() const { return this->holes.size() + 1; }
Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; } Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; }

View File

@ -4,6 +4,7 @@
#include "DistanceField.hpp" //Class we're implementing. #include "DistanceField.hpp" //Class we're implementing.
#include "../FillRectilinear.hpp" #include "../FillRectilinear.hpp"
#include "../../ClipperUtils.hpp" #include "../../ClipperUtils.hpp"
#include "../../Clipper2Utils.hpp"
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
@ -43,7 +44,8 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
m_supporting_radius2 = Slic3r::sqr(int64_t(radius)); m_supporting_radius2 = Slic3r::sqr(int64_t(radius));
// Sample source polygons with a regular grid sampling pattern. // Sample source polygons with a regular grid sampling pattern.
const BoundingBox overhang_bbox = get_extents(current_overhang); const BoundingBox overhang_bbox = get_extents(current_overhang);
ExPolygons expolys = offset2_ex(union_ex(current_overhang), -m_cell_size / 2, m_cell_size / 2); // remove dangling lines which causes sample_grid_pattern crash (fails the OUTER_LOW assertions) // remove dangling lines which causes sample_grid_pattern crash (fails the OUTER_LOW assertions)
ExPolygons expolys = offset2_ex_2(union_ex_2(current_overhang), -m_cell_size / 2, m_cell_size / 2);
for (const ExPolygon &expoly : expolys) { for (const ExPolygon &expoly : expolys) {
const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox); const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox);
const size_t unsupported_points_prev_size = m_unsupported_points.size(); const size_t unsupported_points_prev_size = m_unsupported_points.size();

View File

@ -118,6 +118,21 @@ bool MultiPoint::remove_duplicate_points()
return false; return false;
} }
bool MultiPoint::remove_colinear_points() {
if (points.size() < 3) return false;
bool changed = false;
for (size_t i = 1; i < points.size() - 1; ) {
if (Line::distance_to_infinite_squared(points[i], points[i - 1], points[i+1]) < SCALED_EPSILON) {
points.erase(points.begin() + i);
changed = true;
} else {
++ i;
}
}
if (points.size() < 3) points.clear();
return changed;
}
bool MultiPoint::intersection(const Line& line, Point* intersection) const bool MultiPoint::intersection(const Line& line, Point* intersection) const
{ {
Lines lines = this->lines(); Lines lines = this->lines();

View File

@ -77,6 +77,7 @@ public:
bool has_duplicate_points() const; bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed. // Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points(); bool remove_duplicate_points();
bool remove_colinear_points();
void clear() { this->points.clear(); } void clear() { this->points.clear(); }
void append(const Point &point) { this->points.push_back(point); } void append(const Point &point) { this->points.push_back(point); }
void append(const Points &src) { this->append(src.begin(), src.end()); } void append(const Points &src) { this->append(src.begin(), src.end()); }

View File

@ -2,6 +2,7 @@
#include <math.h> #include <math.h>
#include "format.hpp" #include "format.hpp"
#include "BuildVolume.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Clipper2Utils.hpp" #include "Clipper2Utils.hpp"
#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp"
@ -25,6 +26,13 @@
#include <tbb/parallel_for_each.h> #include <tbb/parallel_for_each.h>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
// #include <boost/log/core.hpp>
// #include <boost/log/expressions.hpp>
// #include <boost/log/sources/severity_logger.hpp>
// #include <boost/log/sources/record_ostream.hpp>
// #include <boost/log/utility/setup/file.hpp>
// #include <boost/log/utility/setup/common_attributes.hpp>
// #include <boost/log/utility/setup/console.hpp>
#ifndef M_PI #ifndef M_PI
#define M_PI 3.1415926535897932384626433832795 #define M_PI 3.1415926535897932384626433832795
@ -605,7 +613,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; SupportMaterialPattern support_pattern = m_object_config->support_base_pattern;
if (m_support_params.support_style == smsTreeHybrid && support_pattern == smpDefault) if (m_support_params.support_style == smsTreeHybrid && support_pattern == smpDefault)
support_pattern = smpRectilinear; support_pattern = smpRectilinear;//smpLightning;
if(support_pattern == smpLightning) if(support_pattern == smpLightning)
m_support_params.base_fill_pattern = ipLightning; m_support_params.base_fill_pattern = ipLightning;
@ -622,7 +630,8 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data = m_object->alloc_tree_support_preview_cache();
top_z_distance = m_object_config->support_top_z_distance.value; top_z_distance = m_object_config->support_top_z_distance.value;
if (top_z_distance > EPSILON) top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height)); if (top_z_distance > EPSILON)
top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height));
#if USE_TREESUPPRT3D #if USE_TREESUPPRT3D
m_support_params.independent_layer_height = false; // do not use independent layer height for 3D tree support m_support_params.independent_layer_height = false; // do not use independent layer height for 3D tree support
#endif #endif
@ -956,7 +965,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted); auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted);
Polygons cluster_boundary = to_polygons(cluster_boundary_ex); Polygons cluster_boundary = to_polygons(cluster_boundary_ex);
if (cluster_boundary.empty()) continue; if (cluster_boundary.empty()) continue;
for (auto &pt : poly.contour.points) { for (auto &pt : poly.contour.points) {
double dist_pt = std::numeric_limits<double>::max(); double dist_pt = std::numeric_limits<double>::max();
for (auto &ply : cluster_boundary) { for (auto &ply : cluster_boundary) {
@ -1589,7 +1598,7 @@ void TreeSupport::generate_toolpaths()
if (area_group.type == SupportLayer::Roof1stLayer) { if (area_group.type == SupportLayer::Roof1stLayer) {
// use higher flow for roof_1st_layer ? // use higher flow for roof_1st_layer ?
Flow roof_1st_layer_flow = support_material_interface_flow(m_object, ts_layer->height)/*.with_flow_ratio(1.5)*/; Flow roof_1st_layer_flow = support_material_interface_flow(m_object, ts_layer->height)/*.with_flow_ratio(1.5)*/;
// Note: spacing means the separation between two lines as if they are tightly extruded // Note: spacing means the separation between two lines as if they are tightly extruded
filler_Roof1stLayer->spacing = roof_1st_layer_flow.spacing(); filler_Roof1stLayer->spacing = roof_1st_layer_flow.spacing();
// generate a perimeter first to support interface better // generate a perimeter first to support interface better
@ -1629,9 +1638,9 @@ void TreeSupport::generate_toolpaths()
if(m_object_config->support_base_pattern==smpDefault) if(m_object_config->support_base_pattern==smpDefault)
need_infill &= area_group.need_infill; need_infill &= area_group.need_infill;
std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern));
filler_support->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object);
filler_support->spacing = support_flow.spacing(); filler_support->spacing = support_flow.spacing();
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);
Polygons loops = to_polygons(poly); Polygons loops = to_polygons(poly);
@ -1786,8 +1795,10 @@ void TreeSupport::generate()
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
//m_object->print()->set_status(56, _u8L("Support: precalculate avoidance")); //m_object->print()->set_status(56, _u8L("Support: precalculate avoidance"));
Points bedpts = m_machine_border.contour.points; Points bedpts = m_machine_border.contour.points;
BuildVolume build_volume{ Pointfs{ unscaled(bedpts[0]), unscaled(bedpts[1]),unscaled(bedpts[2]),unscaled(bedpts[3])}, m_print_config->printable_height }; Pointfs bedptsf;
std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); });
BuildVolume build_volume{bedptsf, m_print_config->printable_height, {}};
TreeSupport3D::TreeSupportSettings tree_support_3d_config{ TreeSupport3D::TreeSupportMeshGroupSettings{ *m_object }, m_slicing_params }; TreeSupport3D::TreeSupportSettings tree_support_3d_config{ TreeSupport3D::TreeSupportMeshGroupSettings{ *m_object }, m_slicing_params };
m_model_volumes = std::make_unique<TreeSupport3D::TreeModelVolumes>( *m_object, build_volume, tree_support_3d_config.maximum_move_distance, tree_support_3d_config.maximum_move_distance_slow, 1); m_model_volumes = std::make_unique<TreeSupport3D::TreeModelVolumes>( *m_object, build_volume, tree_support_3d_config.maximum_move_distance, tree_support_3d_config.maximum_move_distance_slow, 1);
// ### Precalculate avoidances, collision etc. // ### Precalculate avoidances, collision etc.
@ -2101,12 +2112,11 @@ void TreeSupport::draw_circles()
} }
// generate areas // generate areas
const coordf_t layer_height = config.layer_height.value;
const size_t top_interface_layers = config.support_interface_top_layers.value; const size_t top_interface_layers = config.support_interface_top_layers.value;
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning; const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning;
coordf_t support_extrusion_width = m_support_params.support_extrusion_width; coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
const coordf_t line_width_scaled = scale_(support_extrusion_width); const coordf_t line_width_scaled = scale_(support_extrusion_width);
const float tree_brim_width = config.raft_first_layer_expansion.value; const float tree_brim_width = config.raft_first_layer_expansion.value;
if (m_object->support_layer_count() <= m_raft_layers) if (m_object->support_layer_count() <= m_raft_layers)
@ -2243,7 +2253,7 @@ void TreeSupport::draw_circles()
} }
} }
if (layer_nr == 0 && m_raft_layers == 0) if (layer_nr == 0 && m_raft_layers == 0)
area = safe_offset_inc(area, scale_(brim_width), get_collision(false), scale_(MIN_BRANCH_RADIUS * 0.5), 0, 1); area = safe_offset_inc(area, scale_(brim_width), get_collision(false), scale_(MIN_BRANCH_RADIUS * 0.5), 0, 1);
if (obj_layer_nr>0 && node.distance_to_top < 0) if (obj_layer_nr>0 && node.distance_to_top < 0)
append(roof_gap_areas, area); append(roof_gap_areas, area);
@ -2365,8 +2375,6 @@ void TreeSupport::draw_circles()
if (with_lightning_infill) if (with_lightning_infill)
{ {
const bool global_lightning_infill = true;
std::vector<Polygons> contours; std::vector<Polygons> contours;
std::vector<Polygons> overhangs; std::vector<Polygons> overhangs;
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
@ -2391,33 +2399,25 @@ void TreeSupport::draw_circles()
SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers); SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers);
ExPolygons& base_areas_lower = lower_layer->base_areas; ExPolygons& base_areas_lower = lower_layer->base_areas;
ExPolygons overhang; ExPolygons overhang = diff_ex(offset_ex_2(base_areas_lower, -2.5 * line_width_scaled), base_areas);
if (global_lightning_infill) for (auto it = overhang.begin(); it != overhang.end();) {
{ it->remove_colinear_points();
//search overhangs globally if (it->empty())
overhang = std::move(diff_ex(offset_ex(base_areas_lower, -2.0 * scale_(support_extrusion_width)), base_areas)); it = overhang.erase(it);
} else ++it;
else
{
//search overhangs only on floating islands
for (auto& base_area : base_areas)
for (auto& hole : base_area.holes)
{
Polygon rev_hole = hole;
rev_hole.make_counter_clockwise();
ExPolygons ex_hole;
ex_hole.emplace_back(std::move(ExPolygon(rev_hole)));
for (auto& other_area : base_areas)
//if (&other_area != &base_area)
ex_hole = std::move(diff_ex(ex_hole, other_area));
overhang = std::move(union_ex(overhang, ex_hole));
}
overhang = std::move(intersection_ex(overhang, offset_ex(base_areas_lower, -0.5 * scale_(support_extrusion_width))));
} }
overhangs.emplace_back(to_polygons(overhang)); overhangs.emplace_back(to_polygons(overhang));
contours.emplace_back(to_polygons(base_areas_lower)); contours.emplace_back(to_polygons(base_areas_lower));
printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1;
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
if (!overhang.empty() && !base_areas_lower.empty()) {
std::string fname = debug_out_path("lightning_%d_%.2f.svg", layer_nr, ts_layer->print_z);
SVG::export_expolygons(fname, {{base_areas_lower, {"base_areas_lower", "red", 0.5}}, {overhang, {"overhang", "blue", 0.5}}});
}
#endif
} }
@ -3369,11 +3369,11 @@ void TreeSupport::smooth_nodes()
int idx_last_double_wall = -1; int idx_last_double_wall = -1;
for (size_t i = 0; i < pts.size(); i++) { for (size_t i = 0; i < pts.size(); i++) {
if (branch[i]->need_extra_wall) { if (branch[i]->need_extra_wall) {
if (idx_first_double_wall < 0) idx_first_double_wall = i; if (idx_first_double_wall < 0) idx_first_double_wall = i;
idx_last_double_wall = i; idx_last_double_wall = i;
} }
} }
if (idx_last_double_wall >= 0 && idx_first_double_wall >= 0 && idx_last_double_wall > idx_first_double_wall) { if (idx_last_double_wall >= 0 && idx_first_double_wall >= 0 && idx_last_double_wall > idx_first_double_wall) {
for (size_t i = idx_first_double_wall + 1; i < idx_last_double_wall; i++) branch[i]->need_extra_wall = true; for (size_t i = idx_first_double_wall + 1; i < idx_last_double_wall; i++) branch[i]->need_extra_wall = true;
} }
} }
@ -3910,14 +3910,14 @@ TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance
ExPolygons &outline = m_layer_outlines[layer_nr]; ExPolygons &outline = m_layer_outlines[layer_nr];
outline.clear(); outline.clear();
outline.reserve(layer->lslices.size()); outline.reserve(layer->lslices.size());
for (const ExPolygon &poly : layer->lslices) { append(outline, union_ex2( poly.simplify_p(scale_(m_radius_sample_resolution)))); } for (const ExPolygon &poly : layer->lslices) { append(outline, union_ex_2( poly.simplify_p(scale_(m_radius_sample_resolution)))); }
if (layer_nr == 0) if (layer_nr == 0)
m_layer_outlines_below[layer_nr] = outline; m_layer_outlines_below[layer_nr] = outline;
else { else {
m_layer_outlines_below[layer_nr] = m_layer_outlines_below[layer_nr - 1]; m_layer_outlines_below[layer_nr] = m_layer_outlines_below[layer_nr - 1];
m_layer_outlines_below[layer_nr].insert(m_layer_outlines_below[layer_nr].end(), outline.begin(), outline.end()); m_layer_outlines_below[layer_nr].insert(m_layer_outlines_below[layer_nr].end(), outline.begin(), outline.end());
if (layer_nr % 10 == 0) m_layer_outlines_below[layer_nr] = union_ex2(m_layer_outlines_below[layer_nr]); if (layer_nr % 10 == 0) m_layer_outlines_below[layer_nr] = union_ex_2(m_layer_outlines_below[layer_nr]);
} }
} }
} }
@ -4032,6 +4032,10 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
avoidance_areas = std::move(union_ex(avoidance_areas)); avoidance_areas = std::move(union_ex(avoidance_areas));
auto ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)}); auto ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)});
//assert(ret.second); //assert(ret.second);
// BOOST_LOG_TRIVIAL(debug) << format("calculate_avoidance: radius=%.2f, layer_nr=%d, recursions=%d, avoidance_areas=%d", radius, layer_nr, key.recursions,
// avoidance_areas.size());
// boost::log::core::get()->flush();
return ret.first->second; return ret.first->second;
} }