From 798ad56835af4e71e134560a95ca90ee8c6a853a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:17:28 +0200 Subject: [PATCH] Ensure support skin is generated below holes that can not be removed --- include/TreeSupport.h | 80 +++++++-- src/TreeSupport.cpp | 375 ++++++++++++++++++++++++++---------------- 2 files changed, 296 insertions(+), 159 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index b5f6cf8a8f..228a427ae6 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -321,44 +321,97 @@ class TreeSupport std::vector>>& dropped_down_areas, const std::map& inverse_tree_order); - /*! - * \brief Generates support areas with high density infill to support interface above. Also unions the Polygons in support_layer_storage. Has to be called even if no support - skin will generate. + + /*! + * \brief Accumulate areas needed later, union all import and add all roof to storage. * \param support_layer_storage[in,out] Areas where support should be generated. - * \param support_skin_storage[out] Areas where high density support should be generated. + * \param support_layer_storage_fractional[in,out] Areas where support has to be, projected up for fractional height. * \param support_roof_storage[in] Areas where support was replaced with roof. * \param support_roof_extra_wall_storage[in] Areas where support was replaced with roof, but roofs need to have a wall to print correctly. * \param support_roof_storage_fractional[in] Areas of roof that were projected one layer up. * \param support_roof_extra_wall_storage_fractional[in] Areas of roof that were projected one layer up, but roofs need to have a wall to print correctly. - * \param storage[in] The storage where the support should be stored. - * \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on. + * \param fake_roof_areas_combined[out] All areas that contain the fake roofs. + * \param cradle_base_areas[out] Copy of all cradle base areas. Already added to correct storage. + * \param cradle_support_line_areas[out] All cradle lines consisting of regular support. Will still have to be added as support (Done in generateSupportSkin) + * \param storage[in,out] The storage where the support should be stored. * \param cradle_data[in] All currently existing cradles, with its corresponding cradle lines. - */ - void generateSupportSkin( + void prepareSupportAreas( std::vector& support_layer_storage, std::vector& support_layer_storage_fractional, - std::vector& support_skin_storage, std::vector& support_roof_storage, std::vector& support_roof_extra_wall_storage, std::vector& support_roof_storage_fractional, std::vector& support_roof_extra_wall_storage_fractional, + std::vector& fake_roof_areas_combined, + std::vector& cradle_base_areas, + std::vector& cradle_support_line_areas, SliceDataStorage& storage, - std::vector>& layer_tree_polygons, std::vector>& cradle_data); /*! - * \brief Filters out holses that would cause support to be printed mid-air. + * \brief Calculates which holes are valid (rest on walls) and which holes rest on which other holes + * \param support_layer_storage[in] Areas where support should be generated. + * \param hole_parts[out] Parts of holes, ordered by layer. + * \param valid_holes[out] Indices of holes that rest on outer wall, by layer. + * \param non_removable_holes[out] Indices of holes that can not be removed, by layer. + * \param hole_rest_map[out] Ordered by layer, information on which hole index on the layer below a given hole rests on + */ + void calculateSupportHoles(std::vector& support_layer_storage, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map); + + /*! + * \brief Generates support areas with high density infill to support interface above. Has to be called even if no support skin will generate, + * as cradle lines are added to the support_layer_storage here. + * \param support_layer_storage[in,out] Areas where support should be generated. * \param support_skin_storage[out] Areas where high density support should be generated. + * \param fake_roof_areas_combined[in] All areas that contain the fake roofs. + * \param cradle_base_areas[in] Copy of all cradle base areas. Already added to correct storage. + * \param cradle_support_line_areas[in] All cradle lines consisting of regular support. Will be to be added as support. + * \param hole_parts[in] Parts of holes, ordered by layer. + * \param valid_holes[in] Indices of holes that rest on outer wall, by layer. + * \param non_removable_holes[in] Indices of holes that can not be removed, by layer. + * \param hole_rest_map[in] Ordered by layer, information on which hole index on the layer below a given hole rests on + * \param storage[in] The storage where the support should be stored. + * \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on. */ - void filterFloatingLines(std::vector& support_layer_storage, std::vector& support_skin_storage); + void generateSupportSkin( + std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& fake_roof_areas_combined, + std::vector& cradle_base_areas, + std::vector& cradle_support_line_areas, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map, + SliceDataStorage& storage, + std::vector>& layer_tree_polygons); + + /*! + * \brief Filters out holes that would cause support to be printed mid-air. + * \param support_layer_storage[in,out] Areas where support should be generated. + * \param hole_parts[in] Parts of holes, ordered by layer. + * \param valid_holes[in] Indices of holes that rest on outer wall, by layer. + * \param non_removable_holes[in] Indices of holes that can not be removed, by layer. + * \param hole_rest_map[in] Ordered by layer, information on which hole index on the layer below a given hole rests on + */ + void removeFloatingHoles(std::vector& support_layer_storage, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map); /*! * \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage * * \param support_layer_storage[in] Areas where support should be generated. - * \param support_roof_storage[in] Areas where support was replaced with roof. + * \param support_skin_storage[in] Areas where high density support should be generated. + * \param support_layer_storage_fractional[out] Areas where support has to be, projected up for fractional height. * \param storage[in,out] The storage where the support should be stored. */ void finalizeInterfaceAndSupportAreas( @@ -373,7 +426,6 @@ class TreeSupport * \param move_bounds[in] All currently existing influence areas * \param storage[in,out] The storage where the support should be stored. * \param cradle_data[in] All currently existing cradles, with its corresponding cradle lines. - */ void drawAreas(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index e2815e7713..8bf3eab511 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2378,24 +2378,23 @@ void TreeSupport::dropNonGraciousAreas( }); } -void TreeSupport::generateSupportSkin( +void TreeSupport::prepareSupportAreas( std::vector& support_layer_storage, std::vector& support_layer_storage_fractional, - std::vector& support_skin_storage, std::vector& support_roof_storage, std::vector& support_roof_extra_wall_storage, std::vector& support_roof_storage_fractional, std::vector& support_roof_extra_wall_storage_fractional, + std::vector& fake_roof_areas_combined, + std::vector& cradle_base_areas, + std::vector& cradle_support_line_areas, SliceDataStorage& storage, - std::vector>& layer_tree_polygons, std::vector>& cradle_data) { const auto t_start = std::chrono::high_resolution_clock::now(); const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5; // based on calculation in WallToolPath const double small_area_length = INT2MM(static_cast(config.support_line_width) / 2); - std::vector cradle_base_areas(support_layer_storage.size()); // Copy of all cradle base areas. Already added to correct storage. - std::vector cradle_support_line_areas(support_layer_storage.size()); // All cradle lines that have to be added as support std::vector cradle_support_line_roof_areas(support_layer_storage.size()); // All cradle lines that have to be added as roof std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); // All cradle lines offset by xy distance. @@ -2512,8 +2511,6 @@ void TreeSupport::generateSupportSkin( } }); - std::vector fake_roofs(fake_roof_areas.size()); - cura::parallel_for( 0, support_layer_storage.size(), @@ -2531,7 +2528,7 @@ void TreeSupport::generateSupportSkin( } fake_roof_lines = fake_roof_lines.unionPolygons(); fake_roof = fake_roof.unionPolygons(); - fake_roofs[layer_idx] = fake_roof; + fake_roof_areas_combined[layer_idx] = fake_roof; Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; @@ -2643,9 +2640,127 @@ void TreeSupport::generateSupportSkin( Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof.unionPolygons(fractional_roof_extra_wall)); storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof, config.support_roof_line_width, config.support_roof_wall_count, true); }); +} - const auto t_union = std::chrono::high_resolution_clock::now(); +void TreeSupport::calculateSupportHoles(std::vector& support_layer_storage, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map) +{ + + std::function reversePolygon = [&](Polygons& poly) + { + for (size_t idx = 0; idx < poly.size(); idx++) + { + poly[idx].reverse(); + } + }; + + + std::vector support_holes(support_layer_storage.size(), Polygons()); + + // Extract all holes as polygon objects + cura::parallel_for( + + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + std::vector parts = support_layer_storage[layer_idx].sortByNesting(); + + if (parts.size() <= 1) + { + return; + } + + Polygons holes_original; + for (const size_t idx : ranges::views::iota(1UL, parts.size())) + { + Polygons area = parts[idx]; + reversePolygon(area); + holes_original.add(area); + } + support_holes[layer_idx] = holes_original; + }); + + hole_parts.resize(support_layer_storage.size()); + valid_holes.resize(support_layer_storage.size()); + non_removable_holes.resize(support_layer_storage.size()); + hole_rest_map.resize(support_layer_storage.size()); + // Split all holes into parts + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + for (Polygons hole : support_holes[layer_idx].splitIntoParts()) + { + hole_parts[layer_idx].emplace_back(hole); + } + }); + + // Figure out which hole rests on which other hole + cura::parallel_for( + 1, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + if (hole_parts[layer_idx].empty()) + { + return; + } + + Polygons relevant_forbidden = volumes_.getCollision(0, layer_idx, true); + Polygons outer_walls + = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(config.support_line_width * config.support_wall_count, 0); + + + for (auto [idx, hole] : hole_parts[layer_idx] | ranges::views::enumerate) + { + AABB hole_aabb = AABB(hole); + hole_aabb.expand(EPSILON); + if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls, hole_aabb)).empty()) + { + valid_holes[layer_idx].emplace(idx); + } + else + { + if(hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).area() > hole.polygonLength() * EPSILON) + { + non_removable_holes[layer_idx].emplace(idx); // technically not resting outside, also not valid, but the alternative is potentially having lines go though the model + } + + for (auto [idx2, hole2] : hole_parts[layer_idx - 1] | ranges::views::enumerate) + { + if (hole_aabb.hit(AABB(hole2)) + && ! hole.intersection(hole2).empty()) // TODO should technically be outline: Check if this is fine either way as it would save an offset + { + hole_rest_map[layer_idx][idx].emplace_back(idx2); + } + } + } + } + }); +} + + + +void TreeSupport::generateSupportSkin( + std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& fake_roof_areas_combined, + std::vector& cradle_base_areas, + std::vector& cradle_support_line_areas, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map, + SliceDataStorage& storage, + std::vector>& layer_tree_polygons) +{ + std::mutex critical_support_layer_storage; if (config.support_skin_layers) { @@ -2676,8 +2791,28 @@ void TreeSupport::generateSupportSkin( Polygons roof_above = storage.support.supportLayers[layer_idx + 1].getTotalAreaFromParts(storage.support.supportLayers[layer_idx + 1].support_roof); needs_supporting.add(roof_above.difference(support_shell_capable_of_supporting_roof)); - needs_supporting.add(fake_roofs[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); + needs_supporting.add(fake_roof_areas_combined[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); needs_supporting.add(cradle_support_line_areas[layer_idx + 1]); + + for(size_t hole_idx : non_removable_holes[layer_idx + 1]) + { + bool rests_on_another = false; + for(size_t hole_idx_below : hole_rest_map[layer_idx + 1][hole_idx]) + { + if(valid_holes[layer_idx].contains(hole_idx_below) || non_removable_holes[layer_idx].contains(hole_idx_below)) + { + rests_on_another = true; + break; + } + } + + if(!rests_on_another) // Assuming that because of xy distance any hole below will be large enough to support this hole. + { + //Offset required as it is not the hole that needs support, but the surrounding walls! + needs_supporting.add(hole_parts[layer_idx + 1][hole_idx].offset(config.support_line_width * config.support_wall_count + FUDGE_LENGTH)); + } + } + } needs_supporting.add(cradle_base_areas[layer_idx]); // cradle bases should be skin. @@ -2687,7 +2822,7 @@ void TreeSupport::generateSupportSkin( Polygons already_supports; already_supports.add(existing_roof); // roof - already_supports.add(fake_roofs[layer_idx]); + already_supports.add(fake_roof_areas_combined[layer_idx]); already_supports.add(support_layer_storage[layer_idx].getOutsidePolygons().tubeShape(config.support_line_width * config.support_wall_count, 0)); already_supports.add(cradle_support_line_areas[layer_idx]); already_supports = already_supports.unionPolygons().offset(FUDGE_LENGTH).unionPolygons(); @@ -2817,13 +2952,34 @@ void TreeSupport::generateSupportSkin( { support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(cradle_support_line_areas[layer_idx]).difference(support_skin_storage[layer_idx]); + + if(layer_idx > 0) + { + for (auto [idx, hole] : hole_parts[layer_idx] | ranges::views::enumerate) + { + AABB hole_aabb = AABB(hole); + hole_aabb.expand(EPSILON); + if(hole.difference(PolygonUtils::clipPolygonWithAABB(support_skin_storage[layer_idx], hole_aabb)).empty()) + { + // The Hole was replaced by skin, remove it. + // All holes that rest on this are valid, but that is already ensured below + hole_parts[layer_idx][idx] = Polygons(); + } + else if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(support_skin_storage[layer_idx - 1], hole_aabb)).empty()) + { + valid_holes[layer_idx].emplace(idx); + } + } + } }); } -void TreeSupport::filterFloatingLines(std::vector& support_layer_storage, std::vector& support_skin_storage) +void TreeSupport::removeFloatingHoles(std::vector& support_layer_storage, + std::vector>& hole_parts, + std::vector>& valid_holes, + std::vector>& non_removable_holes, + std::vector>>& hole_rest_map) { - const auto t_start = std::chrono::high_resolution_clock::now(); - std::function reversePolygon = [&](Polygons& poly) { for (size_t idx = 0; idx < poly.size(); idx++) @@ -2832,116 +2988,21 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } }; - - std::vector support_holes(support_layer_storage.size(), Polygons()); - - // Extract all holes as polygon objects - cura::parallel_for( - - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - std::vector parts = support_layer_storage[layer_idx].sortByNesting(); - - if (parts.size() <= 1) - { - return; - } - - Polygons holes_original; - for (const size_t idx : ranges::views::iota(1UL, parts.size())) - { - Polygons area = parts[idx]; - reversePolygon(area); - holes_original.add(area); - } - support_holes[layer_idx] = holes_original; - }); - - const auto t_union = std::chrono::high_resolution_clock::now(); - - std::vector> holeparts(support_layer_storage.size()); - // Split all holes into parts - cura::parallel_for( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - for (Polygons hole : support_holes[layer_idx].splitIntoParts()) - { - holeparts[layer_idx].emplace_back(hole); - } - }); - std::vector>> hole_rest_map(holeparts.size()); - std::vector> holes_resting_outside(holeparts.size()); - - - // Figure out which hole rests on which other hole - cura::parallel_for( - 1, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - if (holeparts[layer_idx].empty()) - { - return; - } - - Polygons relevant_forbidden = volumes_.getCollision(0, layer_idx, true); - Polygons outer_walls - = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(config.support_line_width * config.support_wall_count, 0); - - Polygons holes_below; - - for (auto poly : holeparts[layer_idx - 1]) - { - holes_below.add(poly); - } - - for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) - { - AABB hole_aabb = AABB(hole); - hole_aabb.expand(EPSILON); - if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls, hole_aabb)).empty()) - { - holes_resting_outside[layer_idx].emplace(idx); - } - else if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(support_skin_storage[layer_idx - 1], hole_aabb)).empty()) - { - holes_resting_outside[layer_idx].emplace(idx); // technically not resting outside, but valid the same - } - else if(hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).area() > hole.polygonLength() * EPSILON) - { - holes_resting_outside[layer_idx].emplace(idx); // technically not resting outside, also not valid, but the alternative is potentially having lines go though the model - } - else - { - for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) - { - if (hole_aabb.hit(AABB(hole2)) - && ! hole.intersection(hole2).empty()) // TODO should technically be outline: Check if this is fine either way as it would save an offset - { - hole_rest_map[layer_idx][idx].emplace_back(idx2); - } - } - } - } - }); - - const auto t_hole_rest_ordering = std::chrono::high_resolution_clock::now(); - std::unordered_set removed_holes_by_idx; - std::vector valid_holes(support_holes.size(), Polygons()); + std::vector valid_holes_areas(hole_parts.size(), Polygons()); // Check which holes have to be removed as they do not rest on anything. Only keep holes that have to be removed - for (const size_t layer_idx : ranges::views::iota(1UL, support_holes.size())) + for (const size_t layer_idx : ranges::views::iota(1UL, hole_parts.size())) { std::unordered_set next_removed_holes_by_idx; - for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) + for (auto [idx, hole] : hole_parts[layer_idx] | ranges::views::enumerate) { bool found = false; - if (holes_resting_outside[layer_idx].contains(idx)) + if (valid_holes[layer_idx].contains(idx)) + { + found = true; + } + else if (non_removable_holes[layer_idx].contains(idx)) { found = true; } @@ -2965,8 +3026,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } else { - valid_holes[layer_idx].add(hole); - holeparts[layer_idx][idx] = Polygons(); // all remaining holes will have to be removed later, so removing the hole means it is confirmed valid! + valid_holes_areas[layer_idx].add(hole); + hole_parts[layer_idx][idx] = Polygons(); // all remaining holes will have to be removed later, so removing the hole means it is confirmed valid! } } removed_holes_by_idx = next_removed_holes_by_idx; @@ -2980,31 +3041,16 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora support_layer_storage.size(), [&](const LayerIndex layer_idx) { - if (holeparts[layer_idx].empty()) + if (hole_parts[layer_idx].empty()) { return; } support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); - reversePolygon(valid_holes[layer_idx]); - support_layer_storage[layer_idx].add(valid_holes[layer_idx]); + reversePolygon(valid_holes_areas[layer_idx]); + support_layer_storage[layer_idx].add(valid_holes_areas[layer_idx]); }); - - const auto t_end = std::chrono::high_resolution_clock::now(); - - const auto dur_union = 0.001 * std::chrono::duration_cast(t_union - t_start).count(); - const auto dur_hole_rest_ordering = 0.001 * std::chrono::duration_cast(t_hole_rest_ordering - t_union).count(); - const auto dur_hole_removal_tagging = 0.001 * std::chrono::duration_cast(t_hole_removal_tagging - t_hole_rest_ordering).count(); - - const auto dur_hole_removal = 0.001 * std::chrono::duration_cast(t_end - t_hole_removal_tagging).count(); - spdlog::debug( - "Time to union areas: {} ms Time to evaluate which hole rest on which other hole: {} ms Time to see which holes are not resting on anything valid: {} ms remove all holes " - "that are invalid and not close enough to a valid hole: {} ms", - dur_union, - dur_hole_rest_ordering, - dur_hole_removal_tagging, - dur_hole_removal); } void TreeSupport::finalizeInterfaceAndSupportAreas( @@ -3260,6 +3306,13 @@ void TreeSupport::drawAreas(std::vector>& move_bou std::vector support_skin_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); std::vector support_roof_extra_wall_storage(move_bounds.size()); + std::vector> hole_parts(move_bounds.size()); + std::vector> valid_holes(move_bounds.size()); + std::vector> non_removable_holes(move_bounds.size()); + std::vector>> hole_rest_map(move_bounds.size()); + std::vector fake_roof_areas_combined(move_bounds.size()); + std::vector cradle_base_areas(move_bounds.size()); + std::vector cradle_support_line_areas(move_bounds.size()); std::map inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. @@ -3405,18 +3458,41 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - generateSupportSkin( - support_layer_storage, + prepareSupportAreas(support_layer_storage, support_layer_storage_fractional, - support_skin_storage, support_roof_storage, support_roof_extra_wall_storage, support_roof_storage_fractional, support_roof_extra_wall_storage_fractional, + fake_roof_areas_combined, + cradle_base_areas, + cradle_support_line_areas, storage, - layer_tree_polygons, cradle_data); + const auto t_union = std::chrono::high_resolution_clock::now(); + + calculateSupportHoles(support_layer_storage, + hole_parts, + valid_holes, + non_removable_holes, + hole_rest_map); + + const auto t_holes = std::chrono::high_resolution_clock::now(); + + generateSupportSkin( + support_layer_storage, + support_skin_storage, + fake_roof_areas_combined, + cradle_base_areas, + cradle_support_line_areas, + hole_parts, + valid_holes, + non_removable_holes, + hole_rest_map, + storage, + layer_tree_polygons); + for (const auto layer_idx : ranges::views::iota(0UL, support_layer_storage.size())) { scripta::log("tree_support_layer_storage", support_layer_storage[layer_idx], SectionType::SUPPORT, layer_idx); @@ -3424,7 +3500,11 @@ void TreeSupport::drawAreas(std::vector>& move_bou } const auto t_skin = std::chrono::high_resolution_clock::now(); - filterFloatingLines(support_layer_storage, support_skin_storage); + removeFloatingHoles(support_layer_storage, + hole_parts, + valid_holes, + non_removable_holes, + hole_rest_map); const auto t_filter = std::chrono::high_resolution_clock::now(); finalizeInterfaceAndSupportAreas(support_layer_storage, support_skin_storage, support_layer_storage_fractional, storage); @@ -3433,15 +3513,20 @@ void TreeSupport::drawAreas(std::vector>& move_bou const auto dur_gen_tips = 0.001 * std::chrono::duration_cast(t_generate - t_start).count(); const auto dur_smooth = 0.001 * std::chrono::duration_cast(t_smooth - t_generate).count(); const auto dur_drop = 0.001 * std::chrono::duration_cast(t_drop - t_smooth).count(); - const auto dur_skin = 0.001 * std::chrono::duration_cast(t_skin - t_drop).count(); + const auto dur_union = 0.001 * std::chrono::duration_cast(t_union - t_drop).count(); + const auto dur_holes = 0.001 * std::chrono::duration_cast(t_holes - t_union).count(); + const auto dur_skin = 0.001 * std::chrono::duration_cast(t_skin - t_holes).count(); const auto dur_filter = 0.001 * std::chrono::duration_cast(t_filter - t_skin).count(); const auto dur_finalize = 0.001 * std::chrono::duration_cast(t_end - t_filter).count(); spdlog::info( - "Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms generateSupportSkin {} ms filterFloatingLines: {} ms " - "finalizeInterfaceAndSupportAreas {} ms", + "Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms " + "prepareSupportAreas: {} ms calculateSupportHoles: {} ms generateSupportSkin {} ms " + "filterFloatingHoles: {} ms finalizeInterfaceAndSupportAreas {} ms", dur_gen_tips, dur_smooth, dur_drop, + dur_union, + dur_holes, dur_skin, dur_filter, dur_finalize);