From 8593ad1f80011994b33a130c2d634cb83acda5f6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 15:26:41 +0200 Subject: [PATCH] Organic supports improvements: Removing collisions with trees, limiting how far tree bottoms at slanted surfaces could be extended down below their last full circle position. Placable areas are now calculated sitting on slightly inflated top surface to indicate support of tree bottoms at slanted surfaces. --- src/libslic3r/ClipperUtils.cpp | 2 + src/libslic3r/ClipperUtils.hpp | 3 + src/libslic3r/TreeModelVolumes.cpp | 17 ++-- src/libslic3r/TreeModelVolumes.hpp | 3 +- src/libslic3r/TreeSupport.cpp | 156 ++++++++++++++++++++++------- src/libslic3r/TreeSupport.hpp | 12 +++ 6 files changed, 147 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d10e14cc5..85ef53c88 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -685,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index ab967fd8f..774e9cb42 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -453,6 +453,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). +// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 3c82b5c6c..10840d1b5 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -426,7 +426,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L return (*result).get(); if (m_precalculated) { 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!"; - tree_supports_show_error("Not precalculated Placeable areas requested."sv, false); + tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false); } if (orig_radius == 0) // Placable areas for radius 0 are calculated in the general collision code. @@ -597,16 +597,19 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 3) Optionally calculate placables. if (calculate_placable) { // Now calculate the placable areas. - tbb::parallel_for(tbb::blocked_range(std::max(data.idx_begin, 1), data.idx_end), - [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh, - min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel] + tbb::parallel_for(tbb::blocked_range(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end), + [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh, + min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel] (const tbb::blocked_range& range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { - LayerIndex layer_idx_below = layer_idx - 1; + LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1; assert(layer_idx_below >= 0); const Polygons ¤t = collision_areas_offsetted[layer_idx]; - const Polygons &below = collision_areas_offsetted[layer_idx_below]; - Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); + const Polygons &below = outlines[layer_idx_below]; + Polygons placable = diff( + // Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface. + offset(below, xy_distance), + layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); auto &dst = data_placeable[layer_idx]; if (processing_last_mesh) { if (! dst.empty()) diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp index 139b12328..659baf1ff 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -215,6 +215,7 @@ public: void clear() { this->clear_all_but_object_collision(); m_collision_cache.clear(); + m_placeable_areas_cache.clear(); } void clear_all_but_object_collision() { //m_collision_cache.clear_all_but_radius0(); @@ -223,7 +224,7 @@ public: m_avoidance_cache_slow.clear(); m_avoidance_cache_to_model.clear(); m_avoidance_cache_to_model_slow.clear(); - m_placeable_areas_cache.clear(); + m_placeable_areas_cache.clear_all_but_radius0(); m_avoidance_cache_holefree.clear(); m_avoidance_cache_holefree_to_model.clear(); m_wall_restrictions_cache.clear(); diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 820b60d49..9a7b203c6 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -299,7 +299,7 @@ static bool inline g_showed_critical_error = false; static bool inline g_showed_performance_warning = false; void tree_supports_show_error(std::string_view message, bool critical) { // todo Remove! ONLY FOR PUBLIC BETA!! - +// printf("Error: %s, critical: %d\n", message.data(), int(critical)); #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 static bool showed_critical = false; static bool showed_performance = false; @@ -2158,6 +2158,10 @@ static void increase_areas_one_layer( " Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist << " to buildplate " << parent.state.to_buildplate << " gracious " << parent.state.to_model_gracious << " safe " << parent.state.can_use_safe_radius << " until move " << parent.state.dont_move_until; tree_supports_show_error("Potentially lost branch!"sv, true); +#ifdef TREE_SUPPORTS_TRACK_LOST + if (result) + result->lost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } else result = increase_single_area(volumes, config, settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer); @@ -2211,6 +2215,9 @@ static void increase_areas_one_layer( // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); +#ifdef TREE_SUPPORTS_TRACK_LOST + parent.state.verylost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } throw_on_cancel(); @@ -4234,26 +4241,35 @@ static std::vector draw_branches( branch.path.emplace_back(&start_element); // Traverse each branch until it branches again. SupportElement &first_parent = layer_above[start_element.parents[parent_idx]]; + assert(! first_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx); branch.path.emplace_back(&first_parent); if (first_parent.parents.size() < 2) first_parent.state.marked = true; SupportElement *next_branch = nullptr; - if (first_parent.parents.size() == 1) + if (first_parent.parents.size() == 1) { for (SupportElement *parent = &first_parent;;) { + assert(parent->state.marked); SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()]; + assert(! next_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx); branch.path.emplace_back(&next_parent); if (next_parent.parents.size() > 1) { + // Branching point was reached. next_branch = &next_parent; break; } next_parent.state.marked = true; if (next_parent.parents.size() == 0) + // Tip is reached. break; parent = &next_parent; } + } else if (first_parent.parents.size() > 1) + // Branching point was reached. + next_branch = &first_parent; assert(branch.path.size() >= 2); + assert(next_branch == nullptr || ! next_branch->state.marked); branch.has_root = root; branch.has_tip = ! next_branch; out.branches.emplace_back(std::move(branch)); @@ -4263,13 +4279,37 @@ static std::vector draw_branches( } }; - for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) - for (SupportElement &start_element : move_bounds[layer_idx]) - if (! start_element.state.marked && ! start_element.parents.empty()) { + for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) { +// int ielement; + for (SupportElement& start_element : move_bounds[layer_idx]) { + if (!start_element.state.marked && !start_element.parents.empty()) { +#if 0 + int found = 0; + if (layer_idx > 0) { + for (auto& el : move_bounds[layer_idx - 1]) { + for (auto iparent : el.parents) + if (iparent == ielement) + ++found; + } + if (found != 0) + printf("Found: %d\n", found); + } +#endif trees.push_back({}); TreeVisitor::visit_recursive(move_bounds, start_element, trees.back()); - assert(! trees.back().branches.empty()); + assert(!trees.back().branches.empty()); + //FIXME debugging +#if 0 + if (start_element.state.lost) { + } + else if (start_element.state.verylost) { + } else + trees.pop_back(); +#endif } +// ++ ielement; + } + } const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; @@ -4297,49 +4337,89 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); - size_t num_empty = 0; + //FIXME parallelize? + for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i) + slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); + size_t num_empty = 0; if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. - std::vector bottom_extra_slices; - Polygons rest_support; - for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) { + struct BottomExtraSlice { + Polygons polygons; + Polygons supported; + double area; + double supported_area; + }; + std::vector bottom_extra_slices; + Polygons rest_support; + coord_t bottom_radius = config.getRadius(branch.path.front()->state); + // Don't propagate further than 1.5 * bottom radius. + //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; + LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; + LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); + // Only propagate until the rest area is smaller than this threshold. + double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); + // Only propagate until the rest area is smaller than this threshold. + double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); - if (area(rest_support) < tiny_area_threshold) + double rest_support_area = area(rest_support); + if (rest_support_area < support_area_stop) + // Don't propagate a fraction of the tree contact surface. break; - bottom_extra_slices.emplace_back(rest_support); + // Measure how much the rest_support is actually supported. + /* + Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{})); + double supported_area = area(supported); + printf("Supported area: %d, %lf\n", layer_idx, supported_area); + */ + Polygons supported; + double supported_area; + bottom_extra_slices.push_back({ rest_support, std::move(supported), rest_support_area, supported_area }); } + // Now remove those bottom slices that are not supported at all. + while (! bottom_extra_slices.empty() && + area(intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}))) < support_area_min) + bottom_extra_slices.pop_back(); layer_begin -= LayerIndex(bottom_extra_slices.size()); - slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend())); + slices.insert(slices.begin(), bottom_extra_slices.size(), {}); + size_t i = 0; + for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i) + slices[i] = std::move(it->polygons); } else num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); layer_begin += LayerIndex(num_empty); - for (; slices.back().empty(); -- layer_end); - LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); - LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); - size_t new_size = size_t(new_end - new_begin); - if (tree.first_layer_id == -1) { - } else if (tree.slices.capacity() < new_size) { - std::vector new_slices; - new_slices.reserve(new_size); - if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - new_slices.insert(new_slices.end(), dif, {}); - append(new_slices, std::move(tree.slices)); - tree.slices.swap(new_slices); - } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); - tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); - layer_begin -= LayerIndex(num_empty); - for (LayerIndex i = layer_begin; i != layer_end; ++ i) - if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { - Slice &dst = tree.slices[i - new_begin]; - if (++ dst.num_branches > 1) - append(dst.polygons, std::move(src)); - else - dst.polygons = std::move(std::move(src)); - } - tree.first_layer_id = new_begin; + while (! slices.empty() && slices.back().empty()) { + slices.pop_back(); + -- layer_end; + } + if (layer_begin < layer_end) { + LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); + LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); + size_t new_size = size_t(new_end - new_begin); + if (tree.first_layer_id == -1) { + } else if (tree.slices.capacity() < new_size) { + std::vector new_slices; + new_slices.reserve(new_size); + if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + new_slices.insert(new_slices.end(), dif, {}); + append(new_slices, std::move(tree.slices)); + tree.slices.swap(new_slices); + } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); + tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); + layer_begin -= LayerIndex(num_empty); + for (LayerIndex i = layer_begin; i != layer_end; ++ i) + if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { + Slice &dst = tree.slices[i - new_begin]; + if (++ dst.num_branches > 1) + append(dst.polygons, std::move(src)); + else + dst.polygons = std::move(std::move(src)); + } + tree.first_layer_id = new_begin; + } } } }, tbb::simple_partitioner()); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 070903096..2c87a132d 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -93,6 +93,8 @@ struct AreaIncreaseSettings struct TreeSupportSettings; +// #define TREE_SUPPORTS_TRACK_LOST + // C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided. struct SupportElementStateBits { SupportElementStateBits() : @@ -102,6 +104,10 @@ struct SupportElementStateBits { supports_roof(false), can_use_safe_radius(false), skip_ovalisation(false), +#ifdef TREE_SUPPORTS_TRACK_LOST + lost(false), + verylost(false), +#endif // TREE_SUPPORTS_TRACK_LOST deleted(false), marked(false) {} @@ -136,6 +142,12 @@ struct SupportElementStateBits { */ bool skip_ovalisation : 1; +#ifdef TREE_SUPPORTS_TRACK_LOST + // Likely a lost branch, debugging information. + bool lost : 1; + bool verylost : 1; +#endif // TREE_SUPPORTS_TRACK_LOST + // Not valid anymore, to be deleted. bool deleted : 1;