From 108238f93c642740c6f066c2a8074dc693971577 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 7 May 2023 04:45:15 +0200 Subject: [PATCH 001/101] Add feature to better support pointy overhangs. Will create a pull request with more information soon. --- include/SupportInfillPart.h | 4 +- include/TreeModelVolumes.h | 44 +- include/TreeSupport.h | 25 +- include/TreeSupportElement.h | 2 +- include/TreeSupportSettings.h | 16 +- include/TreeSupportTipGenerator.h | 124 ++++- include/TreeSupportUtils.h | 11 +- include/utils/polygon.h | 3 + src/FffGcodeWriter.cpp | 2 +- src/SupportInfillPart.cpp | 3 +- src/TreeModelVolumes.cpp | 113 ++++- src/TreeSupport.cpp | 272 ++++++++-- src/TreeSupportTipGenerator.cpp | 815 +++++++++++++++++++++++++++--- src/utils/polygon.cpp | 35 ++ 14 files changed, 1352 insertions(+), 117 deletions(-) diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 2a1849ab55..94119f030e 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -6,6 +6,7 @@ #include +#include "settings/EnumSettings.h" #include "utils/AABB.h" #include "utils/ExtrusionLine.h" #include "utils/polygon.h" @@ -34,8 +35,9 @@ class SupportInfillPart std::vector wall_toolpaths; //!< Any walls go here, not in the areas, where they could be combined vertically (don't combine walls). Binned by inset_idx. coord_t custom_line_distance; + EFillMethod custom_line_pattern; - SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate = 0, coord_t custom_line_distance = 0 ); + SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); const Polygons& getInfillArea() const; }; diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 4454dc3b6a..0e93fab98a 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -50,6 +50,7 @@ class TreeModelVolumes TreeModelVolumes(const TreeModelVolumes&) = delete; TreeModelVolumes& operator=(const TreeModelVolumes&) = delete; + /*! * \brief Precalculate avoidances and collisions up to this layer. * @@ -57,7 +58,7 @@ class TreeModelVolumes * 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! * */ - void precalculate(coord_t max_layer); + void precalculate(LayerIndex max_layer); /*! * \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. @@ -131,6 +132,7 @@ class TreeModelVolumes */ const Polygons& getWallRestriction(coord_t radius, LayerIndex layer_idx, bool min_xy_dist); + /*! * \brief Round \p radius upwards to either a multiple of radius_sample_resolution_ or a exponentially increasing value * @@ -151,6 +153,22 @@ class TreeModelVolumes */ coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const; + /*! + * \brief Provide hints which areas should be avoided in the future. + * \param area The area that should be avoided in the future. + * \param layer_idx The layer said area is on. + */ + void addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx); + + /*! + * \brief Get areas that were additionally set to be avoided + * \param layer_idx The layer said area is on. + * \returns The area that should be avoided + */ + const Polygons& getAntiPreferredAreas(LayerIndex layer_idx); + + + private: /*! @@ -310,6 +328,17 @@ class TreeModelVolumes calculateAvoidanceToModel(std::deque{ RadiusLayerPair(key) }); } + /*! + * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model with radius 0 from the smallest actual avoidance. + * This is done as a real 0 radius avoidance would not be able to be printed. These 0 radius avoidances are used for calculating roof and cradle. + * + * The result is a 2D area that would cause nodes of radius 0 to + * collide with the model in a not wanted way. Result is saved in the cache. + * \param max_layer The result will be calculated up to the this layer. + */ + void calculateFake0Avoidances(const LayerIndex max_layer); + + /*! * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). * @@ -462,6 +491,16 @@ class TreeModelVolumes */ RestPreference support_rest_preference; + /*! + * \brief How tall the cradle will at most be. + */ + size_t max_cradle_layers = 0; + + /*! + * \brief Largest DTT a cradle supporting tip may have. + */ + size_t max_cradle_dtt = 0; + /*! * \brief Caches for the collision, avoidance and areas on the model where support can be placed safely * at given radius and layer indices. @@ -517,6 +556,9 @@ class TreeModelVolumes mutable std::unordered_map wall_restrictions_cache_min_; std::unique_ptr critical_wall_restrictions_cache_min_ = std::make_unique(); + mutable std::unordered_map anti_preferred_; + std::unique_ptr critical_anti_preferred_ = std::make_unique(); + std::unique_ptr critical_progress = std::make_unique(); Simplify simplifier = Simplify(0, 0, 0); // a simplifier to simplify polygons. Will be properly initialised in the constructor. diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 32f67cd074..56d45631f4 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -278,8 +278,23 @@ class TreeSupport 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. + * + * \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_roof_storage[in,out] Areas where support was replaced with roof. + * \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 generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage, std::vector>& layer_tree_polygons); - void filterFloatingLines(std::vector& support_layer_storage); + /*! + * \brief Filters out holses that would cause support to be printed mid-air. + * \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. + */ + void filterFloatingLines(std::vector& support_layer_storage, std::vector& support_skin_storage); /*! * \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage @@ -288,7 +303,7 @@ class TreeSupport * \param support_roof_storage[in] Areas where support was replaced with roof. * \param storage[in,out] The storage where the support should be stored. */ - void finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_roof_storage, SliceDataStorage& storage); + void finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage); /*! * \brief Draws circles around result_on_layer points of the influence areas and applies some post processing. @@ -313,9 +328,13 @@ class TreeSupport */ std::vector placed_support_lines_support_areas; + /*! + * \brief Areas where no support may be. Areas will be subtracted from support areas. + */ + std::vector support_free_areas; + /*! * \brief Generator for model collision, avoidance and internal guide volumes. - * */ TreeModelVolumes volumes_; diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 237e22baa2..02aa2600cd 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -87,7 +87,7 @@ struct TreeSupportElement target_position(target_position), next_position(target_position), next_height(target_height), - effective_radius_height(distance_to_top), + effective_radius_height(0), to_buildplate(to_buildplate), distance_to_top(distance_to_top), area(nullptr), diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 06a6292230..713c2fa2fe 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -29,7 +29,7 @@ struct TreeSupportSettings layer_height(mesh_group_settings.get("layer_height")), branch_radius(mesh_group_settings.get("support_tree_branch_diameter") / 2), min_radius(mesh_group_settings.get("support_tree_tip_diameter") / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance - max_radius(mesh_group_settings.get("support_tree_max_diameter")/2), + max_radius(mesh_group_settings.get("support_tree_max_diameter") / 2), maximum_move_distance((angle < TAU / 4) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits::max()), maximum_move_distance_slow((angle_slow < TAU / 4) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits::max()), support_bottom_layers(mesh_group_settings.get("support_bottom_enable") ? round_divide(mesh_group_settings.get("support_bottom_height"), layer_height) : 0), @@ -69,6 +69,8 @@ struct TreeSupportSettings min_feature_size(mesh_group_settings.get("min_feature_size")), min_wall_line_width(settings.get("min_wall_line_width")), fill_outline_gaps(settings.get("fill_outline_gaps")), + support_skin_layers(settings.get("support_tree_support_skin_height")/layer_height), + support_skin_line_distance(settings.get("support_tree_support_skin_line_distance")), simplifier(Simplify(mesh_group_settings)) { layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); @@ -370,6 +372,16 @@ struct TreeSupportSettings */ bool fill_outline_gaps; + /*! + * \brief How many high density layers should be below roof and cradle. + */ + size_t support_skin_layers; + + /*! + * \brief Distance between lines of the high density line pattern. + */ + coord_t support_skin_line_distance; + /*! * \brief Simplifier to simplify polygons. */ @@ -421,6 +433,8 @@ struct TreeSupportSettings max_radius == other.max_radius && min_wall_line_width == other.min_wall_line_width && fill_outline_gaps == other.fill_outline_gaps && + support_skin_layers == other.support_skin_layers && + support_skin_line_distance == other.support_skin_line_distance && // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry ( interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 16985e90dc..a75a1ff7e6 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -40,10 +40,11 @@ class TreeSupportTipGenerator * \param mesh[in] The mesh that is currently processed. Contains the overhangs. * \param move_bounds[out] The storage for the tips. * \param additional_support_areas[out] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. Should already be initialised. - + * \param placed_support_lines_support_areas[out] Support-lines that were already placed represented as the area the lines will take when printed. + * \param support_free_areas[out] Areas where no support (including roof) of any kind is to be drawn. * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ - void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas); + void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& support_free_areas); private: @@ -57,6 +58,16 @@ class TreeSupportTipGenerator TO_BP_SAFE }; + struct UnsupportedAreaInformation + { + UnsupportedAreaInformation(const Polygons area, size_t index, size_t height) : area{ area }, index{ index }, height{ height } + { + } + const Polygons area; + size_t index; + size_t height; + }; + using LineInformation = std::vector>; /*! @@ -118,6 +129,35 @@ class TreeSupportTipGenerator */ SierpinskiFillProvider* generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const; + /*! + * \brief Provides areas that do not have a connection to the buildplate or a certain height. + * \param layer_idx The layer said area is on. + * \param idx_of_area_below The index of the area below. Only areas that rest on this area will be returned + * \return A vector containing the areas, how many layers of material they have below them and the idx of each area usable to get the next one layer above. + */ + std::vector getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area_below); + + /*! + * \brief Provides areas that do not have a connection to the buildplate or any other non support material below it. + * \param layer_idx The layer said area is on. + * \return A vector containing the areas, how many layers of material they have below them (always 0) and the idx of each area usable to get the next one layer above. + */ + std::vector getFullyUnsupportedArea(LayerIndex layer_idx); + + /*! + * \brief Calculates which parts of the model to not connect with the buildplate and how many layers of material is below them (height). + * Results are stored in a cache. + * Only area up to the required maximum height are stored. + * \param mesh[in] The mesh that is currently processed. + */ + void calculateFloatingParts(const SliceMeshStorage& mesh); + + /*! + * \brief Generate a cradle to stabilize pointy overhang + * \param mesh[in] The mesh that is currently processed. + * \param support_free_areas[out] Areas where no support may be. + */ + void generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas); /*! * \brief Drops overhang areas further down until they are valid (at most max_overhang_insert_lag layers) @@ -142,8 +182,9 @@ class TreeSupportTipGenerator * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param roof[in] Whether the tip supports a roof. * \param skip_ovalisation[in] Whether the tip may be ovalized when drawn later. + * \param additional_ovalization_targets[in] Additional targets the ovalization should reach. */ - void addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); + void addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); /*! @@ -154,8 +195,9 @@ class TreeSupportTipGenerator * \param insert_layer_idx[in] The layer the tip will be on. * \param supports_roof[in] Whether the tip supports a roof. * \param dont_move_until[in] Until which dtt the branch should not move if possible. + * \param connect_points [in] If the points of said line should be connected by ovalization. */ - void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until, bool connect_points); + void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, bool connect_points); /*! * \brief Remove tips that should not have been added in the first place. @@ -165,7 +207,6 @@ class TreeSupportTipGenerator */ void removeUselessAddedPoints(std::vector>& move_bounds,SliceDataStorage& storage, std::vector& additional_support_areas); - /*! * \brief If large areas should be supported by a roof out of regular support lines. */ @@ -294,10 +335,83 @@ class TreeSupportTipGenerator std::vector roof_tips_drawn; + /*! + * \brief Areas that will become cradle. + */ + std::vector cradle_areas; + + /*! + * \brief Overhang of a cradle that will need support. May be on the same layer as the cradle. + */ + std::vector cradle_overhang; + /*! + * \brief Areas below the first layer of a cradle. + */ + std::vector cradle_base_areas; + + /*! + * \brief Amount of layers of the cradle to support pointy overhangs. + */ + size_t cradle_layers; + + /*! + * \brief Minimum amount of layers of the cradle to support pointy overhangs. + */ + size_t minimum_cradle_layers; // Minimal height of the cradle + + /*! + * \brief Amount of lines used for the cradle. + */ + size_t cradle_lines; + + /*! + * \brief Length of lines used for the cradle. + */ + coord_t cradle_length; + + /*! + * \brief Width of lines used for the cradle. + */ + coord_t cradle_line_width; + + /*! + * \brief If cradle lines should be drawn as roof. + */ + bool cradle_lines_roof; + + /*! + * \brief If the cradle base should be drawn as roof. + */ + bool cradle_base_roof; + + /*! + * \brief If the cradle base should contain all cradle lines (if true) or only the pointy overhang (if false). + */ + bool large_cradle_base; + + + /*! + * \brief Maximum area of an overhang to still receive a cradle. Unit is square-microns! + */ + double cradle_area_threshold; + + /*! + * \brief Distance to top of tips that support either the pointy overhang or the cradle lines at the bottom-most layer. + */ + size_t cradle_tip_dtt; + + /*! + * \brief If the cradle lines should also be supported by larger tips. + */ + bool large_cradle_line_tips; + std::mutex critical_cradle; std::mutex critical_move_bounds; std::mutex critical_roof_tips; + mutable std::vector> floating_parts_cache_; + mutable std::vector>> floating_parts_map_; + std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 2f6378dc20..6b482d0564 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -92,17 +92,18 @@ class TreeSupportUtils * \param support_infill_distance[in] The distance that should be between the infill lines. * \param cross_fill_provider[in] A SierpinskiFillProvider required for cross infill. * \param include_walls[in] If the result should also contain walls, or only the infill. - * todo doku + * \param special_pattern[in] Use a different pattern. None means the default pattern as in config will be used. + * \param disable_connect[in] If the connecting of Infill lines has to be disabled. * \return A Polygons object that represents the resulting infill lines. */ - [[nodiscard]] static Polygons generateSupportInfillLines(const Polygons& area,const TreeSupportSettings& config, bool roof, LayerIndex layer_idx, coord_t support_infill_distance, SierpinskiFillProvider* cross_fill_provider, bool include_walls, bool generate_support_supporting = false) + [[nodiscard]] static Polygons generateSupportInfillLines(const Polygons& area,const TreeSupportSettings& config, bool roof, LayerIndex layer_idx, coord_t support_infill_distance, SierpinskiFillProvider* cross_fill_provider, bool include_walls, EFillMethod special_pattern = EFillMethod::NONE, bool disable_connect = false) { Polygons gaps; - // As we effectivly use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. + // As we effectively use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. - const EFillMethod pattern = generate_support_supporting ? EFillMethod::GRID : roof ? config.roof_pattern : config.support_pattern; + const EFillMethod pattern = (special_pattern != EFillMethod::NONE) ? special_pattern : roof ? config.roof_pattern : config.support_pattern; - const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support; + const bool zig_zaggify_infill =!disable_connect && (roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support); const bool connect_polygons = false; constexpr coord_t support_roof_overlap = 0; constexpr size_t infill_multiplier = 1; diff --git a/include/utils/polygon.h b/include/utils/polygon.h index 8bd2303429..d0e7f8be93 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -1000,6 +1000,9 @@ class Polygons */ Polygons intersectionPolyLines(const Polygons& polylines, bool restitch = true, const coord_t max_stitch_distance = 10_mu) const; + Polygons differencePolyLines(const Polygons& polylines, bool restitch = true, const coord_t max_stitch_distance = 10_mu) const; + + /*! * Add the front to each polygon so that the polygon is represented as a polyline */ diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index bbc83c8a63..277e6246e1 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2813,7 +2813,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const coord_t small_area_width = mesh_group_settings.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. - Infill infill_comp(support_pattern, + Infill infill_comp(part.custom_line_pattern == EFillMethod::NONE ? support_pattern : part.custom_line_pattern, zig_zaggify_infill, connect_polygons, area, diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 76344af468..836c624f38 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -7,12 +7,13 @@ using namespace cura; -SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate, coord_t custom_line_distance) +SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern) : outline(outline) , outline_boundary_box(outline) , support_line_width(support_line_width) , inset_count_to_generate(inset_count_to_generate) , custom_line_distance(custom_line_distance) +, custom_line_pattern(custom_line_pattern) { infill_area_per_combine_per_density.clear(); } diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index f7ab838ab0..91177c277a 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -66,17 +66,20 @@ TreeModelVolumes::TreeModelVolumes } } + // Figure out the rest of the setting(-like variable)s relevant to the class a whole. + current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx]; + const TreeSupportSettings config(layer_outlines_[current_outline_idx].first); + for (const auto data_pair : layer_outlines_) { support_rests_on_model |= data_pair.first.get("support_type") == ESupportType::EVERYWHERE; min_maximum_deviation = std::min(min_maximum_deviation, data_pair.first.get("meshfix_maximum_deviation")); min_maximum_resolution = std::min(min_maximum_resolution, data_pair.first.get("meshfix_maximum_resolution")); min_maximum_area_deviation = std::min(min_maximum_area_deviation, data_pair.first.get("meshfix_maximum_extrusion_area_deviation")); + max_cradle_layers = std::max(coord_t(max_cradle_layers), data_pair.first.get("support_tree_cradle_height") / config.layer_height); + max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * data_pair.first.get("support_tree_cradle_base_tip_percentage") / 100.0)); } - // Figure out the rest of the setting(-like variable)s relevant to the class a whole. - current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx]; - const TreeSupportSettings config(layer_outlines_[current_outline_idx].first); if (config.support_overrides == SupportDistPriority::Z_OVERRIDES_XY) { @@ -172,10 +175,11 @@ TreeModelVolumes::TreeModelVolumes simplifier = Simplify(min_maximum_resolution, min_maximum_deviation, min_maximum_area_deviation); } -void TreeModelVolumes::precalculate(coord_t max_layer) +void TreeModelVolumes::precalculate(LayerIndex max_layer) { const auto t_start = std::chrono::high_resolution_clock::now(); precalculated = true; + max_layer = std::min(max_layer + max_cradle_layers, LayerIndex(layer_outlines_[current_outline_idx].second.size() - 1)); // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant. // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex like initial layer diameter are only done in once. @@ -214,15 +218,15 @@ void TreeModelVolumes::precalculate(coord_t max_layer) const coord_t max_initial_layer_diameter_radius = ceilRadius(config.recommendedMinRadius(current_layer) + current_min_xy_dist_delta); if (! radius_until_layer.count(max_regular_radius)) { - radius_until_layer[max_regular_radius] = current_layer; + radius_until_layer[max_regular_radius] = std::min(current_layer + max_cradle_dtt, max_layer); } if (! radius_until_layer.count(max_min_radius)) { - radius_until_layer[max_min_radius] = current_layer; + radius_until_layer[max_min_radius] = std::min(current_layer + max_cradle_dtt, max_layer); } - if (! radius_until_layer.count(max_initial_layer_diameter_radius)) + if (max_initial_layer_diameter_radius > max_min_radius && ! radius_until_layer.count(max_initial_layer_diameter_radius)) { - radius_until_layer[max_initial_layer_diameter_radius] = current_layer; + radius_until_layer[max_initial_layer_diameter_radius] = std::min(current_layer + max_cradle_dtt, max_layer); } } @@ -313,6 +317,8 @@ void TreeModelVolumes::precalculate(coord_t max_layer) } + calculateFake0Avoidances(max_layer); + precalculationFinished=true; const auto dur_col = 0.001 * std::chrono::duration_cast(t_coll - t_start).count(); const auto dur_acc = 0.001 * std::chrono::duration_cast(t_acc-t_coll).count(); @@ -465,7 +471,12 @@ const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_ { spdlog::warn("Had to calculate Avoidance (to model-bool: {}) at radius {} and layer {} and type {}, but precalculate was called. Performance may suffer!", to_model, key.first, key.second,coord_t(type)); } - if(type == AvoidanceType::COLLISION) + + if(orig_radius == 0) + { + calculateFake0Avoidances(layer_idx); + } + else if(type == AvoidanceType::COLLISION) { calculateCollisionAvoidance(key); } @@ -544,6 +555,26 @@ const Polygons& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved. } + + +void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx) +{ + RadiusLayerPair key(0,layer_idx); + std::lock_guard critical_section(*critical_anti_preferred_); + anti_preferred_[key] = anti_preferred_[key].unionPolygons(area); +} + +const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx) +{ + RadiusLayerPair key(0,layer_idx); + std::lock_guard critical_section(*critical_anti_preferred_); + return anti_preferred_[key]; + +} + + + + coord_t TreeModelVolumes::ceilRadius(coord_t radius, bool min_xy_dist) const { return ceilRadius(radius + (min_xy_dist ? 0 : current_min_xy_dist_delta)); @@ -1138,6 +1169,70 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque critical_section(*critical_avoidance_cache_); + start_layer = 1 + getMaxCalculatedLayer(0, avoidance_cache_); + } + + const coord_t radius_offset = -radius_0; + cura::parallel_for + ( + start_layer, + max_layer+1, + [&](const LayerIndex layer_idx) + { + RadiusLayerPair key = RadiusLayerPair (0,layer_idx); + if(!precalculated || support_rest_preference == RestPreference::BUILDPLATE) + { + Polygons smaller_avoidance_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,false,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,false,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_fast_safe = getAvoidance(1,layer_idx,AvoidanceType::FAST_SAFE,false,true).offset(radius_offset,ClipperLib::jtRound); + { + std::lock_guard critical_section(*critical_avoidance_cache_slow_ ); + avoidance_cache_slow_[key] = smaller_avoidance_slow; + } + { + std::lock_guard critical_section(*critical_avoidance_cache_ ); + avoidance_cache_[key] = smaller_avoidance_fast; + } + { + std::lock_guard critical_section(*critical_avoidance_cache_holefree_ ); + avoidance_cache_hole_[key] = smaller_avoidance_fast_safe; + } + } + + if(!precalculated || support_rests_on_model) + { + Polygons smaller_avoidance_to_model_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,true,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_to_model_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,true,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_to_model_fast_safe = getAvoidance(1,layer_idx,AvoidanceType::FAST_SAFE,true,true).offset(radius_offset,ClipperLib::jtRound); + + { + std::lock_guard critical_section(*critical_avoidance_cache_to_model_slow_ ); + avoidance_cache_to_model_slow_[key] = smaller_avoidance_to_model_slow; + } + { + std::lock_guard critical_section(*critical_avoidance_cache_to_model_ ); + avoidance_cache_to_model_[key] = smaller_avoidance_to_model_fast; + } + { + std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_ ); + avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; + } + } + if (layer_idx > max_layer_idx_without_blocker) + { + Polygons smaller_avoidance_collision = getAvoidance(1,layer_idx,AvoidanceType::COLLISION,true,true).offset(radius_offset,ClipperLib::jtRound); + std::lock_guard critical_section(*critical_avoidance_cache_collision_ ); + avoidance_cache_collision_[key] = smaller_avoidance_collision; + } + }); +} + + void TreeModelVolumes::calculateWallRestrictions(const std::deque& 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. diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 5c7585cfbb..9ac6ca8452 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -94,6 +94,8 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) } placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(),Polygons()); + support_free_areas = std::vector(storage.support.supportLayers.size(), Polygons()); + } @@ -115,8 +117,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) // process each combination of meshes std::vector> move_bounds(storage.support.supportLayers.size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. - additional_required_support_area=std::vector(storage.support.supportLayers.size(),Polygons()); - + additional_required_support_area = std::vector(storage.support.supportLayers.size(), Polygons()); spdlog::info("Processing support tree mesh group {} of {} containing {} meshes.", counter + 1, grouped_meshes.size(), grouped_meshes[counter].second.size()); std::vector exclude(storage.support.supportLayers.size()); @@ -177,7 +178,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) spdlog::info ( "Total time used creating Tree support for the currently grouped meshes: {} ms. Different subtasks:\n" - "Calculating Avoidance: {} ms Creating inital influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as support {} ms", + "Calculating Avoidance: {} ms Creating initial influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as support {} ms", dur_total, dur_pre_gen, dur_gen, @@ -239,7 +240,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector>& move_bounds, SliceDataStorage& storage) { TreeSupportTipGenerator tip_gen(storage, mesh, volumes_); - tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas); + tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, support_free_areas); } void TreeSupport::mergeHelper @@ -875,6 +876,37 @@ std::optional TreeSupport::increaseSingleArea } } + const Polygons& anti_preferred = volumes_.getAntiPreferredAreas(layer_idx - 1); + + + // Remove areas where the branch should not be if possible. + // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. + if(!anti_preferred.empty() && check_layer_data.area() > 1) + { + + if (current_elem.to_buildplate) + { + Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred); + if (to_bp_without_anti.area() > 1) + { + to_bp_data = to_bp_without_anti; + to_model_data = to_model_data.difference(anti_preferred); + increased = increased.difference(anti_preferred); + } + } + else + { + Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred); + if (to_model_data_without_anti.area() > 1) + { + to_bp_data = to_bp_data.difference(anti_preferred); + to_model_data = to_model_data_without_anti; + increased = increased.difference(anti_preferred); + } + } + + } + return check_layer_data.area() > 1 ? std::optional(current_elem) : std::optional(); } @@ -1866,15 +1898,172 @@ void TreeSupport::dropNonGraciousAreas ); } - -void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) +void TreeSupport::generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage, std::vector>& layer_tree_polygons) { const auto t_start = std::chrono::high_resolution_clock::now(); - - const coord_t closing_dist=config.support_line_width*config.support_wall_count; 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::mutex critical_support_layer_storage; + + + cura::parallel_for + ( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); + support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + + //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. + if (!storage.support.supportLayers[layer_idx].support_roof.empty()) + { + switch (config.interface_preference) + { + case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); + break; + + case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]).difference(support_skin_storage[layer_idx]); + break; + + default: + break; + } + } + + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(support_roof_storage[layer_idx]).difference(support_free_areas[layer_idx]); + if(!support_free_areas[layer_idx].empty()) + { + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(support_free_areas[layer_idx]); + } + + } + ); + + const auto t_union = std::chrono::high_resolution_clock::now(); + + cura::parallel_for + ( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + Polygons may_need_skin_area_topmost = storage.support.supportLayers.size() > layer_idx + 1 ? storage.support.supportLayers[layer_idx + 1].support_roof : Polygons(); + may_need_skin_area_topmost = may_need_skin_area_topmost.difference(storage.support.supportLayers[layer_idx].support_roof); + + for (std::pair data_pair : layer_tree_polygons[layer_idx]) + { + bool has_parent_roof = false; + + if (data_pair.first->supports_roof) + { + for (auto parent : data_pair.first->parents) + { + has_parent_roof |= (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top); + } + } + + bool element_viable_for_skin = has_parent_roof; + element_viable_for_skin |= data_pair.first->parents.empty() && (data_pair.first->supports_roof || (config.getRadius(data_pair.first) > 4 * config.support_line_width * config.support_wall_count)); + + if (config.support_skin_layers > 0 && element_viable_for_skin) + { + may_need_skin_area_topmost.add(data_pair.second); + } + } + + may_need_skin_area_topmost = may_need_skin_area_topmost.unionPolygons(); + + Polygons may_need_skin_area = may_need_skin_area_topmost; + + for (LayerIndex support_skin_ctr = 0; support_skin_ctr < std::min(LayerIndex(config.support_skin_layers), layer_idx); support_skin_ctr++) + { + Polygons next_skin; + Polygons layer; + + Polygons remaining_regular_areas; + + { + std::lock_guard critical_section_cradle(critical_support_layer_storage); + layer = support_layer_storage[layer_idx - support_skin_ctr]; + } + + if (support_skin_ctr > 0) + { + may_need_skin_area_topmost = may_need_skin_area_topmost.intersection(layer); + } + + + for (Polygons part : layer.splitIntoParts()) + { + Polygons part_outline = part.getOutsidePolygons(); + if (! part_outline.offset(-(config.support_line_width * config.support_wall_count)).intersection(may_need_skin_area).empty()) + { + // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. + Polygons lines = TreeSupportUtils::generateSupportInfillLines(part_outline, config, false, layer_idx - support_skin_ctr, config.support_skin_line_distance, nullptr, false, EFillMethod::LINES, true); + + Polygons intersecting_lines; + + for (auto line : lines) + { + if (PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) && PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back())) + { + intersecting_lines.addLine(line.front(), line.back()); + } + } + + Polygons partial_skin_area = intersecting_lines.offsetPolyLine(config.support_skin_line_distance).unionPolygons().intersection(part_outline); + double part_area = part.area(); + part = part.difference(partial_skin_area); + Polygons remaining_part; + for (auto sub_part : part.splitIntoParts()) + { + if (sub_part.area() < part_area / 5) + { + partial_skin_area = partial_skin_area.unionPolygons(sub_part); + } + else + { + remaining_part.add(sub_part); + } + } + part = remaining_part; + + next_skin.add(partial_skin_area.intersection(may_need_skin_area_topmost)); + std::lock_guard critical_section_cradle(critical_support_layer_storage); + support_skin_storage[layer_idx - support_skin_ctr].add(partial_skin_area); + } + + remaining_regular_areas.add(part); + } + may_need_skin_area = next_skin.unionPolygons(); + std::lock_guard critical_section_cradle(critical_support_layer_storage); + // This intersection is to prevent a race condition where areas of another layer are stored after the changes here are stored. + support_layer_storage[layer_idx - support_skin_ctr] = support_layer_storage[layer_idx - support_skin_ctr].intersection(remaining_regular_areas); + } + } + ); + + cura::parallel_for + ( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(); + } + ); +} + +void TreeSupport::filterFloatingLines(std::vector& support_layer_storage, std::vector& support_skin_storage) +{ + const auto t_start = std::chrono::high_resolution_clock::now(); + std::function reversePolygon = [&](Polygons& poly) { for (size_t idx = 0; idx < poly.size(); idx++) @@ -1885,6 +2074,7 @@ 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 ( @@ -1892,11 +2082,6 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora support_layer_storage.size(), [&](const LayerIndex layer_idx) { - - - support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - std::vector parts = support_layer_storage[layer_idx].sortByNesting(); if (parts.size() <= 1) @@ -1915,7 +2100,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } ); - const auto t_union = std::chrono::high_resolution_clock::now(); + std::vector> holeparts(support_layer_storage.size()); //Split all holes into parts @@ -1948,7 +2133,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } Polygons outer_walls = - TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(closing_dist,0);//.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); + TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(config.support_line_width*config.support_wall_count,0); Polygons holes_below; @@ -1965,6 +2150,10 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora { 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 { for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) @@ -2045,16 +2234,15 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora 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_rest_ordering = 0.001 * std::chrono::duration_cast(t_hole_rest_ordering - t_start).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); + spdlog::debug("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_hole_rest_ordering,dur_hole_removal_tagging, dur_hole_removal); } -void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_roof_storage, SliceDataStorage& storage) +void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; @@ -2067,20 +2255,19 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage.size(), [&](const LayerIndex layer_idx) { - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); - // Subtract support lines of the branches from the roof - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(support_roof_storage[layer_idx]); - if (!storage.support.supportLayers[layer_idx].support_roof.empty() && support_layer_storage[layer_idx].intersection(storage.support.supportLayers[layer_idx].support_roof).area() > 1) + if (!storage.support.supportLayers[layer_idx].support_roof.empty()) { switch (interface_pref) { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); break; case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]).difference(support_skin_storage[layer_idx]); break; case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: @@ -2089,6 +2276,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor TreeSupportUtils::generateSupportInfillLines(storage.support.supportLayers[layer_idx].support_roof, config, true, layer_idx, config.support_roof_line_distance, storage.support.cross_fill_provider, true) .offsetPolyLine(config.support_roof_line_width / 2); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(interface_lines); } break; @@ -2102,6 +2290,16 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor .offsetPolyLine(config.support_line_width / 2) ); storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); + + Polygons support_skin_lines; + support_skin_lines = + support_skin_lines.unionPolygons + ( + TreeSupportUtils::generateSupportInfillLines(support_skin_storage[layer_idx], config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, true, EFillMethod::LINES) + .offsetPolyLine(config.support_line_width / 2) + ); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_skin_lines); + // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. } break; @@ -2112,10 +2310,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor } // Subtract support floors from the support area and add them to the support floor instead. - if (config.support_bottom_layers > 0 && ! support_layer_storage[layer_idx].empty()) + if (config.support_bottom_layers > 0 && ! (support_layer_storage[layer_idx].empty() && ! support_skin_storage[layer_idx].empty() )) { Polygons floor_layer = storage.support.supportLayers[layer_idx].support_bottom; - Polygons layer_outset = support_layer_storage[layer_idx].offset(config.support_bottom_offset).difference(volumes_.getCollision(0, layer_idx, false)); + Polygons layer_outset = support_layer_storage[layer_idx].unionPolygons(support_skin_storage[layer_idx]).offset(config.support_bottom_offset).difference(volumes_.getCollision(0, layer_idx, false)); size_t layers_below = 0; while (layers_below <= config.support_bottom_layers) { @@ -2136,6 +2334,8 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor floor_layer = floor_layer.unionPolygons(); storage.support.supportLayers[layer_idx].support_bottom = storage.support.supportLayers[layer_idx].support_bottom.unionPolygons(floor_layer); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. + } for (PolygonsPart part : support_layer_storage[layer_idx].splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. @@ -2143,6 +2343,11 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count); } + for (PolygonsPart part : support_skin_storage[layer_idx].splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. + { + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count,config.support_line_width*1.5,EFillMethod::LINES); + } + { std::lock_guard critical_section_progress(critical_sections); progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size(); @@ -2163,6 +2368,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage) { std::vector support_layer_storage(move_bounds.size()); + std::vector support_skin_storage(move_bounds.size()); std::vector support_roof_storage(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. std::vector> linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance issues. @@ -2255,8 +2461,6 @@ void TreeSupport::drawAreas(std::vector>& move_bou } ); - // Single threaded combining all support areas to the right layers. - // Only copies data! for (const auto layer_idx : ranges::views::iota(0UL, layer_tree_polygons.size())) { for (std::pair data_pair : layer_tree_polygons[layer_idx]) @@ -2264,6 +2468,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou ( (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top) ? support_roof_storage : support_layer_storage )[layer_idx].add(data_pair.second); + } } @@ -2276,18 +2481,21 @@ void TreeSupport::drawAreas(std::vector>& move_bou scripta::log("tree_support_layer_storage", support_layer_storage[layer_idx], SectionType::SUPPORT, layer_idx); } - filterFloatingLines(support_layer_storage); + generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons); + const auto t_skin = std::chrono::high_resolution_clock::now(); + filterFloatingLines(support_layer_storage,support_skin_storage); const auto t_filter = std::chrono::high_resolution_clock::now(); - finalizeInterfaceAndSupportAreas(support_layer_storage, support_roof_storage, storage); + finalizeInterfaceAndSupportAreas(support_layer_storage, support_skin_storage, support_roof_storage, storage); const auto t_end = std::chrono::high_resolution_clock::now(); 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_filter = 0.001 * std::chrono::duration_cast(t_filter - t_drop).count(); + const auto dur_skin = 0.001 * std::chrono::duration_cast(t_skin - t_drop).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 filterFloatingLines: {} ms finalizeInterfaceAndSupportAreas {} ms", dur_gen_tips, dur_smooth, dur_drop, dur_filter, dur_finalize); + spdlog::info("Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms generateSupportSkin {} ms filterFloatingLines: {} ms finalizeInterfaceAndSupportAreas {} ms", dur_gen_tips, dur_smooth, dur_drop, dur_skin, dur_filter, dur_finalize); } } // namespace cura diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 8f8c5af516..a4c42275f1 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -32,7 +32,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage support_roof_layers(mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config.layer_height) : use_fake_roof ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0), connect_length((config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)), support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length), - support_roof_line_distance( use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")), //todo propper + support_roof_line_distance(use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")), support_outset(mesh.settings.get("support_offset")), roof_outset(use_fake_roof ? support_outset: mesh.settings.get("support_roof_offset")), force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && !use_fake_roof), @@ -44,8 +44,22 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage already_inserted(mesh.overhang_areas.size()), support_roof_drawn(mesh.overhang_areas.size(),Polygons()), roof_tips_drawn(mesh.overhang_areas.size(),Polygons()), + cradle_areas(mesh.overhang_areas.size(),Polygons()), + cradle_base_areas(mesh.overhang_areas.size(),Polygons()), + cradle_overhang(mesh.overhang_areas.size(),Polygons()), volumes_(volumes_s), - force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) + force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT), + cradle_layers(mesh.settings.get("support_tree_cradle_height") / config.layer_height), + minimum_cradle_layers(mesh.settings.get("support_tree_cradle_min_height") / config.layer_height), + cradle_lines(mesh.settings.get("support_tree_cradle_line_count")), + cradle_length(mesh.settings.get("support_tree_cradle_length")), + cradle_line_width(mesh.settings.get("support_tree_cradle_line_width")), + cradle_lines_roof(mesh.settings.get("support_tree_roof_cradle") != "none"), + cradle_base_roof(mesh.settings.get("support_tree_roof_cradle") == "cradle_and_base" || mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), + large_cradle_base(mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), + cradle_area_threshold(1000 * 1000 * mesh.settings.get("support_tree_maximum_pointy_area")), + cradle_tip_dtt(config.tip_layers * mesh.settings.get("support_tree_cradle_base_tip_percentage") / 100.0), + large_cradle_line_tips(mesh.settings.get("support_tree_large_cradle_line_tips")) { const double support_overhang_angle = mesh.settings.get("support_angle"); @@ -363,6 +377,587 @@ SierpinskiFillProvider* TreeSupportTipGenerator::generateCrossFillProvider(const return nullptr; } +void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mesh) +{ + LayerIndex max_layer = 0; + + for(LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) + { + if(!mesh.overhang_areas[layer_idx].empty()) + { + max_layer = layer_idx; + } + } + max_layer = std::max(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); + + LayerIndex start_layer = 1; + floating_parts_cache_.resize(max_layer+1); + floating_parts_map_.resize(max_layer+1); + + Polygons completely_supported = volumes_.getCollision(0,0,true); + Polygons layer_below = completely_supported; //technically wrong, but the xy distance error on layer 1 should not matter + for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) + { + Polygons next_completely_supported; + + // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. + Polygons layer = mesh.layers[layer_idx].getOutlines(); + + + // todo rewrite to be able to parallelize it + for (const Polygons part : layer.splitIntoParts()) + { + + AABB part_aabb(part); + auto has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); + + if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) + { + next_completely_supported.add(part); + continue; + } + + if (!has_support_below) + { + floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0); + floating_parts_map_ [layer_idx].emplace_back(std::vector()); + continue; + } + + + + size_t min_resting_on_layers = 0; + bool add = false; + for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) + { + if(layer_idx > 1 && floating.height < cradle_layers - 1 && !floating.area.intersection(part).empty()) + { + floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()) ; + min_resting_on_layers = std::max(min_resting_on_layers,floating.height); + add = true; + } + } + + if(min_resting_on_layers < cradle_layers && add) + { + floating_parts_map_ [layer_idx].emplace_back(std::vector()); + floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1); + } + else + { + next_completely_supported.add(part); + } + + } + layer_below = layer; + completely_supported = next_completely_supported; + + } + +} + +std::vector TreeSupportTipGenerator::getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area_below) +{ + std::vector result; + + if(layer_idx == 0) + { + return result; + } + + bool has_result = false; + + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + has_result = layer_idx < floating_parts_cache_.size(); + } + + if (has_result) + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + if (floating_parts_cache_[layer_idx].size()) + { + for (size_t resting_idx : floating_parts_map_[layer_idx - 1][idx_of_area_below]) + { + result.emplace_back(floating_parts_cache_[layer_idx][resting_idx]); + } + } + } + else + { + spdlog::error("Requested not calculated unsupported area.", layer_idx); + return result; + } + + return result; + + +} + +std::vector TreeSupportTipGenerator::getFullyUnsupportedArea(LayerIndex layer_idx) +{ + std::vector result; + + if(layer_idx == 0) + { + return result; + } + + bool has_result = false; + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + has_result = layer_idx < floating_parts_cache_.size(); + } + + if(has_result) + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + for (auto [idx, floating_data] : floating_parts_cache_[layer_idx] | ranges::views::enumerate) + { + if(floating_data.height == 0) + { + result.emplace_back(floating_data); + } + } + } + else + { + spdlog::error("Requested not calculated unsupported area.", layer_idx); + return result; + } + return result; + +} + +void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) +{ + std::mutex critical_support_free_areas_and_cradle_areas; + std::mutex critical_dedupe; + std::vector> dedupe(mesh.overhang_areas.size()); + + calculateFloatingParts(mesh); + + cura::parallel_for( + 1, + mesh.overhang_areas.size() - (z_distance_delta + 1), + [&](const LayerIndex layer_idx) + { + if(mesh.overhang_areas[layer_idx + z_distance_delta].empty()) + { + return; + } + for (auto pointy_info : getFullyUnsupportedArea(layer_idx + z_distance_delta)) + { + AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta]); + if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta], overhang_aabb).intersection(pointy_info.area).empty()) + { + // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. + continue; + } + + std::vector accumulated_model(std::min(cradle_layers + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector all_pointy_idx{ pointy_info.index }; + + Point assumed_center; + + Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. + bool abort = false; + bool contacted_other_pointy = false; + for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) + { + Polygons relevant_forbidden = + volumes_.getAvoidance(0, layer_idx + cradle_up_layer, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + + // shadow model up => not cradle where model + // then drop cradle down + // cut into parts => get close to original pointy that are far enough from each other. + std::vector next_pointy_idx; + Polygons model_outline; + bool blocked_by_dedupe = false; + if (cradle_up_layer > 1) // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up + { + if (cradle_up_layer == 2) + { + assumed_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + } + else + { + Point next_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + assumed_center = (2 * assumed_center + next_center) / 3; + } + + for (size_t pointy_idx : all_pointy_idx) + { + for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta, pointy_idx)) + { + if (next_pointy_data.height != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it + { + contacted_other_pointy = true; + continue; + } + + // Ensure each area is only handles once + std::lock_guard critical_section_cradle(critical_dedupe); + if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) + { + dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data.index); + model_outline.add(next_pointy_data.area); + next_pointy_idx.emplace_back(next_pointy_data.index); + } + else + { + blocked_by_dedupe = true; + } + } + } + all_pointy_idx = next_pointy_idx; + } + else + { + model_outline.add(pointy_info.area); + } + + if (model_outline.empty()) + { + if (cradle_up_layer < minimum_cradle_layers) + { + abort = true; + break; + } + + if (! blocked_by_dedupe) + { + // The model is surrounded with cradle based on the area above (z distance). + // When an area that should have a cradle merges with a buildplate supported area above, it will no longer exist for a cradle. + // But if the cradle stops there will be z distance layer between the end of the cradle and said merge. + // To reduce the impact an area is estimated where the cradle should be for these areas. + Polygons previous_area = shadow; + for (size_t cradle_up_layer_z_delta = cradle_up_layer; cradle_up_layer_z_delta < std::min(cradle_up_layer + z_distance_delta - 1, accumulated_model.size()); cradle_up_layer_z_delta++) + { + Polygons possible_areas = shadow.offset(cradle_up_layer_z_delta * config.maximum_move_distance + config.xy_min_distance).intersection(mesh.layers[layer_idx + cradle_up_layer_z_delta - 1].getOutlines()); + Polygons next_area; + for (auto part : possible_areas.splitIntoParts(true)) + { + if (! previous_area.intersection(part).empty()) + { + next_area.add(part); + } + } + previous_area = next_area; + + accumulated_model[cradle_up_layer_z_delta] = next_area; + } + } + break; + } + + + model_outline = model_outline.unionPolygons(); + shadow = shadow.unionPolygons(model_outline); + + accumulated_model[cradle_up_layer] = shadow.offset(-config.maximum_move_distance).unionPolygons(model_outline); + } + + if (abort) + { + // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface base. + for (size_t cradle_up_layer = 1; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) + { + accumulated_model[cradle_up_layer].clear(); + } + } + + std::vector centers{ assumed_center }; + if (! contacted_other_pointy) + { + for (int extra_points = 0; extra_points < 2; extra_points++) + { + coord_t candidate_min_distance2 = 0; + Point candidate = assumed_center; + for (auto line : shadow) + { + for (auto p : line) + { + coord_t smallest_distance2 = std::numeric_limits::max(); + for (Point already_center : centers) + { + smallest_distance2 = std::min(vSize2(p - already_center), smallest_distance2); + } + if (smallest_distance2 > candidate_min_distance2) + { + candidate_min_distance2 = smallest_distance2; + candidate = p; + } + } + } + + if (candidate_min_distance2 > pow(5 * cradle_length, 2)) + { + centers.emplace_back(candidate); + } + } + } + + + for (Point center : centers) + { + std::vector cradle_areas_calc(accumulated_model.size()); + + for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) + { + if ((idx > 0 || (cradle_base_roof && !large_cradle_base && ! abort)) + && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. + { + coord_t max_distance2 = 0; + Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + + for (auto line : model_shadow) + { + for (Point p : line) + { + max_distance2 = std::max(max_distance2, vSize2(center - p)); + } + } + + Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + cradle_length * 2, (2 * M_PI) / cradle_lines); + + // create lines that go from the furthest possible location to the center + Polygons lines_to_center; + for (Point p : max_outer_points) + { + lines_to_center.addLine(p, center); + } + + // Subtract the model shadow up until this layer from the lines. + if (idx > 0) + { + lines_to_center = model_shadow.offset(config.xy_min_distance).unionPolygons().differencePolyLines(lines_to_center, false); + } + // Store valid distances from the center in relation to the direction of the line. + // Used to detect if a line may be intersecting another model part. + std::vector> vector_distance_map; + + // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing + Polygons shortened_lines_to_center; + for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); + Point closer = front_closer ? line.front() : line.back(); + Point further = front_closer ? line.back() : line.front(); + if (Polygon(line).polylineLength() <= cradle_length) + { + shortened_lines_to_center.add(line); + } + else + { + double scale = (double(cradle_length) / double(vSize(further - closer))); + Point correct_length = closer + (further - closer) * scale; + shortened_lines_to_center.addLine(correct_length, closer); + } + + if (further != closer) + { + vector_distance_map.emplace_back((further - closer), vSize(center - closer)); + } + } + // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent this. + shortened_lines_to_center = relevant_forbidden.offset(config.support_line_width / 2).differencePolyLines(shortened_lines_to_center, false); + + // Evaluate which lines are still valid after the avoidance was subtracted + for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); + Point closer = front_closer ? line.front() : line.back(); + Point further = front_closer ? line.back() : line.front(); + Point current_direction = further - closer; + coord_t distance_from_center = vSize(closer - center); + bool keep_line = false; + bool found_candidate = false; + const bool too_short = (vSize(closer - further)) < std::min((cradle_lines_roof ? config.support_roof_line_width : config.support_line_width) * 2, cradle_length / 2); + if (! too_short) + { + for (auto [direction_idx, direction] : vector_distance_map | ranges::views::enumerate) + { + double cosine = (dot(direction.first, current_direction)) / (vSize(current_direction) * vSize(direction.first)); + if (cosine > 0.99) + { + found_candidate = true; + // found line, check if there is one that is closer, if not then check if distance to center is expected + bool found_close_line = direction.second + config.xy_min_distance + config.min_feature_size > distance_from_center; + if (found_close_line) + { + keep_line = true; + break; + } + } + } + } + + if (too_short || (! keep_line && found_candidate)) + { + shortened_lines_to_center[line_idx].clear(); + } + } + Polygons cradle = shortened_lines_to_center.offsetPolyLine(cradle_line_width / 2).unionPolygons().offset(FUDGE_LENGTH).unionPolygons().offset(-FUDGE_LENGTH).difference(relevant_forbidden); + cradle_areas_calc[idx] = cradle; + } + } + + // The base has to include the pointy overhang that has to be supported. + if (cradle_areas_calc.size() > 1) + { + if (cradle_base_roof && ! large_cradle_base) + { + cradle_areas_calc[0] = cradle_areas_calc[0].unionPolygons(accumulated_model[0]); + } + else + { + cradle_areas_calc[0] = accumulated_model[0]; + } + } + + // Rooflines are more delicate, so ensure that they rest on as much other rooflines if possible. + if (! use_fake_roof && support_roof_layers) + { + for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) + { + if (idx > 1) + { + Polygons relevant_forbidden = + volumes_.getAvoidance(0, layer_idx + idx - 1, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + + Polygons area_above = cradle.difference(relevant_forbidden); + + // If a line would have some areas that are too small to be drawn, there can be minor issues. The idea is that the offsets will filter them out. + for (auto part : area_above.offset(-cradle_line_width / 4).splitIntoParts()) + { + if (part.area() > cradle_line_width * std::max(cradle_length / 4, config.support_roof_line_width)) + { + cradle_areas_calc[idx - 1].add(part.offset(cradle_line_width / 4)); + } + } + + cradle_areas_calc[idx - 1] = cradle_areas_calc[idx - 1].unionPolygons(); + } + } + } + for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) + { + Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + cradle_areas_calc[idx] = cradle_areas_calc[idx].difference(relevant_forbidden); + } + + const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; + const coord_t small_hole_size = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate) + { + if (! cradle.empty()) + { + if (idx == 0) + { + if (! use_fake_roof && support_roof_layers) + { + Polygons cut_line_base; + if (large_cradle_base) + { + // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest on. + // This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just the overhang, but that should not cause any issues. + Point min_p = cradle.min(); + Point max_p = cradle.max(); + Polygons rest_line; + rest_line.addLine(min_p, max_p); + cut_line_base = rest_line.offsetPolyLine(small_hole_size); + } + Polygons closed = cradle.unionPolygons(cradle_areas_calc[1].offset(closing_dist, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) + { + support_free_areas[layer_idx - interface_down].add(cut_line_base); + volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); + } + } + if (large_cradle_base) + { + cradle = closed.difference(cut_line_base); + } + } + + if(large_cradle_line_tips) + { + cradle.add(cradle_areas_calc[1]); + } + + cradle = cradle.unionPolygons(); + cradle_base_areas[layer_idx].add(cradle); + } + else + { + Polygons unsupported_cradle_parts = cradle.difference(cradle_areas_calc[idx - 1].offset(config.maximum_move_distance)); + + Polygons closed = cradle.unionPolygons(cradle.offset(closing_dist + 2 * config.branch_radius, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); + volumes_.addAreaToAntiPreferred(closed, layer_idx - idx); + + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + cradle_areas[layer_idx + idx].add(cradle); + } + + // Check which cradle lines overhang so much that they need support. If possible add support one layer below said line. + const Polygons relevant_forbidden = + volumes_.getAvoidance(0, layer_idx + idx - 1, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + if (! unsupported_cradle_parts.empty()) + { + for (auto unsupported_part : unsupported_cradle_parts.splitIntoParts()) + { + if (unsupported_part.difference(relevant_forbidden).empty()) + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + cradle_overhang[layer_idx + idx].add(unsupported_cradle_parts); + } + else + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + cradle_overhang[layer_idx + idx - 1].add(unsupported_cradle_parts); + } + } + } + } + } + } + } + } + }); + + cura::parallel_for( + 1, + mesh.layers.size(), + [&](const LayerIndex layer_idx) + { + if(layer_idx < cradle_areas[layer_idx].size()) + { + cradle_areas[layer_idx] = cradle_areas[layer_idx].unionPolygons(); + + } + if(layer_idx < cradle_overhang[layer_idx].size()) + { + cradle_overhang[layer_idx] = cradle_overhang[layer_idx].unionPolygons(); + + } + if(layer_idx < cradle_base_areas[layer_idx].size()) + { + cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); + + } + if(layer_idx < support_free_areas[layer_idx].size()) + { + support_free_areas[layer_idx] = support_free_areas[layer_idx].unionPolygons(); + + } + }); + +} + void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof ) { @@ -443,7 +1038,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. // This is intentional here, as support roof is still valid if only a part of the tip may reach it. Polygons forbidden_here = - volumes_.getAvoidance(config.getRadius(0), layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-config.getRadius(0),ClipperLib::jtRound); + volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config.support_line_width replaced with another setting. It should still work in most cases, but it should be possible to create a situation where a overhang outset lags though a wall. // I will take a look at this later. @@ -452,10 +1047,11 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) { const Polygons forbidden_next = - volumes_.getAvoidance(config.getRadius(0), layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-config.getRadius(0),ClipperLib::jtRound); + volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); full_overhang_area = full_overhang_area.difference(forbidden_next); + if(force_minimum_roof_area) { full_overhang_area.removeSmallAreas(minimum_roof_area); @@ -502,7 +1098,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. // This is intentional here, as support roof is still valid if only a part of the tip may reach it. Polygons forbidden_here = - volumes_.getAvoidance(config.getRadius(0), layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides).offset(-(config.getRadius(0)),ClipperLib::jtRound); + volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); if (!force_minimum_roof_area) { @@ -538,6 +1134,57 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m } ); + + if(cradle_base_roof) + { + // The cradle has to be added after the closing operation above, as otherwise the holes in the cradle that ensure it can be removed will be closed. + cura::parallel_for + ( + 1, + cradle_areas.size(), + [&](const LayerIndex layer_idx) + { + if (cradle_areas[layer_idx].empty() && cradle_base_areas[layer_idx].empty()) + { + return; // This is a continue if imagined in a loop context. + } + + // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. + // This is intentional here, as support roof is still valid if only a part of the tip may reach it. + Polygons forbidden_here = + volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + + Polygons full_overhang_area; + full_overhang_area.add(cradle_base_areas[layer_idx]); + full_overhang_area=full_overhang_area.unionPolygons(); + + full_overhang_area = TreeSupportUtils::safeOffsetInc(full_overhang_area,roof_outset,forbidden_here, config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + + + for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) + { + const Polygons forbidden_next = + volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + + full_overhang_area = full_overhang_area.difference(forbidden_next); + + + if (full_overhang_area.area()>EPSILON) + { + std::lock_guard critical_section_potential_support_roofs(critical_potential_support_roofs); + additional_support_roofs[layer_idx-dtt_roof].add((full_overhang_area)); + } + else + { + break; + } + } + + } + ); + } + + cura::parallel_for ( 0, @@ -547,11 +1194,10 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m support_roof_drawn[layer_idx] = support_roof_drawn[layer_idx].unionPolygons(additional_support_roofs[layer_idx]); } ); - } -void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation, std::vector additional_ovalization_targets) +void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets) { const bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; const bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; @@ -584,7 +1230,7 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, size_t dont_move_until, bool connect_points) +void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, bool connect_points) { // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a certain roof area will actually have lines. size_t dtt_roof_tip = 0; - if (config.support_roof_wall_count == 0) + coord_t base_radius = config.getRadius(supports_cradle ? cradle_tip_dtt: 0); + if (config.support_roof_wall_count == 0 && ! supports_cradle) { for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; dtt_roof_tip++) { + + std::function)> evaluateRoofWillGenerate = [&](std::pair p) { Polygon roof_circle; for (Point corner : TreeSupportBaseCircle::getBaseCircle()) { - roof_circle.add(p.first + corner * std::max(config.min_radius / TreeSupportBaseCircle::base_radius, coord_t(1))); + roof_circle.add(p.first + corner * std::max(base_radius / TreeSupportBaseCircle::base_radius, coord_t(1))); } Polygons area = roof_circle.offset(0); return !TreeSupportUtils::generateSupportInfillLines(area, config, true, insert_layer_idx - dtt_roof_tip,support_roof_line_distance,cross_fill_provider,true).empty(); @@ -630,7 +1279,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector point_data : line) { - addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false); + addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); } } @@ -642,7 +1291,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector point_data : line) { - addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false); + addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); } } @@ -655,7 +1304,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - dtt_roof_tip != 0 || supports_roof, disable_ovalization, + dtt_roof_tip != 0 || supports_roof, supports_cradle, disable_ovalization, additional_ovalization_targets ); } @@ -716,8 +1365,10 @@ void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector to_be_removed; - Polygons roof_on_layer_above = use_fake_roof ? support_roof_drawn[layer_idx+1] : storage.support.supportLayers[layer_idx+1].support_roof.unionPolygons(additional_support_areas[layer_idx+1]); - Polygons roof_on_layer = use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof.unionPolygons(additional_support_areas[layer_idx]); + Polygons roof_on_layer_above = (use_fake_roof ? support_roof_drawn[layer_idx+1] : storage.support.supportLayers[layer_idx+1].support_roof).unionPolygons(additional_support_areas[layer_idx+1]); + Polygons roof_on_layer = (use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); + roof_on_layer_above.add(cradle_base_areas[layer_idx]); + roof_on_layer_above = roof_on_layer_above.unionPolygons(cradle_overhang[layer_idx]); for (TreeSupportElement* elem : move_bounds[layer_idx]) { @@ -729,12 +1380,12 @@ void TreeSupportTipGenerator::removeUselessAddedPoints(std::vectorresult_on_layer; PolygonUtils::moveInside(roof_on_layer_above,from); - // Remove branches should have interface above them, but dont. Should never happen. + // Remove branches should have interface above them, but don't. Should never happen. if (roof_on_layer_above.empty() || - (!roof_on_layer_above.inside(elem->result_on_layer) && vSize2(from-elem->result_on_layer)>config.getRadius(0)*config.getRadius(0) + FUDGE_LENGTH * FUDGE_LENGTH)) + (!roof_on_layer_above.inside(elem->result_on_layer) && vSize2(from-elem->result_on_layer) > std::pow(config.getRadius(0) + FUDGE_LENGTH,2))) { to_be_removed.emplace_back(elem); - spdlog::warn("Removing already placed tip that should have roof above it?"); + spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}.",vSize(from-elem->result_on_layer)); } } } @@ -745,14 +1396,13 @@ void TreeSupportTipGenerator::removeUselessAddedPoints(std::vectorarea; delete elem; } - } } ); } -void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas) +void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& support_free_areas) { std::vector> new_tips(move_bounds.size()); @@ -763,6 +1413,11 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice const coord_t extra_outset = std::max(coord_t(0), config.min_radius - config.support_line_width / 2) + (xy_overrides ? 0 : config.support_line_width / 2); // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. + if(cradle_layers>0) + { + generateCradle(mesh,support_free_areas); + } + if (support_roof_layers) { calculateRoofAreas(mesh); @@ -774,7 +1429,11 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice mesh.overhang_areas.size() - z_distance_delta, [&](const LayerIndex layer_idx) { - if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() && (layer_idx+1 >= support_roof_drawn.size() || support_roof_drawn[layer_idx+1].empty())) + if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() && + (layer_idx+1 >= support_roof_drawn.size() || support_roof_drawn[layer_idx+1].empty()) && + (layer_idx >= cradle_base_areas.size() || cradle_base_areas[layer_idx].empty()) && + (layer_idx >= cradle_overhang.size() || cradle_overhang[layer_idx].empty()) + ) { return; // This is a continue if imagined in a loop context. } @@ -789,31 +1448,45 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice { coord_t upper_line_distance = support_supporting_branch_distance; - coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance ); + coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); + bool use_grid = line_distance == upper_line_distance; - return TreeSupportUtils::generateSupportInfillLines(area, config, roof && !use_fake_roof, layer_idx, line_distance , cross_fill_provider, roof && !use_fake_roof, line_distance == upper_line_distance); + return TreeSupportUtils::generateSupportInfillLines(area, config, roof && !use_fake_roof, layer_idx, line_distance , cross_fill_provider, roof && !use_fake_roof, use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; + enum class OverhangType {REGULAR, ROOF, CRADLE}; - std::vector> overhang_processing; + std::vector> overhang_processing; // ^^^ Every overhang has saved if a roof should be generated for it. // This can NOT be done in the for loop as an area may NOT have a roof even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and it would not have a roof if the overhang is offset by support roof horizontal expansion instead. // (At least this is the current behavior of the regular support) - Polygons core_overhang=mesh.overhang_areas[layer_idx + z_distance_delta]; + Polygons core_overhang = mesh.overhang_areas[layer_idx + z_distance_delta]; + + if(cradle_overhang.size() > layer_idx) + { + core_overhang = core_overhang.unionPolygons(cradle_overhang[layer_idx]); + } if (support_roof_layers && layer_idx+1 < support_roof_drawn.size()) { core_overhang = core_overhang.difference(support_roof_drawn[layer_idx]); - for (Polygons roof_part : support_roof_drawn[layer_idx+1].difference(support_roof_drawn[layer_idx]).splitIntoParts(true)) //If there is a roof, the roof will be one layer above the tips. + for (Polygons roof_part : support_roof_drawn[layer_idx+1].difference(support_roof_drawn[layer_idx].offset(config.maximum_move_distance_slow)).splitIntoParts(true)) //If there is a roof, the roof will be one layer above the tips. { //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. - overhang_processing.emplace_back(roof_part, true); + overhang_processing.emplace_back(roof_part, OverhangType::ROOF); } } + core_overhang = core_overhang.difference(TreeSupportUtils::safeOffsetInc(cradle_base_areas[layer_idx], support_outset + EPSILON, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier)); + core_overhang = core_overhang.difference(support_free_areas[layer_idx]); + for (Polygons cradle_base : cradle_base_areas[layer_idx].splitIntoParts(true)) + { + overhang_processing.emplace_back(cradle_base, OverhangType::CRADLE); + } + Polygons overhang_regular = TreeSupportUtils::safeOffsetInc(core_overhang, support_outset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier); Polygons remaining_overhang = @@ -824,17 +1497,24 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice coord_t extra_total_offset_acc = 0; // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. - while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. + { - coord_t offset_current_step = - extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ? - std::min(config.support_line_width / 8, extra_outset - extra_total_offset_acc) : - std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); - extra_total_offset_acc += offset_current_step; - Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(support_roof_drawn[layer_idx].offset(1.5 * extra_total_offset_acc))).unionPolygons(); //overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. - Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); + Polygons already_supported = support_roof_drawn[layer_idx]; + already_supported.add(cradle_areas[layer_idx]); + already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. + already_supported=already_supported.unionPolygons(); + while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. + { + coord_t offset_current_step = + extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ? + std::min(config.support_line_width / 8, extra_outset - extra_total_offset_acc) : + std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); + extra_total_offset_acc += offset_current_step; + Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(already_supported.offset(1.5 * extra_total_offset_acc))).unionPolygons(); //overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. + Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); + } } // If the xy distance overrides the z distance, some support needs to be inserted further down. @@ -851,7 +1531,7 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. // Technically this violates branch distance, but not only is this the only reasonable choice, // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. - // Also it is assumed that the area that is valid a layer below is to small for support roof. + // Also it is assumed that the area that is valid a layer below is too small for support roof. if (polylines.pointCount() <= 3) { // Add the outer wall to ensure it is correct supported instead. @@ -878,20 +1558,23 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice std::function)> evaluatePoint = [&](std::pair p) { return relevant_forbidden_below.inside(p.first, true); }; - if (support_roof_layers) - { - //Remove all points that are for some reason part of a roof area, as the point is already supported by roof - std::function)> evaluatePartOfRoof = - [&](std::pair p) { return support_roof_drawn[layer_idx-lag_ctr].inside(p.first, true); }; + Polygons already_supported = support_roof_drawn[layer_idx-lag_ctr]; + already_supported.add(cradle_areas[layer_idx-lag_ctr]); + already_supported.add(support_free_areas[layer_idx-lag_ctr]); // While point there are not supported, there may be no support anyway. + already_supported=already_supported.unionPolygons(); + + //Remove all points that are for some reason are already supported + std::function)> evaluateAlreadySupported = + [&](std::pair p) { return already_supported.inside(p.first, true); }; + + overhang_lines = splitLines(overhang_lines, evaluateAlreadySupported).second; - overhang_lines = splitLines(overhang_lines, evaluatePartOfRoof).second; - } std::pair, std::vector> split = splitLines(overhang_lines, evaluatePoint); // Keep all lines that are invalid. overhang_lines = split.first; std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, support_roof_layers, false); + addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, false, support_roof_layers, false); } } } @@ -900,14 +1583,16 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice for (Polygons support_part : overhang_regular.splitIntoParts(true)) { - overhang_processing.emplace_back(support_part, false); + overhang_processing.emplace_back(support_part, OverhangType::REGULAR); } - for (std::pair overhang_pair : overhang_processing) + for (std::pair overhang_pair : overhang_processing) { - const bool roof_allowed_for_this_part = overhang_pair.second; + const bool roof_allowed_for_this_part = overhang_pair.second == OverhangType::ROOF; + const bool supports_cradle = overhang_pair.second == OverhangType::CRADLE; + Polygons overhang_outset = overhang_pair.first; - const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); + const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); std::vector overhang_lines; bool only_lines = true; @@ -944,7 +1629,7 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice } } - if (roof_allowed_for_this_part) //Some roof may only be supported by a part of a tip + if (roof_allowed_for_this_part || supports_cradle) //Some roof may only be supported by a part of a tip { polylines = TreeSupportUtils::movePointsOutside(polylines,relevant_forbidden,config.getRadius(0)+FUDGE_LENGTH/2); } @@ -963,12 +1648,12 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice } else { - spdlog::warn("Overhang area has no valid tips! Was roof: {} On Layer: {}", roof_allowed_for_this_part,layer_idx); + spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} On Layer: {}", roof_allowed_for_this_part, supports_cradle ,layer_idx); } } size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (roof_allowed_for_this_part ? 0 : support_roof_layers)) : 0; - addLinesAsInfluenceAreas(new_tips, overhang_lines, force_tip_to_roof ? support_roof_layers : 0, layer_idx, roof_allowed_for_this_part, dont_move_for_layers, only_lines); + addLinesAsInfluenceAreas(new_tips, overhang_lines, force_tip_to_roof ? support_roof_layers : 0, layer_idx, roof_allowed_for_this_part, supports_cradle,dont_move_for_layers, only_lines); } } ); @@ -1020,12 +1705,28 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice removeUselessAddedPoints(new_tips ,storage , additional_support_areas); + if(support_roof_layers && !use_fake_roof && cradle_lines_roof) + { + for (auto [layer_idx, cradle] : cradle_areas | ranges::views::enumerate) + { + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(cradle); + } + } + else + { + for (auto [layer_idx, cradle] : cradle_areas | ranges::views::enumerate) + { + additional_support_areas[layer_idx] = additional_support_areas[layer_idx].unionPolygons(cradle); + } + } + + for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { move_bounds[layer_idx].insert(tips_on_layer.begin(), tips_on_layer.end()); } -} +} }// namespace cura diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 5c8884a6ad..f3f3626b68 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -306,6 +306,41 @@ Polygons Polygons::intersectionPolyLines(const Polygons& polylines, bool restitc return ret; } +Polygons Polygons::differencePolyLines(const Polygons& polylines, bool restitch, const coord_t max_stitch_distance) const +{ + Polygons split_polylines = polylines.splitPolylinesIntoSegments(); + + ClipperLib::PolyTree result; + ClipperLib::Clipper clipper(clipper_init); + clipper.AddPaths(split_polylines.paths, ClipperLib::ptSubject, false); + clipper.AddPaths(paths, ClipperLib::ptClip, true); + clipper.Execute(ClipperLib::ctDifference, result); + Polygons ret; + ClipperLib::OpenPathsFromPolyTree(result, ret.paths); + + if (restitch) + { + Polygons result_lines, result_polygons; + const coord_t snap_distance = 10_mu; + PolylineStitcher::stitch(ret, result_lines, result_polygons, max_stitch_distance, snap_distance); + ret = result_lines; + // if polylines got stitched into polygons, split them back up into a polyline again, because the result only admits polylines + for (PolygonRef poly : result_polygons) + { + if (poly.empty()) + continue; + if (poly.size() > 2) + { + poly.emplace_back(poly[0]); + } + ret.add(poly); + } + } + + return ret; +} + + void Polygons::toPolylines() { for (PolygonRef poly : *this) From c7b6aaa5c98b0f131d36179e39f6aedb03e5ab52 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 7 May 2023 22:29:40 +0200 Subject: [PATCH 002/101] Fix support_skin_line_distance setting not working. --- src/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 9ac6ca8452..e26a3a9791 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2345,7 +2345,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor for (PolygonsPart part : support_skin_storage[layer_idx].splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. { - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count,config.support_line_width*1.5,EFillMethod::LINES); + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count, config.support_skin_line_distance, EFillMethod::LINES); } { From 0997c3bd48a1b6aa1c5efcb7e955abd12c3d48d5 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 8 May 2023 00:09:08 +0200 Subject: [PATCH 003/101] Fix support being sprinkled on top of angled interface when tree support is used. --- src/TreeSupportTipGenerator.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index a4c42275f1..61ac08b05d 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -981,10 +981,11 @@ void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, st // Technically this also makes support blocker smaller, which is wrong as they do not have a xy_distance, but it should be good enough. Polygons model_outline = volumes_.getCollision(0, layer_idx, ! xy_overrides).offset(-config.xy_min_distance, ClipperLib::jtRound); - Polygons overhang_regular = - TreeSupportUtils::safeOffsetInc(mesh.overhang_areas[layer_idx + z_distance_delta], roof ? roof_outset : support_outset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier); + //Use full_overhang to ensure dropped overhang will overlap with overhang further down. not leaving a small hole between model and roof where support could creep into. + Polygons overhang_full = + TreeSupportUtils::safeOffsetInc(mesh.full_overhang_areas[layer_idx + z_distance_delta], roof ? roof_outset : support_outset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier); Polygons remaining_overhang = - mesh.overhang_areas[layer_idx + z_distance_delta].offset(roof ? roof_outset : support_outset).difference(overhang_regular).intersection(relevant_forbidden).difference(model_outline); + mesh.full_overhang_areas[layer_idx + z_distance_delta].offset(roof ? roof_outset : support_outset).difference(overhang_full).intersection(relevant_forbidden).difference(model_outline); for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && layer_idx - coord_t(lag_ctr) >= 1 && !remaining_overhang.empty(); lag_ctr++) { { @@ -1008,9 +1009,6 @@ void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, st result[layer_idx]=result[layer_idx].unionPolygons(); } ); - - - } void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& mesh) From 12f2572b0f4d306fc8197deadbd7cfaea5b7246f Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 8 May 2023 00:12:33 +0200 Subject: [PATCH 004/101] Fix Tree Support Cradle generation causing a crash. --- src/TreeSupportTipGenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 61ac08b05d..80919ead34 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -388,7 +388,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes max_layer = layer_idx; } } - max_layer = std::max(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); + max_layer = std::min(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); LayerIndex start_layer = 1; floating_parts_cache_.resize(max_layer+1); @@ -896,7 +896,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: Polygons unsupported_cradle_parts = cradle.difference(cradle_areas_calc[idx - 1].offset(config.maximum_move_distance)); Polygons closed = cradle.unionPolygons(cradle.offset(closing_dist + 2 * config.branch_radius, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); - volumes_.addAreaToAntiPreferred(closed, layer_idx - idx); + volumes_.addAreaToAntiPreferred(closed, layer_idx + idx); { std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); @@ -1078,8 +1078,8 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // Now, because the avoidance/collision was subtracted above, the overhang parts that are of xy distance were removed, so to merge areas that should have been one offset by xy_min_distance and then undo it. // In a perfect world the offset here would be of a mode that makes sure that area.offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance) = area if there is only one polygon in said area. // I have not encountered issues with using the default mitered here. Could be that i just have not encountered an issue with it yet though. - potential_support_roofs[layer_idx]=potential_support_roofs[layer_idx].unionPolygons().offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance).unionPolygons(potential_support_roofs[layer_idx]); + potential_support_roofs[layer_idx] = potential_support_roofs[layer_idx].unionPolygons().offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance).unionPolygons(potential_support_roofs[layer_idx]); } ); From 624991dd51f174cd2b989b622544997282f455cc Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 8 May 2023 23:31:11 +0200 Subject: [PATCH 005/101] Fix crash when support blockers are used. --- src/TreeModelVolumes.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 91177c277a..c9042f7043 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -1222,12 +1222,12 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_ ); avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; } - } - if (layer_idx > max_layer_idx_without_blocker) - { - Polygons smaller_avoidance_collision = getAvoidance(1,layer_idx,AvoidanceType::COLLISION,true,true).offset(radius_offset,ClipperLib::jtRound); - std::lock_guard critical_section(*critical_avoidance_cache_collision_ ); - avoidance_cache_collision_[key] = smaller_avoidance_collision; + if (layer_idx > max_layer_idx_without_blocker) + { + Polygons smaller_avoidance_collision = getAvoidance(1,layer_idx,AvoidanceType::COLLISION,true,true).offset(radius_offset,ClipperLib::jtRound); + std::lock_guard critical_section(*critical_avoidance_cache_collision_ ); + avoidance_cache_collision_[key] = smaller_avoidance_collision; + } } }); } From 59531dc31abb200fa79b6c2641b331f057fd0ba5 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 14 May 2023 00:19:45 +0200 Subject: [PATCH 006/101] Fix Cradle Tip generation behaving unexpectedly when support interface is disabled, based on support_tree_roof_cradle setting. --- src/TreeSupportTipGenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 80919ead34..b25f1dd08b 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -54,9 +54,9 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage cradle_lines(mesh.settings.get("support_tree_cradle_line_count")), cradle_length(mesh.settings.get("support_tree_cradle_length")), cradle_line_width(mesh.settings.get("support_tree_cradle_line_width")), - cradle_lines_roof(mesh.settings.get("support_tree_roof_cradle") != "none"), - cradle_base_roof(mesh.settings.get("support_tree_roof_cradle") == "cradle_and_base" || mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), - large_cradle_base(mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), + cradle_lines_roof(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") != "none"), + cradle_base_roof(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") == "cradle_and_base" || mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), + large_cradle_base(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base"), cradle_area_threshold(1000 * 1000 * mesh.settings.get("support_tree_maximum_pointy_area")), cradle_tip_dtt(config.tip_layers * mesh.settings.get("support_tree_cradle_base_tip_percentage") / 100.0), large_cradle_line_tips(mesh.settings.get("support_tree_large_cradle_line_tips")) From dc64f9338e8cfaa9d291e54d43ed2894aa0b6fdc Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 16 May 2023 09:47:36 +0200 Subject: [PATCH 007/101] Improved Tree Support Support Skin Generation and fixed bug causing tips to be slightly too large --- include/TreeSupport.h | 8 ++- include/TreeSupportElement.h | 2 +- include/TreeSupportSettings.h | 7 +++ include/TreeSupportTipGenerator.h | 2 +- include/utils/polygon.h | 2 +- src/TreeSupport.cpp | 91 +++++++++++++++++++++++-------- src/TreeSupportTipGenerator.cpp | 7 ++- src/utils/polygon.cpp | 4 +- 8 files changed, 91 insertions(+), 32 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 56d45631f4..129b382b6e 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -319,7 +319,7 @@ class TreeSupport std::vector>> grouped_meshes; /*! - * \brief Areas that should have been support roof, but where the roof settings would not allow any lines to be generated. + * \brief Areas that should have been support roof, but where the roof settings would not allow any lines to be generated. Can also be other placed lines, e.g. Cradles */ std::vector additional_required_support_area; @@ -328,6 +328,12 @@ class TreeSupport */ std::vector placed_support_lines_support_areas; + /*! + * \brief Areas that use a higher density pattern of regular support to support the model (fake_roof). + * placed_support_lines_support_areas contains the lines placed inside of placed_fake_roof_areas. + */ + std::vector placed_fake_roof_areas; + /*! * \brief Areas where no support may be. Areas will be subtracted from support areas. */ diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 02aa2600cd..537ce569a0 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -141,7 +141,7 @@ struct TreeSupportElement /*! * \brief Create a new Element for one layer below the element of the pointer supplied. */ - TreeSupportElement(TreeSupportElement* element_above) : + explicit TreeSupportElement(TreeSupportElement* element_above) : target_height(element_above->target_height), target_position(element_above->target_position), next_position(element_above->next_position), diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 713c2fa2fe..4a4db4b2d8 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -71,6 +71,7 @@ struct TreeSupportSettings fill_outline_gaps(settings.get("fill_outline_gaps")), support_skin_layers(settings.get("support_tree_support_skin_height")/layer_height), support_skin_line_distance(settings.get("support_tree_support_skin_line_distance")), + support_tree_skin_for_large_tips_radius_threshold(settings.get("support_tree_skin_for_large_tips_threshold") / 2), simplifier(Simplify(mesh_group_settings)) { layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); @@ -382,6 +383,11 @@ struct TreeSupportSettings */ coord_t support_skin_line_distance; + /*! + * \brief Tips with a radius of at least this should have skin. + */ + coord_t support_tree_skin_for_large_tips_radius_threshold; + /*! * \brief Simplifier to simplify polygons. */ @@ -435,6 +441,7 @@ struct TreeSupportSettings fill_outline_gaps == other.fill_outline_gaps && support_skin_layers == other.support_skin_layers && support_skin_line_distance == other.support_skin_line_distance && + support_tree_skin_for_large_tips_radius_threshold == other.support_tree_skin_for_large_tips_radius_threshold && // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry ( interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index a75a1ff7e6..95e48d1a89 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -44,7 +44,7 @@ class TreeSupportTipGenerator * \param support_free_areas[out] Areas where no support (including roof) of any kind is to be drawn. * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ - void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& support_free_areas); + void generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh ,std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& placed_fake_roof_areas, std::vector& support_free_areas); private: diff --git a/include/utils/polygon.h b/include/utils/polygon.h index d0e7f8be93..44a2ce0b81 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -1230,7 +1230,7 @@ class Polygons * \param outer_offset Offset relative to the original shape-outline towards the outside of the shape. Comparable to normal offset. * \return The resulting polygons. */ - Polygons tubeShape(const coord_t inner_offset, const coord_t outer_offset) const; + Polygons tubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt = ClipperLib::jtMiter) const; private: /*! diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index e26a3a9791..5ea32f5eb8 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -94,6 +94,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) } placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(),Polygons()); + placed_fake_roof_areas = std::vector(storage.support.supportLayers.size(),Polygons()); support_free_areas = std::vector(storage.support.supportLayers.size(), Polygons()); @@ -240,7 +241,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector>& move_bounds, SliceDataStorage& storage) { TreeSupportTipGenerator tip_gen(storage, mesh, volumes_); - tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, support_free_areas); + tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, placed_fake_roof_areas , support_free_areas); } void TreeSupport::mergeHelper @@ -1470,7 +1471,7 @@ bool TreeSupport::setToModelContact(std::vector>& else // can not add graceful => just place it here and hope for the best { Point best = first_elem->next_position; - Polygons valid_place_area = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); + Polygons valid_place_area = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(*first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); if (!valid_place_area.inside(best, true)) { @@ -1481,13 +1482,13 @@ bool TreeSupport::setToModelContact(std::vector>& else { bool found_partial_placement; - for (coord_t radius_offset : { -config.getCollisionRadius(first_elem), -config.getCollisionRadius(first_elem) / 2, coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. + for (coord_t radius_offset : { -config.getCollisionRadius(*first_elem), -config.getCollisionRadius(*first_elem) / 2, coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. { valid_place_area = first_elem->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx).offset(radius_offset)); if (!valid_place_area.empty()) { PolygonUtils::moveInside(valid_place_area, best); - spdlog::warn("Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", layer_idx, radius_offset, config.getCollisionRadius(first_elem)); + spdlog::warn("Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", layer_idx, radius_offset, config.getCollisionRadius(*first_elem)); found_partial_placement = true; break; } @@ -1635,7 +1636,7 @@ void TreeSupport::generateBranchAreas(std::vectorparents) { Point movement = (parent->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(config.getRadius(parent), config.support_line_width)); + movement_directions.emplace_back(movement, std::max(config.getRadius(*parent), config.support_line_width)); parent_uses_min |= parent->use_min_xy_dist; } @@ -1916,6 +1917,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + placed_fake_roof_areas[layer_idx] = placed_fake_roof_areas[layer_idx].unionPolygons(); //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (!storage.support.supportLayers[layer_idx].support_roof.empty()) @@ -1946,14 +1948,45 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora const auto t_union = std::chrono::high_resolution_clock::now(); + + if(config.support_skin_layers) + { + cura::parallel_for ( 0, support_layer_storage.size(), [&](const LayerIndex layer_idx) { - Polygons may_need_skin_area_topmost = storage.support.supportLayers.size() > layer_idx + 1 ? storage.support.supportLayers[layer_idx + 1].support_roof : Polygons(); - may_need_skin_area_topmost = may_need_skin_area_topmost.difference(storage.support.supportLayers[layer_idx].support_roof); + + if(support_layer_storage[layer_idx].empty()) + { + return; + } + + const coord_t roof_stable_range_after_contact = config.support_roof_line_width * config.support_roof_wall_count; + Polygons support_shell_capable_of_supporting_roof = support_layer_storage[layer_idx] + .getOutsidePolygons() + .tubeShape(config.support_line_width * config.support_wall_count + roof_stable_range_after_contact, roof_stable_range_after_contact, ClipperLib::JoinType::jtRound) + .unionPolygons() + .offset(-config.support_line_width/4).offset(config.support_line_width/4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. + Polygons needs_supporting; + if(storage.support.supportLayers.size() > layer_idx + 1) + { + needs_supporting.add(storage.support.supportLayers[layer_idx + 1].support_roof.difference(support_shell_capable_of_supporting_roof)); + needs_supporting.add(placed_fake_roof_areas[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); + needs_supporting.add(additional_required_support_area[layer_idx + 1]); // E.g. cradle + + needs_supporting = needs_supporting.unionPolygons(); + } + + Polygons already_supports; + already_supports.add(storage.support.supportLayers[layer_idx].support_roof); // roof + already_supports.add(additional_required_support_area[layer_idx]); // E.g. cradle + already_supports.add(placed_fake_roof_areas[layer_idx]); + already_supports = already_supports.unionPolygons(); + + Polygons may_need_skin_area_topmost = needs_supporting.difference(already_supports); for (std::pair data_pair : layer_tree_polygons[layer_idx]) { @@ -1968,47 +2001,46 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } bool element_viable_for_skin = has_parent_roof; - element_viable_for_skin |= data_pair.first->parents.empty() && (data_pair.first->supports_roof || (config.getRadius(data_pair.first) > 4 * config.support_line_width * config.support_wall_count)); + element_viable_for_skin |= data_pair.first->parents.empty() && (config.getRadius(*data_pair.first) >= config.support_tree_skin_for_large_tips_radius_threshold); - if (config.support_skin_layers > 0 && element_viable_for_skin) + if (element_viable_for_skin) { may_need_skin_area_topmost.add(data_pair.second); } } - may_need_skin_area_topmost = may_need_skin_area_topmost.unionPolygons(); Polygons may_need_skin_area = may_need_skin_area_topmost; for (LayerIndex support_skin_ctr = 0; support_skin_ctr < std::min(LayerIndex(config.support_skin_layers), layer_idx); support_skin_ctr++) { + Polygons next_skin; - Polygons layer; + Polygons support_on_layer; Polygons remaining_regular_areas; { std::lock_guard critical_section_cradle(critical_support_layer_storage); - layer = support_layer_storage[layer_idx - support_skin_ctr]; + support_on_layer = support_layer_storage[layer_idx - support_skin_ctr]; } if (support_skin_ctr > 0) { - may_need_skin_area_topmost = may_need_skin_area_topmost.intersection(layer); + may_need_skin_area_topmost = may_need_skin_area_topmost.intersection(support_on_layer); } - - for (Polygons part : layer.splitIntoParts()) + for (Polygons part : support_on_layer.splitIntoParts()) { Polygons part_outline = part.getOutsidePolygons(); - if (! part_outline.offset(-(config.support_line_width * config.support_wall_count)).intersection(may_need_skin_area).empty()) + if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area,AABB(part_outline)).empty()) { // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. - Polygons lines = TreeSupportUtils::generateSupportInfillLines(part_outline, config, false, layer_idx - support_skin_ctr, config.support_skin_line_distance, nullptr, false, EFillMethod::LINES, true); + Polygons scan_lines = TreeSupportUtils::generateSupportInfillLines(part_outline, config, false, layer_idx - support_skin_ctr, config.support_skin_line_distance, nullptr, false, EFillMethod::LINES, true); Polygons intersecting_lines; - for (auto line : lines) + for (auto line : scan_lines) { if (PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) && PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back())) { @@ -2017,12 +2049,27 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } Polygons partial_skin_area = intersecting_lines.offsetPolyLine(config.support_skin_line_distance).unionPolygons().intersection(part_outline); + + // If a some scan lines had contact with two parts of part_outline, but the second part is outside of may_need_skin_area it could cause a separate skin area, that then cuts a branch in half that could have been completely normal. + // This area that does not need to be skin will be filtered out here. + { + Polygons filtered_partial_skin_area; + for(auto p_skin:partial_skin_area.splitIntoParts()) + { + if(!PolygonUtils::clipPolygonWithAABB(may_need_skin_area,AABB(p_skin)).intersection(p_skin).empty()) + { + filtered_partial_skin_area.add(p_skin); + } + } + partial_skin_area = filtered_partial_skin_area; + } + double part_area = part.area(); part = part.difference(partial_skin_area); Polygons remaining_part; for (auto sub_part : part.splitIntoParts()) { - if (sub_part.area() < part_area / 5) + if (sub_part.area() < part_area / 5 && sub_part.area() * 2 < M_PI * pow(config.branch_radius,2)) // Prevent small slivers of a branch to generate as skin. The heuristic to detect if a part is too small or thin could maybe be improved. { partial_skin_area = partial_skin_area.unionPolygons(sub_part); } @@ -2041,12 +2088,10 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora remaining_regular_areas.add(part); } may_need_skin_area = next_skin.unionPolygons(); - std::lock_guard critical_section_cradle(critical_support_layer_storage); - // This intersection is to prevent a race condition where areas of another layer are stored after the changes here are stored. - support_layer_storage[layer_idx - support_skin_ctr] = support_layer_storage[layer_idx - support_skin_ctr].intersection(remaining_regular_areas); } } ); + } cura::parallel_for ( @@ -2055,7 +2100,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora [&](const LayerIndex layer_idx) { support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); } ); } diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index b25f1dd08b..4043ed33f4 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1235,8 +1235,8 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea(std::vectorarea = new Polygons(area); + elem->area = new Polygons(area); for (Point p : additional_ovalization_targets) { elem->additional_ovalization_targets.emplace_back(p); @@ -1400,7 +1400,7 @@ void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& support_free_areas) +void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, std::vector& placed_support_lines_support_areas, std::vector& placed_fake_roof_areas, std::vector& support_free_areas) { std::vector> new_tips(move_bounds.size()); @@ -1690,7 +1690,8 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice { storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, 0, support_roof_line_distance); } - placed_support_lines_support_areas[layer_idx].add( + placed_fake_roof_areas[layer_idx].add(support_roof_drawn[layer_idx]); + placed_support_lines_support_areas[layer_idx].add( // todo Only save the area and add to storage at the end to enable correct handling of Support Interface Priority of fake roofs. TreeSupportUtils::generateSupportInfillLines(support_roof_drawn[layer_idx], config, false, layer_idx, support_roof_line_distance, cross_fill_provider, false).offsetPolyLine(config.support_line_width / 2)); } else diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index f3f3626b68..9a9f4ef110 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -1507,9 +1507,9 @@ void Polygons::sortByNesting_processPolyTreeNode(ClipperLib::PolyNode* node, con } } -Polygons Polygons::tubeShape(const coord_t inner_offset, const coord_t outer_offset) const +Polygons Polygons::tubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt) const { - return this->offset(outer_offset).difference(this->offset(-inner_offset)); + return this->offset(outer_offset, jt).difference(this->offset(-inner_offset, jt)); } unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx) const From 7e0191e44155a96116356f8333f34c9151eaf17e Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:57:56 +0200 Subject: [PATCH 008/101] Fix Support Floor being activated to sometimes cause slicing to freeze. --- src/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 5ea32f5eb8..0cf583d7f5 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2355,7 +2355,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor } // Subtract support floors from the support area and add them to the support floor instead. - if (config.support_bottom_layers > 0 && ! (support_layer_storage[layer_idx].empty() && ! support_skin_storage[layer_idx].empty() )) + if (config.support_bottom_layers > 0 && ! (support_layer_storage[layer_idx].empty() || support_skin_storage[layer_idx].empty())) { Polygons floor_layer = storage.support.supportLayers[layer_idx].support_bottom; Polygons layer_outset = support_layer_storage[layer_idx].unionPolygons(support_skin_storage[layer_idx]).offset(config.support_bottom_offset).difference(volumes_.getCollision(0, layer_idx, false)); From 810d6ea373f844f47b4b4990088899f250ea242a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:15:06 +0200 Subject: [PATCH 009/101] Fix Minimum support area not working always when X/Y overrides Z or if it is larger than the (hardcoded) minimum fake roof area when roof is disabled --- include/TreeSupportTipGenerator.h | 9 ++++----- src/TreeSupportTipGenerator.cpp | 7 +++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 95e48d1a89..bc9d26922a 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -222,16 +222,15 @@ class TreeSupportTipGenerator */ TreeSupportSettings config; - /*! - * \brief Minimum area an overhang has to have to become a roof. + * \brief Minimum area an overhang has to have to be supported. */ - const double minimum_roof_area; + const double minimum_support_area; /*! - * \brief Minimum area an overhang has to have to be supported. + * \brief Minimum area an overhang has to have to become a roof. */ - const double minimum_support_area; + const double minimum_roof_area; /*! * \brief Amount of layers of roof. Zero if roof is disabled diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 4043ed33f4..bd40ea4406 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -27,8 +27,8 @@ namespace cura TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage, const SliceMeshStorage& mesh, TreeModelVolumes& volumes_s): config(mesh.settings), use_fake_roof(!mesh.settings.get("support_roof_enable")), - minimum_roof_area(!use_fake_roof? mesh.settings.get("minimum_roof_area"): SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA), minimum_support_area(mesh.settings.get("minimum_support_area")), + minimum_roof_area(!use_fake_roof? mesh.settings.get("minimum_roof_area"): std::max(double(SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA),minimum_support_area)), support_roof_layers(mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config.layer_height) : use_fake_roof ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0), connect_length((config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)), support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length), @@ -1523,7 +1523,10 @@ void TreeSupportTipGenerator::generateTips(SliceDataStorage& storage,const Slice for (Polygons& remaining_overhang_part:remaining_overhang.splitIntoParts(false)) { - + if(remaining_overhang_part.area() <= MM2_2INT(minimum_support_area)) + { + continue; + } std::vector overhang_lines; Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), config.min_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. From 2538d86ba6aaf4729ee560c954bba04ca248e98f Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:07:10 +0200 Subject: [PATCH 010/101] Fix branches being wrongly regarded as an error (and removed because of that) sometimes when the influence area becomes very small, caused by a very small hole in the avoidance. --- src/TreeSupport.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 0cf583d7f5..e7416a2718 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -694,6 +694,17 @@ std::optional TreeSupport::increaseSingleArea current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it. spdlog::debug("Corrected taint leading to a wrong to model value on layer {} targeting {} with radius {}", layer_idx - 1, current_elem.target_height, radius); } + + // Sometimes the avoidance can contain holes that are smaller than 1, so in that case increase the area slightly, + // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), + // but everything compensates for small rounding errors already, so it will be fine. + // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. + if(! settings.no_error && ! to_bp_data.empty() && to_bp_data.area()<1) + { + to_bp_data = to_bp_data.unionPolygons(to_bp_data.offsetPolyLine(1)); + spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); + } + } if (config.support_rests_on_model) { @@ -714,6 +725,17 @@ std::optional TreeSupport::increaseSingleArea to_model_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, AvoidanceType::COLLISION, true, settings.use_min_distance))); } } + + // Sometimes the avoidance can contain holes that are smaller than 1, so in that case increase the area slightly, + // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), + // but everything compensates for small rounding errors already, so it will be fine. + // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. + if(! settings.no_error && ! to_model_data.empty() && to_model_data.area()<1) + { + to_model_data = to_model_data.unionPolygons(to_model_data.offsetPolyLine(1)); + spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); + } + } check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; From f3ff9f496d1631a2db3227b468c29424406bb9cd Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 18 Nov 2023 22:41:16 +0100 Subject: [PATCH 011/101] Fix issues cause by merge and fix issue caused by previous refactor. --- include/SupportInfillPart.h | 2 +- include/sliceDataStorage.h | 4 +++- src/SupportInfillPart.cpp | 2 +- src/TreeSupport.cpp | 6 ++---- src/TreeSupportTipGenerator.cpp | 2 +- src/sliceDataStorage.cpp | 6 ++++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 39ee8402fc..45026daa97 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -38,7 +38,7 @@ class SupportInfillPart bool use_fractional_config; //!< Request to use the configuration used to fill a partial layer height here, instead of the normal full layer height configuration. EFillMethod custom_line_pattern; - SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config,int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); + SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); const Polygons& getInfillArea() const; }; diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 6db1cd605a..5970c40a41 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -243,7 +243,9 @@ class SupportLayer const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above = 0, - const bool unionAll = false); + const bool unionAll = false, + const coord_t custom_line_distance = 0, + EFillMethod custom_pattern = EFillMethod::NONE); }; class SupportStorage diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 2ada682589..16b4eee01a 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -8,7 +8,7 @@ using namespace cura; -SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance) +SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern) : outline(outline) , outline_boundary_box(outline) , support_line_width(support_line_width) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index f81ee644e6..5419345059 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2486,10 +2486,8 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor storage.support.supportLayers[layer_idx] .fillInfillParts(layer_idx, support_layer_storage, config.support_line_width, config.support_wall_count, config.maximum_move_distance, convert_every_part); - for (PolygonsPart part : support_skin_storage[layer_idx].splitIntoParts(true)) // Convert every part into a PolygonsPart for the support. - { - storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(part, config.support_line_width, config.support_wall_count, config.support_skin_line_distance, EFillMethod::LINES); - } + storage.support.supportLayers[layer_idx] + .fillInfillParts(layer_idx, support_skin_storage, config.support_line_width, config.support_wall_count, config.maximum_move_distance, convert_every_part, config.support_skin_line_distance, EFillMethod::LINES); { std::lock_guard critical_section_progress(critical_sections); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 2b9d0c9d9b..0072a9a2b7 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1821,7 +1821,7 @@ void TreeSupportTipGenerator::generateTips( if (use_fake_roof) { storage.support.supportLayers[layer_idx] - .fillInfillParts(layer_idx, support_roof_drawn, config.support_line_width, support_roof_line_distance, config.maximum_move_distance); + .fillInfillParts(layer_idx, support_roof_drawn, config.support_line_width, 0, config.maximum_move_distance, false, support_roof_line_distance); placed_fake_roof_areas[layer_idx].add(support_roof_drawn[layer_idx]); placed_support_lines_support_areas[layer_idx].add( // todo Only save the area and add to storage at the end to enable correct handling of Support Interface Priority of fake roofs. TreeSupportUtils::generateSupportInfillLines(support_roof_drawn[layer_idx], config, false, layer_idx, support_roof_line_distance, cross_fill_provider, false).offsetPolyLine(config.support_line_width / 2)); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 0dddecb5b7..1f184620ea 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -721,7 +721,9 @@ void SupportLayer::fillInfillParts( const coord_t support_line_width, const coord_t wall_line_count, const coord_t grow_layer_above /*has default 0*/, - const bool unionAll /*has default false*/) + const bool unionAll /*has default false*/, + const coord_t custom_line_distance /*has default false*/, + const EFillMethod custom_pattern /*has default None*/) { const Polygons& support_this_layer = support_fill_per_layer[layer_nr]; const Polygons& support_layer_above @@ -732,7 +734,7 @@ void SupportLayer::fillInfillParts( { for (const PolygonsPart& island_outline : support_areas.splitIntoParts(unionAll)) { - support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count); + support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count, custom_line_distance, custom_pattern); } use_fractional_config = false; } From e0e19abb86624a0e41f080eb036c1ff013488cba Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 19 Nov 2023 23:06:21 +0100 Subject: [PATCH 012/101] Change the way settings are retrieved to enable using a fake plugin to supply the UI with the settings --- include/TreeSupportSettings.h | 28 ++++++++++++++++++++++++---- src/TreeModelVolumes.cpp | 4 ++-- src/TreeSupportTipGenerator.cpp | 24 +++++++++++++----------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 4a4db4b2d8..5b7e114f67 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -1,5 +1,6 @@ //CuraEngine is released under the terms of the AGPLv3 or higher. + #ifndef TREESUPPORTSETTINGS_H #define TREESUPPORTSETTINGS_H @@ -15,6 +16,26 @@ namespace cura { +template +static A retrieveSetting(const Settings& settings, const std::string& key) +{ + if(settings.has(key)) + { + return settings.get(key); + } + else + { + for(std::string setting_key:settings.getKeys()) + { + if(setting_key.find(key) != std::string::npos) + { + return settings.get(setting_key); + } + } + return settings.get(key); // this will cause a crash, but that's the expected behaviour in this case anyway + } +} + /*! * \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter. */ @@ -69,13 +90,12 @@ struct TreeSupportSettings min_feature_size(mesh_group_settings.get("min_feature_size")), min_wall_line_width(settings.get("min_wall_line_width")), fill_outline_gaps(settings.get("fill_outline_gaps")), - support_skin_layers(settings.get("support_tree_support_skin_height")/layer_height), - support_skin_line_distance(settings.get("support_tree_support_skin_line_distance")), - support_tree_skin_for_large_tips_radius_threshold(settings.get("support_tree_skin_for_large_tips_threshold") / 2), + support_skin_layers(retrieveSetting(mesh_group_settings,"support_tree_support_skin_height")/layer_height), + support_skin_line_distance(retrieveSetting(mesh_group_settings,"support_tree_support_skin_line_distance")), + support_tree_skin_for_large_tips_radius_threshold(retrieveSetting(mesh_group_settings,"support_tree_skin_for_large_tips_threshold") / 2), simplifier(Simplify(mesh_group_settings)) { layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); - // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 721aab6821..d179c6f201 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -78,8 +78,8 @@ TreeModelVolumes::TreeModelVolumes( min_maximum_deviation = std::min(min_maximum_deviation, data_pair.first.get("meshfix_maximum_deviation")); min_maximum_resolution = std::min(min_maximum_resolution, data_pair.first.get("meshfix_maximum_resolution")); min_maximum_area_deviation = std::min(min_maximum_area_deviation, data_pair.first.get("meshfix_maximum_extrusion_area_deviation")); - max_cradle_layers = std::max(coord_t(max_cradle_layers), data_pair.first.get("support_tree_cradle_height") / config.layer_height); - max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * data_pair.first.get("support_tree_cradle_base_tip_percentage") / 100.0)); + max_cradle_layers = std::max(coord_t(max_cradle_layers), retrieveSetting(data_pair.first,"support_tree_cradle_height") / config.layer_height); + max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * retrieveSetting(data_pair.first,"support_tree_cradle_base_tip_percentage") / 100.0)); } diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 0072a9a2b7..e57450d909 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -62,17 +62,19 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , cradle_overhang(mesh.overhang_areas.size(),Polygons()) , volumes_(volumes_s) , force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) - , cradle_layers(mesh.settings.get("support_tree_cradle_height") / config.layer_height) - , minimum_cradle_layers(mesh.settings.get("support_tree_cradle_min_height") / config.layer_height) - , cradle_lines(mesh.settings.get("support_tree_cradle_line_count")) - , cradle_length(mesh.settings.get("support_tree_cradle_length")) - , cradle_line_width(mesh.settings.get("support_tree_cradle_line_width")) - , cradle_lines_roof(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") != "none") - , cradle_base_roof(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") == "cradle_and_base" || mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base") - , large_cradle_base(!use_fake_roof && mesh.settings.get("support_tree_roof_cradle") == "large_cradle_and_base") - , cradle_area_threshold(1000 * 1000 * mesh.settings.get("support_tree_maximum_pointy_area")) - , cradle_tip_dtt(config.tip_layers * mesh.settings.get("support_tree_cradle_base_tip_percentage") / 100.0) - , large_cradle_line_tips(mesh.settings.get("support_tree_large_cradle_line_tips")) + , cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config.layer_height) + , minimum_cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config.layer_height) + , cradle_lines(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) + , cradle_length(retrieveSetting(mesh.settings, "support_tree_cradle_length")) + , cradle_line_width(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) + , cradle_lines_roof(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") + , cradle_base_roof( + ! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" + || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") + , large_cradle_base(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") + , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) + , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) + , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) { const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); From 1a7f9c3c280e03b8bd4ca82a68c7b647222afc68 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 19 Nov 2023 23:07:55 +0100 Subject: [PATCH 013/101] Add first version of support_tree_cradle_xy_distance setting. --- include/TreeSupportTipGenerator.h | 4 ++++ src/TreeSupportTipGenerator.cpp | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 65d60e2bfe..78447fe39d 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -406,6 +406,10 @@ class TreeSupportTipGenerator */ bool large_cradle_line_tips; + /*! + * \brief Distance the cradle lines should be from the model. + */ + coord_t cradle_xy_distance; std::mutex critical_cradle; std::mutex critical_move_bounds; diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index e57450d909..db599aa978 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -75,6 +75,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) + , cradle_xy_distance(retrieveSetting(mesh.settings, "support_tree_cradle_xy_distance")) { const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -755,6 +756,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: { coord_t max_distance2 = 0; Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + cradle_xy_distance - config.support_line_width / 2); for (auto line : model_shadow) { @@ -776,7 +778,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: // Subtract the model shadow up until this layer from the lines. if (idx > 0) { - lines_to_center = model_shadow.offset(config.xy_min_distance).unionPolygons().differencePolyLines(lines_to_center, false); + lines_to_center = model_shadow.offset(cradle_xy_distance).unionPolygons().differencePolyLines(lines_to_center, false); } // Store valid distances from the center in relation to the direction of the line. // Used to detect if a line may be intersecting another model part. @@ -874,6 +876,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: Polygons area_above = cradle.difference(relevant_forbidden); // If a line would have some areas that are too small to be drawn, there can be minor issues. The idea is that the offsets will filter them out. + // todo also ensure that every cradle area can be supported, remove if it can not for (auto part : area_above.offset(-cradle_line_width / 4).splitIntoParts()) { if (part.area() > cradle_line_width * std::max(cradle_length / 4, config.support_roof_line_width)) From c2164247a71f86dcdcac661e4029398e72f4d58d Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 14 Dec 2023 16:16:33 +0100 Subject: [PATCH 014/101] Update Conan and GitHub workflows Implemented significant changes to the Conan file and GitHub workflow files in order to increase stability and maintainability. The Conan file now fetches versions and requirements from a new data source, while unnecessary or redundant code in the workflow files has been removed or simplified. This ensures smoother implementation and easier troubleshooting for future needs. Contributes to CURA-11440 --- .github/workflows/benchmark.yml | 150 +++------------------- .github/workflows/conan-package.yml | 110 ++-------------- .github/workflows/gcodeanalyzer.yml | 122 +++++++++--------- .github/workflows/requirements-runner.txt | 0 .github/workflows/unit-test-post.yml | 13 ++ .github/workflows/unit-test.yml | 128 +++--------------- CMakeLists.txt | 4 +- conandata.yml | 5 + conanfile.py | 39 +++--- 9 files changed, 154 insertions(+), 417 deletions(-) create mode 100644 .github/workflows/requirements-runner.txt create mode 100644 .github/workflows/unit-test-post.yml create mode 100644 conandata.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index e3b80ad8ce..adb24aa2ff 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,4 +1,5 @@ name: Benchmark + on: push: paths: @@ -6,11 +7,9 @@ on: - 'src/**' - 'benchmark/**' - '.github/workflows/benchmark.yml' - - '.github/workflows/requirements-conan-package.txt' branches: - main - tags: - - '[0-9].[0-9].[0-9]*' + pull_request: types: [ opened, reopened, synchronize ] paths: @@ -18,153 +17,42 @@ on: - 'src/**' - 'benchmark/**' - '.github/workflows/benchmark.yml' - - '.github/workflows/requirements-conan-package.txt' branches: - main - 'CURA-*' + - 'PP-*' - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' permissions: contents: write deployments: write env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} + jobs: check_actor: - runs-on: ubuntu-latest - outputs: - proceed: ${{ steps.skip_check.outputs.proceed }} - steps: - - id: skip_check - run: | - if [[ "${{ github.actor }}" == *"[bot]"* ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.pull_request }}" == "" ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - else - echo "proceed=false" >> $GITHUB_OUTPUT - fi - shell: bash + uses: ultimaker/cura-workflows/.github/workflows/check-actor.yml@main + secrets: inherit conan-recipe-version: needs: [ check_actor ] if: ${{ needs.check_actor.outputs.proceed == 'true' }} - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine benchmark: needs: [ conan-recipe-version ] - name: Run C++ benchmark - runs-on: ubuntu-22.04 - steps: - - name: Checkout CuraEngine - uses: actions/checkout@v3 - - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.11.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt - - - name: Cache Benchmark library - uses: actions/cache@v1 - with: - path: ./cache - key: ${{ runner.os }}-googlebenchmark-v1.5.0 - - - name: Install Python requirements and Create default Conan profile - run: | - pip install -r .github/workflows/requirements-conan-package.txt - - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - - name: Install GCC-132 on ubuntu - run: | - sudo apt install g++-13 gcc-13 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - - - name: Create the default Conan profile - run: conan profile new default --detect - - - name: Get Conan configuration - run: | - conan config install https://github.com/Ultimaker/conan-config.git - conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }} - - - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=True -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv - - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c - - - name: Set Environment variables from Conan install (bash) - if: ${{ runner.os != 'Windows' }} - run: | - . ./activate_github_actions_runenv.sh - . ./activate_github_actions_buildenv.sh - working-directory: build/Release/generators - - - name: Build CuraEngine and tests - run: | - cmake --preset release - cmake --build --preset release - - - name: Run benchmark CuraEngine - id: run-test - run: ./benchmarks --benchmark_format=json --benchmark_out=benchmark_result.json - working-directory: build/Release/benchmark - - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - name: C++ Benchmark - output-file-path: build/Release/benchmark/benchmark_result.json - gh-repository: github.com/Ultimaker/CuraEngineBenchmarks - gh-pages-branch: main - benchmark-data-dir-path: dev/bench - tool: 'googlecpp' - github-token: ${{ secrets.CURA_BENCHMARK_PAT }} - auto-push: true - # alert-threshold: '175%' - # summary-always: true - # comment-on-alert: true - max-items-in-chart: 250 + uses: ultimaker/cura-workflows/.github/workflows/benchmark.yml@main + with: + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + conan_extra_args: "-o curaengine:enable_benchmarks=True" + benchmark_cmd: "benchmark/benchmarks --benchmark_format=json --benchmark_out=benchmark_result.json" + name: "C++ Benchmark" + output_file_path: "build/Release/benchmark_result.json" + data_dir: "dev/bench" + tool: "googlecpp" + secrets: inherit diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index 749c67ac9d..76060d3063 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -1,143 +1,59 @@ ---- name: conan-package -# Exports the recipe, sources and binaries for Mac, Windows and Linux and upload these to the server such that these can -# be used downstream. -# -# It should run on pushes against main or CURA-* branches, but it will only create the binaries for main and release branches - on: - workflow_dispatch: - inputs: - # FIXME: Not yet implemented - conan_id: - required: false - type: string - description: 'The full conan package ID, e.g. "curaengine/1.2.3@ultimaker/stable"' - create_latest_alias: - required: true - default: false - type: boolean - description: 'Create latest alias' - create_binaries_windows: - required: true - default: false - type: boolean - description: 'create binaries Windows' - create_binaries_linux: - required: true - default: false - type: boolean - description: 'create binaries Linux' - create_binaries_macos: - required: true - default: false - type: boolean - description: 'create binaries Macos' - push: paths: - 'include/**' - 'src/**' - - 'cmake/**' - - 'tests/**' - 'test_package/**' - 'conanfile.py' - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/conan-package.yml' - - '.github/worflows/requirements-conan-package.txt' branches: - main - 'CURA-*' - - '[1-9].[0-9]*' - - '[1-9].[0-9][0-9]*' + - 'PP-*' + - '[0-9].[0-9]*' + - '[0-9].[0-9][0-9]*' tags: - - '[1-9]+.[0-9]+.[0-9]*' - - '[1-9]+.[0-9]+.[0-9]' + - '[0-9]+.[0-9]+.[0-9]*' + - '[0-9]+.[0-9]+.[0-9]' jobs: conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine conan-package-export: needs: [ conan-recipe-version ] - uses: ultimaker/cura/.github/workflows/conan-recipe-export.yml@main + uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-export.yml@main with: recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} recipe_id_latest: ${{ needs.conan-recipe-version.outputs.recipe_id_latest }} - runs_on: 'ubuntu-22.04' - python_version: '3.11.x' - conan_logging_level: 'info' secrets: inherit conan-package-create-macos: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_macos) }} needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) }} + uses: ultimaker/cura-workflows/.github/workflows/conan-package-create-macos.yml@main with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 3 - runs_on: 'macos-11' - python_version: '3.11.x' - conan_logging_level: 'info' secrets: inherit conan-package-create-windows: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true' )) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_windows) }} needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) }} + uses: ultimaker/cura-workflows/.github/workflows/conan-package-create-windows.yml@main with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 4 - runs_on: 'windows-2022' - python_version: '3.11.x' - conan_config_branch: '' - conan_logging_level: 'info' secrets: inherit conan-package-create-linux: - if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux) }} needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main + if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) }} + uses: ultimaker/cura-workflows/.github/workflows/conan-package-create-linux.yml@main with: - project_name: ${{ needs.conan-recipe-version.outputs.project_name }} recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} - build_id: 2 - runs_on: 'ubuntu-22.04' - python_version: '3.11.x' - conan_logging_level: 'info' - secrets: inherit - - notify-export: - if: ${{ always() }} - needs: [ conan-recipe-version, conan-package-export ] - - uses: ultimaker/cura/.github/workflows/notify.yml@main - with: - success: ${{ contains(join(needs.*.result, ','), 'success') }} - success_title: "New Conan recipe exported in ${{ github.repository }}" - success_body: "Exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - failure_title: "Failed to export Conan Export in ${{ github.repository }}" - failure_body: "Failed to exported ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - secrets: inherit - - notify-create: - if: ${{ always() && ((github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master' || needs.conan-recipe-version.outputs.is_release_branch == 'true')) || (github.event_name == 'workflow_dispatch' && inputs.create_binaries_linux)) }} - needs: [ conan-recipe-version, conan-package-create-macos, conan-package-create-windows, conan-package-create-linux ] - - uses: ultimaker/cura/.github/workflows/notify.yml@main - with: - success: ${{ contains(join(needs.*.result, ','), 'success') }} - success_title: "New binaries created in ${{ github.repository }}" - success_body: "Created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" - failure_title: "Failed to create binaries in ${{ github.repository }}" - failure_body: "Failed to created binaries for ${{ needs.conan-recipe-version.outputs.recipe_id_full }}" secrets: inherit diff --git a/.github/workflows/gcodeanalyzer.yml b/.github/workflows/gcodeanalyzer.yml index 123aa31d1b..b73d5900ad 100644 --- a/.github/workflows/gcodeanalyzer.yml +++ b/.github/workflows/gcodeanalyzer.yml @@ -5,7 +5,6 @@ on: - 'include/**' - 'src/**' - '.github/workflows/gcodeanalyzer.yml' - - '.github/workflows/requirements-conan-package.txt' branches: - main pull_request: @@ -14,62 +13,54 @@ on: - 'include/**' - 'src/**' - '.github/workflows/gcodeanalyzer.yml' - - '.github/workflows/requirements-conan-package.txt' branches: - main - 'CURA-*' + - 'PP-*' - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' permissions: contents: write deployments: write env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} + +# TODO: Make cura-workflows/benchmark.yml generic enough to fit the gcodeanalyzer benchmark jobs: check_actor: - runs-on: ubuntu-latest - outputs: - proceed: ${{ steps.skip_check.outputs.proceed }} - steps: - - id: skip_check - run: | - if [[ "${{ github.actor }}" == *"[bot]"* ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.pull_request }}" == "" ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then - echo "proceed=true" >> $GITHUB_OUTPUT - else - echo "proceed=false" >> $GITHUB_OUTPUT - fi - shell: bash + uses: ultimaker/cura-workflows/.github/workflows/check-actor.yml@main + secrets: inherit conan-recipe-version: needs: [ check_actor ] if: ${{ needs.check_actor.outputs.proceed == 'true' }} - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine gcodeanalyzer: needs: [ conan-recipe-version ] name: Run GCodeAnalyzer on the engine - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - name: Checkout CuraEngine - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v4 + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} with: path: 'CuraEngine' + fetch-depth: 1 + ref: ${{ github.head_ref }} + + - name: Checkout repo PR + uses: actions/checkout@v4 + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + with: + path: 'CuraEngine' + fetch-depth: 1 + ref: ${{ github.base_ref }} - name: Checkout GCodeAnalyzer uses: actions/checkout@v3 @@ -77,6 +68,7 @@ jobs: repository: 'Ultimaker/GCodeAnalyzer' ref: 'main' path: 'GCodeAnalyzer' + fetch-depth: 1 token: ${{ secrets.CURA_BENCHMARK_PAT }} - name: Checkout Test Models @@ -85,6 +77,7 @@ jobs: repository: 'Ultimaker/NightlyTestModels' ref: 'main' path: 'NightlyTestModels' + fetch-depth: 1 token: ${{ secrets.GITHUB_TOKEN }} - name: Determine the corresponding Cura branch @@ -104,44 +97,43 @@ jobs: repository: 'Ultimaker/Cura' ref: ${{ steps.curabranch.outputs.branch}} path: 'Cura' + fetch-depth: 1 sparse-checkout: | resources/definitions resources/extruders + - name: Sync pip requirements + run: curl -O https://raw.githubusercontent.com/Ultimaker/cura-workflows/main/.github/workflows/requirements-runner.txt + working-directory: CuraEngine/.github/workflows + - name: Setup Python and pip uses: actions/setup-python@v4 with: - python-version: '3.10.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: CuraEngine/.github/workflows/requirements-conan-package.txt + python-version: 3.11.x + cache: pip + cache-dependency-path: CuraEngine/.github/workflows/requirements-runner.txt - name: Install Python requirements and Create default Conan profile run: | - pip install -r CuraEngine/.github/workflows/requirements-conan-package.txt + pip install -r CuraEngine/.github/workflows/requirements-runner.txt pip install wheel numpy pandas python-dateutil pytz six pip install git+https://github.com/ultimaker/libcharon@CURA-9495_analyzer_requisites#egg=charon pip install pytest pytest-benchmark - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} + - name: Install Linux system requirements for building run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + mkdir runner_scripts + cd runner_scripts + curl -O https://raw.githubusercontent.com/Ultimaker/cura-workflows/main/runner_scripts/ubuntu_setup.sh + chmod +x ubuntu_setup.sh + sudo ./ubuntu_setup.sh - - name: Install GCC-132 on ubuntu + - name: Setup pipeline caches run: | - sudo apt install g++-13 gcc-13 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + mkdir -p /home/runner/.conan/downloads + mkdir -p /home/runner/.conan/data - - name: Create the default Conan profile + - name: Create default Conan profile run: conan profile new default --detect - name: Get Conan configuration @@ -149,21 +141,32 @@ jobs: conan config install https://github.com/Ultimaker/conan-config.git conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - name: Use Conan download cache (Bash) - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + - name: Cache Conan packages + uses: actions/cache@v3 + with: + path: /home/runner/.conan/data + key: ${{ runner.os }}-conan-data-${{ github.run_id }} + restore-keys: | + ${{ runner.os }}-conan-data- + + - name: Cache Conan downloads + uses: actions/cache@v3 + with: + path: /home/runner/.conan/downloads + key: ${{ runner.os }}-conan-downloads-${{ github.run_id }} + restore-keys: | + ${{ runner.os }}-conan-downloads- - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -o enable_benchmarks=False -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv -c tools.build:skip_test=True + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv -c tools.build:skip_test=True working-directory: CuraEngine - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c - - name: Set Environment variables from Conan install (bash) if: ${{ runner.os != 'Windows' }} run: | . ./activate_github_actions_runenv.sh . ./activate_github_actions_buildenv.sh + echo "CURA_ENGINE_SEARCH_PATH=$GITHUB_WORKSPACE/Cura/resources/definitions:$GITHUB_WORKSPACE/Cura/resources/extruders" >> $GITHUB_ENV working-directory: CuraEngine/build/Release/generators - name: Build CuraEngine and tests @@ -174,13 +177,13 @@ jobs: - name: Collect STL-files, run CuraEngine, output GCode-files run: | - export CURA_ENGINE_SEARCH_PATH=../Cura/resources/definitions:../Cura/resources/extruders for file in `ls ../NightlyTestModels/*.stl`; do ./build/Release/CuraEngine slice --force-read-parent --force-read-nondefault -v -p -j ../Cura/resources/definitions/ultimaker_s3.def.json -l $file -o ../`basename $file .stl`.gcode done working-directory: CuraEngine + # TODO: Move this to GCodeAnalyzer - name: Run GCodeAnalyzer on generated GCode files id: gcode_out run: | @@ -306,7 +309,4 @@ jobs: tool: customBiggerIsBetter github-token: ${{ secrets.CURA_BENCHMARK_PAT }} auto-push: true - # alert-threshold: '110%' - # summary-always: true - # comment-on-alert: true max-items-in-chart: 250 diff --git a/.github/workflows/requirements-runner.txt b/.github/workflows/requirements-runner.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.github/workflows/unit-test-post.yml b/.github/workflows/unit-test-post.yml new file mode 100644 index 0000000000..d916418dd4 --- /dev/null +++ b/.github/workflows/unit-test-post.yml @@ -0,0 +1,13 @@ +name: unit-test-post + +on: + workflow_run: + workflows: [ unit-test ] + types: [ completed ] + +jobs: + publish-test-results: + uses: ultimaker/cura-workflows/.github/workflows/unit-test-post.yml@main + with: + event: ${{ github.event.workflow_run.event }} + conclusion: ${{ github.event.workflow_run.conclusion }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 04e325df79..8879bb28a1 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -5,141 +5,53 @@ on: paths: - 'include/**' - 'src/**' - - 'cmake/**' - 'tests/**' - - 'test_package/**' - 'conanfile.py' + - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - - '.github/workflows/requirements-conan-package.txt' + - '.github/workflows/unit-test-post.yml' branches: - main + - 'CURA-*' + - 'PP-*' - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' + pull_request: types: [ opened, reopened, synchronize ] paths: - 'include/**' - 'src/**' - - 'cmake/**' - 'tests/**' - - 'test_package/**' - 'conanfile.py' + - 'conandata.yml' - 'CMakeLists.txt' - '.github/workflows/unit-test.yml' - - '.github/workflows/requirements-conan-package.txt' + - '.github/workflows/unit-test-post.yml' branches: - main - - 'CURA-*' - '[0-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' + +permissions: + contents: read env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} jobs: conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-version.yml@main with: project_name: curaengine testing: - runs-on: ubuntu-22.04 + uses: ultimaker/cura-workflows/.github/workflows/unit-test.yml@main needs: [ conan-recipe-version ] + with: + recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }} + conan_extra_args: '-c tools.build:skip_test=False' + unit_test_cmd: 'ctest --output-junit engine_test.xml' + unit_test_dir: 'build/Release' + build: true - steps: - - name: Checkout CuraEngine - uses: actions/checkout@v3 - - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.10.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt - - - name: Install Python requirements and Create default Conan profile - run: | - pip install -r .github/workflows/requirements-conan-package.txt - - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - - name: Install GCC-132 on ubuntu - run: | - sudo apt install g++-13 gcc-13 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - - - name: Create the default Conan profile - run: conan profile new default --detect - - - name: Get Conan configuration - run: | - conan config install https://github.com/Ultimaker/conan-config.git - conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }} - - - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} -s build_type=Release --build=missing --update -g GitHubActionsRunEnv -g GitHubActionsBuildEnv - - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c - - - name: Set Environment variables from Conan install (bash) - if: ${{ runner.os != 'Windows' }} - run: | - . ./activate_github_actions_runenv.sh - . ./activate_github_actions_buildenv.sh - working-directory: build/Release/generators - - - name: Build CuraEngine and tests - run: | - cmake --preset release - cmake --build --preset release - - - name: Run Unit Test CuraEngine - id: run-test - run: ctest --output-junit engine_test.xml - working-directory: build/Release - - - name: Publish Unit Test Results - id: test-results - uses: EnricoMi/publish-unit-test-result-action@v1 - if: ${{ always() }} - with: - files: | - **/*.xml - - - name: Conclusion - run: echo "Conclusion is ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 27fcdf5bd8..f2eec82974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,7 +200,7 @@ find_package(spdlog REQUIRED) find_package(fmt REQUIRED) find_package(range-v3 REQUIRED) find_package(scripta REQUIRED) -find_package(neargye-semver REQUIRED) +find_package(semver REQUIRED) if (ENABLE_TESTING) find_package(GTest REQUIRED) @@ -216,7 +216,7 @@ target_link_libraries(_CuraEngine stb::stb boost::boost scripta::scripta - neargye-semver::neargye-semver + semver::semver curaengine_grpc_definitions::curaengine_grpc_definitions asio-grpc::asio-grpc grpc::grpc diff --git a/conandata.yml b/conandata.yml new file mode 100644 index 0000000000..b4a8b4ea0a --- /dev/null +++ b/conandata.yml @@ -0,0 +1,5 @@ +version: "5.6.1-beta.0" +requirements: + - "arcus/5.3.0" + - "curaengine_grpc_definitions/0.1.0" + - "scripta/0.1.0@ultimaker/testing" \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index b3e464843a..5fc47436f1 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,7 @@ from conan import ConanFile from conan.errors import ConanInvalidConfiguration -from conan.tools.files import copy, mkdir +from conan.tools.files import copy, mkdir, update_conandata from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake, cmake_layout from conan.tools.build import check_min_cppstd from conan.tools.scm import Version @@ -40,7 +40,10 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.6.0-beta.1" + self.version = self.conan_data["version"] + + def export(self): + update_conandata(self, {"version": self.version}) def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) @@ -60,10 +63,11 @@ def config_options(self): def configure(self): self.options["boost"].header_only = True self.options["clipper"].shared = True - self.options["protobuf"].shared = False if self.options.enable_arcus: self.options["arcus"].shared = True + if self.settings.os == "Linux": + self.options["openssl"].shared = True def validate(self): if self.settings.compiler.get_safe("cppstd"): @@ -81,23 +85,23 @@ def build_requirements(self): self.test_requires("benchmark/1.7.0") def requirements(self): - if self.options.enable_arcus: - self.requires("arcus/5.3.0") + for req in self.conan_data["requirements"]: + if "arcus" in req and not self.options.enable_arcus: + continue + self.requires(req) self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") self.requires("stb/20200203") - self.requires("spdlog/1.10.0") - self.requires("fmt/9.0.0") + self.requires("spdlog/1.12.0") + self.requires("fmt/10.1.1") self.requires("range-v3/0.12.0") - self.requires("scripta/0.1.0@ultimaker/testing") self.requires("neargye-semver/0.3.0") self.requires("protobuf/3.21.9") self.requires("zlib/1.2.12") - self.requires("openssl/1.1.1l") + self.requires("openssl/3.2.0") def generate(self): deps = CMakeDeps(self) @@ -124,15 +128,14 @@ def generate(self): if len(dep.cpp_info.bindirs) > 0: copy(self, "*.dll", dep.cpp_info.bindirs[0], self.build_folder) if not self.conf.get("tools.build:skip_test", False, check_type=bool): - test_path = path.join(self.build_folder, "tests") + test_path = path.join(self.build_folder, "tests") if not path.exists(test_path): mkdir(self, test_path) if len(dep.cpp_info.libdirs) > 0: - copy(self, "*.dylib", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) - copy(self, "*.dll", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) + copy(self, "*.dylib", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) + copy(self, "*.dll", dep.cpp_info.libdirs[0], path.join(self.build_folder, "tests")) if len(dep.cpp_info.bindirs) > 0: - copy(self, "*.dll", dep.cpp_info.bindirs[0], path.join(self.build_folder, "tests")) - + copy(self, "*.dll", dep.cpp_info.bindirs[0], path.join(self.build_folder, "tests")) def layout(self): cmake_layout(self) @@ -146,9 +149,9 @@ def build(self): def package(self): ext = ".exe" if self.settings.os == "Windows" else "" - copy(self, f"CuraEngine{ext}", src = self.build_folder, dst = path.join(self.package_folder, "bin")) - copy(self, f"_CuraEngine.*", src = self.build_folder, dst = path.join(self.package_folder, "lib")) - copy(self, "LICENSE*", src = self.source_folder, dst = path.join(self.package_folder, "license")) + copy(self, f"CuraEngine{ext}", src=self.build_folder, dst=path.join(self.package_folder, "bin")) + copy(self, f"_CuraEngine.*", src=self.build_folder, dst=path.join(self.package_folder, "lib")) + copy(self, "LICENSE*", src=self.source_folder, dst=path.join(self.package_folder, "license")) def package_info(self): ext = ".exe" if self.settings.os == "Windows" else "" From b7d73163a6a085484d2dfce153fd36800d3ce16f Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Thu, 14 Dec 2023 16:36:26 +0100 Subject: [PATCH 015/101] Revert update of fmt and spdlog Contributes to CURA-11440 --- conanfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 5fc47436f1..b1f7306742 100644 --- a/conanfile.py +++ b/conanfile.py @@ -95,8 +95,8 @@ def requirements(self): self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") self.requires("stb/20200203") - self.requires("spdlog/1.12.0") - self.requires("fmt/10.1.1") + self.requires("spdlog/1.10.0") + self.requires("fmt/9.0.0") self.requires("range-v3/0.12.0") self.requires("neargye-semver/0.3.0") self.requires("protobuf/3.21.9") From dffcecf1fefe8cdb78ad76b14600bb8143478d67 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 17 Dec 2023 18:55:17 +0100 Subject: [PATCH 016/101] Fix fractional roof not working correctly with cradles --- src/TreeSupportTipGenerator.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index db599aa978..c49732f2bf 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1839,18 +1839,6 @@ void TreeSupportTipGenerator::generateTips( } }); - cura::parallel_for( - 1, - mesh.overhang_areas.size() - z_distance_delta, - [&](const LayerIndex layer_idx) - { - if (layer_idx > 0) - { - storage.support.supportLayers[layer_idx].support_fractional_roof.add( - storage.support.supportLayers[layer_idx].support_roof.difference(storage.support.supportLayers[layer_idx + 1].support_roof)); - } - }); - removeUselessAddedPoints(new_tips, storage, additional_support_areas); if(support_roof_layers && !use_fake_roof && cradle_lines_roof) @@ -1868,6 +1856,17 @@ void TreeSupportTipGenerator::generateTips( } } + cura::parallel_for( + 1, + mesh.overhang_areas.size() - z_distance_delta, + [&](const LayerIndex layer_idx) + { + if (layer_idx > 0) + { + storage.support.supportLayers[layer_idx].support_fractional_roof.add( + storage.support.supportLayers[layer_idx].support_roof.difference(storage.support.supportLayers[layer_idx + 1].support_roof)); + } + }); for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { From 8cefce5d50d95c102d1669fa5be05f28fd50c79c Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:05:09 +0100 Subject: [PATCH 017/101] Fix cradle xy distance --- src/TreeSupportTipGenerator.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index c49732f2bf..811fc81eff 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -75,7 +75,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_xy_distance(retrieveSetting(mesh.settings, "support_tree_cradle_xy_distance")) + , cradle_xy_distance(retrieveSetting(mesh.settings, "support_tree_cradle_xy_distance")) { const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -845,7 +845,14 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: shortened_lines_to_center[line_idx].clear(); } } - Polygons cradle = shortened_lines_to_center.offsetPolyLine(cradle_line_width / 2).unionPolygons().offset(FUDGE_LENGTH).unionPolygons().offset(-FUDGE_LENGTH).difference(relevant_forbidden); + + Polygons cradle = shortened_lines_to_center.offsetPolyLine(cradle_line_width / 2,ClipperLib::jtRound).unionPolygons().offset(FUDGE_LENGTH).unionPolygons().offset(-FUDGE_LENGTH).difference(relevant_forbidden); + + if(idx > 0 && relevant_forbidden.inside(center)) //todo better check + { + //Ensures that the cradle-lines don't touch to prevent the center from merging together and engulfing the model at low cradle xy distances. + cradle = cradle.difference(PolygonUtils::makeCircle(center,config.support_line_width+FUDGE_LENGTH/2).offset(0)); + } cradle_areas_calc[idx] = cradle; } } @@ -892,6 +899,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) { Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + cradle_xy_distance); cradle_areas_calc[idx] = cradle_areas_calc[idx].difference(relevant_forbidden); } From 36db8a2546cf6f8e3cf04cf42734cfc73628aaae Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:45:19 +0100 Subject: [PATCH 018/101] Fix cradle sometimes not being supported correctly --- src/TreeSupportTipGenerator.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 811fc81eff..d9437ca169 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1546,25 +1546,25 @@ void TreeSupportTipGenerator::generateTips( = relevant_forbidden.offset(EPSILON) .unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. - std::function generateLines = [&](const Polygons& area, bool roof, LayerIndex layer_idx) + enum class OverhangType {REGULAR, ROOF, CRADLE}; + std::function generateLines = [&](const Polygons& area, OverhangType overhang_type, LayerIndex layer_idx) { coord_t upper_line_distance = support_supporting_branch_distance; - coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); + coord_t line_distance = std::max((overhang_type == OverhangType::ROOF) ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); bool use_grid = line_distance == upper_line_distance; return TreeSupportUtils::generateSupportInfillLines( area, config, - roof && ! use_fake_roof, + (overhang_type == OverhangType::ROOF) && ! use_fake_roof, layer_idx, line_distance, cross_fill_provider, - roof && ! use_fake_roof, + ((overhang_type == OverhangType::ROOF) && ! use_fake_roof)|| (overhang_type == OverhangType::CRADLE), use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; - enum class OverhangType {REGULAR, ROOF, CRADLE}; std::vector> overhang_processing; // ^^^ Every overhang has saved if a roof should be generated for it. @@ -1650,7 +1650,7 @@ void TreeSupportTipGenerator::generateTips( } std::vector overhang_lines; - Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), config.min_radius, 1, false); + Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, OverhangType::REGULAR, layer_idx), config.min_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. // Technically this violates branch distance, but not only is this the only reasonable choice, // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. @@ -1721,7 +1721,7 @@ void TreeSupportTipGenerator::generateTips( for (std::pair overhang_pair : overhang_processing) { const bool roof_allowed_for_this_part = overhang_pair.second == OverhangType::ROOF; - const bool supports_cradle = overhang_pair.second == OverhangType::CRADLE; + const bool supports_cradle = overhang_pair.second == OverhangType::CRADLE; Polygons overhang_outset = overhang_pair.first; const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); @@ -1732,7 +1732,7 @@ void TreeSupportTipGenerator::generateTips( // The tip positions are determined here. // todo can cause inconsistent support density if a line exactly aligns with the model Polygons polylines = ensureMaximumDistancePolyline( - generateLines(overhang_outset, roof_allowed_for_this_part, layer_idx + roof_allowed_for_this_part), + generateLines(overhang_outset, overhang_pair.second, layer_idx + roof_allowed_for_this_part), ! roof_allowed_for_this_part ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, From b16c159730af533402683aed8a9c5545d4d599f1 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 25 Dec 2023 01:01:33 +0100 Subject: [PATCH 019/101] More cradle fixes --- src/TreeSupportTipGenerator.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index d9437ca169..dfb08ff4b8 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -69,8 +69,8 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , cradle_line_width(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) , cradle_lines_roof(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") , cradle_base_roof( - ! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" - || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") + ! use_fake_roof && (retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" + || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base")) , large_cradle_base(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) @@ -588,7 +588,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: mesh.overhang_areas.size() - (z_distance_delta + 1), [&](const LayerIndex layer_idx) { - if(mesh.overhang_areas[layer_idx + z_distance_delta].empty()) + if(mesh.overhang_areas[layer_idx + z_distance_delta].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta).empty()) { return; } @@ -609,6 +609,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. bool abort = false; bool contacted_other_pointy = false; + std::vector unsupported_model(accumulated_model.size()); for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) { Polygons relevant_forbidden = @@ -641,7 +642,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: contacted_other_pointy = true; continue; } - + unsupported_model[cradle_up_layer].add(next_pointy_data.area); // Ensure each area is only handles once std::lock_guard critical_section_cradle(critical_dedupe); if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) @@ -678,20 +679,11 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: // But if the cradle stops there will be z distance layer between the end of the cradle and said merge. // To reduce the impact an area is estimated where the cradle should be for these areas. Polygons previous_area = shadow; - for (size_t cradle_up_layer_z_delta = cradle_up_layer; cradle_up_layer_z_delta < std::min(cradle_up_layer + z_distance_delta - 1, accumulated_model.size()); cradle_up_layer_z_delta++) + for (size_t cradle_up_layer_z_distance = cradle_up_layer; + cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta - 1, accumulated_model.size()); + cradle_up_layer_z_distance++) { - Polygons possible_areas = shadow.offset(cradle_up_layer_z_delta * config.maximum_move_distance + config.xy_min_distance).intersection(mesh.layers[layer_idx + cradle_up_layer_z_delta - 1].getOutlines()); - Polygons next_area; - for (auto part : possible_areas.splitIntoParts(true)) - { - if (! previous_area.intersection(part).empty()) - { - next_area.add(part); - } - } - previous_area = next_area; - - accumulated_model[cradle_up_layer_z_delta] = next_area; + accumulated_model[cradle_up_layer_z_distance] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); } } break; From 1ae92f23c8fbe6baabce1e7be79e107febe8b06c Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 25 Dec 2023 01:20:12 +0100 Subject: [PATCH 020/101] Some fixes to support skin --- include/sliceDataStorage.h | 24 ++++++++++++++++++++++++ src/TreeSupport.cpp | 26 +++++++++++++++++++++----- src/sliceDataStorage.cpp | 25 +++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 8f89d6c647..fa5b727419 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -237,6 +237,7 @@ class SupportLayer * \param grow_layer_above (optional, default to 0) In cases where support shrinks per layer up, an appropriate offset may be nescesary. * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. * \param custom_line_distance (optional, default to 0) Distance between lines of the infill pattern. custom_line_distance of 0 means use the default instead. + * \param custom_pattern (optional, default to EFillMethod::NONE) Set if a non default infill pattern should be used */ void fillInfillParts( const LayerIndex layer_nr, @@ -247,6 +248,29 @@ class SupportLayer const bool unionAll = false, const coord_t custom_line_distance = 0, EFillMethod custom_pattern = EFillMethod::NONE); + + + /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height + * support layers. + * + * \param support_fill_new_this_layer Support that should be added. + * \param support_fill_total_this_layer All support that will be on the current layer. + * \param support_fill_total_next_layer All support that will be on the next. + * \param support_line_width Line width of the support extrusions. + * \param wall_line_count Wall-line count around the fill. + * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. + * \param custom_line_distance (optional, default to 0) Distance between lines of the infill pattern. custom_line_distance of 0 means use the default instead. + * \param custom_pattern (optional, default to EFillMethod::NONE) Set if a non default infill pattern should be used + */ + void fillInfillParts( + const Polygons& support_fill_new_this_layer, + const Polygons& support_fill_total_this_layer, + const Polygons& support_fill_total_next_layer, + const coord_t support_line_width, + const coord_t wall_line_count, + const bool unionAll = false, + const coord_t custom_line_distance = 0, + EFillMethod custom_pattern = EFillMethod::NONE); }; class SupportStorage diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 5419345059..a20a18c3ab 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2134,7 +2134,16 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area,AABB(part_outline)).empty()) { // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. - Polygons scan_lines = TreeSupportUtils::generateSupportInfillLines(part_outline, config, false, layer_idx - support_skin_ctr, config.support_skin_line_distance, nullptr, false, EFillMethod::LINES, true); + Polygons scan_lines = TreeSupportUtils::generateSupportInfillLines( + part_outline, + config, + false, + layer_idx - support_skin_ctr, + config.support_skin_line_distance, + nullptr, + false, + EFillMethod::LINES, + true); Polygons intersecting_lines; @@ -2146,7 +2155,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } } - Polygons partial_skin_area = intersecting_lines.offsetPolyLine(config.support_skin_line_distance).unionPolygons().intersection(part_outline); + Polygons partial_skin_area + = intersecting_lines.offsetPolyLine(config.support_skin_line_distance + FUDGE_LENGTH).unionPolygons().intersection(part_outline); // If a some scan lines had contact with two parts of part_outline, but the second part is outside of may_need_skin_area it could cause a separate skin area, that then cuts a branch in half that could have been completely normal. // This area that does not need to be skin will be filtered out here. @@ -2167,7 +2177,9 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora Polygons remaining_part; for (auto sub_part : part.splitIntoParts()) { - if (sub_part.area() < part_area / 5 && sub_part.area() * 2 < M_PI * pow(config.branch_radius,2)) // Prevent small slivers of a branch to generate as skin. The heuristic to detect if a part is too small or thin could maybe be improved. + // Prevent small slivers of a branch to generate as support. The heuristic to detect if a part is too small or thin could maybe be improved. + if ((sub_part.area() < part_area / 5 && sub_part.area() * 2 < M_PI * pow(config.branch_radius,2)) + || sub_part.offset(-config.support_line_width).area() < 1) { partial_skin_area = partial_skin_area.unionPolygons(sub_part); } @@ -2483,11 +2495,15 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor } constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. + const Polygons all_this_layer = support_skin_storage[layer_idx].unionPolygons(support_layer_storage[layer_idx]); + const Polygons all_next_layer = support_layer_storage.size() critical_section_progress(critical_sections); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 1f184620ea..2196b4f139 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -727,8 +727,29 @@ void SupportLayer::fillInfillParts( { const Polygons& support_this_layer = support_fill_per_layer[layer_nr]; const Polygons& support_layer_above - = (layer_nr + 1) >= support_fill_per_layer.size() || layer_nr <= 0 ? Polygons() : support_fill_per_layer[layer_nr + 1].offset(grow_layer_above); - const auto all_support_areas_in_layer = { support_this_layer.difference(support_layer_above), support_this_layer.intersection(support_layer_above) }; + = (layer_nr + 1) >= support_fill_per_layer.size() || layer_nr <= 0 ? Polygons() : support_fill_per_layer[layer_nr + 1].unionPolygons().offset(grow_layer_above); + + fillInfillParts(support_this_layer, support_this_layer, support_layer_above, support_line_width, wall_line_count, unionAll, custom_line_distance, custom_pattern); +} + +void SupportLayer::fillInfillParts( + const Polygons& support_fill_new_this_layer, + const Polygons& support_fill_total_this_layer, + const Polygons& support_fill_total_next_layer, + const coord_t support_line_width, + const coord_t wall_line_count, + const bool unionAll, + const coord_t custom_line_distance, + EFillMethod custom_pattern) +{ + std::vector all_support_areas_in_layer + = { support_fill_total_this_layer.difference(support_fill_total_next_layer), support_fill_new_this_layer.intersection(support_fill_total_next_layer) }; + + if (&support_fill_new_this_layer != &support_fill_total_this_layer) + { + all_support_areas_in_layer[0] = all_support_areas_in_layer[0].intersection(support_fill_new_this_layer); + } + bool use_fractional_config = true; for (auto& support_areas : all_support_areas_in_layer) { From 9e613374414b479e9c51065b95130307bc893d60 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 24 Dec 2023 13:14:48 +0100 Subject: [PATCH 021/101] Fix crashes caused by tree support. --- include/TreeSupportElement.h | 5 ++++- src/TreeSupport.cpp | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 537ce569a0..f983671cf7 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -437,20 +437,23 @@ struct TreeSupportElement } } } + void setToBuildplateForAllParents(bool new_value) { to_buildplate = new_value; + to_model_gracious_ |= new_value; std::vector grandparents {parents}; while (!grandparents.empty()){ std::vector next_parents; for (TreeSupportElement* grandparent:grandparents){ next_parents.insert(next_parents.end(),grandparent->parents.begin(),grandparent->parents.end()); grandparent->to_buildplate = new_value; + grandparent->to_model_gracious |= new_value; // If we set to_buildplate to true, update to_model_gracious } grandparents = next_parents; } } - + inline bool isResultOnLayerSet() const { return result_on_layer != Point(-1, -1); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index a20a18c3ab..d3dfbe98c5 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -1709,7 +1709,7 @@ void TreeSupport::generateBranchAreas( } std::vector linear_inserts(linear_data.size()); - const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps; + const size_t progress_inserts_check_interval = std::max(linear_data.size() / progress_report_steps,size_t(1)); std::mutex critical_sections; cura::parallel_for( @@ -1984,7 +1984,9 @@ void TreeSupport::dropNonGraciousAreas( TreeSupportElement* elem = linear_data[idx].second; bool non_gracious_model_contact = ! elem->to_model_gracious - && ! inverse_tree_order.count(elem); // If an element has no child, it connects to whatever is below as no support further down for it will exist. + && ! inverse_tree_order.count(elem) + && linear_data[idx].first > 0 + && ! elem->to_buildplate; // If an element has no child, it connects to whatever is below as no support further down for it will exist. if (non_gracious_model_contact) { Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem].intersection(volumes_.getAccumulatedPlaceable0(linear_data[idx].first)); @@ -2493,7 +2495,13 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. } + }); + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. const Polygons all_this_layer = support_skin_storage[layer_idx].unionPolygons(support_layer_storage[layer_idx]); const Polygons all_next_layer = support_layer_storage.size() Date: Mon, 1 Jan 2024 15:56:34 +0100 Subject: [PATCH 022/101] Fix compile error caused by previous commit --- include/TreeSupportElement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index f983671cf7..49daca295b 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -441,7 +441,7 @@ struct TreeSupportElement void setToBuildplateForAllParents(bool new_value) { to_buildplate = new_value; - to_model_gracious_ |= new_value; + to_model_gracious |= new_value; std::vector grandparents {parents}; while (!grandparents.empty()){ std::vector next_parents; From d357561db2f523c4f9e617b42252745a8f9c8899 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 6 Jan 2024 18:19:48 +0100 Subject: [PATCH 023/101] Made cradle xy distance a vector to enable more flexibility --- include/TreeSupportTipGenerator.h | 4 ++-- src/TreeSupportTipGenerator.cpp | 18 ++++++++++++++---- src/settings/Settings.cpp | 13 +++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 78447fe39d..7c8b5735dd 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -407,9 +407,9 @@ class TreeSupportTipGenerator bool large_cradle_line_tips; /*! - * \brief Distance the cradle lines should be from the model. + * \brief Distances the cradle lines should be from the model. First value corresponds to cradle line on the same layer as the first model line. */ - coord_t cradle_xy_distance; + std::vector cradle_xy_distance; std::mutex critical_cradle; std::mutex critical_move_bounds; diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index dfb08ff4b8..a0cd475607 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -75,7 +75,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_xy_distance(retrieveSetting(mesh.settings, "support_tree_cradle_xy_distance")) + , cradle_xy_distance(retrieveSetting>(mesh.settings, "support_tree_cradle_xy_distance")) { const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -582,6 +582,13 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: std::vector> dedupe(mesh.overhang_areas.size()); calculateFloatingParts(mesh); + std::vector effective_cradle_xy_distances(1+config.z_distance_top_layers,config.xy_min_distance); + effective_cradle_xy_distances.insert(effective_cradle_xy_distances.end(),cradle_xy_distance.begin(),cradle_xy_distance.end()); + + for(int ctr=effective_cradle_xy_distances.size(); ctr <= cradle_layers + 1; ctr++) + { + effective_cradle_xy_distances.emplace_back(effective_cradle_xy_distances.back()); + } cura::parallel_for( 1, @@ -743,12 +750,13 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { + coord_t current_cradle_xy_distance = effective_cradle_xy_distances[idx]; if ((idx > 0 || (cradle_base_roof && !large_cradle_base && ! abort)) && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. { coord_t max_distance2 = 0; Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + cradle_xy_distance - config.support_line_width / 2); + relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + current_cradle_xy_distance - config.support_line_width / 2); for (auto line : model_shadow) { @@ -770,7 +778,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: // Subtract the model shadow up until this layer from the lines. if (idx > 0) { - lines_to_center = model_shadow.offset(cradle_xy_distance).unionPolygons().differencePolyLines(lines_to_center, false); + lines_to_center = model_shadow.offset(current_cradle_xy_distance).unionPolygons().differencePolyLines(lines_to_center, false); } // Store valid distances from the center in relation to the direction of the line. // Used to detect if a line may be intersecting another model part. @@ -890,8 +898,10 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: } for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) { + coord_t current_cradle_xy_distance = effective_cradle_xy_distances[idx]; + Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + cradle_xy_distance); + relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + current_cradle_xy_distance); cradle_areas_calc[idx] = cradle_areas_calc[idx].difference(relevant_forbidden); } diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 74ec0d3606..80c6c01ff7 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -733,6 +733,19 @@ std::vector Settings::get>(const std::string& key) const return values_ints; } +template<> +std::vector Settings::get>(const std::string& key) const +{ + std::vector values_doubles = get>(key); + std::vector values_ints; + values_ints.reserve(values_doubles.size()); + for (double value : values_doubles) + { + values_ints.push_back(std::round(MM2INT(value))); // Round to nearest integer. + } + return values_ints; +} + template<> std::vector Settings::get>(const std::string& key) const { From b25a88ebd973a22e9fe4704ef2bbfc11dda1282b Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 6 Jan 2024 18:50:29 +0100 Subject: [PATCH 024/101] Changed support skin pattern to ZigZag and reduced support skin wall line count by one to improve strength of support skin. --- src/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index d3dfbe98c5..00c7437314 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2511,7 +2511,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor .fillInfillParts(support_layer_storage[layer_idx], all_this_layer, all_next_layer, config.support_line_width, config.support_wall_count, convert_every_part); storage.support.supportLayers[layer_idx] - .fillInfillParts(support_skin_storage[layer_idx], all_this_layer, all_next_layer, config.support_line_width, config.support_wall_count, convert_every_part, config.support_skin_line_distance, EFillMethod::LINES); + .fillInfillParts(support_skin_storage[layer_idx], all_this_layer, all_next_layer, config.support_line_width, std::max(config.support_wall_count-1,0), convert_every_part, config.support_skin_line_distance, EFillMethod::ZIG_ZAG); { std::lock_guard critical_section_progress(critical_sections); From c983b586b00e013c8fc675551ec9c670000f7d18 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 9 Jan 2024 04:42:40 +0100 Subject: [PATCH 025/101] Fix bug related to cradle and fractional roof again --- src/TreeSupportTipGenerator.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index a0cd475607..05c747bfd2 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1845,6 +1845,17 @@ void TreeSupportTipGenerator::generateTips( { storage.support.supportLayers[layer_idx].support_roof.add(support_roof_drawn[layer_idx]); storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(roof_tips_drawn[layer_idx]); + + if (layer_idx + 1 < support_roof_drawn.size() && layer_idx > 0) + { + Polygons all_roof = support_roof_drawn[layer_idx].unionPolygons(roof_tips_drawn[layer_idx]); + Polygons all_roof_above = support_roof_drawn[layer_idx + 1] + .unionPolygons(roof_tips_drawn[layer_idx + 1]) + .unionPolygons(cradle_areas[layer_idx]) + .offset(FUDGE_LENGTH) + .unionPolygons(); + storage.support.supportLayers[layer_idx].support_fractional_roof.add(all_roof.difference(all_roof_above)); + } } } }); @@ -1866,18 +1877,6 @@ void TreeSupportTipGenerator::generateTips( } } - cura::parallel_for( - 1, - mesh.overhang_areas.size() - z_distance_delta, - [&](const LayerIndex layer_idx) - { - if (layer_idx > 0) - { - storage.support.supportLayers[layer_idx].support_fractional_roof.add( - storage.support.supportLayers[layer_idx].support_roof.difference(storage.support.supportLayers[layer_idx + 1].support_roof)); - } - }); - for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { move_bounds[layer_idx].insert(tips_on_layer.begin(), tips_on_layer.end()); From 5867e2088bb02eaf65316f4255f955682a8a56de Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 12 Feb 2024 12:07:33 +0100 Subject: [PATCH 026/101] Cradle refactoring --- include/TreeModelVolumes.h | 12 +- include/TreeSupportTipGenerator.h | 34 +- src/TreeModelVolumes.cpp | 55 ++- src/TreeSupport.cpp | 51 +- src/TreeSupportTipGenerator.cpp | 769 ++++++++++++++++++++++++------ 5 files changed, 740 insertions(+), 181 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 0e93fab98a..fb0091b511 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -163,9 +163,17 @@ class TreeModelVolumes /*! * \brief Get areas that were additionally set to be avoided * \param layer_idx The layer said area is on. + * \param radius The radius of the node of interest. * \returns The area that should be avoided */ - const Polygons& getAntiPreferredAreas(LayerIndex layer_idx); + const Polygons& getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius); + + /*! + * \brief Get areas that were are classified as support blocker + * \param layer_idx The layer said area is on. + * \returns The area should not contain support + */ + const Polygons& getSupportBlocker(LayerIndex layer_idx); @@ -562,6 +570,8 @@ class TreeModelVolumes std::unique_ptr critical_progress = std::make_unique(); Simplify simplifier = Simplify(0, 0, 0); // a simplifier to simplify polygons. Will be properly initialised in the constructor. + + Polygons empty_polygon = Polygons(); }; } diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 7c8b5735dd..986643a4f7 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -57,12 +57,17 @@ class TreeSupportTipGenerator struct UnsupportedAreaInformation { - UnsupportedAreaInformation(const Polygons area, size_t index, size_t height) : area{ area }, index{ index }, height{ height } + UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) : + area{ area }, + index{ index }, + height{ height }, + accumulated_supportable_overhang { accumulated_supportable_overhang } { } const Polygons area; size_t index; size_t height; + coord_t accumulated_supportable_overhang; }; using LineInformation = std::vector>; @@ -148,6 +153,21 @@ class TreeSupportTipGenerator */ void calculateFloatingParts(const SliceMeshStorage& mesh); + using CradleShadowCenterPair = std::pair,std::vector>; + using CenterSubCenterPointPair = std::pair; //todo rename + + //todo one function that calculates centers and corresponding shadows Returns: Model_shadows with corresponding centers + std::vector> generateCradleCenters(const SliceMeshStorage& mesh); + + //todo maybe one function that moves cradles up if they dont do anything yet + //todo one function that generates the lines. Returns vector of lines relative to a certain cradle index. WHERE GET INDEX? + std::vector,CenterSubCenterPointPair>>> generateCradleLines(std::vector>& center_data); + //todo one function that cleans overlaps between lines. Updates cradle lines previously generated + void cleanCradleLineOverlaps(std::vector,CenterSubCenterPointPair>>>& cradle_polylines); + //todo one function that renders the cradle + void finalizeCradleAreas( std::vector>>& cradle_polygons,std::vector& support_free_areas,std::vector>& center_data); + + /*! * \brief Generate a cradle to stabilize pointy overhang * \param mesh[in] The mesh that is currently processed. @@ -363,13 +383,18 @@ class TreeSupportTipGenerator /*! * \brief Amount of lines used for the cradle. */ - size_t cradle_lines; + size_t cradle_line_count; /*! * \brief Length of lines used for the cradle. */ coord_t cradle_length; + /*! + * \brief Minimum length of lines used for the cradle. + */ + coord_t cradle_length_min; + /*! * \brief Width of lines used for the cradle. */ @@ -411,6 +436,11 @@ class TreeSupportTipGenerator */ std::vector cradle_xy_distance; + /*! + * \brief Distances in lines between the cradle and the support they are supported by. + */ + size_t cradle_z_distance_layers; + std::mutex critical_cradle; std::mutex critical_move_bounds; std::mutex critical_roof_tips; diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index d179c6f201..dd4b27faab 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -571,16 +571,65 @@ void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex la RadiusLayerPair key(0,layer_idx); std::lock_guard critical_section(*critical_anti_preferred_); anti_preferred_[key] = anti_preferred_[key].unionPolygons(area); + + //todo calculated all required radiis and invalidate all non 0 radiis + //todo add function to recalculate anti-pref for non 0 radiis } -const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx) + + + +const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius) { - RadiusLayerPair key(0,layer_idx); + + coord_t ceiled_radius = ceilRadius(radius); + RadiusLayerPair key(ceilRadius(ceiled_radius),layer_idx); + + std::optional> result; + { + std::lock_guard critical_section(*critical_anti_preferred_); + result = getArea(anti_preferred_, key); + } + if (result) + { + return result.value().get(); + } + + { + key.first = 0; + std::lock_guard critical_section(*critical_anti_preferred_); + result = getArea(anti_preferred_, key); + } + + if(!result) + { + return empty_polygon; + } + + if (precalculated) + { + //todo enable logging when precalc for anti preferred was implemented + // spdlog::warn("Had to calculate anti preferred at radius {} and layer {}, but precalculate was called. Performance may suffer!", ceiled_radius, key.second); + } + key.first = ceiled_radius; std::lock_guard critical_section(*critical_anti_preferred_); - return anti_preferred_[key]; + anti_preferred_[key] = result->get().offset(ceiled_radius + current_min_xy_dist); + + return anti_preferred_[key]; } +const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) +{ + if(layer_idx < anti_overhang_.size()) + { + return anti_overhang_[layer_idx]; + } + else + { + return empty_polygon; + } +} diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 00c7437314..a0c523983e 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -905,7 +905,7 @@ std::optional TreeSupport::increaseSingleArea( } } - const Polygons& anti_preferred = volumes_.getAntiPreferredAreas(layer_idx - 1); + const Polygons& anti_preferred = volumes_.getAntiPreferredAreas(layer_idx - 1, radius); // Remove areas where the branch should not be if possible. @@ -916,7 +916,7 @@ std::optional TreeSupport::increaseSingleArea( if (current_elem.to_buildplate) { Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred); - if (to_bp_without_anti.area() > 1) + if (to_bp_without_anti.area() > 1 || settings.type == AvoidanceType::SLOW) { to_bp_data = to_bp_without_anti; to_model_data = to_model_data.difference(anti_preferred); @@ -926,7 +926,7 @@ std::optional TreeSupport::increaseSingleArea( else { Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred); - if (to_model_data_without_anti.area() > 1) + if (to_model_data_without_anti.area() > 1 || settings.type == AvoidanceType::SLOW) { to_bp_data = to_bp_data.difference(anti_preferred); to_model_data = to_model_data_without_anti; @@ -936,6 +936,9 @@ std::optional TreeSupport::increaseSingleArea( } + check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + + return check_layer_data.area() > 1 ? std::optional(current_elem) : std::optional(); } @@ -2018,6 +2021,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); placed_fake_roof_areas[layer_idx] = placed_fake_roof_areas[layer_idx].unionPolygons(); + additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx].unionPolygons(); + //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (!storage.support.supportLayers[layer_idx].support_roof.empty()) @@ -2026,10 +2031,14 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); + break; case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]).difference(support_skin_storage[layer_idx]); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof + .difference(support_layer_storage[layer_idx]) + .difference(support_skin_storage[layer_idx]) + .difference(additional_required_support_area[layer_idx]); break; default: @@ -2042,7 +2051,10 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(support_free_areas[layer_idx]); } - + additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx] //todo cradle lines vs Interface priority + .difference(storage.support.supportLayers[layer_idx].support_roof) + .difference(support_layer_storage[layer_idx]) + .difference(support_skin_storage[layer_idx]); } ); @@ -2084,7 +2096,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora already_supports.add(storage.support.supportLayers[layer_idx].support_roof); // roof already_supports.add(additional_required_support_area[layer_idx]); // E.g. cradle already_supports.add(placed_fake_roof_areas[layer_idx]); - already_supports = already_supports.unionPolygons(); + already_supports.add(support_layer_storage[layer_idx].getOutsidePolygons().tubeShape(config.support_line_width*config.support_wall_count,0)); + already_supports = already_supports.unionPolygons().offset(FUDGE_LENGTH).unionPolygons(); Polygons may_need_skin_area_topmost = needs_supporting.difference(already_supports); @@ -2108,13 +2121,12 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora may_need_skin_area_topmost.add(data_pair.second); } } - may_need_skin_area_topmost = may_need_skin_area_topmost.unionPolygons(); + may_need_skin_area_topmost = may_need_skin_area_topmost.unionPolygons(); Polygons may_need_skin_area = may_need_skin_area_topmost; for (LayerIndex support_skin_ctr = 0; support_skin_ctr < std::min(LayerIndex(config.support_skin_layers), layer_idx); support_skin_ctr++) { - Polygons next_skin; Polygons support_on_layer; @@ -2146,17 +2158,21 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora false, EFillMethod::LINES, true); - Polygons intersecting_lines; - for (auto line : scan_lines) { - if (PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) && PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back())) + bool valid_for_may_need_skin = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) || + may_need_skin_area.inside(line.front()) || + may_need_skin_area.inside(line.back()); + bool valid_for_may_need_skin_area_topmost = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back()) || + may_need_skin_area_topmost.inside(line.front()) || + may_need_skin_area_topmost.inside(line.back()); + if (valid_for_may_need_skin && valid_for_may_need_skin_area_topmost) { intersecting_lines.addLine(line.front(), line.back()); } - } + } Polygons partial_skin_area = intersecting_lines.offsetPolyLine(config.support_skin_line_distance + FUDGE_LENGTH).unionPolygons().intersection(part_outline); @@ -2191,7 +2207,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } } part = remaining_part; - next_skin.add(partial_skin_area.intersection(may_need_skin_area_topmost)); std::lock_guard critical_section_cradle(critical_support_layer_storage); support_skin_storage[layer_idx - support_skin_ctr].add(partial_skin_area); @@ -2212,7 +2227,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora [&](const LayerIndex layer_idx) { support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); } ); } @@ -2629,16 +2644,18 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } + generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons); + + // cradle being added before skin causes cradle lines to become skin. for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) { - if (support_layer_storage.size() > layer_idx) + if(layer_idx < support_layer_storage.size()) { - support_layer_storage[layer_idx].add(additional_required_support_area[layer_idx]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(additional_required_support_area[layer_idx]); } scripta::log("tree_support_layer_storage", support_layer_storage[layer_idx], SectionType::SUPPORT, layer_idx); } - generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons); const auto t_skin = std::chrono::high_resolution_clock::now(); filterFloatingLines(support_layer_storage,support_skin_storage); const auto t_filter = std::chrono::high_resolution_clock::now(); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 05c747bfd2..50a5b2375f 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -44,8 +44,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")) - , // todo propper - support_outset(0) + , support_outset(0) , // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") roof_outset(use_fake_roof ? support_outset : mesh.settings.get("support_roof_offset")) , force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && ! use_fake_roof) @@ -57,26 +56,34 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , already_inserted(mesh.overhang_areas.size()) , support_roof_drawn(mesh.overhang_areas.size(), Polygons()) , roof_tips_drawn(mesh.overhang_areas.size(), Polygons()) - , cradle_areas(mesh.overhang_areas.size(),Polygons()) - , cradle_base_areas(mesh.overhang_areas.size(),Polygons()) - , cradle_overhang(mesh.overhang_areas.size(),Polygons()) + , cradle_areas(mesh.overhang_areas.size(), Polygons()) + , cradle_base_areas(mesh.overhang_areas.size(), Polygons()) + , cradle_overhang(mesh.overhang_areas.size(), Polygons()) , volumes_(volumes_s) , force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) , cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config.layer_height) , minimum_cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config.layer_height) - , cradle_lines(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) + , cradle_line_count(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) , cradle_length(retrieveSetting(mesh.settings, "support_tree_cradle_length")) + , cradle_length_min(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) , cradle_line_width(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) , cradle_lines_roof(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") , cradle_base_roof( - ! use_fake_roof && (retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" - || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base")) + ! use_fake_roof + && (retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" + || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base")) , large_cradle_base(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_xy_distance(retrieveSetting>(mesh.settings, "support_tree_cradle_xy_distance")) + , cradle_z_distance_layers(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance") , config.layer_height)) { + + if(cradle_layers) + { + cradle_layers += cradle_z_distance_layers; + } + const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -118,6 +125,38 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage dtt_when_tips_can_merge = config.tip_layers; // arbitrary default for when there is no guarantee that the while loop above will terminate } support_supporting_branch_distance = 2 * config.getRadius(dtt_when_tips_can_merge) + config.support_line_width + FUDGE_LENGTH; + + + for(size_t cradle_z_dist_ctr=0; cradle_z_dist_ctr < cradle_z_distance_layers + 1; cradle_z_dist_ctr++) + { + cradle_xy_distance.emplace_back(config.xy_min_distance); + } + + for (int i = 0; i < 9; i++) + { + std::stringstream setting_name_dist; + setting_name_dist << "support_tree_cradle_xy_dist_"; + setting_name_dist << i; + coord_t next_cradle_xy_dist = retrieveSetting(mesh.settings, setting_name_dist.str()); + std::stringstream setting_name_height; + setting_name_height << "support_tree_cradle_xy_height_"; + setting_name_height << i; + coord_t next_cradle_xy_dist_height = retrieveSetting(mesh.settings, setting_name_height.str()); + if (next_cradle_xy_dist_height == 0) + { + break; + } + + for (int layer_delta = 0; layer_delta < round_up_divide(next_cradle_xy_dist_height, config.layer_height); layer_delta++) + { + cradle_xy_distance.emplace_back(next_cradle_xy_dist); + } + } + + for (int cradle_xy_dist_fill = cradle_xy_distance.size(); cradle_xy_dist_fill < cradle_layers + 1; cradle_xy_dist_fill++) + { + cradle_xy_distance.emplace_back(config.xy_min_distance); + } } @@ -449,7 +488,6 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. Polygons layer = mesh.layers[layer_idx].getOutlines(); - // todo rewrite to be able to parallelize it for (const Polygons part : layer.splitIntoParts()) { @@ -457,37 +495,46 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes AABB part_aabb(part); auto has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); - if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) + if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) //todo accumulate overhangs, only cradle if below threashold? { next_completely_supported.add(part); continue; } + Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); + coord_t overhang_area = std::max(overhang.area(), M_PI * config.min_wall_line_width * config.min_wall_line_width); if (!has_support_below) { - floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0); + floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0,overhang_area); floating_parts_map_ [layer_idx].emplace_back(std::vector()); continue; } - - size_t min_resting_on_layers = 0; + coord_t supported_overhang_area = 0; bool add = false; + std::vector idx_of_floating_below; for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) { if(layer_idx > 1 && floating.height < cradle_layers - 1 && !floating.area.intersection(part).empty()) { - floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()) ; + idx_of_floating_below.emplace_back(idx); + supported_overhang_area += floating.accumulated_supportable_overhang; min_resting_on_layers = std::max(min_resting_on_layers,floating.height); add = true; } } - if(min_resting_on_layers < cradle_layers && add) + if(min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) { - floating_parts_map_ [layer_idx].emplace_back(std::vector()); - floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1); + + for(size_t idx: idx_of_floating_below) + { + floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); + } + + floating_parts_map_[layer_idx].emplace_back(std::vector()); + floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1,overhang_area + supported_overhang_area); } else { @@ -531,7 +578,7 @@ std::vector TreeSupportTipG } else { - spdlog::error("Requested not calculated unsupported area.", layer_idx); + spdlog::error("Requested not calculated unsupported area."); return result; } @@ -575,27 +622,17 @@ std::vector TreeSupportTipG } -void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) +std::vector, std::vector>>> TreeSupportTipGenerator::generateCradleCenters(const SliceMeshStorage& mesh) { - std::mutex critical_support_free_areas_and_cradle_areas; std::mutex critical_dedupe; std::vector> dedupe(mesh.overhang_areas.size()); - - calculateFloatingParts(mesh); - std::vector effective_cradle_xy_distances(1+config.z_distance_top_layers,config.xy_min_distance); - effective_cradle_xy_distances.insert(effective_cradle_xy_distances.end(),cradle_xy_distance.begin(),cradle_xy_distance.end()); - - for(int ctr=effective_cradle_xy_distances.size(); ctr <= cradle_layers + 1; ctr++) - { - effective_cradle_xy_distances.emplace_back(effective_cradle_xy_distances.back()); - } - + std::vector, std::vector>>> result(mesh.overhang_areas.size()); cura::parallel_for( 1, mesh.overhang_areas.size() - (z_distance_delta + 1), [&](const LayerIndex layer_idx) { - if(mesh.overhang_areas[layer_idx + z_distance_delta].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta).empty()) + if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta).empty()) { return; } @@ -611,40 +648,57 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: std::vector accumulated_model(std::min(cradle_layers + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); std::vector all_pointy_idx{ pointy_info.index }; - Point assumed_center; - + Point assumed_center_prev; + Point initial_center; + std::vector centers; Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. - bool abort = false; + bool aborted = false; bool contacted_other_pointy = false; std::vector unsupported_model(accumulated_model.size()); for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) { - Polygons relevant_forbidden = - volumes_.getAvoidance(0, layer_idx + cradle_up_layer, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - // shadow model up => not cradle where model // then drop cradle down // cut into parts => get close to original pointy that are far enough from each other. std::vector next_pointy_idx; Polygons model_outline; bool blocked_by_dedupe = false; - if (cradle_up_layer > 1) // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up + // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up + if (cradle_up_layer > 1) { if (cradle_up_layer == 2) { - assumed_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + assumed_center_prev = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + initial_center = assumed_center_prev; } else { Point next_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - assumed_center = (2 * assumed_center + next_center) / 3; + if (vSize(assumed_center_prev - next_center) < cradle_length && (! centers.empty() || vSize(assumed_center_prev - initial_center) < cradle_length)) + { + assumed_center_prev = (2 * assumed_center_prev + next_center) / 3; + } + else + { + // Large center shift. May need its own cradle + if (centers.empty()) + { + centers.emplace_back(initial_center); + } + else + { + centers.emplace_back(assumed_center_prev); + } + assumed_center_prev = next_center; + } } for (size_t pointy_idx : all_pointy_idx) { for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta, pointy_idx)) { - if (next_pointy_data.height != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it + if (next_pointy_data.height + != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it { contacted_other_pointy = true; continue; @@ -675,7 +729,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: { if (cradle_up_layer < minimum_cradle_layers) { - abort = true; + aborted = true; break; } @@ -703,22 +757,33 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: accumulated_model[cradle_up_layer] = shadow.offset(-config.maximum_move_distance).unionPolygons(model_outline); } - if (abort) + if (aborted) { - // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface base. - for (size_t cradle_up_layer = 1; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) - { - accumulated_model[cradle_up_layer].clear(); - } + // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface + // base. + + Polygons cradle_0 = accumulated_model[0]; + accumulated_model.clear(); + accumulated_model.emplace_back(cradle_0); + } + + coord_t min_distance_to_assumed_center_prev = std::numeric_limits::max(); + for(Point existing_center:centers) + { + min_distance_to_assumed_center_prev = std::min(min_distance_to_assumed_center_prev, vSize(existing_center-assumed_center_prev)); + } + + if(min_distance_to_assumed_center_prev > 2*cradle_length) + { + centers.emplace_back(assumed_center_prev); } - std::vector centers{ assumed_center }; if (! contacted_other_pointy) { for (int extra_points = 0; extra_points < 2; extra_points++) { coord_t candidate_min_distance2 = 0; - Point candidate = assumed_center; + Point candidate = assumed_center_prev; // todo should do for all centers for (auto line : shadow) { for (auto p : line) @@ -742,21 +807,75 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: } } } + result[layer_idx].emplace_back(accumulated_model, centers); + } + }); + return result; +} - for (Point center : centers) +std::vector, TreeSupportTipGenerator::CenterSubCenterPointPair>>> + TreeSupportTipGenerator::generateCradleLines(std::vector>& center_data) +{ + const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance.begin(), cradle_xy_distance.end()); + const coord_t min_cradle_line_length = (cradle_lines_roof ? config.support_roof_line_width : config.support_line_width) * 2; + + std::vector, CenterSubCenterPointPair>>> cradle_polylines(center_data.size()); + cura::parallel_for( + 1, + center_data.size(), + [&](const LayerIndex layer_idx) + { + for (auto [center_idx, shadow_centers_pair] : center_data[layer_idx] | ranges::views::enumerate) + { + for (auto [center_point_idx, center] : shadow_centers_pair.second | ranges::views::enumerate) { - std::vector cradle_areas_calc(accumulated_model.size()); + cradle_polylines[layer_idx].emplace_back(std::vector(shadow_centers_pair.first.size()), CenterSubCenterPointPair(center_idx, center_point_idx)); - for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) + Polygons removed_lines; + + for (auto [idx, model_shadow] : shadow_centers_pair.first | ranges::views::enumerate) { - coord_t current_cradle_xy_distance = effective_cradle_xy_distances[idx]; - if ((idx > 0 || (cradle_base_roof && !large_cradle_base && ! abort)) + bool aborted = shadow_centers_pair.first.size() == 1; + const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; + const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; + + if ((idx > 0 || (cradle_base_roof && ! large_cradle_base && ! aborted)) && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. { coord_t max_distance2 = 0; - Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + current_cradle_xy_distance - config.support_line_width / 2); + + Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + + Polygons this_part_influence = model_shadow.offset(config.xy_min_distance); + + for (size_t layer_offset = 1; layer_offset <= config.z_distance_bottom_layers && layer_offset<=idx; layer_offset++) + { + this_part_influence.add(shadow_centers_pair.first[idx - layer_offset]); + } + + for (coord_t layer_offset = 1; layer_offset <= config.z_distance_top_layers + && layer_offset + layer_idx < shadow_centers_pair.first.size(); layer_offset++) + { + const coord_t required_range_x = coord_t(config.xy_min_distance - ((layer_offset - (config.z_distance_top_layers == 1 ? 0.5 : 0)) * config.xy_min_distance / config.z_distance_top_layers)); + this_part_influence.add(shadow_centers_pair.first[idx + layer_offset].offset(required_range_x)); + } + + this_part_influence = this_part_influence.unionPolygons().offset(FUDGE_LENGTH , ClipperLib::jtRound).unionPolygons(); + + coord_t cradle_min_xy_distance_delta = std::max(config.xy_min_distance - current_cradle_xy_distance, coord_t(0)); + + //Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. + // To prevent this offset my the delta before removing the influence of the model. + relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta).difference(this_part_influence) + .offset(cradle_min_xy_distance_delta) + .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2)) + .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width / 2)); for (auto line : model_shadow) { @@ -766,7 +885,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: } } - Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + cradle_length * 2, (2 * M_PI) / cradle_lines); + Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + current_cradle_length * 2, (2 * M_PI) / cradle_line_count); // create lines that go from the furthest possible location to the center Polygons lines_to_center; @@ -775,10 +894,10 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: lines_to_center.addLine(p, center); } - // Subtract the model shadow up until this layer from the lines. + // Subtract the model shadow up until this layer from the lines. todo couldnt this also just be the relevant_forbidden? if (idx > 0) { - lines_to_center = model_shadow.offset(current_cradle_xy_distance).unionPolygons().differencePolyLines(lines_to_center, false); + lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2).unionPolygons().differencePolyLines(lines_to_center, false); } // Store valid distances from the center in relation to the direction of the line. // Used to detect if a line may be intersecting another model part. @@ -791,13 +910,13 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); Point closer = front_closer ? line.front() : line.back(); Point further = front_closer ? line.back() : line.front(); - if (Polygon(line).polylineLength() <= cradle_length) + if (Polygon(line).polylineLength() <= current_cradle_length) { shortened_lines_to_center.add(line); } else { - double scale = (double(cradle_length) / double(vSize(further - closer))); + double scale = (double(current_cradle_length) / double(vSize(further - closer))); Point correct_length = closer + (further - closer) * scale; shortened_lines_to_center.addLine(correct_length, closer); } @@ -807,8 +926,16 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: vector_distance_map.emplace_back((further - closer), vSize(center - closer)); } } - // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent this. - shortened_lines_to_center = relevant_forbidden.offset(config.support_line_width / 2).differencePolyLines(shortened_lines_to_center, false); + // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent + // this. + shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); + + Polygons actually_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true).unionPolygons(removed_lines); // Evaluate which lines are still valid after the avoidance was subtracted for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) @@ -818,9 +945,16 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: Point further = front_closer ? line.back() : line.front(); Point current_direction = further - closer; coord_t distance_from_center = vSize(closer - center); + + shortened_lines_to_center[line_idx].clear(); + shortened_lines_to_center[line_idx].add(closer); + shortened_lines_to_center[line_idx].add(further); bool keep_line = false; bool found_candidate = false; - const bool too_short = (vSize(closer - further)) < std::min((cradle_lines_roof ? config.support_roof_line_width : config.support_line_width) * 2, cradle_length / 2); + bool too_short = (vSize(closer - further)) < cradle_length_min; + // a cradle line should also be removed if there will be no way to support it + too_short + |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < min_cradle_line_length; //todo rename or redo if (! too_short) { for (auto [direction_idx, direction] : vector_distance_map | ranges::views::enumerate) @@ -842,144 +976,313 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: if (too_short || (! keep_line && found_candidate)) { + removed_lines.add(Polygon(line).offset(FUDGE_LENGTH)); //todo Maybe direction instead of area? shortened_lines_to_center[line_idx].clear(); } } - Polygons cradle = shortened_lines_to_center.offsetPolyLine(cradle_line_width / 2,ClipperLib::jtRound).unionPolygons().offset(FUDGE_LENGTH).unionPolygons().offset(-FUDGE_LENGTH).difference(relevant_forbidden); + size_t line_idx = 0; - if(idx > 0 && relevant_forbidden.inside(center)) //todo better check + while (line_idx < shortened_lines_to_center.size()) { - //Ensures that the cradle-lines don't touch to prevent the center from merging together and engulfing the model at low cradle xy distances. - cradle = cradle.difference(PolygonUtils::makeCircle(center,config.support_line_width+FUDGE_LENGTH/2).offset(0)); + if (shortened_lines_to_center[line_idx].empty()) + { + shortened_lines_to_center.remove(line_idx); + } + else + { + line_idx++; + } } - cradle_areas_calc[idx] = cradle; - } - } - // The base has to include the pointy overhang that has to be supported. - if (cradle_areas_calc.size() > 1) - { - if (cradle_base_roof && ! large_cradle_base) - { - cradle_areas_calc[0] = cradle_areas_calc[0].unionPolygons(accumulated_model[0]); - } - else - { - cradle_areas_calc[0] = accumulated_model[0]; + cradle_polylines[layer_idx].back().first[idx] = shortened_lines_to_center; } } + } + } + }); + + return cradle_polylines; +} + + +void TreeSupportTipGenerator::cleanCradleLineOverlaps(std::vector, CenterSubCenterPointPair>>>& cradle_polylines) +{ + std::vector> all_cradles_per_layer(cradle_polylines.size() + cradle_layers); + for (LayerIndex layer_idx = 0; layer_idx < cradle_polylines.size(); layer_idx++) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_polylines[layer_idx].size(); cradle_idx++) + { + for (size_t height = 0; height < cradle_polylines[layer_idx][cradle_idx].first.size(); height++) + { + Polygons* result = &cradle_polylines[layer_idx][cradle_idx].first[height]; + all_cradles_per_layer[layer_idx + height].emplace_back(result); + } + } + } - // Rooflines are more delicate, so ensure that they rest on as much other rooflines if possible. - if (! use_fake_roof && support_roof_layers) + const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath + std::vector removed_lines(cradle_polylines.size()); + + cura::parallel_for( + 1, + all_cradles_per_layer.size(), + [&](const LayerIndex layer_idx) + { + std::vector all_cradles_on_layer = all_cradles_per_layer[layer_idx]; + std::vector> lines_to_remove(all_cradles_on_layer.size()); + for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) + { + AABB bounding_box_current = AABB(*all_cradles_on_layer[cradle_idx]); + bounding_box_current.expand(cradle_line_width * 2 + FUDGE_LENGTH); + for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) + { + if (bounding_box_current.hit(AABB(*all_cradles_on_layer[cradle_idx_inner]))) { - for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) + for (size_t line_idx = 0; line_idx < all_cradles_on_layer[cradle_idx]->size(); line_idx++) { - if (idx > 1) + for (size_t line_idx_inner = 0; line_idx_inner < all_cradles_on_layer[cradle_idx_inner]->size(); line_idx_inner++) { - Polygons relevant_forbidden = - volumes_.getAvoidance(0, layer_idx + idx - 1, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); + Polygon outer_line = (*all_cradles_on_layer[cradle_idx])[line_idx]; + Polygon inner_line = (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner]; + Point intersect; + if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) + { + coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); + coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); + const coord_t end_dist = FUDGE_LENGTH + min_distance_between_lines + cradle_line_width; // todo everywhere: This is dist to prevent lines from merging, not touching ... Maybe also should not touch - Polygons area_above = cradle.difference(relevant_forbidden); + if (inner_intersect_dist > outer_intersect_dist) + { + Point new_end_inner = intersect + normal((inner_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_inner, inner_line.front(), inner_line.back()) + || vSize(new_end_inner - inner_line.front()) < cradle_length_min) + { + (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = intersect; + lines_to_remove[cradle_idx_inner].emplace_back(line_idx_inner); + } + else + { + (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = new_end_inner; + } + } + if (outer_intersect_dist > inner_intersect_dist) + { + Point new_end_outer = intersect + normal((outer_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); - // If a line would have some areas that are too small to be drawn, there can be minor issues. The idea is that the offsets will filter them out. - // todo also ensure that every cradle area can be supported, remove if it can not - for (auto part : area_above.offset(-cradle_line_width / 4).splitIntoParts()) + if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_outer, outer_line.front(), outer_line.back()) + || vSize(new_end_outer - outer_line.front()) < cradle_length_min) + { + (*all_cradles_on_layer[cradle_idx])[line_idx].back() = intersect; + lines_to_remove[cradle_idx].emplace_back(line_idx); + } + else + { + (*all_cradles_on_layer[cradle_idx])[line_idx].back() = new_end_outer; + } + } + } + else { - if (part.area() > cradle_line_width * std::max(cradle_length / 4, config.support_roof_line_width)) + // Touching lines have the same issue Lines touch if the end is to close to another line + coord_t inner_end_to_outer_distance = LinearAlg2D::getDistFromLine(inner_line.back(),outer_line.front(), outer_line.back()); + if(inner_end_to_outer_distance < 2 * cradle_line_width) { - cradle_areas_calc[idx - 1].add(part.offset(cradle_line_width / 4)); + Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),2 * cradle_line_width - inner_end_to_outer_distance); + coord_t error = LinearAlg2D::getDistFromLine(new_end_inner,outer_line.front(), outer_line.back()); + double error_correction_factor = 1.0 + error/(2 * cradle_line_width - inner_end_to_outer_distance); + new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),(2 * cradle_line_width - inner_end_to_outer_distance)*error_correction_factor); + (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = new_end_inner; } + else + { + coord_t outer_end_to_inner_distance = LinearAlg2D::getDistFromLine(outer_line.back(),inner_line.front(), inner_line.back()); + if(outer_end_to_inner_distance < 2 * cradle_line_width) + { + + Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),2 * cradle_line_width - outer_end_to_inner_distance); + coord_t error = LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back()); + double error_correction_factor = 1.0 + error/(2 * cradle_line_width - outer_end_to_inner_distance); + new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),(2 * cradle_line_width - outer_end_to_inner_distance)*error_correction_factor); + (*all_cradles_on_layer[cradle_idx])[line_idx].back() = new_end_outer; + } + } } - cradle_areas_calc[idx - 1] = cradle_areas_calc[idx - 1].unionPolygons(); + // Either they cross or the end of one is close to the other } } } + } + } + + for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) + { + if(!lines_to_remove[cradle_idx].empty()) + { + std::sort(lines_to_remove[cradle_idx].begin(), lines_to_remove[cradle_idx].end(), std::greater()); //Ensure largest idx is removed first. + for(size_t remove_idx = 0; remove_idx < lines_to_remove[cradle_idx].size(); remove_idx++) + { + removed_lines[layer_idx].add((*all_cradles_on_layer[cradle_idx])[lines_to_remove[cradle_idx][remove_idx]]); + (*all_cradles_on_layer[cradle_idx]).remove(lines_to_remove[cradle_idx][remove_idx]); + } + } + } + }); + + //todo if removed line has similar cosine to one above, and on of the points lies close to the other one Remove that part of the line => Only keep above if still longer than min + +} +void TreeSupportTipGenerator::finalizeCradleAreas( + std::vector>>& cradle_polygons, + std::vector& support_free_areas, + std::vector>& center_data) +{ + std::mutex critical_support_free_areas_and_cradle_areas; + + cura::parallel_for( + 1, + cradle_polygons.size(), + [&](const LayerIndex layer_idx) + { + for (auto [cradle_idx, cradle_areas_calc] : cradle_polygons[layer_idx] | ranges::views::enumerate) + { + // Rooflines are more delicate, so ensure that they rest on as much other rooflines if possible. + if (! use_fake_roof && support_roof_layers) + { for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) { - coord_t current_cradle_xy_distance = effective_cradle_xy_distances[idx]; + if (idx > 1) + { + Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx - 1, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + + Polygons area_above = cradle.difference(relevant_forbidden); - Polygons relevant_forbidden = volumes_.getAvoidance(0, layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - relevant_forbidden = relevant_forbidden.offset(-config.xy_min_distance + current_cradle_xy_distance); - cradle_areas_calc[idx] = cradle_areas_calc[idx].difference(relevant_forbidden); + // If a line would have some areas that are too small to be drawn, there can be minor issues. The idea is that the offsets will filter them out. + for (auto part : area_above.offset(-cradle_line_width / 4).splitIntoParts()) + { + if (part.area() > cradle_line_width * std::max(cradle_length / 4, config.support_roof_line_width)) + { + cradle_areas_calc[idx - 1].add(part.offset(cradle_line_width / 4)); + } + } + + cradle_areas_calc[idx - 1] = cradle_areas_calc[idx - 1].unionPolygons(); + } } + } + + const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; + const coord_t small_hole_size + = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; - const coord_t small_hole_size = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate) + + for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate) + { + if (! cradle.empty()) { - if (! cradle.empty()) + if(idx < cradle_z_distance_layers + 1 && idx != 0) + { + continue; + } + if (idx == 0) { - if (idx == 0) + if (! use_fake_roof && support_roof_layers) { - if (! use_fake_roof && support_roof_layers) + Polygons cut_line_base; + if (large_cradle_base) { - Polygons cut_line_base; - if (large_cradle_base) - { - // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest on. - // This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just the overhang, but that should not cause any issues. - Point min_p = cradle.min(); - Point max_p = cradle.max(); - Polygons rest_line; - rest_line.addLine(min_p, max_p); - cut_line_base = rest_line.offsetPolyLine(small_hole_size); - } - Polygons closed = cradle.unionPolygons(cradle_areas_calc[1].offset(closing_dist, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); - { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) - { - support_free_areas[layer_idx - interface_down].add(cut_line_base); - volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); - } - } - if (large_cradle_base) + // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest + // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just + // the overhang, but that should not cause any issues. + Point min_p = cradle.min(); + Point max_p = cradle.max(); + Polygons rest_line; + rest_line.addLine(min_p, max_p); + cut_line_base = rest_line.offsetPolyLine(small_hole_size); + } + Polygons closed = cradle.unionPolygons(cradle_areas_calc[1].offset(closing_dist, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) { - cradle = closed.difference(cut_line_base); + support_free_areas[layer_idx - interface_down].add(cut_line_base); + volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); } } - - if(large_cradle_line_tips) + if (large_cradle_base) { - cradle.add(cradle_areas_calc[1]); + cradle = closed.difference(cut_line_base); } - - cradle = cradle.unionPolygons(); - cradle_base_areas[layer_idx].add(cradle); } - else + + if (large_cradle_line_tips) { - Polygons unsupported_cradle_parts = cradle.difference(cradle_areas_calc[idx - 1].offset(config.maximum_move_distance)); + cradle.add(cradle_areas_calc[1]); + } - Polygons closed = cradle.unionPolygons(cradle.offset(closing_dist + 2 * config.branch_radius, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); - volumes_.addAreaToAntiPreferred(closed, layer_idx + idx); + cradle = cradle.unionPolygons(); + cradle_base_areas[layer_idx].add(cradle); + } + else + { + Polygons closed = cradle.unionPolygons( + cradle.offset(closing_dist + 2 * config.branch_radius, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); //todo? + volumes_.addAreaToAntiPreferred(cradle, layer_idx + idx); + + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + cradle_areas[layer_idx + idx].add(cradle); + } + + Polygons unsupported_cradle; + Polygons max_supported_by_below = cradle_areas_calc[ idx == cradle_z_distance_layers + 1 ? 0 : idx - 1].offset(config.maximum_move_distance); + for (auto cradle_part : cradle.splitIntoParts()) + { + if (cradle_part.intersection(max_supported_by_below).empty() || (! large_cradle_line_tips && idx == cradle_z_distance_layers + 1)) { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_areas[layer_idx + idx].add(cradle); + unsupported_cradle.add(cradle_part); } + } + + for(int idx_delta_down = std::min(coord_t(cradle_z_distance_layers+1), coord_t(layer_idx+idx)); idx_delta_down >=0; idx_delta_down--) + { - // Check which cradle lines overhang so much that they need support. If possible add support one layer below said line. - const Polygons relevant_forbidden = - volumes_.getAvoidance(0, layer_idx + idx - 1, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); - if (! unsupported_cradle_parts.empty()) + const Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx - idx_delta_down, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + + Polygons next_unsupported_cradle; + for (auto cradle_part : unsupported_cradle.splitIntoParts()) { - for (auto unsupported_part : unsupported_cradle_parts.splitIntoParts()) + if (!cradle_part.difference(relevant_forbidden).empty()) { - if (unsupported_part.difference(relevant_forbidden).empty()) + if (large_cradle_line_tips) { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_overhang[layer_idx + idx].add(unsupported_cradle_parts); + cradle_base_areas[layer_idx + idx - idx_delta_down].add(cradle_part); } else { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_overhang[layer_idx + idx - 1].add(unsupported_cradle_parts); + cradle_overhang[layer_idx + idx - idx_delta_down].add(cradle_part); } } + else + { + next_unsupported_cradle.add(cradle_part); + } } + unsupported_cradle=next_unsupported_cradle; } } } @@ -987,33 +1290,182 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: } }); + cura::parallel_for( 1, - mesh.layers.size(), + cradle_areas.size(), [&](const LayerIndex layer_idx) { - if(layer_idx < cradle_areas[layer_idx].size()) + if (layer_idx < cradle_areas.size()) { cradle_areas[layer_idx] = cradle_areas[layer_idx].unionPolygons(); - } - if(layer_idx < cradle_overhang[layer_idx].size()) + if (layer_idx < cradle_overhang.size()) { cradle_overhang[layer_idx] = cradle_overhang[layer_idx].unionPolygons(); - } - if(layer_idx < cradle_base_areas[layer_idx].size()) + if (layer_idx < cradle_base_areas.size()) { cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); - } - if(layer_idx < support_free_areas[layer_idx].size()) + if (layer_idx < support_free_areas.size()) { support_free_areas[layer_idx] = support_free_areas[layer_idx].unionPolygons(); + } + }); +} + + +void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) +{ + calculateFloatingParts(mesh); + + + // todo move up cradle if no line contacts model + // todo generate additional overhang if model may bend... Is this a TreeSupport Feature though? + + + std::vector> center_data = generateCradleCenters(mesh); + std::vector, CenterSubCenterPointPair>>> cradle_polylines = generateCradleLines(center_data); + cleanCradleLineOverlaps(cradle_polylines); + + + std::vector>> cradle_polygons(cradle_polylines.size()); + + const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath + // Some angles needed to move cradle lines outwards to prevent them from toughing. + double center_angle = (2.0 * M_PI) / double(cradle_line_count); + double outer_angle = (M_PI - center_angle) / 2; + coord_t outer_radius = (double(min_distance_between_lines + config.support_line_width) / sin(center_angle)) * sin(outer_angle); + + + cura::parallel_for( + 0, + cradle_polylines.size(), + [&](const LayerIndex layer_idx) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_polylines[layer_idx].size(); cradle_idx++) + { + cradle_polygons[layer_idx].emplace_back(); + for (size_t cradle_height = 0; cradle_height < cradle_polylines[layer_idx][cradle_idx].first.size(); cradle_height++) + { + size_t max_close = 0; + + for (auto [line_idx, line] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) + { + size_t close = 0; + for (auto [line_idx2, line2] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) + { + if (line_idx == line_idx2) + { + continue; + } + if (vSize(line.front() - line2.front()) < min_distance_between_lines + cradle_line_width) + { + close++; + } + } + max_close = std::max(max_close, close); + } + + Polygons line_tips; + + if (max_close > 0) + { + std::vector> all_tips_center; + // generate trapezoid line tip with front width of support line width, back cradle_width. + { + for (auto [line_idx, line] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) + { + // 2*cradle_line_count would be distance between cradle lines stays the same + coord_t triangle_length = (cradle_line_width - config.support_line_width) / 2 * tan(M_PI / 2 - M_PI / double(3 * cradle_line_count)); + + Point direction = line.back() - line.front(); + Point center_front = line.front() - normal(direction, cradle_line_width / 2); + + + Point direction_up_center = normal(rotate(direction, M_PI / 2), config.support_line_width / 2); + Point center_up = center_front + direction_up_center; + Point center_down = center_front - direction_up_center; + + for (auto existing_center : all_tips_center) + { + Point intersect; + bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); + if (centers_touch + || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) + { + // This cradle line is to close to another. + // Move it back + CenterSubCenterPointPair center_index_data = cradle_polylines[layer_idx][cradle_idx].second; + center_front = center_front + normal(direction, outer_radius - vSize(center_front - center_data[layer_idx][center_index_data.first].second[center_index_data.second])); + center_up = center_front + direction_up_center; + center_down = center_front - direction_up_center; + } + } + Point back_center = center_front + normal(direction, triangle_length); + Point direction_up_back = normal(rotate(direction, M_PI / 2), cradle_line_width / 2); + + Point back_up = back_center + direction_up_back; + Point back_down = back_center - direction_up_back; + + cradle_polylines[layer_idx][cradle_idx].first[cradle_height][line_idx][0] + = back_center + normal(direction, cradle_line_width / 2 - FUDGE_LENGTH / 2); + + + all_tips_center.emplace_back(center_up, center_down); + + Polygon line_tip; + line_tip.add(back_down); + line_tip.add(back_up); + line_tip.add(center_up); + line_tip.add(center_down); + if (line_tip.area() < 0) // todo there has to be a faster way to check if direction is correct + { + line_tip.reverse(); + } + line_tips.add(line_tip); + } + } + } + + + Polygons cradle + = cradle_polylines[layer_idx][cradle_idx].first[cradle_height].offsetPolyLine(cradle_line_width / 2, ClipperLib::jtMiter).unionPolygons(line_tips); + + // Idea: Get number of too close tips => If size then issue else place triangle and maybe recess until fine. + // Needs to be deterministic independent of line order. Sort lines by start xy coord! + + if (cradle_height == 0 && cradle_polylines[layer_idx][cradle_idx].first.size() > 1) + { + Polygons model_shadow = center_data[layer_idx][cradle_polylines[layer_idx][cradle_idx].second.first].first[0]; + if (cradle_base_roof && ! large_cradle_base) + { + cradle = cradle.unionPolygons(model_shadow); + } + else + { + cradle = model_shadow; + } + } + + cradle_polygons[layer_idx][cradle_idx].emplace_back(cradle.unionPolygons()); + + + Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + cradle_height, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + } } }); + finalizeCradleAreas(cradle_polygons, support_free_areas, center_data); } void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof) @@ -1597,6 +2049,7 @@ void TreeSupportTipGenerator::generateTips( core_overhang = core_overhang.difference(TreeSupportUtils::safeOffsetInc(cradle_base_areas[layer_idx], support_outset + EPSILON, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier)); core_overhang = core_overhang.difference(support_free_areas[layer_idx]); + core_overhang = core_overhang.difference(cradle_areas[layer_idx].offset(config.support_line_width)); //todo better ensure it is counted as a tip for (Polygons cradle_base : cradle_base_areas[layer_idx].splitIntoParts(true)) { overhang_processing.emplace_back(cradle_base, OverhangType::CRADLE); From 7eb33bbbc8f831266f77c109d14880aab80cd58a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 19 Feb 2024 03:17:19 +0100 Subject: [PATCH 027/101] Cradle fixes --- src/TreeSupportTipGenerator.cpp | 68 ++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 50a5b2375f..3f8dfbe772 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -818,7 +818,6 @@ std::vector, TreeSupportTipGenerator TreeSupportTipGenerator::generateCradleLines(std::vector>& center_data) { const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance.begin(), cradle_xy_distance.end()); - const coord_t min_cradle_line_length = (cradle_lines_roof ? config.support_roof_line_width : config.support_line_width) * 2; std::vector, CenterSubCenterPointPair>>> cradle_polylines(center_data.size()); cura::parallel_for( @@ -832,7 +831,7 @@ std::vector, TreeSupportTipGenerator { cradle_polylines[layer_idx].emplace_back(std::vector(shadow_centers_pair.first.size()), CenterSubCenterPointPair(center_idx, center_point_idx)); - Polygons removed_lines; + std::vector removed_directions; for (auto [idx, model_shadow] : shadow_centers_pair.first | ranges::views::enumerate) { @@ -876,7 +875,6 @@ std::vector, TreeSupportTipGenerator .offset(cradle_min_xy_distance_delta) .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2)) .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width / 2)); - for (auto line : model_shadow) { for (Point p : line) @@ -910,6 +908,11 @@ std::vector, TreeSupportTipGenerator bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); Point closer = front_closer ? line.front() : line.back(); Point further = front_closer ? line.back() : line.front(); + coord_t cradle_line_length = Polygon(line).polylineLength(); + if(cradle_line_length < cradle_length_min) + { + continue ; + } if (Polygon(line).polylineLength() <= current_cradle_length) { shortened_lines_to_center.add(line); @@ -935,7 +938,7 @@ std::vector, TreeSupportTipGenerator layer_idx + idx, (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, - true).unionPolygons(removed_lines); + true); // Evaluate which lines are still valid after the avoidance was subtracted for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) @@ -954,8 +957,8 @@ std::vector, TreeSupportTipGenerator bool too_short = (vSize(closer - further)) < cradle_length_min; // a cradle line should also be removed if there will be no way to support it too_short - |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < min_cradle_line_length; //todo rename or redo - if (! too_short) + |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config.support_line_width; + if (! too_short) { for (auto [direction_idx, direction] : vector_distance_map | ranges::views::enumerate) { @@ -974,9 +977,24 @@ std::vector, TreeSupportTipGenerator } } - if (too_short || (! keep_line && found_candidate)) + bool was_removed = false; + + for (auto [direction_idx, direction] : removed_directions | ranges::views::enumerate) { - removed_lines.add(Polygon(line).offset(FUDGE_LENGTH)); //todo Maybe direction instead of area? + double cosine = (dot(direction, current_direction)) / (vSize(current_direction) * vSize(direction)); + if (cosine > 0.99) + { + was_removed = true; + + } + } + + if (too_short || was_removed || (! keep_line && found_candidate)) + { + if(!was_removed) + { + removed_directions.emplace_back(current_direction); + } shortened_lines_to_center[line_idx].clear(); } } @@ -1094,7 +1112,17 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps(std::vector()); //Ensure largest idx is removed first. + //remove duplicates + lines_to_remove[cradle_idx].erase(std::unique(lines_to_remove[cradle_idx].begin(), lines_to_remove[cradle_idx].end()), lines_to_remove[cradle_idx].end()); for(size_t remove_idx = 0; remove_idx < lines_to_remove[cradle_idx].size(); remove_idx++) { removed_lines[layer_idx].add((*all_cradles_on_layer[cradle_idx])[lines_to_remove[cradle_idx][remove_idx]]); @@ -1229,14 +1269,12 @@ void TreeSupportTipGenerator::finalizeCradleAreas( } cradle = cradle.unionPolygons(); + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); cradle_base_areas[layer_idx].add(cradle); } else { - - Polygons closed = cradle.unionPolygons( - cradle.offset(closing_dist + 2 * config.branch_radius, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); //todo? - volumes_.addAreaToAntiPreferred(cradle, layer_idx + idx); + volumes_.addAreaToAntiPreferred(cradle.offset(config.xy_distance), layer_idx + idx); { std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); @@ -1270,10 +1308,12 @@ void TreeSupportTipGenerator::finalizeCradleAreas( { if (large_cradle_line_tips) { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); cradle_base_areas[layer_idx + idx - idx_delta_down].add(cradle_part); } else { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); cradle_overhang[layer_idx + idx - idx_delta_down].add(cradle_part); } } From 91f6fd5df66abdbcf4bac2d7923b5d0b40f1898d Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:49:27 +0100 Subject: [PATCH 028/101] Work im progress refactoring to enable removing cradle-lines while the tree pathing is calculated. --- include/TreeModelVolumes.h | 27 +- include/TreeSupport.h | 64 +- include/TreeSupportCradle.h | 190 +++++ include/TreeSupportElement.h | 62 +- include/TreeSupportTipGenerator.h | 68 +- src/TreeModelVolumes.cpp | 187 ++++- src/TreeSupport.cpp | 453 +++++++++-- src/TreeSupportTipGenerator.cpp | 1259 ++++++++++++++--------------- 8 files changed, 1497 insertions(+), 813 deletions(-) create mode 100644 include/TreeSupportCradle.h diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index fb0091b511..b3d65ea716 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -147,12 +147,14 @@ class TreeModelVolumes /*! * \brief Round \p radius upwards to the maximum that would still round up to the same value as the provided one. * - * \param radius The radius of the node of interest + * \param radius The radius of the element of interest * \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; + LayerIndex getFirstAntiPreferredLayerIdx(); + /*! * \brief Provide hints which areas should be avoided in the future. * \param area The area that should be avoided in the future. @@ -160,6 +162,8 @@ class TreeModelVolumes */ void addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx); + void precalculateAntiPreferred(); + /*! * \brief Get areas that were additionally set to be avoided * \param layer_idx The layer said area is on. @@ -168,6 +172,14 @@ class TreeModelVolumes */ const Polygons& getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius); + /*! + * \brief Get avoidance areas for areas that were additionally set to be avoided + * \param layer_idx The layer said area is on. + * \param radius The radius of the node of interest. + * \returns The area that should be avoided + */ + const Polygons& getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist); + /*! * \brief Get areas that were are classified as support blocker * \param layer_idx The layer said area is on. @@ -509,6 +521,13 @@ class TreeModelVolumes */ size_t max_cradle_dtt = 0; + LayerIndex first_anti_preferred_layer_idx = 0; + + /*! + * \brief radii for which avoidance was already precalculated. Used to calculate anti preferred avoidance. + */ + std::deque precalculated_avoidance_radii; + /*! * \brief Caches for the collision, avoidance and areas on the model where support can be placed safely * at given radius and layer indices. @@ -567,6 +586,12 @@ class TreeModelVolumes mutable std::unordered_map anti_preferred_; std::unique_ptr critical_anti_preferred_ = std::make_unique(); + mutable std::unordered_map anti_preferred_cache_; + mutable std::unordered_map anti_preferred_cache_to_model_; + mutable std::unordered_map anti_preferred_cache_collision; + std::unique_ptr critical_anti_preferred_caches = std::make_unique(); + + std::unique_ptr critical_progress = std::make_unique(); Simplify simplifier = Simplify(0, 0, 0); // a simplifier to simplify polygons. Will be properly initialised in the constructor. diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 5f6abdac73..527f1c5437 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -6,6 +6,7 @@ #include "TreeModelVolumes.h" #include "TreeSupportBaseCircle.h" +#include "TreeSupportCradle.h" #include "TreeSupportElement.h" #include "TreeSupportEnums.h" #include "TreeSupportSettings.h" @@ -86,8 +87,10 @@ class TreeSupport * \param mesh[in] The mesh that is currently processed. * \param move_bounds[out] Storage for the influence areas. * \param storage[in] Background storage, required for adding roofs. + * \param cradle_data_model[out] All generated cradles, with its corresponding cradle lines. + */ - void generateInitialAreas(const SliceMeshStorage& mesh, std::vector>& move_bounds, SliceDataStorage& storage); + void generateInitialAreas(const SliceMeshStorage& mesh, std::vector>& move_bounds, SliceDataStorage& storage,std::vector>& cradle_data_model); /*! @@ -127,9 +130,10 @@ class TreeSupport * \param to_bp_areas[in] The Elements of the current Layer that will reach the buildplate. * Value is the influence area where the center of a circle of support may be placed. * \param to_model_areas[in] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is - * not forced to. Value is the influence area where the center of a circle of support may be placed. \param influence_areas[in] The Elements of the current Layer without - * avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed. \param - * layer_idx[in] The current layer. + * not forced to. Value is the influence area where the center of a circle of support may be placed. + * \param influence_areas[in] The Elements of the current Layer without + * avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed. + * \param layer_idx[in] The current layer. */ void mergeInfluenceAreas(PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, LayerIndex layer_idx); @@ -179,27 +183,52 @@ class TreeSupport * * \param to_bp_areas[out] Influence areas that can reach the buildplate * \param to_model_areas[out] Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also - * considered valid areas to the model. This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area. \param - * influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the - * user-supplied settings. \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging. \param last_layer[in] Influence areas - * of the current layer. \param layer_idx[in] Number of the current layer. \param mergelayer[in] Will the merge method be called on this layer. This information is required as - * some calculation can be avoided if they are not required for merging. + * considered valid areas to the model. This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area. + * \param influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the + * user-supplied settings. + * \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging. + * \param last_layer[in] Influence areas of the current layer. + * \param layer_idx[in] Number of the current layer. + * \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging. */ void increaseAreas( PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, - std::vector& bypass_merge_areas, + PropertyAreas& bypass_merge_areas, const std::vector& last_layer, const LayerIndex layer_idx, const bool mergelayer); + /*! + * \brief Evaluate which cradle lines will have to be removed, remove them and remove tips that support said lines + * + + * \param to_bp_areas[in,out] The Elements of the current Layer that will reach the buildplate. + * Value is the influence area where the center of a circle of support may be placed. + * \param to_model_areas[in,out] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is + * not forced to. Value is the influence area where the center of a circle of support may be placed. + * \param influence_areas[in,out] The Elements of the current Layer without + * avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed. + * \param layer_idx[in] The current layer. + * \param move_bounds[in,out] All currently existing influence areas + * \param cradle_data[in] Information about all cradle lines on this layer. + */ + void handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, + PropertyAreas& to_model_areas, + PropertyAreas& influence_areas, + PropertyAreas& bypass_merge_areas, + LayerIndex layer_idx, + std::vector>& move_bounds, + std::vector>& cradle_data); + /*! * \brief Propagates influence downwards, and merges overlapping ones. * * \param move_bounds[in,out] All currently existing influence areas + * \param cradle_data[in,out] All currently existing cradles, with its corresponding cradle lines. */ - void createLayerPathing(std::vector>& move_bounds); + void createLayerPathing(std::vector>& move_bounds, std::vector>& cradle_data); /*! @@ -268,8 +297,15 @@ class TreeSupport * \param support_roof_storage[in,out] Areas where support was replaced with roof. * \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 cradle_data[in] All currently existing cradles, with its corresponding cradle lines. + */ - void generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage, std::vector>& layer_tree_polygons); + void generateSupportSkin(std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_roof_storage, + SliceDataStorage& storage, + std::vector>& layer_tree_polygons, + std::vector>& cradle_data); /*! * \brief Filters out holses that would cause support to be printed mid-air. @@ -292,8 +328,10 @@ 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); + void drawAreas(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data); /*! * \brief Settings with the indexes of meshes that use these settings. diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h new file mode 100644 index 0000000000..589c435d01 --- /dev/null +++ b/include/TreeSupportCradle.h @@ -0,0 +1,190 @@ +// +// Created by Thomas on 04/03/2024. +// + +#ifndef CURAENGINE_TREESUPPORTCRADLE_H +#define CURAENGINE_TREESUPPORTCRADLE_H +#include "TreeSupportElement.h" +#include "TreeSupportEnums.h" +#include "utils/Coord_t.h" +#include "utils/polygon.h" +#include + +namespace cura +{ + +struct TreeSupportCradleLine +{ + //required to shrink a vector using resize + TreeSupportCradleLine() + { + spdlog::error("Dummy TreeSupportCradleLine constructor called"); + } + + TreeSupportCradleLine(Polygon line, LayerIndex layer_idx) + : line(line) + , layer_idx(layer_idx) + { + } + Polygons area; + Polygon line; + Polygon removed_line; + std::vector tips; + LayerIndex layer_idx; + bool is_base = false; + + void addLineToRemoved(Polygon& line_to_add) + { + if (removed_line.empty()) + { + removed_line.add(line_to_add.front()); + removed_line.add(line_to_add.back()); + } + else + { + if (vSize2(removed_line.front() - removed_line.back()) < vSize2(line_to_add.front() - removed_line.back())) + { + removed_line.front() = line_to_add.front(); + } + + if (vSize2(removed_line.front() - removed_line.back()) < vSize2(removed_line.front() - line_to_add.back())) + { + removed_line.back() = line_to_add.back(); + } + } + } +}; + +struct TreeSupportCradleOverhangInformation +{ + TreeSupportCradleOverhangInformation() + : TreeSupportCradleOverhangInformation(Polygons(), false) + { + } + TreeSupportCradleOverhangInformation(Polygons area, bool is_line, bool is_roof = false, LayerIndex line_layer_idx = -1, int32_t line_idx = -1) + : area(area) + , is_line(is_line) + , is_roof(is_roof) + , line_information(std::pair(line_layer_idx, line_idx)) + { + + } + Polygons area; + bool is_line; + std::pair line_information = std::pair(-1, -1); + bool is_roof; +}; + +struct TreeSupportCradle +{ + std::vector> lines; + bool is_roof; + LayerIndex layer_idx; + std::vector base_below; + Point center; + size_t shadow_idx; + std::unordered_map> overhang; + + size_t config_cradle_layers_min; + coord_t config_cradle_length_min; + + TreeSupportCradle(LayerIndex layer_idx, Point center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min) + : layer_idx(layer_idx) + , center(center) + , shadow_idx(shadow_idx) + , is_roof(roof) + , config_cradle_layers_min(cradle_layers_min) + , config_cradle_length_min(cradle_length_min) + { + } + + + std::optional getCradleLineOfIndex(LayerIndex requested_layer_idx, size_t cradle_line_idx) + { + if (cradle_line_idx < lines.size()) + { + for (size_t height_idx = 0; height_idx < lines[cradle_line_idx].size(); height_idx++) + { + if (lines[cradle_line_idx][height_idx].layer_idx == requested_layer_idx) + { + return &lines[cradle_line_idx][height_idx]; + } + } + } + return {}; + } + + void verifyLines() + { + for (size_t line_idx = 0; line_idx < lines.size(); line_idx++) + { + if (lines[line_idx].size() < config_cradle_layers_min) + { + lines[line_idx].clear(); + continue; + } + LayerIndex previous_layer_idx; + + for (size_t up_idx = 0; up_idx < lines[line_idx].size(); up_idx++) + { + if(!lines[line_idx][up_idx].is_base) + { + previous_layer_idx = lines[line_idx][up_idx].layer_idx; + break; + } + } + for (size_t up_idx = 1; up_idx < lines[line_idx].size(); up_idx++) + { + if(!lines[line_idx][up_idx].is_base) + { + if (lines[line_idx][up_idx].layer_idx > previous_layer_idx + up_idx || + lines[line_idx][up_idx].line.size()<2 || + lines[line_idx][up_idx].line.polylineLength() < config_cradle_length_min) + { + if (up_idx <= config_cradle_layers_min) + { + + spdlog::info("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx,line_idx,lines[line_idx][up_idx].layer_idx); + lines[line_idx].clear(); + break; + } + else + { + spdlog::info("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx,line_idx,up_idx); + lines[line_idx].resize(up_idx-1); + break; + } + } + } + } + } + } +}; + +struct CradlePresenceInformation +{ + CradlePresenceInformation(TreeSupportCradle* cradle,LayerIndex layer_idx,size_t line_idx): + cradle(cradle), + layer_idx(layer_idx), + line_idx(line_idx) + {} + TreeSupportCradle* cradle; + LayerIndex layer_idx; + size_t line_idx; + + TreeSupportCradleLine* getCradleLine() + { + return cradle->getCradleLineOfIndex(layer_idx,line_idx).value(); + } + + bool cradleLineExists() + { + return cradle->getCradleLineOfIndex(layer_idx,line_idx).has_value(); + } + +}; + + +} + +#endif // CURAENGINE_TREESUPPORTCRADLE_H diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 49daca295b..ba37085a5a 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -16,6 +16,7 @@ namespace cura { +struct CradlePresenceInformation; struct AreaIncreaseSettings { AreaIncreaseSettings() : @@ -24,6 +25,7 @@ struct AreaIncreaseSettings increase_radius(false), no_error(false), use_min_distance(false), + use_anti_preferred(false), move(false) { } @@ -35,6 +37,7 @@ struct AreaIncreaseSettings bool increase_radius, bool simplify, bool use_min_distance, + bool use_anti_preferred, bool move ) : type(type), @@ -42,6 +45,7 @@ struct AreaIncreaseSettings increase_radius(increase_radius), no_error(simplify), use_min_distance(use_min_distance), + use_anti_preferred(use_anti_preferred), move(move) { } @@ -51,6 +55,7 @@ struct AreaIncreaseSettings bool increase_radius; bool no_error; bool use_min_distance; + bool use_anti_preferred; bool move; bool operator==(const AreaIncreaseSettings& other) const @@ -99,7 +104,8 @@ struct TreeSupportElement 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)), + can_avoid_anti_preferred(false), //todo init? + last_area_increase(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)), missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), skip_ovalisation(skip_ovalisation), all_tips({ target_position }), @@ -127,6 +133,7 @@ struct TreeSupportElement supports_roof(elem.supports_roof), dont_move_until(elem.dont_move_until), can_use_safe_radius(elem.can_use_safe_radius), + can_avoid_anti_preferred(elem.can_avoid_anti_preferred), last_area_increase(elem.last_area_increase), missing_roof_layers(elem.missing_roof_layers), skip_ovalisation(elem.skip_ovalisation), @@ -138,36 +145,6 @@ struct TreeSupportElement parents.insert(parents.begin(), elem.parents.begin(), elem.parents.end()); } - /*! - * \brief Create a new Element for one layer below the element of the pointer supplied. - */ - explicit TreeSupportElement(TreeSupportElement* element_above) : - target_height(element_above->target_height), - target_position(element_above->target_position), - next_position(element_above->next_position), - next_height(element_above->next_height), - effective_radius_height(element_above->effective_radius_height), - to_buildplate(element_above->to_buildplate), - distance_to_top(element_above->distance_to_top + 1), - area(element_above->area), - result_on_layer(Point(-1, -1)), // set to invalid as we are a new node on a new layer - increased_to_model_radius(element_above->increased_to_model_radius), - to_model_gracious(element_above->to_model_gracious), - buildplate_radius_increases(element_above->buildplate_radius_increases), - use_min_xy_dist(element_above->use_min_xy_dist), - supports_roof(element_above->supports_roof), - dont_move_until(element_above->dont_move_until), - can_use_safe_radius(element_above->can_use_safe_radius), - last_area_increase(element_above->last_area_increase), - missing_roof_layers(element_above->missing_roof_layers), - skip_ovalisation(false), - all_tips(element_above->all_tips), - influence_area_limit_area(element_above->influence_area_limit_area), - influence_area_limit_range(element_above->influence_area_limit_range), - influence_area_limit_active(element_above->influence_area_limit_active) - { - parents = { element_above }; - } // ONLY to be called in merge as it assumes a few assurances made by it. TreeSupportElement @@ -238,6 +215,7 @@ struct TreeSupportElement first.last_area_increase.increase_radius || second.last_area_increase.increase_radius, first.last_area_increase.no_error || second.last_area_increase.no_error, first.last_area_increase.use_min_distance && second.last_area_increase.use_min_distance, + first.can_avoid_anti_preferred && second.can_avoid_anti_preferred, first.last_area_increase.move || second.last_area_increase.move ); @@ -340,6 +318,11 @@ struct TreeSupportElement */ bool can_use_safe_radius; + /*! + * \brief An influence area can avoid anti-preferred when the difference with it is non empty. + */ + bool can_avoid_anti_preferred; + /*! * \brief Settings used to increase the influence area to its current state. */ @@ -380,6 +363,9 @@ struct TreeSupportElement */ std::vector additional_ovalization_targets; + std::shared_ptr cradle_line; + + bool operator==(const TreeSupportElement& other) const { @@ -458,6 +444,20 @@ struct TreeSupportElement { return result_on_layer != Point(-1, -1); } + /*! + * \brief Create a new Element for one layer below the element. + */ + + TreeSupportElement createNewElement() + { + TreeSupportElement result(*this); + result.parents = { this }; + result.distance_to_top += 1; + result.skip_ovalisation = false; + result.result_on_layer = Point(-1, -1); + //result.area = nullptr; + return result; + } }; diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 986643a4f7..f69d393c19 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -16,11 +16,11 @@ #include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" +#include "TreeSupportCradle.h" namespace cura { - class TreeSupportTipGenerator { public: @@ -35,6 +35,7 @@ class TreeSupportTipGenerator * \param additional_support_areas[out] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. Should already be initialised. * \param placed_support_lines_support_areas[out] Support-lines that were already placed represented as the area the lines will take when printed. * \param support_free_areas[out] Areas where no support (including roof) of any kind is to be drawn. + * \param cradle_data_export[out] Generated cradle lines. * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ void generateTips( @@ -42,7 +43,10 @@ class TreeSupportTipGenerator const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, - std::vector& placed_support_lines_support_areas, std::vector& placed_fake_roof_areas, std::vector& support_free_areas); + std::vector& placed_support_lines_support_areas, + std::vector& placed_fake_roof_areas, + std::vector& support_free_areas, + std::vector>& cradle_data_export); private: enum class LineStatus @@ -153,19 +157,31 @@ class TreeSupportTipGenerator */ void calculateFloatingParts(const SliceMeshStorage& mesh); - using CradleShadowCenterPair = std::pair,std::vector>; - using CenterSubCenterPointPair = std::pair; //todo rename + /*! + * \brief Generate the center points of all generated cradles. + * \param shadow_data[in] The accumulated model of given pointy overhangs + * \returns The accumulated model of given pointy overhangs + */ + std::vector>> generateCradleCenters(const SliceMeshStorage& mesh); - //todo one function that calculates centers and corresponding shadows Returns: Model_shadows with corresponding centers - std::vector> generateCradleCenters(const SliceMeshStorage& mesh); + /*! + * \brief Generate lines from center and model information + * Only area up to the required maximum height are stored. + * \param shadow_data[in] The accumulated model of given pointy overhangs + */ + void generateCradleLines(std::vector>>& shadow_data); + + /*! + * \brief Ensures cradle-lines do not intersect with each other. + */ + void cleanCradleLineOverlaps(); - //todo maybe one function that moves cradles up if they dont do anything yet - //todo one function that generates the lines. Returns vector of lines relative to a certain cradle index. WHERE GET INDEX? - std::vector,CenterSubCenterPointPair>>> generateCradleLines(std::vector>& center_data); - //todo one function that cleans overlaps between lines. Updates cradle lines previously generated - void cleanCradleLineOverlaps(std::vector,CenterSubCenterPointPair>>>& cradle_polylines); - //todo one function that renders the cradle - void finalizeCradleAreas( std::vector>>& cradle_polygons,std::vector& support_free_areas,std::vector>& center_data); + /*! + * \brief Finishes the cradle areas that represent cradle base and lines and calculates overhang for them. + * \param shadow_data[in] The accumulated model of given pointy overhangs + * \param support_free_areas[out] Areas where support will have to be removed, to guarantee that a line is around it. + */ + void generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas); /*! @@ -200,7 +216,7 @@ class TreeSupportTipGenerator * \param skip_ovalisation[in] Whether the tip may be ovalized when drawn later. * \param additional_ovalization_targets[in] Additional targets the ovalization should reach. */ - void addPointAsInfluenceArea( + TreeSupportElement* addPointAsInfluenceArea( std::vector>& move_bounds, std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); @@ -216,7 +232,7 @@ class TreeSupportTipGenerator * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param connect_points [in] If the points of said line should be connected by ovalization. */ - void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, + std::vector addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, bool connect_points); /*! @@ -356,19 +372,7 @@ class TreeSupportTipGenerator std::vector roof_tips_drawn; - /*! - * \brief Areas that will become cradle. - */ - std::vector cradle_areas; - - /*! - * \brief Overhang of a cradle that will need support. May be on the same layer as the cradle. - */ - std::vector cradle_overhang; - /*! - * \brief Areas below the first layer of a cradle. - */ - std::vector cradle_base_areas; + std::vector> cradle_data; /*! * \brief Amount of layers of the cradle to support pointy overhangs. @@ -378,7 +382,7 @@ class TreeSupportTipGenerator /*! * \brief Minimum amount of layers of the cradle to support pointy overhangs. */ - size_t minimum_cradle_layers; // Minimal height of the cradle + size_t cradle_layers_min; // Minimal height of the cradle /*! * \brief Amount of lines used for the cradle. @@ -391,7 +395,7 @@ class TreeSupportTipGenerator coord_t cradle_length; /*! - * \brief Minimum length of lines used for the cradle. + * \brief Minimum length of lines used for the cradle. TODO Width is effectively added to length ... fix or document? */ coord_t cradle_length_min; @@ -427,7 +431,7 @@ class TreeSupportTipGenerator size_t cradle_tip_dtt; /*! - * \brief If the cradle lines should also be supported by larger tips. + * \brief If the cradle lines should also be supported by larger tips. todo */ bool large_cradle_line_tips; @@ -454,4 +458,4 @@ class TreeSupportTipGenerator } // namespace cura -#endif /* TREESUPPORT_H */ \ No newline at end of file +#endif /* TREESUPPORTTIPGENERATOR_H */ \ No newline at end of file diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index dd4b27faab..585e8151da 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -78,7 +78,8 @@ TreeModelVolumes::TreeModelVolumes( min_maximum_deviation = std::min(min_maximum_deviation, data_pair.first.get("meshfix_maximum_deviation")); min_maximum_resolution = std::min(min_maximum_resolution, data_pair.first.get("meshfix_maximum_resolution")); min_maximum_area_deviation = std::min(min_maximum_area_deviation, data_pair.first.get("meshfix_maximum_extrusion_area_deviation")); - max_cradle_layers = std::max(coord_t(max_cradle_layers), retrieveSetting(data_pair.first,"support_tree_cradle_height") / config.layer_height); + const coord_t extra_cradle_distance = round_divide(retrieveSetting(data_pair.first, "support_tree_cradle_z_distance") , config.layer_height); + max_cradle_layers = std::max(coord_t(max_cradle_layers), extra_cradle_distance + retrieveSetting(data_pair.first,"support_tree_cradle_height") / config.layer_height); max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * retrieveSetting(data_pair.first,"support_tree_cradle_base_tip_percentage") / 100.0)); } @@ -234,6 +235,7 @@ void TreeModelVolumes::precalculate(LayerIndex max_layer) std::deque relevant_avoidance_radiis_to_model; relevant_avoidance_radiis.insert(relevant_avoidance_radiis.end(), radius_until_layer.begin(), radius_until_layer.end()); relevant_avoidance_radiis_to_model.insert(relevant_avoidance_radiis_to_model.end(), radius_until_layer.begin(), radius_until_layer.end()); + precalculated_avoidance_radii.insert(precalculated_avoidance_radii.end(), radius_until_layer.begin(), radius_until_layer.end()); // Append additional radiis needed for collision. @@ -577,19 +579,121 @@ void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex la } +void TreeModelVolumes::precalculateAntiPreferred() +{ + + cura::parallel_for( + 0, + precalculated_avoidance_radii.size(), + [&, precalculated_avoidance_radiis=precalculated_avoidance_radii](const size_t key_idx) + { + const coord_t radius = precalculated_avoidance_radiis[key_idx].first; + const LayerIndex max_required_layer = precalculated_avoidance_radiis[key_idx].second; + + const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist * 1.9); + RadiusLayerPair key(radius, 0); + + Polygons latest_avoidance; + Polygons latest_avoidance_to_model; + Polygons latest_avoidance_collision; + LayerIndex start_layer = 0; + std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + std::vector> data_to_model(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + std::vector> data_collision(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + std::vector> data_raw_anti(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + + bool encountered_anti = false; + // ### main loop doing the calculation + for (const LayerIndex layer : ranges::views::iota(static_cast(start_layer), max_required_layer + 1UL)) + { + key.second = layer; + RadiusLayerPair key_0(0, layer); + Polygons anti; + { + std::lock_guard critical_section(*critical_anti_preferred_); + anti=anti_preferred_[key_0]; + } + + if(!encountered_anti && ! anti.empty()) + { + encountered_anti = true; + if (support_rest_preference == RestPreference::BUILDPLATE) + { + latest_avoidance = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, false, true); + } + if(support_rests_on_model) + { + latest_avoidance_to_model = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, true, true); + } + if(max_layer_idx_without_blocker <= layer && support_rests_on_model) + { + latest_avoidance_collision = getAvoidance(radius,layer,AvoidanceType::COLLISION, true, true); + } + + std::lock_guard critical_section(*critical_anti_preferred_); + first_anti_preferred_layer_idx = layer; + } + if(!encountered_anti) + { + continue; + } + + Polygons col = getCollisionHolefree(radius, layer, true); + anti = anti.unionPolygons().offset(radius).unionPolygons(); + data_raw_anti[layer] = std::pair(key, anti); + + if (support_rest_preference == RestPreference::BUILDPLATE) + { + latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + Polygons next_latest_avoidance = simplifier.polygon(latest_avoidance); + latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); + data[layer] = std::pair(key, latest_avoidance); + } + + if(support_rests_on_model) + { + latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + Polygons next_latest_avoidance_to_model = simplifier.polygon(latest_avoidance_to_model); + latest_avoidance_to_model = next_latest_avoidance_to_model.unionPolygons(latest_avoidance_to_model); + latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius,layer)); + data_to_model[layer] = std::pair(key, latest_avoidance_to_model); + + } + + if(max_layer_idx_without_blocker <= layer && support_rests_on_model) + { + latest_avoidance_collision = safeOffset(latest_avoidance_collision, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius), ClipperLib::jtRound); + latest_avoidance_collision = latest_avoidance_collision.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); + data_collision[layer] = std::pair(key, latest_avoidance_collision); + } + } + { + std::lock_guard critical_section(*critical_anti_preferred_caches); + anti_preferred_cache_.insert(data.begin(), data.end()); + anti_preferred_cache_to_model_.insert(data_to_model.begin(), data_to_model.end()); + anti_preferred_cache_collision.insert(data_collision.begin(), data_collision.end()); + } + { + std::lock_guard critical_section(*critical_anti_preferred_); + anti_preferred_.insert(data_raw_anti.begin(), data_raw_anti.end()); + } + }); +} const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius) { - coord_t ceiled_radius = ceilRadius(radius); RadiusLayerPair key(ceilRadius(ceiled_radius),layer_idx); - std::optional> result; + + std::unordered_map* cache_ptr = &anti_preferred_; { - std::lock_guard critical_section(*critical_anti_preferred_); - result = getArea(anti_preferred_, key); + std::lock_guard critical_section(*critical_anti_preferred_caches); + result = getArea(*cache_ptr, key); } + if (result) { return result.value().get(); @@ -601,22 +705,75 @@ const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, co result = getArea(anti_preferred_, key); } - if(!result) + if(!result || result.value().get().empty()) //todo where and why are empty areas inserted? { return empty_polygon; } if (precalculated) { - //todo enable logging when precalc for anti preferred was implemented - // spdlog::warn("Had to calculate anti preferred at radius {} and layer {}, but precalculate was called. Performance may suffer!", ceiled_radius, key.second); + spdlog::warn("Missing anti preferred area at radius {} and layer {} Returning Empty! Result had area of {}", ceiled_radius, key.second, result.value().get().area()); } - key.first = ceiled_radius; - std::lock_guard critical_section(*critical_anti_preferred_); + return empty_polygon; + +} + + +const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) +{ + + coord_t ceiled_radius = ceilRadius(radius,min_xy_dist); + RadiusLayerPair key(ceilRadius(ceiled_radius),layer_idx); + std::optional> result; + + std::unordered_map* cache_ptr = nullptr; + + if(type == AvoidanceType::COLLISION) + { + if(max_layer_idx_without_blocker <= layer_idx && to_model) + { + cache_ptr = &anti_preferred_cache_collision; + } + else + { + cache_ptr = &anti_preferred_; + } + } + else if(to_model) + { + cache_ptr = &anti_preferred_cache_to_model_; + } + else + { + cache_ptr = &anti_preferred_cache_; - anti_preferred_[key] = result->get().offset(ceiled_radius + current_min_xy_dist); + } + + { + std::lock_guard critical_section(*critical_anti_preferred_caches); + result = getArea(*cache_ptr, key); + } + if (result) + { + return result.value().get(); + } + + { + key.first = 0; + std::lock_guard critical_section(*critical_anti_preferred_); + result = getArea(anti_preferred_, key); + } - return anti_preferred_[key]; + if(!result || result.value().get().empty()) + { + return empty_polygon; + } + + if (precalculated) + { + spdlog::warn("Missing anti preferred calculated at radius {} and layer {} and type {} to model {}, but precalculate was called. Returning Empty!", ceiled_radius, key.second, type == AvoidanceType::COLLISION, to_model); + } + return empty_polygon; } const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) @@ -643,6 +800,12 @@ coord_t TreeModelVolumes::getRadiusNextCeil(coord_t radius, bool min_xy_dist) co return ceilRadius(radius, min_xy_dist) - (min_xy_dist ? 0 : current_min_xy_dist_delta); } +LayerIndex TreeModelVolumes::getFirstAntiPreferredLayerIdx() +{ + return first_anti_preferred_layer_idx; +} + + bool TreeModelVolumes::checkSettingsEquality(const Settings& me, const Settings& other) const { return TreeSupportSettings(me) == TreeSupportSettings(other); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index a0c523983e..80d440ace7 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -164,15 +164,26 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) precalculate(storage, processing.second); const auto t_precalc = std::chrono::high_resolution_clock::now(); + std::vector> cradle_data; // ### Place tips of the support tree for (size_t mesh_idx : processing.second) { - generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage); + std::vector> cradle_data_mesh; + generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage, cradle_data_mesh); + if(cradle_data.size()(t_precalc - t_start).count(); @@ -250,10 +261,13 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector>& move_bounds, SliceDataStorage& storage) +void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, + std::vector>& move_bounds, + SliceDataStorage& storage, + std::vector>& cradle_data_model) { TreeSupportTipGenerator tip_gen(storage, mesh, volumes_); - tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, placed_fake_roof_areas , support_free_areas); + tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, placed_fake_roof_areas , support_free_areas, cradle_data_model); } void TreeSupport::mergeHelper( @@ -298,6 +312,9 @@ void TreeSupport::mergeHelper( // But because a different collision may be removed from the in drawArea generated circles, this assumption could be wrong. const bool merging_different_range_limits = reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; + const bool merging_cant_avoiding_pref = !reduced_check.first.can_avoid_anti_preferred || !influence.first.can_avoid_anti_preferred; //todo just put them in bypass merge then? + + coord_t increased_to_model_radius = 0; size_t larger_to_model_dtt = 0; @@ -329,7 +346,7 @@ void TreeSupport::mergeHelper( // would merge to model before it is known they will even been drawn the merge is skipped if (merging_min_and_regular_xy || merging_gracious_and_non_gracious || increased_to_model_radius > config.max_to_model_radius_increase || (! merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && ! reduced_check.first.supports_roof && ! influence.first.supports_roof) - || merging_different_range_limits) + || merging_different_range_limits || merging_cant_avoiding_pref) { continue; } @@ -644,7 +661,7 @@ std::optional TreeSupport::increaseSingleArea( const coord_t overspeed, const bool mergelayer) { - TreeSupportElement current_elem(parent); // Also increases DTT by one. + TreeSupportElement current_elem = parent->createNewElement(); // Also increases DTT by one. Polygons check_layer_data; if (settings.increase_radius) { @@ -731,11 +748,71 @@ std::optional TreeSupport::increaseSingleArea( to_model_data = to_model_data.unionPolygons(to_model_data.offsetPolyLine(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); } - } + + coord_t actual_radius = config.getRadius(current_elem); + // Removing cradle areas from influence areas if possible. + Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx-1, actual_radius); + bool anti_preferred_applied = false; + if(!anti_preferred_areas.empty()) + { + + + //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. Todo? + coord_t anti_radius_extra = std::max(settings.increase_speed-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); //todo better real radius. How prevent problems? + if(anti_radius_extra) + { + anti_preferred_areas = anti_preferred_areas.offset(anti_radius_extra).unionPolygons(); + } + if (current_elem.to_buildplate) + { + Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); + if(to_bp_without_anti.area()>EPSILON || settings.use_anti_preferred) + { + to_bp_data = to_bp_without_anti; + Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + to_model_data = to_model_data_without_anti; + Polygons increased_without_anti = increased.difference(anti_preferred_areas); + increased = increased_without_anti; + anti_preferred_applied = true; + } + } + else + { + Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + if(to_model_data_without_anti.area()>EPSILON || settings.use_anti_preferred) + { + to_model_data = to_model_data_without_anti; + Polygons increased_without_anti = increased.difference(anti_preferred_areas); + increased = increased_without_anti; + anti_preferred_applied = true; + } + } + } check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + // Remove areas where the branch should not be if possible. + // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. + if(volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred) + { + const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance ); + if (current_elem.to_buildplate) + { + to_bp_data = to_bp_data.difference(anti_preferred); + to_model_data = to_model_data.difference(anti_preferred); + } + else + { + to_model_data = to_model_data.difference(anti_preferred); + } + check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + if(check_layer_data.area() > 1) + { + current_elem.can_avoid_anti_preferred = true; + } + } + if (settings.increase_radius && check_layer_data.area() > 1) { std::function validWithRadius = [&](coord_t next_radius) @@ -749,7 +826,16 @@ std::optional TreeSupport::increaseSingleArea( if (current_elem.to_buildplate) { // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. + to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); + if(settings.use_anti_preferred) //todo ensure anti pref contains avoidance then we can save one diff here + { + to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); + } + else if(anti_preferred_applied) + { + to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); + } } Polygons to_model_data_2; if (config.support_rests_on_model && ! current_elem.to_buildplate) @@ -762,6 +848,14 @@ std::optional TreeSupport::increaseSingleArea( true, settings.use_min_distance)) .unionPolygons(); + if(settings.use_anti_preferred) //todo ensure anti pref contains avoidance then we can save one diff here + { + to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); + } + else if(anti_preferred_applied) + { + to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); + } } Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; @@ -905,38 +999,7 @@ std::optional TreeSupport::increaseSingleArea( } } - const Polygons& anti_preferred = volumes_.getAntiPreferredAreas(layer_idx - 1, radius); - - - // Remove areas where the branch should not be if possible. - // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. - if(!anti_preferred.empty() && check_layer_data.area() > 1) - { - - if (current_elem.to_buildplate) - { - Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred); - if (to_bp_without_anti.area() > 1 || settings.type == AvoidanceType::SLOW) - { - to_bp_data = to_bp_without_anti; - to_model_data = to_model_data.difference(anti_preferred); - increased = increased.difference(anti_preferred); - } - } - else - { - Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred); - if (to_model_data_without_anti.area() > 1 || settings.type == AvoidanceType::SLOW) - { - to_bp_data = to_bp_data.difference(anti_preferred); - to_model_data = to_model_data_without_anti; - increased = increased.difference(anti_preferred); - } - } - - } - check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; return check_layer_data.area() > 1 ? std::optional(current_elem) : std::optional(); @@ -946,7 +1009,7 @@ void TreeSupport::increaseAreas( PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, - std::vector& bypass_merge_areas, + PropertyAreas& bypass_merge_areas, const std::vector& last_layer, const LayerIndex layer_idx, const bool mergelayer) @@ -958,7 +1021,7 @@ void TreeSupport::increaseAreas( [&](const size_t idx) { TreeSupportElement* parent = last_layer[idx]; - TreeSupportElement elem(parent); // Also increases dtt. + TreeSupportElement elem = parent->createNewElement(); // Also increases dtt. // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall. const Polygons wall_restriction = volumes_.getWallRestriction(config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist); @@ -1033,6 +1096,7 @@ void TreeSupport::increaseAreas( constexpr bool increase_radius = true; constexpr bool no_error = true; constexpr bool use_min_radius = true; + constexpr bool use_anti_preferred = true; constexpr bool move = true; // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found @@ -1066,6 +1130,7 @@ void TreeSupport::increaseAreas( increase_radius, elem.last_area_increase.no_error, ! use_min_radius, + use_anti_preferred, elem.last_area_increase.move), true); insertSetting( @@ -1075,6 +1140,7 @@ void TreeSupport::increaseAreas( ! increase_radius, elem.last_area_increase.no_error, ! use_min_radius, + use_anti_preferred, elem.last_area_increase.move), true); } @@ -1083,31 +1149,49 @@ void TreeSupport::increaseAreas( { // 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. - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); // Did we go through the hole. + insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! 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(config.tip_layers, 2)) { - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! move), true); } insertSetting( - AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, ! move), + AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! move), true); // Did we manage to avoid the hole, - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); } else { - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, 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(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, move), true); // a (See above.) + insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); // a (See above.) if (elem.distance_to_top < config.tip_layers) { - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, slow_speed, increase_radius, no_error, ! use_min_radius, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); + } + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); // b (See above.) + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); + } + + if(!elem.can_avoid_anti_preferred) + { + std::deque old_order = order; + for (AreaIncreaseSettings settings : old_order) + { + if(elem.effective_radius_height < config.increase_radius_until_dtt && !settings.increase_radius) + { + continue ; + } + if(!settings.move) + { + continue ; + } + + insertSetting(AreaIncreaseSettings(settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, !settings.use_anti_preferred, settings.move),true); } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, move), true); // b (See above.) - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); } if (elem.use_min_xy_dist) @@ -1118,13 +1202,13 @@ void TreeSupport::increaseAreas( for (AreaIncreaseSettings settings : order) { new_order.emplace_back(settings); - new_order.emplace_back(settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, settings.move); + new_order.emplace_back(settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, settings.use_anti_preferred, settings.move); } order = new_order; } insertSetting( - AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), + AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist, !use_anti_preferred, move), true); // simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has // caused issues @@ -1135,12 +1219,12 @@ void TreeSupport::increaseAreas( || (! elem.to_model_gracious && (parent->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty()))) // Error case. { // It is normal that we won't be able to find a new area at some point in time if we won't be able to reach layer 0 aka have to connect with the model. - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist, !use_anti_preferred, 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. { insertSetting( - AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, ! use_min_radius, ! move), + AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! move), false); // Only do not move when already in a no hole avoidance with the regular xy distance. } @@ -1303,21 +1387,19 @@ void TreeSupport::increaseAreas( std::lock_guard critical_section_newLayer(critical_sections); if (bypass_merge) { - Polygons* new_area = new Polygons(max_influence_area); - TreeSupportElement* next = new TreeSupportElement(elem, new_area); - bypass_merge_areas.emplace_back(next); + bypass_merge_areas.emplace(elem, max_influence_area); } else { influence_areas.emplace(elem, max_influence_area); - if (elem.to_buildplate) - { - to_bp_areas.emplace(elem, to_bp_data); - } - if (config.support_rests_on_model) - { - to_model_areas.emplace(elem, to_model_data); - } + } + if (elem.to_buildplate) + { + to_bp_areas.emplace(elem, to_bp_data); + } + if (config.support_rests_on_model) + { + to_model_areas.emplace(elem, to_model_data); } } } @@ -1331,7 +1413,128 @@ void TreeSupport::increaseAreas( }); } -void TreeSupport::createLayerPathing(std::vector>& move_bounds) +void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, + PropertyAreas& to_model_areas, + PropertyAreas& influence_areas, + PropertyAreas& bypass_merge_areas, + LayerIndex layer_idx, + std::vector>& move_bounds, + std::vector>& cradle_data) +{ + std::unordered_set removed_lines_idx; + // Evaluate which lines have to be removed for all influence areas to be valid. + // Goal is to remove as few lines as possible + // Correctly solving this is very hard. + // So for now any solution will do. Todo find a better way. Also parallelize + + std::vector all_elements_on_layer; + all_elements_on_layer.insert(all_elements_on_layer.end(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); + for (auto& elem_influence_pair : influence_areas) + { + all_elements_on_layer.emplace_back(&elem_influence_pair.first); + } + for (auto& elem_influence_pair : bypass_merge_areas) + { + all_elements_on_layer.emplace_back(&elem_influence_pair.first); + } + + for (const TreeSupportElement* elem : all_elements_on_layer) + { + // todo the coll/ regular radius difference can cause issues with slow vs fast avoidance causing lines to be removed even though the branch will not be here. It just could have been... + if(!elem->can_avoid_anti_preferred || config.getCollisionRadius(*elem) != config.getRadius(*elem)) + { + const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + + bool immutable = elem->area != nullptr; + bool to_bp = elem->to_buildplate; + + Polygons relevant_influence; + Polygons full_influence; + if(!immutable) + { + relevant_influence = to_bp? to_bp_areas[*elem] : to_model_areas[*elem]; + full_influence = bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]; + } + else + { + relevant_influence = elem->area->difference(volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,elem->use_min_xy_dist)); + full_influence = relevant_influence; + } + AABB relevant_influence_aabb = AABB(relevant_influence); + + + + for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) + { + if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base && !removed_lines_idx.contains(cradle_idx)) + { + //The branch created by the influence area cant lag though the model... So the offset needs to be safe... + AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area); + cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); + if(cradle_area_aabb.hit(relevant_influence_aabb)) + { + Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area, + config.getRadius(*elem) + config.xy_distance, + volumes_.getCollision(0,layer_idx,true), + safe_movement_distance, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + Polygons next_relevant_influence = relevant_influence.difference(cradle_influence); + + if(next_relevant_influence.area()>EPSILON) + { + relevant_influence = next_relevant_influence; + full_influence = full_influence.difference(cradle_influence).unionPolygons(relevant_influence); + + } + else + { + //todo Check if non remove options are available eg shortening cradle line... + removed_lines_idx.emplace(cradle_idx); + cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line); + cradle.getCradleLine()->line.clear(); + spdlog::info("Flagging to remove cradle line {} {} ", cradle.layer_idx, cradle.line_idx); + } + } + } + } + if(!immutable) + { + (bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]) = full_influence; + (to_bp? to_bp_areas[*elem] : to_model_areas[*elem]) = relevant_influence; + } + + } + } + for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) + { + if(cradle.cradleLineExists()) + { + cradle.cradle->verifyLines(); + } + } + + //todo would be great if removed cradle lines could be eliminated from the avoidance... + + std::vector next_layer; + next_layer.insert(next_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); + for(TreeSupportElement* elem:next_layer) + { + if(elem->cradle_line != nullptr && !elem->cradle_line->cradleLineExists()) + { + move_bounds[layer_idx].erase(elem); + delete elem->area; + delete elem; + } + } + +} + + +void TreeSupport::createLayerPathing(std::vector>& move_bounds, std::vector>& cradle_data) { const double data_size_inverse = 1 / double(move_bounds.size()); double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES; @@ -1351,6 +1554,27 @@ void TreeSupport::createLayerPathing(std::vector>& 3000 / config.layer_height); size_t merge_every_x_layers = 1; + + std::vector> all_cradles_with_line_presence(move_bounds.size()); + for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + { + for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + { + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + { + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + { + LayerIndex cradle_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + if(cradle_layer_idx == 84) + { + printf(""); + } + all_cradles_with_line_presence[cradle_layer_idx].emplace_back(cradle_data[layer_idx][cradle_idx],cradle_layer_idx,line_idx); + } + } + } + } + // Calculate the influence areas for each layer below (Top down) // This is done by first increasing the influence area by the allowed movement distance, and merging them with other influence areas if possible for (const auto layer_idx : ranges::views::iota(1UL, move_bounds.size()) | ranges::views::reverse) @@ -1366,13 +1590,12 @@ void TreeSupport::createLayerPathing(std::vector>& PropertyAreas influence_areas; // Over this map will be iterated when merging, as such it has to be ordered to ensure deterministic results. PropertyAreas to_model_areas; // The area of these SupportElement is not set, to avoid to much allocation and deallocation on the heap. PropertyAreasUnordered to_bp_areas; // Same. - std::vector - bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. + PropertyAreas bypass_merge_areas; const auto time_a = std::chrono::high_resolution_clock::now(); std::vector last_layer; - last_layer.insert(last_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); + last_layer.insert(last_layer.end(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); // ### Increase the influence areas by the allowed movement distance increaseAreas(to_bp_areas, to_model_areas, influence_areas, bypass_merge_areas, last_layer, layer_idx, merge_this_layer); @@ -1399,6 +1622,13 @@ void TreeSupport::createLayerPathing(std::vector>& new_element = ! move_bounds[layer_idx - 1].empty(); + + // ### Cradle lines may be removed, causing tips to be removed. + if(layer_idx>0) + { + handleCradleLineValidity(to_bp_areas, to_model_areas,influence_areas,bypass_merge_areas,layer_idx-1,move_bounds,all_cradles_with_line_presence); + } + // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again. for (std::pair tup : influence_areas) { @@ -1414,13 +1644,16 @@ void TreeSupport::createLayerPathing(std::vector>& } // Place already fully constructed elements in the output. - for (TreeSupportElement* elem : bypass_merge_areas) + for (std::pair tup : bypass_merge_areas) { - if (elem->area->area() < 1) + const TreeSupportElement elem = tup.first; + Polygons* new_area = new Polygons(TreeSupportUtils::safeUnion(tup.second)); + TreeSupportElement* next = new TreeSupportElement(elem, new_area); + move_bounds[layer_idx - 1].emplace(next); + if (new_area->area() < 1) { spdlog::error("Insert Error of Influence area bypass on layer {}.", layer_idx - 1); } - move_bounds[layer_idx - 1].emplace(elem); } progress_total += data_size_inverse * TREE_PROGRESS_AREA_CALC; @@ -2002,7 +2235,12 @@ void TreeSupport::dropNonGraciousAreas( }); } -void TreeSupport::generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage, std::vector>& layer_tree_polygons) +void TreeSupport::generateSupportSkin(std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_roof_storage, + 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 @@ -2011,6 +2249,60 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::mutex critical_support_layer_storage; + std::vector cradle_support_base_areas(support_layer_storage.size()); + std::vector cradle_support_line_areas(support_layer_storage.size()); + std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); + + //todo parallelize + for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + { + for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + { + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + { + if(cradle_data[layer_idx][cradle_idx]->is_roof) + { + support_roof_storage[layer_idx-base_idx].add(base); + } + else + { + cradle_support_base_areas[layer_idx-base_idx].add(base); + support_layer_storage[layer_idx-base_idx].add(base); + } + } + + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + { + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + { + Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].area; + LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + if(cradle_data[layer_idx][cradle_idx]->is_roof) + { + if(support_roof_storage.size()<=layer_idx) + { + support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + } + support_roof_storage[cradle_line_layer_idx].add(line_area); + } + else + { + if(cradle_support_line_areas.size()<=layer_idx) + { + cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + } + cradle_support_line_areas[cradle_line_layer_idx].add(line_area); + } + if(!cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_base) + { + //todo cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_area.offset(config.xy_distance)); todo offset needs to be safe... + } + } + } + } + } + + cura::parallel_for ( 0, @@ -2018,7 +2310,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora [&](const LayerIndex layer_idx) { support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx].unionPolygons()); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx].unionPolygons()); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); placed_fake_roof_areas[layer_idx] = placed_fake_roof_areas[layer_idx].unionPolygons(); additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx].unionPolygons(); @@ -2088,9 +2381,11 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora needs_supporting.add(storage.support.supportLayers[layer_idx + 1].support_roof.difference(support_shell_capable_of_supporting_roof)); needs_supporting.add(placed_fake_roof_areas[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); needs_supporting.add(additional_required_support_area[layer_idx + 1]); // E.g. cradle + needs_supporting.add(cradle_support_line_areas[layer_idx+1]); - needs_supporting = needs_supporting.unionPolygons(); } + needs_supporting.add(cradle_support_base_areas[layer_idx]); + needs_supporting = needs_supporting.unionPolygons(); Polygons already_supports; already_supports.add(storage.support.supportLayers[layer_idx].support_roof); // roof @@ -2227,7 +2522,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora [&](const LayerIndex layer_idx) { support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(cradle_support_line_areas[layer_idx]).difference(support_skin_storage[layer_idx]); } ); } @@ -2544,7 +2839,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor }); } -void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage) +void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data) { std::vector support_layer_storage(move_bounds.size()); std::vector support_skin_storage(move_bounds.size()); @@ -2644,7 +2939,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons); + generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons,cradle_data); // cradle being added before skin causes cradle lines to become skin. for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 3f8dfbe772..4c9e698e98 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -56,13 +56,11 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage , already_inserted(mesh.overhang_areas.size()) , support_roof_drawn(mesh.overhang_areas.size(), Polygons()) , roof_tips_drawn(mesh.overhang_areas.size(), Polygons()) - , cradle_areas(mesh.overhang_areas.size(), Polygons()) - , cradle_base_areas(mesh.overhang_areas.size(), Polygons()) - , cradle_overhang(mesh.overhang_areas.size(), Polygons()) + , cradle_data(mesh.overhang_areas.size()) , volumes_(volumes_s) , force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) , cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config.layer_height) - , minimum_cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config.layer_height) + , cradle_layers_min(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config.layer_height) , cradle_line_count(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) , cradle_length(retrieveSetting(mesh.settings, "support_tree_cradle_length")) , cradle_length_min(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) @@ -83,6 +81,11 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceDataStorage& storage { cradle_layers += cradle_z_distance_layers; } + if(cradle_length-cradle_line_width>0) + { + cradle_length-= cradle_line_width; + cradle_length_min -= cradle_line_width; + } const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config.layer_height) : std::numeric_limits::max(); @@ -495,7 +498,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes AABB part_aabb(part); auto has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); - if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) //todo accumulate overhangs, only cradle if below threashold? + if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) { next_completely_supported.add(part); continue; @@ -622,11 +625,11 @@ std::vector TreeSupportTipG } -std::vector, std::vector>>> TreeSupportTipGenerator::generateCradleCenters(const SliceMeshStorage& mesh) +std::vector>> TreeSupportTipGenerator::generateCradleCenters(const SliceMeshStorage& mesh) { std::mutex critical_dedupe; std::vector> dedupe(mesh.overhang_areas.size()); - std::vector, std::vector>>> result(mesh.overhang_areas.size()); + std::vector>> shadows(mesh.overhang_areas.size()); cura::parallel_for( 1, mesh.overhang_areas.size() - (z_distance_delta + 1), @@ -674,6 +677,7 @@ std::vector, std::vector>>> T else { Point next_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + // todo Long lines can cause very missplaced centers. Best way to choose when to keep initial center if (vSize(assumed_center_prev - next_center) < cradle_length && (! centers.empty() || vSize(assumed_center_prev - initial_center) < cradle_length)) { assumed_center_prev = (2 * assumed_center_prev + next_center) / 3; @@ -727,7 +731,7 @@ std::vector, std::vector>>> T if (model_outline.empty()) { - if (cradle_up_layer < minimum_cradle_layers) + if (cradle_up_layer < cradle_layers_min) { aborted = true; break; @@ -807,705 +811,646 @@ std::vector, std::vector>>> T } } } - result[layer_idx].emplace_back(accumulated_model, centers); + + + for(Point center:centers) + { + TreeSupportCradle* cradle = new TreeSupportCradle(layer_idx,center,shadows[layer_idx].size(), cradle_base_roof, cradle_layers_min, cradle_length_min); + cradle_data[layer_idx].emplace_back(cradle); + } + shadows[layer_idx].emplace_back(accumulated_model); } }); - return result; + return shadows; } -std::vector, TreeSupportTipGenerator::CenterSubCenterPointPair>>> - TreeSupportTipGenerator::generateCradleLines(std::vector>& center_data) +void TreeSupportTipGenerator::generateCradleLines(std::vector>>& shadow_data) { const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance.begin(), cradle_xy_distance.end()); - std::vector, CenterSubCenterPointPair>>> cradle_polylines(center_data.size()); cura::parallel_for( 1, - center_data.size(), + cradle_data.size(), [&](const LayerIndex layer_idx) { - for (auto [center_idx, shadow_centers_pair] : center_data[layer_idx] | ranges::views::enumerate) + for (auto [center_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - for (auto [center_point_idx, center] : shadow_centers_pair.second | ranges::views::enumerate) + std::vector removed_directions; + const auto& accumulated_model = shadow_data[layer_idx][cradle->shadow_idx]; + for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { - cradle_polylines[layer_idx].emplace_back(std::vector(shadow_centers_pair.first.size()), CenterSubCenterPointPair(center_idx, center_point_idx)); + bool aborted = accumulated_model.size() == 1; + const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; + const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; - std::vector removed_directions; + if(cradle->lines.empty()) + { + cradle->lines.resize(cradle_line_count); + } - for (auto [idx, model_shadow] : shadow_centers_pair.first | ranges::views::enumerate) + if ((idx > 0 || (cradle_base_roof && ! large_cradle_base && ! aborted)) + && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. { - bool aborted = shadow_centers_pair.first.size() == 1; - const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; - const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; + coord_t max_distance2 = 0; + + Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + + Polygons this_part_influence = model_shadow.offset(config.xy_min_distance); - if ((idx > 0 || (cradle_base_roof && ! large_cradle_base && ! aborted)) - && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. + for (size_t layer_offset = 1; layer_offset <= config.z_distance_bottom_layers && layer_offset <= idx; layer_offset++) { - coord_t max_distance2 = 0; + this_part_influence.add(accumulated_model[idx - layer_offset]); + } - Polygons relevant_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); + for (coord_t layer_offset = 1; layer_offset <= config.z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) + { + const coord_t required_range_x = coord_t( + config.xy_min_distance - ((layer_offset - (config.z_distance_top_layers == 1 ? 0.5 : 0)) * config.xy_min_distance / config.z_distance_top_layers)); + this_part_influence.add(accumulated_model[idx + layer_offset].offset(required_range_x)); + } - Polygons this_part_influence = model_shadow.offset(config.xy_min_distance); + this_part_influence = this_part_influence.unionPolygons().offset(FUDGE_LENGTH, ClipperLib::jtRound).unionPolygons(); - for (size_t layer_offset = 1; layer_offset <= config.z_distance_bottom_layers && layer_offset<=idx; layer_offset++) - { - this_part_influence.add(shadow_centers_pair.first[idx - layer_offset]); - } + coord_t cradle_min_xy_distance_delta = std::max(config.xy_min_distance - current_cradle_xy_distance, coord_t(0)); - for (coord_t layer_offset = 1; layer_offset <= config.z_distance_top_layers - && layer_offset + layer_idx < shadow_centers_pair.first.size(); layer_offset++) + // Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. + // To prevent this offset my the delta before removing the influence of the model. + relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta) + .difference(this_part_influence) + .offset(cradle_min_xy_distance_delta) + .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2)) + .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width / 2)); + for (auto line : model_shadow) + { + for (Point p : line) { - const coord_t required_range_x = coord_t(config.xy_min_distance - ((layer_offset - (config.z_distance_top_layers == 1 ? 0.5 : 0)) * config.xy_min_distance / config.z_distance_top_layers)); - this_part_influence.add(shadow_centers_pair.first[idx + layer_offset].offset(required_range_x)); + max_distance2 = std::max(max_distance2, vSize2(cradle->center - p)); } + } - this_part_influence = this_part_influence.unionPolygons().offset(FUDGE_LENGTH , ClipperLib::jtRound).unionPolygons(); + Polygon max_outer_points = PolygonUtils::makeCircle(cradle->center, sqrt(max_distance2) + current_cradle_length * 2, (2.0 * M_PI) / double(cradle_line_count)); - coord_t cradle_min_xy_distance_delta = std::max(config.xy_min_distance - current_cradle_xy_distance, coord_t(0)); + // create lines that go from the furthest possible location to the center + Polygons lines_to_center; + for (Point p : max_outer_points) + { + lines_to_center.addLine(p, cradle->center); + } + + // Subtract the model shadow up until this layer from the lines. + if (idx > 0) + { + lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2).unionPolygons().differencePolyLines(lines_to_center, false); + } + // Store valid distances from the center in relation to the direction of the line. + // Used to detect if a line may be intersecting another model part. + std::vector> vector_distance_map; - //Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. - // To prevent this offset my the delta before removing the influence of the model. - relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta).difference(this_part_influence) - .offset(cradle_min_xy_distance_delta) - .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2)) - .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width / 2)); - for (auto line : model_shadow) + // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing + Polygons shortened_lines_to_center; + for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - cradle->center) < vSize2(line.back() - cradle->center); + Point closer = front_closer ? line.front() : line.back(); + Point further = front_closer ? line.back() : line.front(); + coord_t cradle_line_length = Polygon(line).polylineLength(); + if (cradle_line_length < cradle_length_min) { - for (Point p : line) - { - max_distance2 = std::max(max_distance2, vSize2(center - p)); - } + continue; } - - Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + current_cradle_length * 2, (2 * M_PI) / cradle_line_count); - - // create lines that go from the furthest possible location to the center - Polygons lines_to_center; - for (Point p : max_outer_points) + if (Polygon(line).polylineLength() <= current_cradle_length) { - lines_to_center.addLine(p, center); + shortened_lines_to_center.add(line); } - - // Subtract the model shadow up until this layer from the lines. todo couldnt this also just be the relevant_forbidden? - if (idx > 0) + else { - lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2).unionPolygons().differencePolyLines(lines_to_center, false); + double scale = (double(current_cradle_length) / double(vSize(further - closer))); + Point correct_length = closer + (further - closer) * scale; + shortened_lines_to_center.addLine(correct_length, closer); } - // Store valid distances from the center in relation to the direction of the line. - // Used to detect if a line may be intersecting another model part. - std::vector> vector_distance_map; - // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing - Polygons shortened_lines_to_center; - for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) + if (further != closer) { - bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); - Point closer = front_closer ? line.front() : line.back(); - Point further = front_closer ? line.back() : line.front(); - coord_t cradle_line_length = Polygon(line).polylineLength(); - if(cradle_line_length < cradle_length_min) - { - continue ; - } - if (Polygon(line).polylineLength() <= current_cradle_length) - { - shortened_lines_to_center.add(line); - } - else - { - double scale = (double(current_cradle_length) / double(vSize(further - closer))); - Point correct_length = closer + (further - closer) * scale; - shortened_lines_to_center.addLine(correct_length, closer); - } - - if (further != closer) - { - vector_distance_map.emplace_back((further - closer), vSize(center - closer)); - } + vector_distance_map.emplace_back((further - closer), vSize(cradle->center - closer)); } - // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent - // this. - shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); - - Polygons actually_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); - - // Evaluate which lines are still valid after the avoidance was subtracted - for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) - { - bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); - Point closer = front_closer ? line.front() : line.back(); - Point further = front_closer ? line.back() : line.front(); - Point current_direction = further - closer; - coord_t distance_from_center = vSize(closer - center); + } + // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent + // this. + shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); - shortened_lines_to_center[line_idx].clear(); - shortened_lines_to_center[line_idx].add(closer); - shortened_lines_to_center[line_idx].add(further); - bool keep_line = false; - bool found_candidate = false; - bool too_short = (vSize(closer - further)) < cradle_length_min; - // a cradle line should also be removed if there will be no way to support it - too_short - |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config.support_line_width; - if (! too_short) + Polygons actually_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + + // Evaluate which lines are still valid after the avoidance was subtracted + for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - cradle->center) < vSize2(line.back() - cradle->center); + Point closer = front_closer ? line.front() : line.back(); + Point further = front_closer ? line.back() : line.front(); + Point current_direction = further - closer; + coord_t distance_from_center = vSize(closer - cradle->center); + + shortened_lines_to_center[line_idx].clear(); + shortened_lines_to_center[line_idx].add(closer); + shortened_lines_to_center[line_idx].add(further); + bool keep_line = false; + bool found_candidate = false; + bool too_short = (vSize(closer - further)) < cradle_length_min; + // a cradle line should also be removed if there will be no way to support it + too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config.support_line_width; + if (! too_short) + { + for (auto [direction_idx, direction] : vector_distance_map | ranges::views::enumerate) { - for (auto [direction_idx, direction] : vector_distance_map | ranges::views::enumerate) + double cosine = (dot(direction.first, current_direction)) / (vSize(current_direction) * vSize(direction.first)); + if (cosine > 0.99) { - double cosine = (dot(direction.first, current_direction)) / (vSize(current_direction) * vSize(direction.first)); - if (cosine > 0.99) + found_candidate = true; + // found line, check if there is one that is closer, if not then check if distance to center is expected + bool found_close_line = direction.second + config.xy_min_distance + config.min_feature_size > distance_from_center; + if (found_close_line) { - found_candidate = true; - // found line, check if there is one that is closer, if not then check if distance to center is expected - bool found_close_line = direction.second + config.xy_min_distance + config.min_feature_size > distance_from_center; - if (found_close_line) - { - keep_line = true; - break; - } + keep_line = true; + break; } } } + } - bool was_removed = false; + bool was_removed = false; - for (auto [direction_idx, direction] : removed_directions | ranges::views::enumerate) + for (auto [direction_idx, direction] : removed_directions | ranges::views::enumerate) + { + double cosine = (dot(direction, current_direction)) / (vSize(current_direction) * vSize(direction)); + if (cosine > 0.99) { - double cosine = (dot(direction, current_direction)) / (vSize(current_direction) * vSize(direction)); - if (cosine > 0.99) - { - was_removed = true; - - } + was_removed = true; } + } - if (too_short || was_removed || (! keep_line && found_candidate)) + if (too_short || was_removed || (! keep_line && found_candidate)) + { + if (! was_removed) { - if(!was_removed) - { - removed_directions.emplace_back(current_direction); - } - shortened_lines_to_center[line_idx].clear(); + removed_directions.emplace_back(current_direction); } + shortened_lines_to_center[line_idx].clear(); + } + } + + size_t line_remove_idx = 0; + + while (line_remove_idx < shortened_lines_to_center.size()) + { + if (shortened_lines_to_center[line_remove_idx].empty()) + { + shortened_lines_to_center.remove(line_remove_idx); + } + else + { + line_remove_idx++; } + } - size_t line_idx = 0; - while (line_idx < shortened_lines_to_center.size()) + for (auto [next_line_idx, next_line] : shortened_lines_to_center | ranges::views::enumerate) + { + Point current_direction = next_line.front() - next_line.back(); + bool found = false; + double angle = std::atan2(current_direction.X,current_direction.Y); + + size_t angle_idx = std::min(size_t(((angle+M_PI)/(2.0*M_PI)) * double(cradle_line_count)),cradle_line_count-1); + Polygon line(next_line); + //Handle cradle_z_distance_layers by overwriting first element in the vector until valid distance is reached. Todo that means overhang prefered on layer of cradle + if(idx <= cradle_z_distance_layers + 1 && !cradle->lines[angle_idx].empty()) { - if (shortened_lines_to_center[line_idx].empty()) - { - shortened_lines_to_center.remove(line_idx); - } - else + cradle->lines[angle_idx][0]=TreeSupportCradleLine(line,layer_idx+idx); + } + else + { + cradle->lines[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx)); + if(cradle->lines[angle_idx].size()>cradle_layers) { - line_idx++; + printf("Issue detected!\n"); + PolygonUtils::makeCircle(cradle->center, sqrt(max_distance2) + current_cradle_length * 2, (2.0 * M_PI) / double(cradle_line_count)); } } - - cradle_polylines[layer_idx].back().first[idx] = shortened_lines_to_center; } } } } }); - return cradle_polylines; } -void TreeSupportTipGenerator::cleanCradleLineOverlaps(std::vector, CenterSubCenterPointPair>>>& cradle_polylines) +void TreeSupportTipGenerator::cleanCradleLineOverlaps() { - std::vector> all_cradles_per_layer(cradle_polylines.size() + cradle_layers); - for (LayerIndex layer_idx = 0; layer_idx < cradle_polylines.size(); layer_idx++) + std::vector> all_cradles_per_layer(cradle_data.size() + cradle_layers); + for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { - for (size_t cradle_idx = 0; cradle_idx < cradle_polylines[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (size_t height = 0; height < cradle_polylines[layer_idx][cradle_idx].first.size(); height++) + for (size_t cradle_line_idx = 0; cradle_line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); cradle_line_idx++) { - Polygons* result = &cradle_polylines[layer_idx][cradle_idx].first[height]; - all_cradles_per_layer[layer_idx + height].emplace_back(result); + for (size_t height = 0; height < cradle_data[layer_idx][cradle_idx]->lines[cradle_line_idx].size(); height++) + { + + TreeSupportCradleLine* result = &cradle_data[layer_idx][cradle_idx]->lines[cradle_line_idx][height]; + all_cradles_per_layer[layer_idx + height].emplace_back(result); + } } } } const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath - std::vector removed_lines(cradle_polylines.size()); + std::vector removed_lines(cradle_data.size()); cura::parallel_for( 1, all_cradles_per_layer.size(), [&](const LayerIndex layer_idx) { - std::vector all_cradles_on_layer = all_cradles_per_layer[layer_idx]; - std::vector> lines_to_remove(all_cradles_on_layer.size()); + std::vector all_cradles_on_layer = all_cradles_per_layer[layer_idx]; + + std::function handleNewEnd = [&](size_t cradle_line_idx, Point new_end) + { + auto& line = (*all_cradles_on_layer[cradle_line_idx]); + if(LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line.front(), line.line.back()) + || vSize(new_end - line.line.front()) < cradle_length_min) + { + all_cradles_on_layer[cradle_line_idx]->addLineToRemoved(line.line); + all_cradles_on_layer[cradle_line_idx]->line.clear(); + } + else + { + all_cradles_on_layer[cradle_line_idx]->line.back() = new_end; + } + }; + for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) { - AABB bounding_box_current = AABB(*all_cradles_on_layer[cradle_idx]); + AABB bounding_box_current = AABB(all_cradles_on_layer[cradle_idx]->line); bounding_box_current.expand(cradle_line_width * 2 + FUDGE_LENGTH); for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) { - if (bounding_box_current.hit(AABB(*all_cradles_on_layer[cradle_idx_inner]))) + if(all_cradles_on_layer[cradle_idx_inner]->line.empty()||all_cradles_on_layer[cradle_idx]->line.empty()) + { + continue ; + } + if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line))) { - for (size_t line_idx = 0; line_idx < all_cradles_on_layer[cradle_idx]->size(); line_idx++) + Polygon outer_line = (*all_cradles_on_layer[cradle_idx]).line; + Polygon inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line; + Point intersect; + if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) { - for (size_t line_idx_inner = 0; line_idx_inner < all_cradles_on_layer[cradle_idx_inner]->size(); line_idx_inner++) - { - Polygon outer_line = (*all_cradles_on_layer[cradle_idx])[line_idx]; - Polygon inner_line = (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner]; - Point intersect; - if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) - { - coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); - coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); - const coord_t end_dist = FUDGE_LENGTH + min_distance_between_lines + cradle_line_width; // todo everywhere: This is dist to prevent lines from merging, not touching ... Maybe also should not touch + coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); + coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); + const coord_t end_dist = FUDGE_LENGTH + min_distance_between_lines + cradle_line_width; // todo everywhere: This is dist to prevent lines from merging, not touching ... Maybe also should not touch - if (inner_intersect_dist > outer_intersect_dist) - { - Point new_end_inner = intersect + normal((inner_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_inner, inner_line.front(), inner_line.back()) - || vSize(new_end_inner - inner_line.front()) < cradle_length_min) - { - (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = intersect; - lines_to_remove[cradle_idx_inner].emplace_back(line_idx_inner); - } - else - { - (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = new_end_inner; - } - } - if (outer_intersect_dist > inner_intersect_dist) - { - Point new_end_outer = intersect + normal((outer_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + if (inner_intersect_dist > outer_intersect_dist) + { + Point new_end_inner = intersect + normal((inner_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + handleNewEnd(cradle_idx_inner,new_end_inner); - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_outer, outer_line.front(), outer_line.back()) - || vSize(new_end_outer - outer_line.front()) < cradle_length_min) - { - (*all_cradles_on_layer[cradle_idx])[line_idx].back() = intersect; - lines_to_remove[cradle_idx].emplace_back(line_idx); - } - else - { - (*all_cradles_on_layer[cradle_idx])[line_idx].back() = new_end_outer; - } - } - } - else + } + if (outer_intersect_dist > inner_intersect_dist) + { + Point new_end_outer = intersect + normal((outer_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + handleNewEnd(cradle_idx,new_end_outer); + } + } + else + { + // Touching lines have the same issue Lines touch if the end is to close to another line + coord_t inner_end_to_outer_distance = LinearAlg2D::getDistFromLine(inner_line.back(),outer_line.front(), outer_line.back()); + if(inner_end_to_outer_distance < 2 * cradle_line_width) + { + Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),2 * cradle_line_width - inner_end_to_outer_distance); + coord_t error = LinearAlg2D::getDistFromLine(new_end_inner,outer_line.front(), outer_line.back()); + double error_correction_factor = 1.0 + error/(2 * cradle_line_width - inner_end_to_outer_distance); + new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),(2 * cradle_line_width - inner_end_to_outer_distance)*error_correction_factor); + handleNewEnd(cradle_idx_inner,new_end_inner); + } + else + { + coord_t outer_end_to_inner_distance = LinearAlg2D::getDistFromLine(outer_line.back(),inner_line.front(), inner_line.back()); + if(outer_end_to_inner_distance < 2 * cradle_line_width) { - // Touching lines have the same issue Lines touch if the end is to close to another line - coord_t inner_end_to_outer_distance = LinearAlg2D::getDistFromLine(inner_line.back(),outer_line.front(), outer_line.back()); - if(inner_end_to_outer_distance < 2 * cradle_line_width) - { - Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),2 * cradle_line_width - inner_end_to_outer_distance); - coord_t error = LinearAlg2D::getDistFromLine(new_end_inner,outer_line.front(), outer_line.back()); - double error_correction_factor = 1.0 + error/(2 * cradle_line_width - inner_end_to_outer_distance); - new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),(2 * cradle_line_width - inner_end_to_outer_distance)*error_correction_factor); - - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_inner, inner_line.front(), inner_line.back()) //todo to lambda - || vSize(new_end_inner - inner_line.front()) < cradle_length_min) - { - (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = intersect; - lines_to_remove[cradle_idx_inner].emplace_back(line_idx_inner); - } - else - { - (*all_cradles_on_layer[cradle_idx_inner])[line_idx_inner].back() = new_end_inner; - } - } - else - { - coord_t outer_end_to_inner_distance = LinearAlg2D::getDistFromLine(outer_line.back(),inner_line.front(), inner_line.back()); - if(outer_end_to_inner_distance < 2 * cradle_line_width) - { - - Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),2 * cradle_line_width - outer_end_to_inner_distance); - coord_t error = LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back()); - double error_correction_factor = 1.0 + error/(2 * cradle_line_width - outer_end_to_inner_distance); - new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),(2 * cradle_line_width - outer_end_to_inner_distance)*error_correction_factor); - - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end_outer, outer_line.front(), outer_line.back()) - || vSize(new_end_outer - outer_line.front()) < cradle_length_min) - { - (*all_cradles_on_layer[cradle_idx])[line_idx].back() = intersect; - lines_to_remove[cradle_idx].emplace_back(line_idx); - } - else - { - (*all_cradles_on_layer[cradle_idx])[line_idx].back() = new_end_outer; - } - } - } + Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),2 * cradle_line_width - outer_end_to_inner_distance); + coord_t error = LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back()); + double error_correction_factor = 1.0 + error/(2 * cradle_line_width - outer_end_to_inner_distance); + new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),(2 * cradle_line_width - outer_end_to_inner_distance)*error_correction_factor); + handleNewEnd(cradle_idx,new_end_outer); } - - // Either they cross or the end of one is close to the other } } } } } + }); - for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) + cura::parallel_for( + 1, + cradle_data.size(), + [&](const LayerIndex layer_idx) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - if(!lines_to_remove[cradle_idx].empty()) + for (size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) { - std::sort(lines_to_remove[cradle_idx].begin(), lines_to_remove[cradle_idx].end(), std::greater()); //Ensure largest idx is removed first. - //remove duplicates - lines_to_remove[cradle_idx].erase(std::unique(lines_to_remove[cradle_idx].begin(), lines_to_remove[cradle_idx].end()), lines_to_remove[cradle_idx].end()); - for(size_t remove_idx = 0; remove_idx < lines_to_remove[cradle_idx].size(); remove_idx++) + std::deque& cradle_lines = cradle_data[layer_idx][cradle_idx]->lines[line_idx]; + if(cradle_lines.size() < cradle_layers_min) { - removed_lines[layer_idx].add((*all_cradles_on_layer[cradle_idx])[lines_to_remove[cradle_idx][remove_idx]]); - (*all_cradles_on_layer[cradle_idx]).remove(lines_to_remove[cradle_idx][remove_idx]); + cradle_lines.clear(); + continue; + } + LayerIndex previous_layer_idx = cradle_lines.front().layer_idx; + for (size_t up_idx = 1; up_idx < cradle_lines.size(); up_idx++) + { + if(cradle_lines[up_idx].layer_idx > previous_layer_idx + up_idx + || cradle_lines[up_idx].line.size() < 2 || cradle_lines[up_idx].line.polylineLength() < cradle_length_min) + { + if(up_idx <= cradle_layers_min) + { + cradle_lines.clear(); + break; + } + } } } } - }); - + }); //todo if removed line has similar cosine to one above, and on of the points lies close to the other one Remove that part of the line => Only keep above if still longer than min } -void TreeSupportTipGenerator::finalizeCradleAreas( - std::vector>>& cradle_polygons, - std::vector& support_free_areas, - std::vector>& center_data) + + +void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas) { - std::mutex critical_support_free_areas_and_cradle_areas; - cura::parallel_for( - 1, - cradle_polygons.size(), - [&](const LayerIndex layer_idx) - { - for (auto [cradle_idx, cradle_areas_calc] : cradle_polygons[layer_idx] | ranges::views::enumerate) + const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath + // Some angles needed to move cradle lines outwards to prevent them from toughing. + double center_angle = (2.0 * M_PI) / double(cradle_line_count); + double outer_angle = (M_PI - center_angle) / 2; + coord_t outer_radius = (double(min_distance_between_lines + config.support_line_width) / sin(center_angle)) * sin(outer_angle); + const coord_t small_hole_size + = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; + std::mutex critical_support_free_areas_and_cradle_areas; + + cura::parallel_for( + 0, + cradle_data.size(), + [&](const LayerIndex layer_idx) { - // Rooflines are more delicate, so ensure that they rest on as much other rooflines if possible. - if (! use_fake_roof && support_roof_layers) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate | ranges::views::reverse) + TreeSupportCradle& cradle = *cradle_data[layer_idx][cradle_idx]; + for (size_t cradle_height = 0; cradle_height <= cradle_layers; cradle_height++) { - if (idx > 1) + Polygons line_tips; + + std::vector> all_tips_center; + // generate trapezoid line tip with front width of support line width, back cradle_width. + + for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) { - Polygons relevant_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx - 1, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); - Polygons area_above = cradle.difference(relevant_forbidden); + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx+cradle_height,line_idx); + if(!line_opt) + { + continue; + } + TreeSupportCradleLine* line = line_opt.value(); + + // 2*cradle_line_count would be distance between cradle lines stays the same + coord_t triangle_length = (cradle_line_width - config.support_line_width) / 2 * tan(M_PI / 2 - M_PI / double(3 * cradle_line_count)); + + Point direction = line->line.back() - line->line.front(); + Point center_front = line->line.front() - normal(direction, cradle_line_width / 2); + + + Point direction_up_center = normal(rotate(direction, M_PI / 2), config.support_line_width / 2); + Point center_up = center_front + direction_up_center; + Point center_down = center_front - direction_up_center; - // If a line would have some areas that are too small to be drawn, there can be minor issues. The idea is that the offsets will filter them out. - for (auto part : area_above.offset(-cradle_line_width / 4).splitIntoParts()) + for (auto existing_center : all_tips_center) { - if (part.area() > cradle_line_width * std::max(cradle_length / 4, config.support_roof_line_width)) + Point intersect; + bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); + if (centers_touch + || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) { - cradle_areas_calc[idx - 1].add(part.offset(cradle_line_width / 4)); + // This cradle line is to close to another. + // Move it back + center_front = center_front + normal(direction, outer_radius - vSize(center_front - cradle.center)); + center_up = center_front + direction_up_center; + center_down = center_front - direction_up_center; } } - cradle_areas_calc[idx - 1] = cradle_areas_calc[idx - 1].unionPolygons(); + Point back_center = center_front + normal(direction, triangle_length); + Point direction_up_back = normal(rotate(direction, M_PI / 2), cradle_line_width / 2); + + Point back_up = back_center + direction_up_back; + Point back_down = back_center - direction_up_back; + + line->line.front() = back_center + normal(direction, cradle_line_width / 2 - FUDGE_LENGTH / 2); + + + all_tips_center.emplace_back(center_up, center_down); + + Polygon line_tip; + line_tip.add(back_down); + line_tip.add(back_up); + line_tip.add(center_up); + line_tip.add(center_down); + if (line_tip.area() < 0) // todo there has to be a faster way to check if direction is correct + { + line_tip.reverse(); + } + line->area.add(line_tip); + line->area=line->area.unionPolygons(line->line.offset(0).offsetPolyLine(cradle_line_width / 2, ClipperLib::jtMiter)); + Polygons anti_preferred = line->area.offset(config.xy_distance); + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for(size_t z_distance_idx = 0; z_distance_idx < config.z_distance_top_layers; z_distance_idx++) + { + volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); + } } - } - } - const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; - const coord_t small_hole_size - = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + } + Polygons shadow = shadow_data[layer_idx][cradle.shadow_idx][0]; + Polygons cradle_base = shadow; + Polygons first_cradle_areas; - for (auto [idx, cradle] : cradle_areas_calc | ranges::views::enumerate) - { - if (! cradle.empty()) + if (! use_fake_roof && support_roof_layers) { - if(idx < cradle_z_distance_layers + 1 && idx != 0) + Polygons cut_line_base; + if (large_cradle_base) { - continue; + // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest + // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just + // the overhang, but that should not cause any issues. + Point min_p = cradle_base.min(); + Point max_p = cradle_base.max(); + Polygons rest_line; + rest_line.addLine(min_p, max_p); + cut_line_base = rest_line.offsetPolyLine(small_hole_size); } - if (idx == 0) + Polygons closed = cradle_base.unionPolygons(first_cradle_areas.offset(closing_dist, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); { - if (! use_fake_roof && support_roof_layers) + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) { - Polygons cut_line_base; - if (large_cradle_base) - { - // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest - // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just - // the overhang, but that should not cause any issues. - Point min_p = cradle.min(); - Point max_p = cradle.max(); - Polygons rest_line; - rest_line.addLine(min_p, max_p); - cut_line_base = rest_line.offsetPolyLine(small_hole_size); - } - Polygons closed = cradle.unionPolygons(cradle_areas_calc[1].offset(closing_dist, ClipperLib::jtRound).unionPolygons().offset(-closing_dist, ClipperLib::jtRound)); - { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) - { - support_free_areas[layer_idx - interface_down].add(cut_line_base); - volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); - } - } - if (large_cradle_base) - { - cradle = closed.difference(cut_line_base); - } + support_free_areas[layer_idx - interface_down].add(cut_line_base); + volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); } + } + if (large_cradle_base) + { + cradle_base = closed.difference(cut_line_base); + } + } + + cradle_base = cradle_base.unionPolygons(); + + if(cradle_lines_roof) + { + Polygons forbidden_here = + volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + std::vector> roofs; + roofs.emplace_back(cradle_base, -1); + - if (large_cradle_line_tips) + for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) + { + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers + 1,line_idx); + if(!line_opt) { - cradle.add(cradle_areas_calc[1]); + continue; } - cradle = cradle.unionPolygons(); - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_base_areas[layer_idx].add(cradle); + coord_t line_base_offset = large_cradle_base ? std::max(coord_t(0),config.getRadius(cradle_tip_dtt) - cradle_line_width/2) : 0;//todo what offset ? Closing ? + roofs.emplace_back(line_opt.value()->area.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); + } - else - { - volumes_.addAreaToAntiPreferred(cradle.offset(config.xy_distance), layer_idx + idx); - { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_areas[layer_idx + idx].add(cradle); - } - Polygons unsupported_cradle; - Polygons max_supported_by_below = cradle_areas_calc[ idx == cradle_z_distance_layers + 1 ? 0 : idx - 1].offset(config.maximum_move_distance); - for (auto cradle_part : cradle.splitIntoParts()) - { - if (cradle_part.intersection(max_supported_by_below).empty() || (! large_cradle_line_tips && idx == cradle_z_distance_layers + 1)) - { - unsupported_cradle.add(cradle_part); - } - } - for(int idx_delta_down = std::min(coord_t(cradle_z_distance_layers+1), coord_t(layer_idx+idx)); idx_delta_down >=0; idx_delta_down--) + for(auto roof_area_pair : roofs) + { + Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( + roof_area_pair.first,roof_outset,forbidden_here, config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) { + const Polygons forbidden_next = + volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); - const Polygons relevant_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx - idx_delta_down, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); + Polygons roof_area_before = full_overhang_area; + full_overhang_area = full_overhang_area.difference(forbidden_next); - Polygons next_unsupported_cradle; - for (auto cradle_part : unsupported_cradle.splitIntoParts()) + if (full_overhang_area.area()>EPSILON && dtt_roof < support_roof_layers) { - if (!cradle_part.difference(relevant_forbidden).empty()) + if(roof_area_pair.second != -1) //If is_line + { + TreeSupportCradleLine roof_base_line(cradle.lines[roof_area_pair.second].front()); + roof_base_line.area = full_overhang_area; + roof_base_line.is_base = true; + roof_base_line.layer_idx = layer_idx - dtt_roof; + cradle.lines[roof_area_pair.second].emplace_front(roof_base_line); + } + else { - if (large_cradle_line_tips) + if(dtt_roof+1 < cradle.base_below.size()) { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_base_areas[layer_idx + idx - idx_delta_down].add(cradle_part); + cradle.base_below[dtt_roof+1].add(full_overhang_area); } else { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - cradle_overhang[layer_idx + idx - idx_delta_down].add(cradle_part); + cradle.base_below.emplace_back(full_overhang_area); } } - else + Polygons overhang_part = roof_area_before.difference(full_overhang_area.offset(config.maximum_move_distance)); //todo safe? + if(overhang_part.area()>EPSILON) { - next_unsupported_cradle.add(cradle_part); + TreeSupportCradleOverhangInformation cradle_overhang(overhang_part,roof_area_pair.second >= 0,true,layer_idx,roof_area_pair.second); + cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang);; } - } - unsupported_cradle=next_unsupported_cradle; - } - } - } - } - } - }); - - - cura::parallel_for( - 1, - cradle_areas.size(), - [&](const LayerIndex layer_idx) - { - if (layer_idx < cradle_areas.size()) - { - cradle_areas[layer_idx] = cradle_areas[layer_idx].unionPolygons(); - } - if (layer_idx < cradle_overhang.size()) - { - cradle_overhang[layer_idx] = cradle_overhang[layer_idx].unionPolygons(); - } - if (layer_idx < cradle_base_areas.size()) - { - cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); - } - if (layer_idx < support_free_areas.size()) - { - support_free_areas[layer_idx] = support_free_areas[layer_idx].unionPolygons(); - } - }); -} - - -void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) -{ - calculateFloatingParts(mesh); - - - // todo move up cradle if no line contacts model - // todo generate additional overhang if model may bend... Is this a TreeSupport Feature though? - - - std::vector> center_data = generateCradleCenters(mesh); - std::vector, CenterSubCenterPointPair>>> cradle_polylines = generateCradleLines(center_data); - cleanCradleLineOverlaps(cradle_polylines); - - - std::vector>> cradle_polygons(cradle_polylines.size()); - const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath - // Some angles needed to move cradle lines outwards to prevent them from toughing. - double center_angle = (2.0 * M_PI) / double(cradle_line_count); - double outer_angle = (M_PI - center_angle) / 2; - coord_t outer_radius = (double(min_distance_between_lines + config.support_line_width) / sin(center_angle)) * sin(outer_angle); - - - cura::parallel_for( - 0, - cradle_polylines.size(), - [&](const LayerIndex layer_idx) - { - for (size_t cradle_idx = 0; cradle_idx < cradle_polylines[layer_idx].size(); cradle_idx++) - { - cradle_polygons[layer_idx].emplace_back(); - for (size_t cradle_height = 0; cradle_height < cradle_polylines[layer_idx][cradle_idx].first.size(); cradle_height++) - { - size_t max_close = 0; - - for (auto [line_idx, line] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) - { - size_t close = 0; - for (auto [line_idx2, line2] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) - { - if (line_idx == line_idx2) - { - continue; - } - if (vSize(line.front() - line2.front()) < min_distance_between_lines + cradle_line_width) - { - close++; + } + else + { + TreeSupportCradleOverhangInformation cradle_overhang(roof_area_before,roof_area_pair.second >= 0,true,layer_idx,roof_area_pair.second); + cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); + break; + } } } - max_close = std::max(max_close, close); } - - Polygons line_tips; - - if (max_close > 0) + else { - std::vector> all_tips_center; - // generate trapezoid line tip with front width of support line width, back cradle_width. + cradle.base_below.emplace_back(cradle_base); + TreeSupportCradleOverhangInformation cradle_overhang(cradle_base, false,false,layer_idx); + cradle.overhang[layer_idx-1].emplace_back(cradle_overhang); + for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) { - for (auto [line_idx, line] : cradle_polylines[layer_idx][cradle_idx].first[cradle_height] | ranges::views::enumerate) + if(!cradle.lines[line_idx].empty()) { - // 2*cradle_line_count would be distance between cradle lines stays the same - coord_t triangle_length = (cradle_line_width - config.support_line_width) / 2 * tan(M_PI / 2 - M_PI / double(3 * cradle_line_count)); - - Point direction = line.back() - line.front(); - Point center_front = line.front() - normal(direction, cradle_line_width / 2); - - - Point direction_up_center = normal(rotate(direction, M_PI / 2), config.support_line_width / 2); - Point center_up = center_front + direction_up_center; - Point center_down = center_front - direction_up_center; - - for (auto existing_center : all_tips_center) + LayerIndex support_cradle_on_layer_idx; + if(cradle.lines[line_idx].front().layer_idx == layer_idx) { - Point intersect; - bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); - if (centers_touch - || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) - { - // This cradle line is to close to another. - // Move it back - CenterSubCenterPointPair center_index_data = cradle_polylines[layer_idx][cradle_idx].second; - center_front = center_front + normal(direction, outer_radius - vSize(center_front - center_data[layer_idx][center_index_data.first].second[center_index_data.second])); - center_up = center_front + direction_up_center; - center_down = center_front - direction_up_center; - } + support_cradle_on_layer_idx = layer_idx - 1; } - - Point back_center = center_front + normal(direction, triangle_length); - Point direction_up_back = normal(rotate(direction, M_PI / 2), cradle_line_width / 2); - - Point back_up = back_center + direction_up_back; - Point back_down = back_center - direction_up_back; - - cradle_polylines[layer_idx][cradle_idx].first[cradle_height][line_idx][0] - = back_center + normal(direction, cradle_line_width / 2 - FUDGE_LENGTH / 2); - - - all_tips_center.emplace_back(center_up, center_down); - - Polygon line_tip; - line_tip.add(back_down); - line_tip.add(back_up); - line_tip.add(center_up); - line_tip.add(center_down); - if (line_tip.area() < 0) // todo there has to be a faster way to check if direction is correct + else { - line_tip.reverse(); + support_cradle_on_layer_idx = cradle.lines[line_idx].front().layer_idx - cradle_z_distance_layers; } - line_tips.add(line_tip); + TreeSupportCradleOverhangInformation line_overhang(cradle.lines[line_idx].front().area,true,false,cradle.lines[line_idx].front().layer_idx,line_idx); + cradle.overhang[support_cradle_on_layer_idx].emplace_back(line_overhang); } } + } - Polygons cradle - = cradle_polylines[layer_idx][cradle_idx].first[cradle_height].offsetPolyLine(cradle_line_width / 2, ClipperLib::jtMiter).unionPolygons(line_tips); + } + }); - // Idea: Get number of too close tips => If size then issue else place triangle and maybe recess until fine. - // Needs to be deterministic independent of line order. Sort lines by start xy coord! +} - if (cradle_height == 0 && cradle_polylines[layer_idx][cradle_idx].first.size() > 1) - { - Polygons model_shadow = center_data[layer_idx][cradle_polylines[layer_idx][cradle_idx].second.first].first[0]; - if (cradle_base_roof && ! large_cradle_base) - { - cradle = cradle.unionPolygons(model_shadow); - } - else - { - cradle = model_shadow; - } - } - cradle_polygons[layer_idx][cradle_idx].emplace_back(cradle.unionPolygons()); +void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) +{ + calculateFloatingParts(mesh); - Polygons relevant_forbidden = volumes_.getAvoidance( - 0, - layer_idx + cradle_height, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); - } - } - }); + // todo move up cradle if no line contacts model + // todo generate additional overhang if model may bend... Is this a TreeSupport Feature though? + // todo enlarge lines to prevent overhang + - finalizeCradleAreas(cradle_polygons, support_free_areas, center_data); + std::vector>> shadow_data = generateCradleCenters(mesh); + generateCradleLines(shadow_data); + cleanCradleLineOverlaps(); + generateCradleLineAreasAndBase(shadow_data,support_free_areas); } void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof) @@ -1710,57 +1655,6 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m } }); - - if(cradle_base_roof) - { - // The cradle has to be added after the closing operation above, as otherwise the holes in the cradle that ensure it can be removed will be closed. - cura::parallel_for - ( - 1, - cradle_areas.size(), - [&](const LayerIndex layer_idx) - { - if (cradle_areas[layer_idx].empty() && cradle_base_areas[layer_idx].empty()) - { - return; // This is a continue if imagined in a loop context. - } - - // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with -radius. - // This is intentional here, as support roof is still valid if only a part of the tip may reach it. - Polygons forbidden_here = - volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); - - Polygons full_overhang_area; - full_overhang_area.add(cradle_base_areas[layer_idx]); - full_overhang_area=full_overhang_area.unionPolygons(); - - full_overhang_area = TreeSupportUtils::safeOffsetInc(full_overhang_area,roof_outset,forbidden_here, config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - - - for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) - { - const Polygons forbidden_next = - volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); - - full_overhang_area = full_overhang_area.difference(forbidden_next); - - - if (full_overhang_area.area()>EPSILON) - { - std::lock_guard critical_section_potential_support_roofs(critical_potential_support_roofs); - additional_support_roofs[layer_idx-dtt_roof].add((full_overhang_area)); - } - else - { - break; - } - } - - } - ); - } - - cura::parallel_for( 0, @@ -1773,7 +1667,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m } -void TreeSupportTipGenerator::addPointAsInfluenceArea( +TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( std::vector>& move_bounds, std::pair p, size_t dtt, @@ -1789,7 +1683,7 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea( if (! config.support_rests_on_model && ! to_bp) { spdlog::warn("Tried to add an invalid support point"); - return; + return nullptr; } Polygon circle; Polygon base_circle = TreeSupportBaseCircle::getBaseCircle(); @@ -1825,15 +1719,19 @@ void TreeSupportTipGenerator::addPointAsInfluenceArea( } move_bounds[insert_layer].emplace(elem); + return elem; } } + return nullptr; } -void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, +std::vector TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, bool connect_points) { - // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a + std::vector inserted_elements; + // Add tip area as roof (happens when minimum roof area > minimum tip + // area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a // certain roof area will actually have lines. size_t dtt_roof_tip = 0; coord_t base_radius = config.getRadius(supports_cradle ? cradle_tip_dtt: 0); @@ -1862,7 +1760,11 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector point_data : line) { - addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); + TreeSupportElement* elem = addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); + if(elem) + { + inserted_elements.emplace_back(elem); + } } } @@ -1874,7 +1776,11 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector point_data : line) { - addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); + TreeSupportElement* elem = addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); + if(elem) + { + inserted_elements.emplace_back(elem); + } } } @@ -1921,7 +1827,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, dtt_roof_tip != 0 || supports_roof, supports_cradle, disable_ovalization, additional_ovalization_targets); + + if(elem) + { + inserted_elements.emplace_back(elem); + } } } + return inserted_elements; } @@ -1951,8 +1863,7 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( : storage.support.supportLayers[layer_idx + 1].support_roof).unionPolygons(additional_support_areas[layer_idx + 1]); Polygons roof_on_layer = (use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); - roof_on_layer_above.add(cradle_base_areas[layer_idx]); - roof_on_layer_above = roof_on_layer_above.unionPolygons(cradle_overhang[layer_idx]); + roof_on_layer_above = roof_on_layer_above.unionPolygons(); for (TreeSupportElement* elem : move_bounds[layer_idx]) { @@ -1960,7 +1871,7 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( { to_be_removed.emplace_back(elem); } - else if (elem->supports_roof) + else if (elem->supports_roof && elem->cradle_line.get() == nullptr) { Point from = elem->result_on_layer; PolygonUtils::moveInside(roof_on_layer_above, from); @@ -1990,7 +1901,10 @@ void TreeSupportTipGenerator::generateTips( const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, - std::vector& placed_support_lines_support_areas, std::vector& placed_fake_roof_areas, std::vector& support_free_areas) + std::vector& placed_support_lines_support_areas, + std::vector& placed_fake_roof_areas, + std::vector& support_free_areas, + std::vector>& cradle_data_export) { std::vector> new_tips(move_bounds.size()); @@ -2015,6 +1929,27 @@ void TreeSupportTipGenerator::generateTips( calculateRoofAreas(mesh); } + std::vector> all_cradles_requiring_support(move_bounds.size()); + std::vector all_cradle_line_areas(move_bounds.size()); + for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + { + for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + { + for(auto overhang_pair:cradle_data[layer_idx][cradle_idx]->overhang) + { + all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data[layer_idx][cradle_idx]); + } + + for (auto [line_idx, lines] : cradle_data[layer_idx][cradle_idx]->lines | ranges::views::enumerate) + { + for (auto [height_idx, line] : lines | ranges::views::enumerate) + { + all_cradle_line_areas[line.layer_idx].add(line.area); + } + } + } + } + cura::parallel_for( 1, mesh.overhang_areas.size() - z_distance_delta, @@ -2022,8 +1957,7 @@ void TreeSupportTipGenerator::generateTips( { if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() && (layer_idx + 1 >= support_roof_drawn.size() || support_roof_drawn[layer_idx + 1].empty()) && - (layer_idx >= cradle_base_areas.size() || cradle_base_areas[layer_idx].empty()) && - (layer_idx >= cradle_overhang.size() || cradle_overhang[layer_idx].empty()) + (layer_idx >= all_cradles_requiring_support.size() || all_cradles_requiring_support[layer_idx].empty()) ) { return; // This is a continue if imagined in a loop context. @@ -2041,6 +1975,25 @@ void TreeSupportTipGenerator::generateTips( .unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. enum class OverhangType {REGULAR, ROOF, CRADLE}; + + struct OverhangInformation + { + + OverhangInformation(Polygons overhang, OverhangType type, TreeSupportCradle* cradle = nullptr, TreeSupportCradleOverhangInformation* cradle_overhang = nullptr): + overhang(overhang), + type(type), + cradle(cradle), + cradle_overhang(cradle_overhang) + { + + } + + Polygons overhang; + OverhangType type; + TreeSupportCradle* cradle; + TreeSupportCradleOverhangInformation* cradle_overhang; + }; + std::function generateLines = [&](const Polygons& area, OverhangType overhang_type, LayerIndex layer_idx) { coord_t upper_line_distance = support_supporting_branch_distance; @@ -2060,7 +2013,7 @@ void TreeSupportTipGenerator::generateTips( }; - std::vector> overhang_processing; + std::vector overhang_processing; // ^^^ Every overhang has saved if a roof should be generated for it. // This can NOT be done in the for loop as an area may NOT have a roof even if it is larger than the minimum_roof_area when it is only larger because of the support // horizontal expansion and it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior @@ -2068,11 +2021,6 @@ void TreeSupportTipGenerator::generateTips( Polygons core_overhang = mesh.overhang_areas[layer_idx + z_distance_delta]; - if(cradle_overhang.size() > layer_idx) - { - core_overhang = core_overhang.unionPolygons(cradle_overhang[layer_idx]); - } - if (support_roof_layers && layer_idx + 1 < support_roof_drawn.size()) { @@ -2086,15 +2034,21 @@ void TreeSupportTipGenerator::generateTips( overhang_processing.emplace_back(roof_part, OverhangType::ROOF); } } - - core_overhang = core_overhang.difference(TreeSupportUtils::safeOffsetInc(cradle_base_areas[layer_idx], support_outset + EPSILON, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1, config.support_line_distance / 2, &config.simplifier)); + all_cradle_line_areas[layer_idx] = all_cradle_line_areas[layer_idx].unionPolygons(); core_overhang = core_overhang.difference(support_free_areas[layer_idx]); - core_overhang = core_overhang.difference(cradle_areas[layer_idx].offset(config.support_line_width)); //todo better ensure it is counted as a tip - for (Polygons cradle_base : cradle_base_areas[layer_idx].splitIntoParts(true)) + core_overhang = core_overhang.difference(all_cradle_line_areas[layer_idx]); + + for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) { - overhang_processing.emplace_back(cradle_base, OverhangType::CRADLE); + for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx].size(); cradle_overhang_idx++) + { + TreeSupportCradleOverhangInformation* cradle_overhang = &(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx][cradle_overhang_idx]); + overhang_processing.emplace_back(cradle_overhang->area, OverhangType::CRADLE, all_cradles_requiring_support[layer_idx][cradle_idx], cradle_overhang); + + } } + Polygons overhang_regular = TreeSupportUtils::safeOffsetInc( core_overhang, support_outset, @@ -2113,11 +2067,11 @@ void TreeSupportTipGenerator::generateTips( // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. - { + { Polygons already_supported = support_roof_drawn[layer_idx]; - already_supported.add(cradle_areas[layer_idx]); + already_supported.add(all_cradle_line_areas[layer_idx]); already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. - already_supported=already_supported.unionPolygons(); + already_supported = already_supported.unionPolygons(); while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. { coord_t offset_current_step = @@ -2143,7 +2097,7 @@ void TreeSupportTipGenerator::generateTips( { continue; } - + all_cradle_line_areas[layer_idx] = all_cradle_line_areas[layer_idx].unionPolygons(); std::vector overhang_lines; Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, OverhangType::REGULAR, layer_idx), config.min_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. @@ -2183,16 +2137,15 @@ void TreeSupportTipGenerator::generateTips( }; Polygons already_supported = support_roof_drawn[layer_idx-lag_ctr]; - already_supported.add(cradle_areas[layer_idx-lag_ctr]); - already_supported.add(support_free_areas[layer_idx-lag_ctr]); // While point there are not supported, there may be no support anyway. - already_supported=already_supported.unionPolygons(); + already_supported.add(support_free_areas[layer_idx-lag_ctr]); // While point there are not supported, there may be no support anyway. + already_supported=already_supported.unionPolygons(); - //Remove all points that are for some reason are already supported - std::function)> evaluateAlreadySupported = - [&](std::pair p) { return already_supported.inside(p.first, true); - }; + //Remove all points that are for some reason are already supported + std::function)> evaluateAlreadySupported = + [&](std::pair p) { return already_supported.inside(p.first, true); + }; - overhang_lines = splitLines(overhang_lines, evaluateAlreadySupported).second; + overhang_lines = splitLines(overhang_lines, evaluateAlreadySupported).second; std::pair, std::vector> split = splitLines(overhang_lines, evaluatePoint); // Keep all lines that are invalid. @@ -2200,8 +2153,8 @@ void TreeSupportTipGenerator::generateTips( std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, false, support_roof_layers, - false); + addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, false, support_roof_layers, + false); } } } @@ -2213,21 +2166,26 @@ void TreeSupportTipGenerator::generateTips( overhang_processing.emplace_back(support_part, OverhangType::REGULAR); } - for (std::pair overhang_pair : overhang_processing) + for (OverhangInformation overhang_pair : overhang_processing) { - const bool roof_allowed_for_this_part = overhang_pair.second == OverhangType::ROOF; - const bool supports_cradle = overhang_pair.second == OverhangType::CRADLE; + const bool supports_cradle = overhang_pair.type == OverhangType::CRADLE; + const bool roof_allowed_for_this_part = overhang_pair.type == OverhangType::ROOF || (supports_cradle && overhang_pair.cradle->is_roof && layer_idx <= overhang_pair.cradle->layer_idx ); - Polygons overhang_outset = overhang_pair.first; + Polygons overhang_outset = overhang_pair.overhang; const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); std::vector overhang_lines; bool only_lines = true; - + std::optional cradle_line_opt; + if(supports_cradle && overhang_pair.cradle_overhang->is_line) + { + auto line_idx_data = overhang_pair.cradle_overhang->line_information; //todo refactor line information + cradle_line_opt = overhang_pair.cradle->getCradleLineOfIndex(line_idx_data.first,line_idx_data.second); + } // The tip positions are determined here. // todo can cause inconsistent support density if a line exactly aligns with the model Polygons polylines = ensureMaximumDistancePolyline( - generateLines(overhang_outset, overhang_pair.second, layer_idx + roof_allowed_for_this_part), + cradle_line_opt.has_value() ? cradle_line_opt.value()->line.offset(0) : generateLines(overhang_outset, overhang_pair.type, layer_idx + roof_allowed_for_this_part), //todo type may cause issues now as roofcradles are cradles ! roof_allowed_for_this_part ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, @@ -2263,7 +2221,7 @@ void TreeSupportTipGenerator::generateTips( if (roof_allowed_for_this_part || supports_cradle) // Some roof may only be supported by a part of a tip { - polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, config.getRadius(0) + FUDGE_LENGTH / 2); + polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (supports_cradle?2:1) * config.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); @@ -2285,7 +2243,8 @@ void TreeSupportTipGenerator::generateTips( } size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (roof_allowed_for_this_part ? 0 : support_roof_layers)) : 0; - addLinesAsInfluenceAreas( + + std::vector inserted_elements = addLinesAsInfluenceAreas( new_tips, overhang_lines, force_tip_to_roof ? support_roof_layers : 0, @@ -2293,6 +2252,26 @@ void TreeSupportTipGenerator::generateTips( roof_allowed_for_this_part, supports_cradle,dont_move_for_layers, only_lines); + + if(supports_cradle) + { + for(TreeSupportElement* elem:inserted_elements) + { + if(overhang_pair.cradle_overhang->is_line) + { + auto line_idx_data = overhang_pair.cradle_overhang->line_information; //todo refactor line information + if(cradle_line_opt) + { + cradle_line_opt.value()->tips.emplace_back(elem); + elem->cradle_line = std::make_shared(overhang_pair.cradle,line_idx_data.first,line_idx_data.second); + } + else + { + spdlog::warn("Wrong data to access cradle {} {}",line_idx_data.first,line_idx_data.second); + } + } + } + } } }); @@ -2344,7 +2323,6 @@ void TreeSupportTipGenerator::generateTips( Polygons all_roof = support_roof_drawn[layer_idx].unionPolygons(roof_tips_drawn[layer_idx]); Polygons all_roof_above = support_roof_drawn[layer_idx + 1] .unionPolygons(roof_tips_drawn[layer_idx + 1]) - .unionPolygons(cradle_areas[layer_idx]) .offset(FUDGE_LENGTH) .unionPolygons(); storage.support.supportLayers[layer_idx].support_fractional_roof.add(all_roof.difference(all_roof_above)); @@ -2352,24 +2330,15 @@ void TreeSupportTipGenerator::generateTips( } } }); + cradle_data_export.resize(cradle_data.size()); - removeUselessAddedPoints(new_tips, storage, additional_support_areas); - - if(support_roof_layers && !use_fake_roof && cradle_lines_roof) - { - for (auto [layer_idx, cradle] : cradle_areas | ranges::views::enumerate) - { - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(cradle); - } - } - else + for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) { - for (auto [layer_idx, cradle] : cradle_areas | ranges::views::enumerate) - { - additional_support_areas[layer_idx] = additional_support_areas[layer_idx].unionPolygons(cradle); - } + cradle_data_export[layer_idx] = cradles; } + removeUselessAddedPoints(new_tips, storage, additional_support_areas); + for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { move_bounds[layer_idx].insert(tips_on_layer.begin(), tips_on_layer.end()); From 4787452be1c897a05643ff95796d2e6887dac2b6 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:30:06 +0100 Subject: [PATCH 029/101] Fix first and last vertex created by makeCircle being close together, or even the same. --- src/utils/polygonUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index d1f4debe21..b254f4c025 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1417,7 +1417,7 @@ double PolygonUtils::relativeHammingDistance(const Polygons& poly_a, const Polyg Polygon PolygonUtils::makeCircle(const Point mid, const coord_t radius, const AngleRadians a_step) { Polygon circle; - for (float a = 0; a < 2 * M_PI; a += a_step) + for (float a = 0; a + a_step / 2 < 2 * M_PI; a += a_step) { circle.emplace_back(mid + Point(radius * cos(a), radius * sin(a))); } From a533391830a5513c8de3eb2b9ee3fdf2bba60fba Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:30:35 +0100 Subject: [PATCH 030/101] Fix branches not merging below anti preferred areas --- src/TreeSupport.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 80d440ace7..90d9934561 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -813,6 +813,11 @@ std::optional TreeSupport::increaseSingleArea( } } + if(volumes_.getFirstAntiPreferredLayerIdx() >= layer_idx) + { + current_elem.can_avoid_anti_preferred = true; + } + if (settings.increase_radius && check_layer_data.area() > 1) { std::function validWithRadius = [&](coord_t next_radius) From 39d2a914f912bf4cd365954d5e9467bb305fd8dd Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:55:46 +0100 Subject: [PATCH 031/101] Cradle fixes related to cradle roof type and a bit refactoring --- include/TreeSupportCradle.h | 67 +++++--- include/TreeSupportElement.h | 2 +- include/TreeSupportTipGenerator.h | 11 +- src/TreeSupport.cpp | 3 +- src/TreeSupportTipGenerator.cpp | 244 ++++++++++++++---------------- 5 files changed, 171 insertions(+), 156 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 589c435d01..03233b8de2 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -12,7 +12,47 @@ namespace cura { +//todo rename file as now general TreeSupportTipDataStructures +struct TreeSupportCradle; +struct OverhangInformation +{ + + OverhangInformation(Polygons overhang, bool roof): + overhang(overhang), + is_roof(roof), + is_cradle(false), + cradle_layer_idx(-1), + cradle_line_idx(-1), + cradle(nullptr) + { + + } + + OverhangInformation(Polygons overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1,int32_t cradle_line_idx = -1): + overhang(overhang), + is_roof(roof), + is_cradle(true), + cradle_layer_idx(cradle_layer_idx), + cradle_line_idx(cradle_line_idx), + cradle(cradle) + { + + } + + Polygons overhang; + bool is_roof; + bool is_cradle; + int32_t cradle_layer_idx; + int32_t cradle_line_idx; + TreeSupportCradle* cradle; + + bool isCradleLine() + { + return is_cradle && cradle_line_idx >= 0; + } + +}; struct TreeSupportCradleLine { //required to shrink a vector using resize @@ -21,17 +61,18 @@ struct TreeSupportCradleLine spdlog::error("Dummy TreeSupportCradleLine constructor called"); } - TreeSupportCradleLine(Polygon line, LayerIndex layer_idx) + TreeSupportCradleLine(Polygon line, LayerIndex layer_idx, bool is_roof) : line(line) , layer_idx(layer_idx) + , is_roof(is_roof) { } Polygons area; Polygon line; Polygon removed_line; - std::vector tips; LayerIndex layer_idx; bool is_base = false; + bool is_roof; void addLineToRemoved(Polygon& line_to_add) { @@ -55,26 +96,6 @@ struct TreeSupportCradleLine } }; -struct TreeSupportCradleOverhangInformation -{ - TreeSupportCradleOverhangInformation() - : TreeSupportCradleOverhangInformation(Polygons(), false) - { - } - TreeSupportCradleOverhangInformation(Polygons area, bool is_line, bool is_roof = false, LayerIndex line_layer_idx = -1, int32_t line_idx = -1) - : area(area) - , is_line(is_line) - , is_roof(is_roof) - , line_information(std::pair(line_layer_idx, line_idx)) - { - - } - Polygons area; - bool is_line; - std::pair line_information = std::pair(-1, -1); - bool is_roof; -}; - struct TreeSupportCradle { std::vector> lines; @@ -83,7 +104,7 @@ struct TreeSupportCradle std::vector base_below; Point center; size_t shadow_idx; - std::unordered_map> overhang; + std::unordered_map> overhang; size_t config_cradle_layers_min; coord_t config_cradle_length_min; diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index ba37085a5a..1a887b94ae 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -455,7 +455,7 @@ struct TreeSupportElement result.distance_to_top += 1; result.skip_ovalisation = false; result.result_on_layer = Point(-1, -1); - //result.area = nullptr; + result.area = nullptr; return result; } diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index f69d393c19..b337c0f607 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -218,7 +218,8 @@ class TreeSupportTipGenerator */ TreeSupportElement* addPointAsInfluenceArea( std::vector>& move_bounds, - std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool cradle, bool skip_ovalisation, + std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, + bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); @@ -232,8 +233,12 @@ class TreeSupportTipGenerator * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param connect_points [in] If the points of said line should be connected by ovalization. */ - std::vector addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, - bool connect_points); + void addLinesAsInfluenceAreas(std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, LayerIndex insert_layer_idx, + OverhangInformation& overhang_data, + size_t dont_move_until, + bool connect_points); /*! * \brief Remove tips that should not have been added in the first place. diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 90d9934561..87d89d9c06 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2281,8 +2281,9 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) { Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].area; + bool is_roof = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_roof; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; - if(cradle_data[layer_idx][cradle_idx]->is_roof) + if(is_roof) { if(support_roof_storage.size()<=layer_idx) { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 4c9e698e98..78310adee8 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -840,7 +840,6 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorshadow_idx]; for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { - bool aborted = accumulated_model.size() == 1; const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; @@ -849,8 +848,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines.resize(cradle_line_count); } - if ((idx > 0 || (cradle_base_roof && ! large_cradle_base && ! aborted)) - && ! model_shadow.empty()) // also calculate lines for the small cradle, idea is that the lines will touch the overhang and support it that way. + if (idx > 0 && ! model_shadow.empty()) { coord_t max_distance2 = 0; @@ -1034,16 +1032,11 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines[angle_idx].empty()) { - cradle->lines[angle_idx][0]=TreeSupportCradleLine(line,layer_idx+idx); + cradle->lines[angle_idx][0]=TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof); } else { - cradle->lines[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx)); - if(cradle->lines[angle_idx].size()>cradle_layers) - { - printf("Issue detected!\n"); - PolygonUtils::makeCircle(cradle->center, sqrt(max_distance2) + current_cradle_length * 2, (2.0 * M_PI) / double(cradle_line_count)); - } + cradle->lines[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof)); } } } @@ -1299,11 +1292,12 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector critical_section_cradle(critical_support_free_areas_and_cradle_areas); for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) { support_free_areas[layer_idx - interface_down].add(cut_line_base); - volumes_.addAreaToAntiPreferred(closed, layer_idx - interface_down); + volumes_.addAreaToAntiPreferred(cradle_base, layer_idx - interface_down); } } if (large_cradle_base) { - cradle_base = closed.difference(cut_line_base); + cradle_base = cradle_base.difference(cut_line_base); + } + else if(cradle_base_roof) + { + // collect all inner points and connect to center for thin cradle base + Polygons connected_cradle_base; + for(size_t line_idx = 0;line_idx line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers +1, line_idx); + if(line_opt) + { + connected_cradle_base.addLine(cradle.center,line_opt.value()->line.front()); + } + } + Polygons relevant_forbidden = + volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width/2).unionPolygons(cradle_base).difference(relevant_forbidden); } } @@ -1339,7 +1348,6 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector> roofs; roofs.emplace_back(cradle_base, -1); - for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) { std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers + 1,line_idx); @@ -1354,7 +1362,6 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorEPSILON) { - TreeSupportCradleOverhangInformation cradle_overhang(overhang_part,roof_area_pair.second >= 0,true,layer_idx,roof_area_pair.second); - cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang);; + OverhangInformation cradle_overhang(overhang_part,true, cradle_data[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); + cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); } - } else { - TreeSupportCradleOverhangInformation cradle_overhang(roof_area_before,roof_area_pair.second >= 0,true,layer_idx,roof_area_pair.second); + OverhangInformation cradle_overhang(roof_area_before, true, cradle_data[layer_idx][cradle_idx],layer_idx-dtt_roof+1,roof_area_pair.second); cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); break; } @@ -1408,29 +1414,21 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, bool supports_roof, bool supports_cradle, size_t dont_move_until, - bool connect_points) +void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + OverhangInformation& overhang_data, + size_t dont_move_until, + bool connect_points) { std::vector inserted_elements; // Add tip area as roof (happens when minimum roof area > minimum tip // area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a // certain roof area will actually have lines. size_t dtt_roof_tip = 0; - coord_t base_radius = config.getRadius(supports_cradle ? cradle_tip_dtt: 0); - if (config.support_roof_wall_count == 0 && ! supports_cradle) + coord_t base_radius = config.getRadius(0); + if (config.support_roof_wall_count == 0 && ! overhang_data.is_cradle) //todo cradle base maybe { for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; dtt_roof_tip++) { - - std::function)> evaluateRoofWillGenerate = [&](std::pair p) { Polygon roof_circle; @@ -1827,22 +1828,34 @@ std::vector TreeSupportTipGenerator::addLinesAsInfluenceAre additional_ovalization_targets.emplace_back(line[idx + 1].first); } } + + size_t tip_dtt = 0; + if(overhang_data.is_cradle) + { + if((overhang_data.isCradleLine() && large_cradle_line_tips) || (!overhang_data.isCradleLine() && (!cradle_base_roof || large_cradle_line_tips))) + { + tip_dtt = cradle_tip_dtt; + } + } + TreeSupportElement* elem = addPointAsInfluenceArea( move_bounds, point_data, - supports_cradle ? cradle_tip_dtt : 0, - insert_layer_idx - dtt_roof_tip, - dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - dtt_roof_tip != 0 || supports_roof, supports_cradle, disable_ovalization, + tip_dtt, + insert_layer_idx - dtt_roof_tip, + dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, + dtt_roof_tip != 0 || overhang_data.is_roof, overhang_data.is_cradle, disable_ovalization, additional_ovalization_targets); if(elem) { - inserted_elements.emplace_back(elem); + if(overhang_data.isCradleLine()) + { + elem->cradle_line = std::make_shared(overhang_data.cradle,overhang_data.cradle_layer_idx,overhang_data.cradle_line_idx); + } } } } - return inserted_elements; } @@ -1851,6 +1864,23 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( SliceDataStorage& storage, std::vector& additional_support_areas) { + + std::vector all_cradle_roofs(storage.support.supportLayers.size()); + for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) + { + for(auto cradle:cradles) + { + if(cradle->is_roof) + { + + for (auto [base_idx, base] : cradle->base_below | ranges::views::enumerate) + { + all_cradle_roofs[layer_idx-base_idx].add(base); + } + } + } + } + cura::parallel_for( 0, move_bounds.size(), @@ -1861,9 +1891,9 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( std::vector to_be_removed; Polygons roof_on_layer_above = (use_fake_roof ? support_roof_drawn[layer_idx + 1] : storage.support.supportLayers[layer_idx + 1].support_roof).unionPolygons(additional_support_areas[layer_idx + 1]); + roof_on_layer_above = roof_on_layer_above.unionPolygons(all_cradle_roofs[layer_idx+1]); Polygons roof_on_layer = (use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); - roof_on_layer_above = roof_on_layer_above.unionPolygons(); for (TreeSupportElement* elem : move_bounds[layer_idx]) { @@ -1930,7 +1960,7 @@ void TreeSupportTipGenerator::generateTips( } std::vector> all_cradles_requiring_support(move_bounds.size()); - std::vector all_cradle_line_areas(move_bounds.size()); + std::vector all_cradle_areas(move_bounds.size()); for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) @@ -1944,9 +1974,13 @@ void TreeSupportTipGenerator::generateTips( { for (auto [height_idx, line] : lines | ranges::views::enumerate) { - all_cradle_line_areas[line.layer_idx].add(line.area); + all_cradle_areas[line.layer_idx].add(line.area); } } + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + { + all_cradle_areas[layer_idx-base_idx].add(base); + } } } @@ -1974,41 +2008,23 @@ void TreeSupportTipGenerator::generateTips( = relevant_forbidden.offset(EPSILON) .unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. - enum class OverhangType {REGULAR, ROOF, CRADLE}; - - struct OverhangInformation - { - - OverhangInformation(Polygons overhang, OverhangType type, TreeSupportCradle* cradle = nullptr, TreeSupportCradleOverhangInformation* cradle_overhang = nullptr): - overhang(overhang), - type(type), - cradle(cradle), - cradle_overhang(cradle_overhang) - { - } - Polygons overhang; - OverhangType type; - TreeSupportCradle* cradle; - TreeSupportCradleOverhangInformation* cradle_overhang; - }; - - std::function generateLines = [&](const Polygons& area, OverhangType overhang_type, LayerIndex layer_idx) + std::function generateLines = [&](const Polygons& area, bool roof, bool cradle, LayerIndex layer_idx) { coord_t upper_line_distance = support_supporting_branch_distance; - coord_t line_distance = std::max((overhang_type == OverhangType::ROOF) ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); + coord_t line_distance = std::max(roof ? support_roof_line_distance : support_tree_branch_distance, upper_line_distance); bool use_grid = line_distance == upper_line_distance; return TreeSupportUtils::generateSupportInfillLines( area, config, - (overhang_type == OverhangType::ROOF) && ! use_fake_roof, + roof && ! use_fake_roof, layer_idx, line_distance, cross_fill_provider, - ((overhang_type == OverhangType::ROOF) && ! use_fake_roof)|| (overhang_type == OverhangType::CRADLE), + (roof && ! use_fake_roof) || cradle, use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; @@ -2031,23 +2047,20 @@ void TreeSupportTipGenerator::generateTips( { //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors // introduced since then. But this does not fully prevent some rounding errors either way, so just handle the error later. - overhang_processing.emplace_back(roof_part, OverhangType::ROOF); - } + overhang_processing.emplace_back(roof_part, true); } - all_cradle_line_areas[layer_idx] = all_cradle_line_areas[layer_idx].unionPolygons(); - core_overhang = core_overhang.difference(support_free_areas[layer_idx]); - core_overhang = core_overhang.difference(all_cradle_line_areas[layer_idx]); + } + all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons().offset(EPSILON).unionPolygons(); + core_overhang = core_overhang.difference(support_free_areas[layer_idx]); + core_overhang = core_overhang.difference(all_cradle_areas[layer_idx]); - for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) + for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) + { + for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx].size(); cradle_overhang_idx++) { - for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx].size(); cradle_overhang_idx++) - { - TreeSupportCradleOverhangInformation* cradle_overhang = &(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx][cradle_overhang_idx]); - overhang_processing.emplace_back(cradle_overhang->area, OverhangType::CRADLE, all_cradles_requiring_support[layer_idx][cradle_idx], cradle_overhang); - - } + overhang_processing.emplace_back(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx][cradle_overhang_idx]); } - + } Polygons overhang_regular = TreeSupportUtils::safeOffsetInc( core_overhang, @@ -2069,7 +2082,7 @@ void TreeSupportTipGenerator::generateTips( { Polygons already_supported = support_roof_drawn[layer_idx]; - already_supported.add(all_cradle_line_areas[layer_idx]); + already_supported.add(all_cradle_areas[layer_idx]); already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. already_supported = already_supported.unionPolygons(); while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. @@ -2097,13 +2110,13 @@ void TreeSupportTipGenerator::generateTips( { continue; } - all_cradle_line_areas[layer_idx] = all_cradle_line_areas[layer_idx].unionPolygons(); + all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons(); std::vector overhang_lines; - Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, OverhangType::REGULAR, layer_idx), config.min_radius, 1, false); + Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, false, layer_idx), config.min_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. // Technically this violates branch distance, but not only is this the only reasonable choice, // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. - // Also it is assumed that the area that is valid a layer below is to small for support roof. + // Also it is assumed that the area that is valid a layer below is too small for support roof. if (polylines.pointCount() <= 3) { // Add the outer wall to ensure it is correct supported instead. @@ -2153,8 +2166,9 @@ void TreeSupportTipGenerator::generateTips( std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, layer_idx - lag_ctr, false, false, support_roof_layers, - false); + OverhangInformation dummy_overhang_info(remaining_overhang_part,false); + addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, + layer_idx - lag_ctr, dummy_overhang_info, support_roof_layers, false); } } } @@ -2163,30 +2177,24 @@ void TreeSupportTipGenerator::generateTips( for (Polygons support_part : overhang_regular.splitIntoParts(true)) { - overhang_processing.emplace_back(support_part, OverhangType::REGULAR); + overhang_processing.emplace_back(support_part, false); } - for (OverhangInformation overhang_pair : overhang_processing) + for (OverhangInformation overhang_data : overhang_processing) { - const bool supports_cradle = overhang_pair.type == OverhangType::CRADLE; - const bool roof_allowed_for_this_part = overhang_pair.type == OverhangType::ROOF || (supports_cradle && overhang_pair.cradle->is_roof && layer_idx <= overhang_pair.cradle->layer_idx ); - - Polygons overhang_outset = overhang_pair.overhang; + Polygons overhang_outset = overhang_data.overhang; const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); std::vector overhang_lines; bool only_lines = true; std::optional cradle_line_opt; - if(supports_cradle && overhang_pair.cradle_overhang->is_line) - { - auto line_idx_data = overhang_pair.cradle_overhang->line_information; //todo refactor line information - cradle_line_opt = overhang_pair.cradle->getCradleLineOfIndex(line_idx_data.first,line_idx_data.second); - } // The tip positions are determined here. // todo can cause inconsistent support density if a line exactly aligns with the model Polygons polylines = ensureMaximumDistancePolyline( - cradle_line_opt.has_value() ? cradle_line_opt.value()->line.offset(0) : generateLines(overhang_outset, overhang_pair.type, layer_idx + roof_allowed_for_this_part), //todo type may cause issues now as roofcradles are cradles - ! roof_allowed_for_this_part ? config.min_radius * 2 + cradle_line_opt.has_value() ? cradle_line_opt.value()->line.offset(0) : generateLines(overhang_outset, overhang_data.is_roof, + overhang_data.is_cradle, + layer_idx + overhang_data.is_roof), //todo type may cause issues now as roofcradles are cradles + ! overhang_data.is_roof ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, 1, @@ -2219,9 +2227,9 @@ void TreeSupportTipGenerator::generateTips( } } - if (roof_allowed_for_this_part || supports_cradle) // Some roof may only be supported by a part of a tip + if (overhang_data.is_roof || overhang_data.is_cradle) // Some roof may only be supported by a part of a tip { - polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (supports_cradle?2:1) * config.getRadius(0) + FUDGE_LENGTH / 2); + polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.is_cradle?2:1) * config.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); @@ -2238,40 +2246,20 @@ void TreeSupportTipGenerator::generateTips( } else { - spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} On Layer: {}", roof_allowed_for_this_part, supports_cradle ,layer_idx); + spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} On Layer: {}", overhang_data.is_roof, overhang_data.is_cradle ,layer_idx); } } - size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (roof_allowed_for_this_part ? 0 : support_roof_layers)) : 0; + size_t dont_move_for_layers = support_roof_layers ? (force_tip_to_roof ? support_roof_layers : (overhang_data.is_roof ? 0 : support_roof_layers)) : 0; - std::vector inserted_elements = addLinesAsInfluenceAreas( + addLinesAsInfluenceAreas( new_tips, overhang_lines, force_tip_to_roof ? support_roof_layers : 0, layer_idx, - roof_allowed_for_this_part, - supports_cradle,dont_move_for_layers, + overhang_data, + dont_move_for_layers, only_lines); - - if(supports_cradle) - { - for(TreeSupportElement* elem:inserted_elements) - { - if(overhang_pair.cradle_overhang->is_line) - { - auto line_idx_data = overhang_pair.cradle_overhang->line_information; //todo refactor line information - if(cradle_line_opt) - { - cradle_line_opt.value()->tips.emplace_back(elem); - elem->cradle_line = std::make_shared(overhang_pair.cradle,line_idx_data.first,line_idx_data.second); - } - else - { - spdlog::warn("Wrong data to access cradle {} {}",line_idx_data.first,line_idx_data.second); - } - } - } - } } }); From 20f2755387bab3306c03be7985954bc607b5d156 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:23:19 +0100 Subject: [PATCH 032/101] Ensure cradle lines have no overhang if possible, also performance improvements. --- include/TreeSupportCradle.h | 5 ++-- include/TreeSupportUtils.h | 10 ++++--- src/TreeSupport.cpp | 34 ++++++++++++------------ src/TreeSupportTipGenerator.cpp | 46 ++++++++++++++++++++++++++++++--- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 03233b8de2..57d6dc1fb2 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -164,14 +164,13 @@ struct TreeSupportCradle { if (up_idx <= config_cradle_layers_min) { - - spdlog::info("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx,line_idx,lines[line_idx][up_idx].layer_idx); + spdlog::debug("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx,line_idx,lines[line_idx][up_idx].layer_idx); lines[line_idx].clear(); break; } else { - spdlog::info("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx,line_idx,up_idx); + spdlog::debug("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx,line_idx,up_idx); lines[line_idx].resize(up_idx-1); break; } diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index ff2e256cc4..ddbd68ac45 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -203,11 +203,13 @@ class TreeSupportUtils * \param me[in] Polygons object that has to be offset. * \param distance[in] The distance by which me should be offset. Expects values >=0. * \param collision[in] The area representing obstacles. - * \param last_step_offset_without_check[in] The most it is allowed to offset in one step. + * \param safe_step_size[in] The most it is allowed to offset in one step. + * \param last_step_offset_without_check[in] The amount of distance for which the collision could be violated at the end. * \param min_amount_offset[in] How many steps have to be done at least. As this uses round offset this increases the amount of vertices, which may be required if Polygons get - * very small. Required as arcTolerance is not exposed in offset, which should result with a similar result, benefit may be eliminated by simplifying. \param - * min_offset_per_step Don't get below this amount of offset per step taken. Fine-tune tradeoff between speed and accuracy. \param simplifier[in] Pointer to Simplify object if - * the offset operation also simplify the Polygon. Improves performance. \return The resulting Polygons object. + * very small. Required as arcTolerance is not exposed in offset, which should result with a similar result, benefit may be eliminated by simplifying. + * \param min_offset_per_step Don't get below this amount of offset per step taken. Fine-tune tradeoff between speed and accuracy. + * \param simplifier[in] Pointer to Simplify object if the offset operation also simplify the Polygon. Improves performance. + * \return The resulting Polygons object. */ [[nodiscard]] static Polygons safeOffsetInc( const Polygons& me, diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 87d89d9c06..a75cf1bf94 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -757,8 +757,6 @@ std::optional TreeSupport::increaseSingleArea( bool anti_preferred_applied = false; if(!anti_preferred_areas.empty()) { - - //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. Todo? coord_t anti_radius_extra = std::max(settings.increase_speed-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); //todo better real radius. How prevent problems? if(anti_radius_extra) @@ -1426,6 +1424,12 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, std::vector>& move_bounds, std::vector>& cradle_data) { + + if(cradle_data.size()<=layer_idx || cradle_data[layer_idx].empty()) + { + return; + } + std::unordered_set removed_lines_idx; // Evaluate which lines have to be removed for all influence areas to be valid. // Goal is to remove as few lines as possible @@ -1448,7 +1452,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, // todo the coll/ regular radius difference can cause issues with slow vs fast avoidance causing lines to be removed even though the branch will not be here. It just could have been... if(!elem->can_avoid_anti_preferred || config.getCollisionRadius(*elem) != config.getRadius(*elem)) { - const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + config.getRadius(*elem) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); bool immutable = elem->area != nullptr; @@ -1468,8 +1472,6 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, } AABB relevant_influence_aabb = AABB(relevant_influence); - - for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base && !removed_lines_idx.contains(cradle_idx)) @@ -1481,7 +1483,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, { Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area, config.getRadius(*elem) + config.xy_distance, - volumes_.getCollision(0,layer_idx,true), + volumes_.getCollision(config.getRadius(*elem),layer_idx,true), safe_movement_distance, 0, 1, @@ -1501,7 +1503,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, removed_lines_idx.emplace(cradle_idx); cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line); cradle.getCradleLine()->line.clear(); - spdlog::info("Flagging to remove cradle line {} {} ", cradle.layer_idx, cradle.line_idx); + spdlog::debug("Flagging to remove cradle line {} {} ", cradle.layer_idx, cradle.line_idx); } } } @@ -1546,9 +1548,7 @@ void TreeSupport::createLayerPathing(std::vector>& auto dur_inc = std::chrono::duration_values::zero(); auto dur_merge = std::chrono::duration_values::zero(); - - const auto dur_inc_recent = std::chrono::duration_values::zero(); - const auto dur_merge_recent = std::chrono::duration_values::zero(); + auto dur_cradle = std::chrono::duration_values::zero(); LayerIndex last_merge = move_bounds.size(); bool new_element = false; @@ -1620,19 +1620,19 @@ void TreeSupport::createLayerPathing(std::vector>& merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1); } } - const auto time_c = std::chrono::high_resolution_clock::now(); - - dur_inc += time_b - time_a; - dur_merge += time_c - time_b; - new_element = ! move_bounds[layer_idx - 1].empty(); - + const auto time_c = std::chrono::high_resolution_clock::now(); // ### Cradle lines may be removed, causing tips to be removed. if(layer_idx>0) { handleCradleLineValidity(to_bp_areas, to_model_areas,influence_areas,bypass_merge_areas,layer_idx-1,move_bounds,all_cradles_with_line_presence); } + const auto time_d = std::chrono::high_resolution_clock::now(); + + dur_inc += time_b - time_a; + dur_merge += time_c - time_b; + dur_cradle += time_d - time_c; // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again. for (std::pair tup : influence_areas) @@ -1665,7 +1665,7 @@ void TreeSupport::createLayerPathing(std::vector>& Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); } - spdlog::info("Time spent with creating influence areas' subtasks: Increasing areas {} ms merging areas: {} ms", dur_inc.count() / 1000000, dur_merge.count() / 1000000); + spdlog::info("Time spent with creating influence areas' subtasks: Increasing areas {} ms merging areas: {} ms CradleLineValidity: {} ms ", dur_inc.count() / 1000000, dur_merge.count() / 1000000, dur_cradle.count() / 1000000); } void TreeSupport::setPointsOnAreas(const TreeSupportElement* elem) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 78310adee8..5b3393f73d 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1029,7 +1029,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines[angle_idx].empty()) { cradle->lines[angle_idx][0]=TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof); @@ -1040,10 +1040,50 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines | ranges::views::enumerate) + { + if(!cradle_lines.empty()) + { + Point line_end = cradle_lines.back().line.back(); + for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) + { + if(vSize2(line_end-cradle->center) > vSize2(line.line.back()-cradle->center)) + { + + Polygons line_extension; + line_extension.addLine(line.line.back(),line_end); + coord_t line_length_before = line_extension.polyLineLength(); + Polygons actually_forbidden = volumes_.getAvoidance( + 0, + line.layer_idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + line_extension = actually_forbidden.differencePolyLines(line_extension); + + if(line_extension.polyLineLength()+EPSILON < line_length_before) + { + for(auto line_part:line_extension) + { + bool front_closer = vSize2(line_part.front() - cradle->center) < vSize2(line_part.back() - cradle->center); + Point closer = front_closer ? line_part.front() : line_part.back(); + Point further = front_closer ? line_part.back() : line_part.front(); + + if(vSize2(closer-line.line.back() < EPSILON * EPSILON)) + { + line_end = further; + } + } + } + line.line.back() = line_end; + } + } + } + } } } }); - } @@ -2246,7 +2286,7 @@ void TreeSupportTipGenerator::generateTips( } else { - spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} On Layer: {}", overhang_data.is_roof, overhang_data.is_cradle ,layer_idx); + spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} Was Line {} On Layer: {}", overhang_data.is_roof, overhang_data.is_cradle,overhang_data.isCradleLine() ,layer_idx); } } From 0cfba23b70d078a6459fced1cc545e489e2f5f75 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:41:56 +0100 Subject: [PATCH 033/101] Prevent potential issues with applying anti preferred areas to cause influence areas to become invalid when collision radius != radius --- src/TreeSupport.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index a75cf1bf94..7b6f8bfa8e 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -750,15 +750,15 @@ std::optional TreeSupport::increaseSingleArea( } } - coord_t actual_radius = config.getRadius(current_elem); // Removing cradle areas from influence areas if possible. Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx-1, actual_radius); bool anti_preferred_applied = false; if(!anti_preferred_areas.empty()) { - //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. Todo? - coord_t anti_radius_extra = std::max(settings.increase_speed-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); //todo better real radius. How prevent problems? + bool is_fast = settings.increase_speed >= config.maximum_move_distance; + //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. + coord_t anti_radius_extra = std::max(settings.increase_speed-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); if(anti_radius_extra) { anti_preferred_areas = anti_preferred_areas.offset(anti_radius_extra).unionPolygons(); @@ -766,7 +766,8 @@ std::optional TreeSupport::increaseSingleArea( if (current_elem.to_buildplate) { Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); - if(to_bp_without_anti.area()>EPSILON || settings.use_anti_preferred) + // If already moving fast there is not much to do. The anti preferred with collision radius will then later be subtracted if it is not subtracted here. + if(to_bp_without_anti.area()>EPSILON || (settings.use_anti_preferred &&!is_fast)) { to_bp_data = to_bp_without_anti; Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); @@ -779,7 +780,7 @@ std::optional TreeSupport::increaseSingleArea( else { Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); - if(to_model_data_without_anti.area()>EPSILON || settings.use_anti_preferred) + if(to_model_data_without_anti.area()>EPSILON || (settings.use_anti_preferred &&!is_fast)) { to_model_data = to_model_data_without_anti; Polygons increased_without_anti = increased.difference(anti_preferred_areas); @@ -804,6 +805,12 @@ std::optional TreeSupport::increaseSingleArea( { to_model_data = to_model_data.difference(anti_preferred); } + + if(!anti_preferred_applied) + { + increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); + } + check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; if(check_layer_data.area() > 1) { From 3d84b9404817a6d998c5472599e76609ee75b9ca Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 15 Mar 2024 01:24:00 +0100 Subject: [PATCH 034/101] More cradle fixes. --- src/TreeSupport.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 7b6f8bfa8e..3406f2ebaf 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -312,8 +312,6 @@ void TreeSupport::mergeHelper( // But because a different collision may be removed from the in drawArea generated circles, this assumption could be wrong. const bool merging_different_range_limits = reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; - const bool merging_cant_avoiding_pref = !reduced_check.first.can_avoid_anti_preferred || !influence.first.can_avoid_anti_preferred; //todo just put them in bypass merge then? - coord_t increased_to_model_radius = 0; size_t larger_to_model_dtt = 0; @@ -346,7 +344,7 @@ void TreeSupport::mergeHelper( // would merge to model before it is known they will even been drawn the merge is skipped if (merging_min_and_regular_xy || merging_gracious_and_non_gracious || increased_to_model_radius > config.max_to_model_radius_increase || (! merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && ! reduced_check.first.supports_roof && ! influence.first.supports_roof) - || merging_different_range_limits || merging_cant_avoiding_pref) + || merging_different_range_limits) { continue; } @@ -1355,10 +1353,10 @@ void TreeSupport::increaseAreas( radius = config.getCollisionRadius(elem); elem.last_area_increase = settings; add = true; - bypass_merge - = ! settings.move - || (settings.use_min_distance - && elem.distance_to_top < config.tip_layers); // Do not merge if the branch should not move or the priority has to be to get farther away from the model. + // Do not merge if the branch should not move or the priority has to be to get farther away from the model. + bypass_merge = ! settings.move + || (settings.use_min_distance && elem.distance_to_top < config.tip_layers) + || !elem.can_avoid_anti_preferred; // todo less aggressive merge prevention? if (settings.move) { elem.dont_move_until = 0; @@ -1459,7 +1457,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, // todo the coll/ regular radius difference can cause issues with slow vs fast avoidance causing lines to be removed even though the branch will not be here. It just could have been... if(!elem->can_avoid_anti_preferred || config.getCollisionRadius(*elem) != config.getRadius(*elem)) { - const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + config.getRadius(*elem) + const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); bool immutable = elem->area != nullptr; @@ -1490,7 +1488,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, { Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area, config.getRadius(*elem) + config.xy_distance, - volumes_.getCollision(config.getRadius(*elem),layer_idx,true), + volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,true), safe_movement_distance, 0, 1, From 302657ca39412be9ab6f89c142b85c866e1f1786 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:27:43 +0100 Subject: [PATCH 035/101] Made center of the cradle move slightly with the object it supports Also removed code generating additional centers for long pointy overhangs for now. --- include/TreeSupportCradle.h | 18 ++++- src/TreeSupportTipGenerator.cpp | 126 ++++++++------------------------ 2 files changed, 47 insertions(+), 97 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 57d6dc1fb2..eef65a482f 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -102,7 +102,7 @@ struct TreeSupportCradle bool is_roof; LayerIndex layer_idx; std::vector base_below; - Point center; + std::vector centers; size_t shadow_idx; std::unordered_map> overhang; @@ -111,7 +111,7 @@ struct TreeSupportCradle TreeSupportCradle(LayerIndex layer_idx, Point center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min) : layer_idx(layer_idx) - , center(center) + , centers({center}) , shadow_idx(shadow_idx) , is_roof(roof) , config_cradle_layers_min(cradle_layers_min) @@ -135,6 +135,20 @@ struct TreeSupportCradle return {}; } + Point getCenter(LayerIndex layer_idx_req) + { + if(layer_idx_req>> TreeSupportTipGenerator::generat { return; } + for (auto pointy_info : getFullyUnsupportedArea(layer_idx + z_distance_delta)) { AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta]); @@ -651,15 +652,16 @@ std::vector>> TreeSupportTipGenerator::generat std::vector accumulated_model(std::min(cradle_layers + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); std::vector all_pointy_idx{ pointy_info.index }; - Point assumed_center_prev; - Point initial_center; - std::vector centers; + Point center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); + std::vector additional_centers; + TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof, cradle_layers_min, cradle_length_min); Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. bool aborted = false; bool contacted_other_pointy = false; std::vector unsupported_model(accumulated_model.size()); for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) { + // shadow model up => not cradle where model // then drop cradle down // cut into parts => get close to original pointy that are far enough from each other. @@ -669,34 +671,6 @@ std::vector>> TreeSupportTipGenerator::generat // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up if (cradle_up_layer > 1) { - if (cradle_up_layer == 2) - { - assumed_center_prev = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - initial_center = assumed_center_prev; - } - else - { - Point next_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - // todo Long lines can cause very missplaced centers. Best way to choose when to keep initial center - if (vSize(assumed_center_prev - next_center) < cradle_length && (! centers.empty() || vSize(assumed_center_prev - initial_center) < cradle_length)) - { - assumed_center_prev = (2 * assumed_center_prev + next_center) / 3; - } - else - { - // Large center shift. May need its own cradle - if (centers.empty()) - { - centers.emplace_back(initial_center); - } - else - { - centers.emplace_back(assumed_center_prev); - } - assumed_center_prev = next_center; - } - } - for (size_t pointy_idx : all_pointy_idx) { for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta, pointy_idx)) @@ -754,11 +728,18 @@ std::vector>> TreeSupportTipGenerator::generat break; } - model_outline = model_outline.unionPolygons(); - shadow = shadow.unionPolygons(model_outline); + shadow = shadow.offset(-config.maximum_move_distance).unionPolygons(model_outline); + accumulated_model[cradle_up_layer] = shadow; - accumulated_model[cradle_up_layer] = shadow.offset(-config.maximum_move_distance).unionPolygons(model_outline); + if(cradle_up_layer > 0) + { + Point shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + coord_t center_move_distance = std::min(config.maximum_move_distance_slow, config.support_line_width/3); + center_move_distance = std::min(center_move_distance, vSize(shadow_center-center_prev)); + center_prev = center_prev + normal(shadow_center-center_prev, center_move_distance); //todo support line width roof if roof + cradle_main->centers.emplace_back(center_prev); + } } if (aborted) @@ -770,54 +751,7 @@ std::vector>> TreeSupportTipGenerator::generat accumulated_model.clear(); accumulated_model.emplace_back(cradle_0); } - - coord_t min_distance_to_assumed_center_prev = std::numeric_limits::max(); - for(Point existing_center:centers) - { - min_distance_to_assumed_center_prev = std::min(min_distance_to_assumed_center_prev, vSize(existing_center-assumed_center_prev)); - } - - if(min_distance_to_assumed_center_prev > 2*cradle_length) - { - centers.emplace_back(assumed_center_prev); - } - - if (! contacted_other_pointy) - { - for (int extra_points = 0; extra_points < 2; extra_points++) - { - coord_t candidate_min_distance2 = 0; - Point candidate = assumed_center_prev; // todo should do for all centers - for (auto line : shadow) - { - for (auto p : line) - { - coord_t smallest_distance2 = std::numeric_limits::max(); - for (Point already_center : centers) - { - smallest_distance2 = std::min(vSize2(p - already_center), smallest_distance2); - } - if (smallest_distance2 > candidate_min_distance2) - { - candidate_min_distance2 = smallest_distance2; - candidate = p; - } - } - } - - if (candidate_min_distance2 > pow(5 * cradle_length, 2)) - { - centers.emplace_back(candidate); - } - } - } - - - for(Point center:centers) - { - TreeSupportCradle* cradle = new TreeSupportCradle(layer_idx,center,shadows[layer_idx].size(), cradle_base_roof, cradle_layers_min, cradle_length_min); - cradle_data[layer_idx].emplace_back(cradle); - } + cradle_data[layer_idx].emplace_back(cradle_main); shadows[layer_idx].emplace_back(accumulated_model); } }); @@ -840,6 +774,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorshadow_idx]; for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { + Point center = cradle->getCenter(layer_idx + idx); const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; @@ -850,7 +785,6 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector 0 && ! model_shadow.empty()) { - coord_t max_distance2 = 0; Polygons relevant_forbidden = volumes_.getAvoidance( 0, @@ -884,21 +818,23 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter - p)); + max_distance2 = std::max(max_distance2, vSize2(center - p)); } } - Polygon max_outer_points = PolygonUtils::makeCircle(cradle->center, sqrt(max_distance2) + current_cradle_length * 2, (2.0 * M_PI) / double(cradle_line_count)); + Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + current_cradle_length * 2.0, (2.0 * M_PI) / double(cradle_line_count)); // create lines that go from the furthest possible location to the center Polygons lines_to_center; for (Point p : max_outer_points) { - lines_to_center.addLine(p, cradle->center); + Point direction = p - center; + lines_to_center.addLine(p, center + normal(direction,config.support_line_width)); } // Subtract the model shadow up until this layer from the lines. @@ -914,7 +850,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter) < vSize2(line.back() - cradle->center); + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); Point closer = front_closer ? line.front() : line.back(); Point further = front_closer ? line.back() : line.front(); coord_t cradle_line_length = Polygon(line).polylineLength(); @@ -935,7 +871,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter - closer)); + vector_distance_map.emplace_back((further - closer), vSize(center - closer)); } } // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent @@ -952,11 +888,11 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter) < vSize2(line.back() - cradle->center); + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); Point closer = front_closer ? line.front() : line.back(); Point further = front_closer ? line.back() : line.front(); Point current_direction = further - closer; - coord_t distance_from_center = vSize(closer - cradle->center); + coord_t distance_from_center = vSize(closer - center); shortened_lines_to_center[line_idx].clear(); shortened_lines_to_center[line_idx].add(closer); @@ -1048,7 +984,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter) > vSize2(line.line.back()-cradle->center)) + if(vSize2(line_end - center) > vSize2(line.line.back() - center)) { Polygons line_extension; @@ -1066,7 +1002,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorcenter) < vSize2(line_part.back() - cradle->center); + bool front_closer = vSize2(line_part.front() - center) < vSize2(line_part.back() - center); Point closer = front_closer ? line_part.front() : line_part.back(); Point further = front_closer ? line_part.back() : line_part.front(); @@ -1076,6 +1012,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines[cradle_line_idx].size(); height++) { - TreeSupportCradleLine* result = &cradle_data[layer_idx][cradle_idx]->lines[cradle_line_idx][height]; all_cradles_per_layer[layer_idx + height].emplace_back(result); } @@ -1292,7 +1228,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorlayer_idx))); center_up = center_front + direction_up_center; center_down = center_front - direction_up_center; } @@ -1370,7 +1306,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers +1, line_idx); if(line_opt) { - connected_cradle_base.addLine(cradle.center,line_opt.value()->line.front()); + connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx),line_opt.value()->line.front()); } } Polygons relevant_forbidden = From f275c9c9b26e0d54c6b0139135adb33704d6071e Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:13:45 +0100 Subject: [PATCH 036/101] Some parallelization and performance improvements --- src/TreeModelVolumes.cpp | 4 +- src/TreeSupport.cpp | 116 +++++++++++++++++++++++++++----- src/TreeSupportTipGenerator.cpp | 93 +++++++++++++------------ 3 files changed, 152 insertions(+), 61 deletions(-) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 585e8151da..e08d9f982c 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -766,14 +766,14 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye if(!result || result.value().get().empty()) { - return empty_polygon; + return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } if (precalculated) { spdlog::warn("Missing anti preferred calculated at radius {} and layer {} and type {} to model {}, but precalculate was called. Returning Empty!", ceiled_radius, key.second, type == AvoidanceType::COLLISION, to_model); } - return empty_polygon; + return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 3406f2ebaf..cab3789995 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -835,8 +835,8 @@ std::optional TreeSupport::increaseSingleArea( { // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. - to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); - if(settings.use_anti_preferred) //todo ensure anti pref contains avoidance then we can save one diff here + to_bp_data_2 = increased; + if(settings.use_anti_preferred) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); } @@ -844,19 +844,16 @@ std::optional TreeSupport::increaseSingleArea( { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); } + else + { + to_bp_data_2 = to_bp_data_2.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); + } } Polygons to_model_data_2; if (config.support_rests_on_model && ! current_elem.to_buildplate) { - to_model_data_2 = increased - .difference(volumes_.getAvoidance( - next_radius, - layer_idx - 1, - current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, - true, - settings.use_min_distance)) - .unionPolygons(); - if(settings.use_anti_preferred) //todo ensure anti pref contains avoidance then we can save one diff here + to_model_data_2 = increased; + if(settings.use_anti_preferred) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); } @@ -864,6 +861,15 @@ std::optional TreeSupport::increaseSingleArea( { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); } + else + { + to_model_data_2 = to_model_data_2.difference(volumes_.getAvoidance( + next_radius, + layer_idx - 1, + current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, + true, + settings.use_min_distance)); + } } Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; @@ -1454,7 +1460,6 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, for (const TreeSupportElement* elem : all_elements_on_layer) { - // todo the coll/ regular radius difference can cause issues with slow vs fast avoidance causing lines to be removed even though the branch will not be here. It just could have been... if(!elem->can_avoid_anti_preferred || config.getCollisionRadius(*elem) != config.getRadius(*elem)) { const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) @@ -2263,7 +2268,78 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::vector cradle_support_line_areas(support_layer_storage.size()); std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); - //todo parallelize + std::mutex critical_cradle_line_xy_distance_areas; + std::mutex critical_cradle_support_line_areas; + std::mutex critical_support_roof_storage; + + + cura::parallel_for + ( + 0, + cradle_data.size(), + [&](const LayerIndex layer_idx) + { + for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + { + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + { + if(cradle_data[layer_idx][cradle_idx]->is_roof) + { + support_roof_storage[layer_idx-base_idx].add(base); + } + else + { + cradle_support_base_areas[layer_idx-base_idx].add(base); + support_layer_storage[layer_idx-base_idx].add(base); + } + } + + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + { + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + { + Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].area; + bool is_roof = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_roof; + LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + if(is_roof) + { + std::lock_guard critical_section_cradle(critical_support_roof_storage); + + if(support_roof_storage.size()<=layer_idx) + { + support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + } + support_roof_storage[cradle_line_layer_idx].add(line_area); + } + else + { + std::lock_guard critical_section_cradle(critical_cradle_support_line_areas); + + if(cradle_support_line_areas.size()<=layer_idx) + { + cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + } + cradle_support_line_areas[cradle_line_layer_idx].add(line_area); + } + if(!cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_base) + { + const coord_t radius = config.getRadius(0); + Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, + config.xy_distance, + volumes_.getCollision(0,cradle_line_layer_idx), + config.xy_min_distance + config.min_feature_size, + 0, + 0, + config.min_feature_size, + &config.simplifier); + std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); + cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); + } + } + } + } + }); + for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) @@ -2306,7 +2382,16 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } if(!cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_base) { - //todo cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_area.offset(config.xy_distance)); todo offset needs to be safe... + const coord_t radius = config.getRadius(0); + Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, + config.xy_distance, + volumes_.getCollision(0,cradle_line_layer_idx), + config.xy_min_distance + config.min_feature_size, + 0, + 0, + config.min_feature_size, + &config.simplifier); + cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); } } } @@ -2323,6 +2408,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx].unionPolygons()); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx].unionPolygons()); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(cradle_line_xy_distance_areas[layer_idx]); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); placed_fake_roof_areas[layer_idx] = placed_fake_roof_areas[layer_idx].unionPolygons(); additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx].unionPolygons(); @@ -2355,7 +2441,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(support_free_areas[layer_idx]); } - additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx] //todo cradle lines vs Interface priority + additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx] .difference(storage.support.supportLayers[layer_idx].support_roof) .difference(support_layer_storage[layer_idx]) .difference(support_skin_storage[layer_idx]); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 4206d144dc..6177d92f7c 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -481,6 +481,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes LayerIndex start_layer = 1; floating_parts_cache_.resize(max_layer+1); floating_parts_map_.resize(max_layer+1); + std::mutex critical_sections; Polygons completely_supported = volumes_.getCollision(0,0,true); Polygons layer_below = completely_supported; //technically wrong, but the xy distance error on layer 1 should not matter @@ -491,60 +492,64 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. Polygons layer = mesh.layers[layer_idx].getOutlines(); - // todo rewrite to be able to parallelize it - for (const Polygons part : layer.splitIntoParts()) + const std::vector layer_parts = layer.splitIntoParts(); + cura::parallel_for( + 0, + layer_parts.size(), + [&](const size_t part_idx) { + const PolygonsPart& part = layer_parts[part_idx]; + AABB part_aabb(part); + bool has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); - AABB part_aabb(part); - auto has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); - - if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) - { - next_completely_supported.add(part); - continue; - } - - Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); - coord_t overhang_area = std::max(overhang.area(), M_PI * config.min_wall_line_width * config.min_wall_line_width); - if (!has_support_below) - { - floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0,overhang_area); - floating_parts_map_ [layer_idx].emplace_back(std::vector()); - continue; - } - - size_t min_resting_on_layers = 0; - coord_t supported_overhang_area = 0; - bool add = false; - std::vector idx_of_floating_below; - for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) - { - if(layer_idx > 1 && floating.height < cradle_layers - 1 && !floating.area.intersection(part).empty()) + if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) { - idx_of_floating_below.emplace_back(idx); - supported_overhang_area += floating.accumulated_supportable_overhang; - min_resting_on_layers = std::max(min_resting_on_layers,floating.height); - add = true; + next_completely_supported.add(part); + return ; } - } - if(min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) - { + Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); + coord_t overhang_area = std::max(overhang.area(), M_PI * config.min_wall_line_width * config.min_wall_line_width); + if (!has_support_below) + { + std::lock_guard critical_section_add(critical_sections); + floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0,overhang_area); + floating_parts_map_ [layer_idx].emplace_back(std::vector()); + return ; + } - for(size_t idx: idx_of_floating_below) + size_t min_resting_on_layers = 0; + coord_t supported_overhang_area = 0; + bool add = false; + std::vector idx_of_floating_below; + for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) { - floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); + if(layer_idx > 1 && floating.height < cradle_layers - 1 && !floating.area.intersection(part).empty()) + { + idx_of_floating_below.emplace_back(idx); + supported_overhang_area += floating.accumulated_supportable_overhang; + min_resting_on_layers = std::max(min_resting_on_layers,floating.height); + add = true; + } } - floating_parts_map_[layer_idx].emplace_back(std::vector()); - floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1,overhang_area + supported_overhang_area); - } - else - { - next_completely_supported.add(part); - } + if(min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) + { + std::lock_guard critical_section_add(critical_sections); + for(size_t idx: idx_of_floating_below) + { + floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); + } - } + floating_parts_map_[layer_idx].emplace_back(std::vector()); + floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1,overhang_area + supported_overhang_area); + } + else + { + std::lock_guard critical_section_add(critical_sections); + next_completely_supported.add(part); + } + }); layer_below = layer; completely_supported = next_completely_supported; From 64f8ef710cf5e730fca4b0f0fc2b6f6348032fdd Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:15:30 +0100 Subject: [PATCH 037/101] More cradle fixes --- src/TreeSupportTipGenerator.cpp | 48 +++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 6177d92f7c..3abdc91e9c 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -740,9 +740,9 @@ std::vector>> TreeSupportTipGenerator::generat if(cradle_up_layer > 0) { Point shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - coord_t center_move_distance = std::min(config.maximum_move_distance_slow, config.support_line_width/3); + coord_t center_move_distance = std::min(config.maximum_move_distance_slow, cradle_line_width/3); center_move_distance = std::min(center_move_distance, vSize(shadow_center-center_prev)); - center_prev = center_prev + normal(shadow_center-center_prev, center_move_distance); //todo support line width roof if roof + center_prev = center_prev + normal(shadow_center-center_prev, center_move_distance); cradle_main->centers.emplace_back(center_prev); } } @@ -788,7 +788,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines.resize(cradle_line_count); } - if (idx > 0 && ! model_shadow.empty()) + if (idx > cradle_z_distance_layers && ! model_shadow.empty()) { Polygons relevant_forbidden = volumes_.getAvoidance( @@ -883,13 +883,6 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines[angle_idx].empty()) { - cradle->lines[angle_idx][0]=TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof); + cradle->lines[angle_idx][0] = TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof); } else { @@ -1246,8 +1248,6 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorline.front() = back_center + normal(direction, cradle_line_width / 2 - FUDGE_LENGTH / 2); - - all_tips_center.emplace_back(center_up, center_down); Polygon line_tip; @@ -1255,7 +1255,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorarea.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); - } @@ -1376,7 +1375,18 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorEPSILON) { OverhangInformation cradle_overhang(overhang_part,true, cradle_data[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); @@ -1423,8 +1433,6 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: // todo move up cradle if no line contacts model // todo generate additional overhang if model may bend... Is this a TreeSupport Feature though? - // todo enlarge lines to prevent overhang - std::vector>> shadow_data = generateCradleCenters(mesh); generateCradleLines(shadow_data); @@ -2174,7 +2182,7 @@ void TreeSupportTipGenerator::generateTips( Polygons polylines = ensureMaximumDistancePolyline( cradle_line_opt.has_value() ? cradle_line_opt.value()->line.offset(0) : generateLines(overhang_outset, overhang_data.is_roof, overhang_data.is_cradle, - layer_idx + overhang_data.is_roof), //todo type may cause issues now as roofcradles are cradles + layer_idx + overhang_data.is_roof), ! overhang_data.is_roof ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, From 0f4a6022e6b2dca8d3aced5dec3f6b4ebea3d695 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:48:41 +0100 Subject: [PATCH 038/101] Fix crash causes by missing lock. --- src/TreeSupportTipGenerator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 3abdc91e9c..1936b1473d 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -504,6 +504,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) { + std::lock_guard critical_section_add(critical_sections); next_completely_supported.add(part); return ; } From 82835b465f2572a3e63afbfb59dfdc7173a266c5 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:05:57 +0100 Subject: [PATCH 039/101] Fix increaseSingleArea bug caused by a previous optimisation --- src/TreeSupport.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index cab3789995..60c19c1d40 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -836,15 +836,17 @@ std::optional TreeSupport::increaseSingleArea( // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. to_bp_data_2 = increased; - if(settings.use_anti_preferred) + bool avoidance_handled = false; + if(settings.use_anti_preferred && current_elem.can_use_safe_radius) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); + avoidance_handled = settings.type != AvoidanceType::SLOW; } else if(anti_preferred_applied) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); } - else + if(! avoidance_handled) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); } @@ -853,15 +855,17 @@ std::optional TreeSupport::increaseSingleArea( if (config.support_rests_on_model && ! current_elem.to_buildplate) { to_model_data_2 = increased; - if(settings.use_anti_preferred) + bool avoidance_handled = false; + if(settings.use_anti_preferred && current_elem.can_use_safe_radius) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); + avoidance_handled = settings.type != AvoidanceType::SLOW; //There is no slow anti-preferred avoidance. } else if(anti_preferred_applied) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); } - else + if(! avoidance_handled) { to_model_data_2 = to_model_data_2.difference(volumes_.getAvoidance( next_radius, From ccf229bd7057de3abcea6f8d12dbdb4edc8393b0 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 00:54:07 +0100 Subject: [PATCH 040/101] Fix another crash caused by missing lock --- src/TreeSupport.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 60c19c1d40..e2b29a2cc4 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2265,7 +2265,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora 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::mutex critical_support_layer_storage; std::vector cradle_support_base_areas(support_layer_storage.size()); @@ -2275,7 +2274,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::mutex critical_cradle_line_xy_distance_areas; std::mutex critical_cradle_support_line_areas; std::mutex critical_support_roof_storage; - + std::mutex critical_support_layer_storage; cura::parallel_for ( @@ -2289,10 +2288,12 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { if(cradle_data[layer_idx][cradle_idx]->is_roof) { + std::lock_guard critical_section_cradle(critical_support_roof_storage); support_roof_storage[layer_idx-base_idx].add(base); } else { + std::lock_guard critical_section_cradle(critical_support_layer_storage); cradle_support_base_areas[layer_idx-base_idx].add(base); support_layer_storage[layer_idx-base_idx].add(base); } From ea35c28ebdba6b02f53a9b8f8cb2b67da763474a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 00:56:59 +0100 Subject: [PATCH 041/101] More cradle fixes --- src/TreeSupportTipGenerator.cpp | 150 ++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 63 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 1936b1473d..be1a122674 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -751,13 +751,17 @@ std::vector>> TreeSupportTipGenerator::generat if (aborted) { // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface - // base. + // base. todo reimplement that Polygons cradle_0 = accumulated_model[0]; accumulated_model.clear(); accumulated_model.emplace_back(cradle_0); + delete cradle_main; + } + else + { + cradle_data[layer_idx].emplace_back(cradle_main); } - cradle_data[layer_idx].emplace_back(cradle_main); shadows[layer_idx].emplace_back(accumulated_model); } }); @@ -914,7 +918,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector 0.99) { found_candidate = true; @@ -984,45 +988,47 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines | ranges::views::enumerate) + // enlarge cradle lines below to minimize overhang of cradle lines. + for (auto [line_idx, cradle_lines] : cradle->lines | ranges::views::enumerate) + { + if(!cradle_lines.empty()) { - if(!cradle_lines.empty()) + Point line_end = cradle_lines.back().line.back(); + for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { - Point line_end = cradle_lines.back().line.back(); - for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) + Point center = cradle->getCenter(line.layer_idx); + if(vSize2(line_end - center) > vSize2(line.line.back() - center)) { - if(vSize2(line_end - center) > vSize2(line.line.back() - center)) - { + Polygons line_extension; + line_extension.addLine(line.line.back(),line_end); + coord_t line_length_before = line_extension.polyLineLength(); + Polygons actually_forbidden = volumes_.getAvoidance( + 0, + line.layer_idx, + (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config.support_rests_on_model, + true); + line_extension = actually_forbidden.differencePolyLines(line_extension); - Polygons line_extension; - line_extension.addLine(line.line.back(),line_end); - coord_t line_length_before = line_extension.polyLineLength(); - Polygons actually_forbidden = volumes_.getAvoidance( - 0, - line.layer_idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - true); - line_extension = actually_forbidden.differencePolyLines(line_extension); - - if(line_extension.polyLineLength()+EPSILON < line_length_before) + if(line_extension.polyLineLength()+EPSILON < line_length_before) + { + for(auto line_part:line_extension) { - for(auto line_part:line_extension) + bool front_closer = vSize2(line_part.front() - center) < vSize2(line_part.back() - center); + Point closer = front_closer ? line_part.front() : line_part.back(); + Point further = front_closer ? line_part.back() : line_part.front(); + + if(vSize2(closer-line.line.back() < EPSILON * EPSILON)) { - bool front_closer = vSize2(line_part.front() - center) < vSize2(line_part.back() - center); - Point closer = front_closer ? line_part.front() : line_part.back(); - Point further = front_closer ? line_part.back() : line_part.front(); - - if(vSize2(closer-line.line.back() < EPSILON * EPSILON)) - { - line_end = further; - } + line_end = further; } } - line_end = LinearAlg2D::getClosestOnLine(line_end,line.line.front(),line.line.back()); - line.line.back() = line_end; } + //As the center can move there is no guarantee that the point of the current line lies on the line below. + line_end = LinearAlg2D::getClosestOnLine(line_end,line.line.front(),line.line.back()); + line.line.back() = line_end; } } } @@ -1034,6 +1040,9 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector> all_cradles_per_layer(cradle_data.size() + cradle_layers); for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { @@ -1050,7 +1059,6 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() } } - const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath std::vector removed_lines(cradle_data.size()); cura::parallel_for( @@ -1078,7 +1086,7 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) { AABB bounding_box_current = AABB(all_cradles_on_layer[cradle_idx]->line); - bounding_box_current.expand(cradle_line_width * 2 + FUDGE_LENGTH); + bounding_box_current.expand(min_distance_between_lines); for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) { if(all_cradles_on_layer[cradle_idx_inner]->line.empty()||all_cradles_on_layer[cradle_idx]->line.empty()) @@ -1087,8 +1095,8 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() } if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line))) { - Polygon outer_line = (*all_cradles_on_layer[cradle_idx]).line; - Polygon inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line; + Polygon& outer_line = (*all_cradles_on_layer[cradle_idx]).line; + Polygon& inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line; Point intersect; if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) @@ -1096,42 +1104,52 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() { coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); - const coord_t end_dist = FUDGE_LENGTH + min_distance_between_lines + cradle_line_width; // todo everywhere: This is dist to prevent lines from merging, not touching ... Maybe also should not touch if (inner_intersect_dist > outer_intersect_dist) { - Point new_end_inner = intersect + normal((inner_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + //this does not ensure that the line ends will not touch. Line ends not touching is handled later + Point new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); handleNewEnd(cradle_idx_inner,new_end_inner); } if (outer_intersect_dist > inner_intersect_dist) { - Point new_end_outer = intersect + normal((outer_line.front() - intersect), FUDGE_LENGTH + min_distance_between_lines + cradle_line_width + config.xy_distance); + Point new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); handleNewEnd(cradle_idx,new_end_outer); } } - else + + if(!outer_line.empty() && !inner_line.empty()) { // Touching lines have the same issue Lines touch if the end is to close to another line - coord_t inner_end_to_outer_distance = LinearAlg2D::getDistFromLine(inner_line.back(),outer_line.front(), outer_line.back()); - if(inner_end_to_outer_distance < 2 * cradle_line_width) - { - Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),2 * cradle_line_width - inner_end_to_outer_distance); - coord_t error = LinearAlg2D::getDistFromLine(new_end_inner,outer_line.front(), outer_line.back()); - double error_correction_factor = 1.0 + error/(2 * cradle_line_width - inner_end_to_outer_distance); - new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),(2 * cradle_line_width - inner_end_to_outer_distance)*error_correction_factor); - handleNewEnd(cradle_idx_inner,new_end_inner); - } - else + Point inner_direction = inner_line.back() - inner_line.front(); + Point outer_direction = outer_line.back() - outer_line.front(); + double cosine = std::abs((dot(inner_direction, outer_direction)) / double(vSize(outer_direction) * vSize(inner_direction))); + // If both lines point in the same/opposite direction check that them being to close is not the end line of one to the start of the other + if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) { - coord_t outer_end_to_inner_distance = LinearAlg2D::getDistFromLine(outer_line.back(),inner_line.front(), inner_line.back()); - if(outer_end_to_inner_distance < 2 * cradle_line_width) + coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); + if(inner_end_to_outer_distance < min_distance_between_lines) + { + Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),min_distance_between_lines - inner_end_to_outer_distance); + double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); + double error_correction_factor = 1.0 + error/double(min_distance_between_lines - inner_end_to_outer_distance); + new_end_inner = inner_line.back() + + normal(inner_line.front()-inner_line.back(),(min_distance_between_lines - inner_end_to_outer_distance)*error_correction_factor); + handleNewEnd(cradle_idx_inner,new_end_inner); + } + else { - Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),2 * cradle_line_width - outer_end_to_inner_distance); - coord_t error = LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back()); - double error_correction_factor = 1.0 + error/(2 * cradle_line_width - outer_end_to_inner_distance); - new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),(2 * cradle_line_width - outer_end_to_inner_distance)*error_correction_factor); - handleNewEnd(cradle_idx,new_end_outer); + coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); + if(outer_end_to_inner_distance < min_distance_between_lines) + { + Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),min_distance_between_lines - outer_end_to_inner_distance); + double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back())); + double error_correction_factor = 1.0 + error/double(min_distance_between_lines - outer_end_to_inner_distance); + new_end_outer = outer_line.back() + + normal(outer_line.front()-outer_line.back(),(min_distance_between_lines - outer_end_to_inner_distance)*error_correction_factor); + handleNewEnd(cradle_idx,new_end_outer); + } } } } @@ -1301,7 +1319,15 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector1) + { + cradle_base = center_removed; + } } else if(cradle_base_roof) { @@ -1338,7 +1364,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorarea.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); } @@ -1413,14 +1439,11 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector Date: Mon, 18 Mar 2024 00:58:14 +0100 Subject: [PATCH 042/101] Shorten cradle lines again if lines were removed that made them longer. --- src/TreeSupportTipGenerator.cpp | 47 ++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index be1a122674..f7e41963a2 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1165,32 +1165,47 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() { for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + auto& cradle = cradle_data[layer_idx][cradle_idx]; + cradle->verifyLines(); + // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. + for (auto [line_idx, cradle_lines] : cradle_data[layer_idx][cradle_idx]->lines | ranges::views::enumerate) { - std::deque& cradle_lines = cradle_data[layer_idx][cradle_idx]->lines[line_idx]; - if(cradle_lines.size() < cradle_layers_min) - { - cradle_lines.clear(); - continue; - } - LayerIndex previous_layer_idx = cradle_lines.front().layer_idx; - for (size_t up_idx = 1; up_idx < cradle_lines.size(); up_idx++) + if(!cradle_lines.empty()) { - if(cradle_lines[up_idx].layer_idx > previous_layer_idx + up_idx - || cradle_lines[up_idx].line.size() < 2 || cradle_lines[up_idx].line.polylineLength() < cradle_length_min) + Point line_end = cradle_lines.back().line.back(); + Point line_front_uppermost = cradle_lines.back().line.front(); + + if(vSize2(line_end - cradle_lines.back().line.front()) > cradle_length * cradle_length) { - if(up_idx <= cradle_layers_min) + cradle_lines.back().line.back() = line_front_uppermost + normal(line_end - line_front_uppermost, cradle_length); + for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { - cradle_lines.clear(); - break; + Point center = cradle->getCenter(line.layer_idx); + Point line_back_inner = line.line.back(); + Point line_front_inner = line.line.front(); + if(vSize2(line_back_inner - line_front_inner) > cradle_length * cradle_length) + { + //As the center can move there is no guarantee that the point of the current line lies on the line below. + Point projected_line_end = LinearAlg2D::getClosestOnLine(line_end,line.line.front(),line.line.back()); + const coord_t current_cradle_xy_distance = cradle_xy_distance[line.layer_idx - layer_idx]; + const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; + if(vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) + { + line.line.back() = projected_line_end; + } + else + { + line.line.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); + } + } + line_end = line.line.back(); } } + } } } }); - //todo if removed line has similar cosine to one above, and on of the points lies close to the other one Remove that part of the line => Only keep above if still longer than min - } From 79c7e36dca96037c7b5c31853f9d9c7cbbd3616b Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 05:06:25 +0100 Subject: [PATCH 043/101] Prevent invalid cradle lines from generating and fix for cradle related tip placement --- src/TreeSupportTipGenerator.cpp | 81 +++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index f7e41963a2..692f409857 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -904,11 +904,11 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector= cradle_z_distance_layers + 1) { const Polygons actually_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx, + config.getRadius(0), + layer_idx + idx - (cradle_z_distance_layers + 1) , (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, true); @@ -1201,7 +1201,6 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() line_end = line.line.back(); } } - } } } @@ -1227,6 +1226,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector valid_cradles; for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { TreeSupportCradle& cradle = *cradle_data[layer_idx][cradle_idx]; @@ -1308,7 +1308,6 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector1) + { + LayerIndex line_layer_idx = roof_area_pair.second<0 ? LayerIndex(-1) : cradle_data[layer_idx][cradle_idx]->lines[roof_area_pair.second].front().layer_idx; + OverhangInformation cradle_overhang(roof_area_before, true, cradle_data[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); + cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); + } break; } } @@ -1446,20 +1449,36 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector1) + { + OverhangInformation cradle_overhang(cradle_base, false, cradle_data[layer_idx][cradle_idx]); + cradle.overhang[layer_idx].emplace_back(cradle_overhang); + } + for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) { if(!cradle.lines[line_idx].empty()) { - LayerIndex support_cradle_on_layer_idx = cradle.lines[line_idx].front().layer_idx - cradle_z_distance_layers; + LayerIndex support_cradle_on_layer_idx = cradle.lines[line_idx].front().layer_idx - (cradle_z_distance_layers + 1); OverhangInformation line_overhang(cradle.lines[line_idx].front().area,false,cradle_data[layer_idx][cradle_idx],cradle.lines[line_idx].front().layer_idx,line_idx); cradle.overhang[support_cradle_on_layer_idx].emplace_back(line_overhang); } } } + if(!cradle.overhang.empty()) + { + valid_cradles.emplace_back(cradle_data[layer_idx][cradle_idx]); + } + else + { + delete cradle_data[layer_idx][cradle_idx]; + } } + cradle_data[layer_idx] = valid_cradles; }); } @@ -1732,7 +1751,8 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( gracious, ! xy_overrides, dont_move_until, - roof || cradle, + roof, + cradle, safe_radius, force_tip_to_roof, skip_ovalisation, @@ -2215,18 +2235,29 @@ void TreeSupportTipGenerator::generateTips( std::vector overhang_lines; bool only_lines = true; - std::optional cradle_line_opt; + Polygons polylines; // The tip positions are determined here. - // todo can cause inconsistent support density if a line exactly aligns with the model - Polygons polylines = ensureMaximumDistancePolyline( - cradle_line_opt.has_value() ? cradle_line_opt.value()->line.offset(0) : generateLines(overhang_outset, overhang_data.is_roof, - overhang_data.is_cradle, - layer_idx + overhang_data.is_roof), - ! overhang_data.is_roof ? config.min_radius * 2 - : use_fake_roof ? support_supporting_branch_distance - : connect_length, - 1, - false); + if(overhang_data.isCradleLine()) + { + std::optional cradle_line_opt = overhang_data.cradle->getCradleLineOfIndex(overhang_data.cradle_layer_idx,overhang_data.cradle_line_idx); + Polygons line = cradle_line_opt.value()->line.offset(0); + polylines = ensureMaximumDistancePolyline( + line, + ! overhang_data.is_roof ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, + 1, + false); + } + else + { + // todo can cause inconsistent support density if a line exactly aligns with the model + polylines = ensureMaximumDistancePolyline( + generateLines(overhang_outset, overhang_data.is_roof, overhang_data.is_cradle, layer_idx + overhang_data.is_roof), + ! overhang_data.is_roof ? config.min_radius * 2 + : use_fake_roof ? support_supporting_branch_distance + : connect_length, + 1, + false); + } // support_line_width to form a line here as otherwise most will be unsupported. From 3cbf074e3bf31676afec9e2978a7f0c8afe1f542 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:36:49 +0100 Subject: [PATCH 044/101] Commit code missing from previous commit and fix some comments --- include/TreeSupportCradle.h | 2 +- include/TreeSupportElement.h | 9 +++++++++ include/TreeSupportTipGenerator.h | 6 +++--- src/TreeSupportTipGenerator.cpp | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index eef65a482f..4f6858cc02 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -49,7 +49,7 @@ struct OverhangInformation bool isCradleLine() { - return is_cradle && cradle_line_idx >= 0; + return is_cradle && cradle_line_idx >= 0 && cradle_layer_idx>=0; } }; diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 1a887b94ae..742af9fd0e 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -82,6 +82,7 @@ struct TreeSupportElement bool use_min_xy_dist, size_t dont_move_until, bool supports_roof, + bool supports_cradle, bool can_use_safe_radius, bool force_tips_to_roof, bool skip_ovalisation, @@ -102,6 +103,7 @@ struct TreeSupportElement buildplate_radius_increases(0), use_min_xy_dist(use_min_xy_dist), supports_roof(supports_roof), + supports_cradle(supports_cradle), dont_move_until(dont_move_until), can_use_safe_radius(can_use_safe_radius), can_avoid_anti_preferred(false), //todo init? @@ -131,6 +133,7 @@ struct TreeSupportElement buildplate_radius_increases(elem.buildplate_radius_increases), use_min_xy_dist(elem.use_min_xy_dist), supports_roof(elem.supports_roof), + supports_cradle(elem.supports_cradle), dont_move_until(elem.dont_move_until), can_use_safe_radius(elem.can_use_safe_radius), can_avoid_anti_preferred(elem.can_avoid_anti_preferred), @@ -165,6 +168,7 @@ struct TreeSupportElement 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), + supports_cradle(first.supports_cradle || second.supports_cradle), 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)), @@ -308,6 +312,11 @@ struct TreeSupportElement */ bool supports_roof; + /*! + * \brief True if this Element or any parent provides support to a cradle or cradle line. + */ + bool supports_cradle; + /*! * \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move. */ diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index b337c0f607..5f1ac04e40 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -280,7 +280,7 @@ class TreeSupportTipGenerator const size_t support_roof_layers; /*! - * \brief Distance between tips, so that the tips form a lime. Is smaller than Tip Diameter. + * \brief Distance between tips, so that the tips form a line. Is smaller than Tip Diameter. */ const coord_t connect_length; @@ -420,7 +420,7 @@ class TreeSupportTipGenerator bool cradle_base_roof; /*! - * \brief If the cradle base should contain all cradle lines (if true) or only the pointy overhang (if false). + * \brief If the (roof) cradle base should contain all cradle lines at cradle_tip_dtt size. */ bool large_cradle_base; @@ -436,7 +436,7 @@ class TreeSupportTipGenerator size_t cradle_tip_dtt; /*! - * \brief If the cradle lines should also be supported by larger tips. todo + * \brief If the cradle lines should also be supported by larger tips. */ bool large_cradle_line_tips; diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 692f409857..aebbac7130 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -2243,7 +2243,7 @@ void TreeSupportTipGenerator::generateTips( Polygons line = cradle_line_opt.value()->line.offset(0); polylines = ensureMaximumDistancePolyline( line, - ! overhang_data.is_roof ? config.min_radius * 2 : use_fake_roof ? support_supporting_branch_distance : connect_length, + ! overhang_data.is_roof ? config.min_radius * 2 : support_supporting_branch_distance, 1, false); } From 6966fa04e5fa0cb4512f1b79f65a68e29db669ee Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:24:31 +0100 Subject: [PATCH 045/101] Merge part 2: Fixing variable names/usage. --- include/SupportInfillPart.h | 2 +- include/TreeSupportCradle.h | 176 ++++---- include/TreeSupportElement.h | 180 ++++----- include/TreeSupportTipGenerator.h | 32 +- include/sliceDataStorage.h | 22 - src/FffGcodeWriter.cpp | 2 +- src/SupportInfillPart.cpp | 14 +- src/TreeModelVolumes.cpp | 41 +- src/TreeSupport.cpp | 156 +++---- src/TreeSupportTipGenerator.cpp | 650 +++++++++++++++--------------- 10 files changed, 629 insertions(+), 646 deletions(-) diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 311a8d2517..f262d37c96 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -36,7 +36,7 @@ class SupportInfillPart coord_t custom_line_distance_; //!< The distance between support infill lines. 0 means use the default line distance instead. bool use_fractional_config_; //!< Request to use the configuration used to fill a partial layer height here, instead of the normal full layer height configuration. - EFillMethod custom_line_pattern; + EFillMethod custom_line_pattern_; SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 4f6858cc02..1fada75d38 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -19,37 +19,37 @@ struct OverhangInformation { OverhangInformation(Polygons overhang, bool roof): - overhang(overhang), - is_roof(roof), - is_cradle(false), - cradle_layer_idx(-1), - cradle_line_idx(-1), - cradle(nullptr) + overhang_(overhang), + is_roof_(roof), + is_cradle_(false), + cradle_layer_idx_(-1), + cradle_line_idx_(-1), + cradle_(nullptr) { } OverhangInformation(Polygons overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1,int32_t cradle_line_idx = -1): - overhang(overhang), - is_roof(roof), - is_cradle(true), - cradle_layer_idx(cradle_layer_idx), - cradle_line_idx(cradle_line_idx), - cradle(cradle) + overhang_(overhang), + is_roof_(roof), + is_cradle_(true), + cradle_layer_idx_(cradle_layer_idx), + cradle_line_idx_(cradle_line_idx), + cradle_(cradle) { } - Polygons overhang; - bool is_roof; - bool is_cradle; - int32_t cradle_layer_idx; - int32_t cradle_line_idx; - TreeSupportCradle* cradle; + Polygons overhang_; + bool is_roof_; + bool is_cradle_; + int32_t cradle_layer_idx_; + int32_t cradle_line_idx_; + TreeSupportCradle* cradle_; bool isCradleLine() { - return is_cradle && cradle_line_idx >= 0 && cradle_layer_idx>=0; + return is_cradle_ && cradle_line_idx_ >= 0 && cradle_layer_idx_>=0; } }; @@ -62,35 +62,35 @@ struct TreeSupportCradleLine } TreeSupportCradleLine(Polygon line, LayerIndex layer_idx, bool is_roof) - : line(line) - , layer_idx(layer_idx) - , is_roof(is_roof) + : line_(line) + , layer_idx_(layer_idx) + , is_roof_(is_roof) { } - Polygons area; - Polygon line; - Polygon removed_line; - LayerIndex layer_idx; - bool is_base = false; - bool is_roof; + Polygons area_; + Polygon line_; + Polygon removed_line_; + LayerIndex layer_idx_; + bool is_base_ = false; + bool is_roof_; void addLineToRemoved(Polygon& line_to_add) { - if (removed_line.empty()) + if (removed_line_.empty()) { - removed_line.add(line_to_add.front()); - removed_line.add(line_to_add.back()); + removed_line_.add(line_to_add.front()); + removed_line_.add(line_to_add.back()); } else { - if (vSize2(removed_line.front() - removed_line.back()) < vSize2(line_to_add.front() - removed_line.back())) + if (vSize2(removed_line_.front() - removed_line_.back()) < vSize2(line_to_add.front() - removed_line_.back())) { - removed_line.front() = line_to_add.front(); + removed_line_.front() = line_to_add.front(); } - if (vSize2(removed_line.front() - removed_line.back()) < vSize2(removed_line.front() - line_to_add.back())) + if (vSize2(removed_line_.front() - removed_line_.back()) < vSize2(removed_line_.front() - line_to_add.back())) { - removed_line.back() = line_to_add.back(); + removed_line_.back() = line_to_add.back(); } } } @@ -98,94 +98,94 @@ struct TreeSupportCradleLine struct TreeSupportCradle { - std::vector> lines; - bool is_roof; - LayerIndex layer_idx; - std::vector base_below; - std::vector centers; - size_t shadow_idx; - std::unordered_map> overhang; - - size_t config_cradle_layers_min; - coord_t config_cradle_length_min; - - TreeSupportCradle(LayerIndex layer_idx, Point center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min) - : layer_idx(layer_idx) - , centers({center}) - , shadow_idx(shadow_idx) - , is_roof(roof) - , config_cradle_layers_min(cradle_layers_min) - , config_cradle_length_min(cradle_length_min) + std::vector> lines_; + bool is_roof_; + LayerIndex layer_idx_; + std::vector base_below_; + std::vector centers_; + size_t shadow_idx_; + std::unordered_map> overhang_; + + size_t config_cradle_layers_min_; + coord_t config_cradle_length_min_; + + TreeSupportCradle(LayerIndex layer_idx, Point2LL center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min) + : layer_idx_(layer_idx) + , centers_({center}) + , shadow_idx_(shadow_idx) + , is_roof_(roof) + , config_cradle_layers_min_(cradle_layers_min) + , config_cradle_length_min_(cradle_length_min) { } std::optional getCradleLineOfIndex(LayerIndex requested_layer_idx, size_t cradle_line_idx) { - if (cradle_line_idx < lines.size()) + if (cradle_line_idx < lines_.size()) { - for (size_t height_idx = 0; height_idx < lines[cradle_line_idx].size(); height_idx++) + for (size_t height_idx = 0; height_idx < lines_[cradle_line_idx].size(); height_idx++) { - if (lines[cradle_line_idx][height_idx].layer_idx == requested_layer_idx) + if (lines_[cradle_line_idx][height_idx].layer_idx_ == requested_layer_idx) { - return &lines[cradle_line_idx][height_idx]; + return &lines_[cradle_line_idx][height_idx]; } } } return {}; } - Point getCenter(LayerIndex layer_idx_req) + Point2LL getCenter(LayerIndex layer_idx_req) { - if(layer_idx_req previous_layer_idx + up_idx || - lines[line_idx][up_idx].line.size()<2 || - lines[line_idx][up_idx].line.polylineLength() < config_cradle_length_min) + if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || + lines_[line_idx][up_idx].line_.size()<2 || + lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) { - if (up_idx <= config_cradle_layers_min) + if (up_idx <= config_cradle_layers_min_) { - spdlog::debug("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx,line_idx,lines[line_idx][up_idx].layer_idx); - lines[line_idx].clear(); + spdlog::debug("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx_,line_idx,lines_[line_idx][up_idx].layer_idx_); + lines_[line_idx].clear(); break; } else { - spdlog::debug("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx,line_idx,up_idx); - lines[line_idx].resize(up_idx-1); + spdlog::debug("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx_,line_idx,up_idx); + lines_[line_idx].resize(up_idx-1); break; } } @@ -198,22 +198,22 @@ struct TreeSupportCradle struct CradlePresenceInformation { CradlePresenceInformation(TreeSupportCradle* cradle,LayerIndex layer_idx,size_t line_idx): - cradle(cradle), - layer_idx(layer_idx), - line_idx(line_idx) + cradle_(cradle), + layer_idx_(layer_idx), + line_idx_(line_idx) {} - TreeSupportCradle* cradle; - LayerIndex layer_idx; - size_t line_idx; + TreeSupportCradle* cradle_; + LayerIndex layer_idx_; + size_t line_idx_; TreeSupportCradleLine* getCradleLine() { - return cradle->getCradleLineOfIndex(layer_idx,line_idx).value(); + return cradle_->getCradleLineOfIndex(layer_idx_,line_idx_).value(); } bool cradleLineExists() { - return cradle->getCradleLineOfIndex(layer_idx,line_idx).has_value(); + return cradle_->getCradleLineOfIndex(layer_idx_,line_idx_).has_value(); } }; diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 2d1a9230e8..ce201c4bf3 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -21,13 +21,13 @@ struct CradlePresenceInformation; struct AreaIncreaseSettings { AreaIncreaseSettings() : - type(AvoidanceType::FAST), - increase_speed(0), - increase_radius(false), - no_error(false), - use_min_distance(false), - use_anti_preferred(false), - move(false) + type_(AvoidanceType::FAST), + increase_speed_(0), + increase_radius_(false), + no_error_(false), + use_min_distance_(false), + use_anti_preferred_(false), + move_(false) { } @@ -41,23 +41,23 @@ struct AreaIncreaseSettings bool use_anti_preferred, bool move ) : - type(type), - increase_speed(increase_speed), - increase_radius(increase_radius), - no_error(simplify), - use_min_distance(use_min_distance), - use_anti_preferred(use_anti_preferred), - move(move) + type_(type), + increase_speed_(increase_speed), + increase_radius_(increase_radius), + no_error_(simplify), + use_min_distance_(use_min_distance), + use_anti_preferred_(use_anti_preferred), + move_(move) { } - AvoidanceType type; - coord_t increase_speed; - bool increase_radius; - bool no_error; - bool use_min_distance; - bool use_anti_preferred; - bool move; + AvoidanceType type_; + coord_t increase_speed_; + bool increase_radius_; + bool no_error_; + bool use_min_distance_; + bool use_anti_preferred_; + bool move_; bool operator==(const AreaIncreaseSettings& other) const { @@ -84,61 +84,61 @@ struct TreeSupportElement bool influence_area_limit_active, coord_t influence_area_limit_range ) : - target_height(target_height), - target_position(target_position), - next_position(target_position), - next_height(target_height), - effective_radius_height(0), - 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), - buildplate_radius_increases(0), - use_min_xy_dist(use_min_xy_dist), - supports_roof(supports_roof), - supports_cradle(supports_cradle), - dont_move_until(dont_move_until), - can_use_safe_radius(can_use_safe_radius), - can_avoid_anti_preferred(false), //todo init? - last_area_increase(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)), - missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), - skip_ovalisation(skip_ovalisation), - all_tips({ target_position }), - influence_area_limit_active(influence_area_limit_active), - influence_area_limit_range(influence_area_limit_range + target_height_(target_height), + target_position_(target_position), + next_position_(target_position), + next_height_(target_height), + effective_radius_height_(0), + 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), + buildplate_radius_increases_(0), + use_min_xy_dist_(use_min_xy_dist), + supports_roof_(supports_roof), + supports_cradle_(supports_cradle), + dont_move_until_(dont_move_until), + can_use_safe_radius_(can_use_safe_radius), + can_avoid_anti_preferred_(false), //todo init? + last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)), + missing_roof_layers_(force_tips_to_roof ? dont_move_until : 0), + skip_ovalisation_(skip_ovalisation), + all_tips_({ target_position }), + influence_area_limit_active_(influence_area_limit_active), + influence_area_limit_range_(influence_area_limit_range ) { RecreateInfluenceLimitArea(); } TreeSupportElement(const TreeSupportElement& elem, Polygons* newArea = nullptr) : // copy constructor with possibility to set a new area - target_height(elem.target_height), - target_position(elem.target_position), - next_position(elem.next_position), - next_height(elem.next_height), - effective_radius_height(elem.effective_radius_height), - to_buildplate(elem.to_buildplate), - distance_to_top(elem.distance_to_top), - area(newArea != nullptr ? newArea : elem.area), - result_on_layer(elem.result_on_layer), - increased_to_model_radius(elem.increased_to_model_radius), - to_model_gracious(elem.to_model_gracious), - buildplate_radius_increases(elem.buildplate_radius_increases), - use_min_xy_dist(elem.use_min_xy_dist), - supports_roof(elem.supports_roof), - supports_cradle(elem.supports_cradle), - dont_move_until(elem.dont_move_until), - can_use_safe_radius(elem.can_use_safe_radius), - can_avoid_anti_preferred(elem.can_avoid_anti_preferred), - last_area_increase(elem.last_area_increase), - missing_roof_layers(elem.missing_roof_layers), - skip_ovalisation(elem.skip_ovalisation), - all_tips(elem.all_tips), - influence_area_limit_area(elem.influence_area_limit_area), - influence_area_limit_range(elem.influence_area_limit_range), - influence_area_limit_active(elem.influence_area_limit_active) + target_height_(elem.target_height_), + target_position_(elem.target_position_), + next_position_(elem.next_position_), + next_height_(elem.next_height_), + effective_radius_height_(elem.effective_radius_height_), + to_buildplate_(elem.to_buildplate_), + distance_to_top_(elem.distance_to_top_), + area_(newArea != nullptr ? newArea : elem.area_), + result_on_layer_(elem.result_on_layer_), + increased_to_model_radius_(elem.increased_to_model_radius_), + to_model_gracious_(elem.to_model_gracious_), + buildplate_radius_increases_(elem.buildplate_radius_increases_), + use_min_xy_dist_(elem.use_min_xy_dist_), + supports_roof_(elem.supports_roof_), + supports_cradle_(elem.supports_cradle_), + dont_move_until_(elem.dont_move_until_), + can_use_safe_radius_(elem.can_use_safe_radius_), + can_avoid_anti_preferred_(elem.can_avoid_anti_preferred_), + last_area_increase_(elem.last_area_increase_), + missing_roof_layers_(elem.missing_roof_layers_), + skip_ovalisation_(elem.skip_ovalisation_), + all_tips_(elem.all_tips_), + influence_area_limit_area_(elem.influence_area_limit_area_), + influence_area_limit_range_(elem.influence_area_limit_range_), + influence_area_limit_active_(elem.influence_area_limit_active_) { parents_.insert(parents_.begin(), elem.parents_.begin(), elem.parents_.end()); } @@ -156,17 +156,17 @@ struct TreeSupportElement coord_t branch_radius, double diameter_angle_scale_factor ) : - 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), - supports_cradle(first.supports_cradle || second.supports_cradle), - 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) + 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_), + supports_cradle_(first.supports_cradle_ || second.supports_cradle_), + 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_) { @@ -207,7 +207,7 @@ struct TreeSupportElement first.last_area_increase_.increase_radius_ || second.last_area_increase_.increase_radius_, first.last_area_increase_.no_error_ || second.last_area_increase_.no_error_, first.last_area_increase_.use_min_distance_ && second.last_area_increase_.use_min_distance_, - first.can_avoid_anti_preferred && second.can_avoid_anti_preferred, + first.can_avoid_anti_preferred_ && second.can_avoid_anti_preferred_, first.last_area_increase_.move_ || second.last_area_increase_.move_); all_tips_ = first.all_tips_; @@ -302,7 +302,7 @@ struct TreeSupportElement /*! * \brief True if this Element or any parent provides support to a cradle or cradle line. */ - bool supports_cradle; + bool supports_cradle_; /*! * \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move. @@ -317,7 +317,7 @@ struct TreeSupportElement /*! * \brief An influence area can avoid anti-preferred when the difference with it is non empty. */ - bool can_avoid_anti_preferred; + bool can_avoid_anti_preferred_; /*! * \brief Settings used to increase the influence area to its current state. @@ -359,7 +359,7 @@ struct TreeSupportElement */ std::vector additional_ovalization_targets_; - std::shared_ptr cradle_line; + std::shared_ptr cradle_line_; @@ -449,11 +449,11 @@ struct TreeSupportElement TreeSupportElement createNewElement() { TreeSupportElement result(*this); - result.parents = { this }; - result.distance_to_top += 1; - result.skip_ovalisation = false; - result.result_on_layer = Point(-1, -1); - result.area = nullptr; + result.parents_ = { this }; + result.distance_to_top_ += 1; + result.skip_ovalisation_ = false; + result.result_on_layer_ = Point2LL(-1, -1); + result.area_ = nullptr; return result; } diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 2bc9958926..6d57b50673 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -376,80 +376,80 @@ class TreeSupportTipGenerator std::vector roof_tips_drawn_; - std::vector> cradle_data; + std::vector> cradle_data_; /*! * \brief Amount of layers of the cradle to support pointy overhangs. */ - size_t cradle_layers; + size_t cradle_layers_; /*! * \brief Minimum amount of layers of the cradle to support pointy overhangs. */ - size_t cradle_layers_min; // Minimal height of the cradle + size_t cradle_layers_min_; /*! * \brief Amount of lines used for the cradle. */ - size_t cradle_line_count; + size_t cradle_line_count_; /*! * \brief Length of lines used for the cradle. */ - coord_t cradle_length; + coord_t cradle_length_; /*! * \brief Minimum length of lines used for the cradle. TODO Width is effectively added to length ... fix or document? */ - coord_t cradle_length_min; + coord_t cradle_length_min_; /*! * \brief Width of lines used for the cradle. */ - coord_t cradle_line_width; + coord_t cradle_line_width_; /*! * \brief If cradle lines should be drawn as roof. */ - bool cradle_lines_roof; + bool cradle_lines_roof_; /*! * \brief If the cradle base should be drawn as roof. */ - bool cradle_base_roof; + bool cradle_base_roof_; /*! * \brief If the (roof) cradle base should contain all cradle lines at cradle_tip_dtt size. */ - bool large_cradle_base; + bool large_cradle_base_; /*! * \brief Maximum area of an overhang to still receive a cradle. Unit is square-microns! */ - double cradle_area_threshold; + double cradle_area_threshold_; /*! * \brief Distance to top of tips that support either the pointy overhang or the cradle lines at the bottom-most layer. */ - size_t cradle_tip_dtt; + size_t cradle_tip_dtt_; /*! * \brief If the cradle lines should also be supported by larger tips. */ - bool large_cradle_line_tips; + bool large_cradle_line_tips_; /*! * \brief Distances the cradle lines should be from the model. First value corresponds to cradle line on the same layer as the first model line. */ - std::vector cradle_xy_distance; + std::vector cradle_xy_distance_; /*! * \brief Distances in lines between the cradle and the support they are supported by. */ - size_t cradle_z_distance_layers; + size_t cradle_z_distance_layers_; - std::mutex critical_cradle; + std::mutex critical_cradle_; std::mutex critical_move_bounds_; std::mutex critical_roof_tips_; mutable std::vector> floating_parts_cache_; diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 7f1095e26b..4d8e26a763 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -253,28 +253,6 @@ class SupportLayer const coord_t custom_line_distance = 0, EFillMethod custom_pattern = EFillMethod::NONE); - - /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height - * support layers. - * - * \param support_fill_new_this_layer Support that should be added. - * \param support_fill_total_this_layer All support that will be on the current layer. - * \param support_fill_total_next_layer All support that will be on the next. - * \param support_line_width Line width of the support extrusions. - * \param wall_line_count Wall-line count around the fill. - * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. - * \param custom_line_distance (optional, default to 0) Distance between lines of the infill pattern. custom_line_distance of 0 means use the default instead. - * \param custom_pattern (optional, default to EFillMethod::NONE) Set if a non default infill pattern should be used - */ - void fillInfillParts( - const Polygons& support_fill_new_this_layer, - const Polygons& support_fill_total_this_layer, - const Polygons& support_fill_total_next_layer, - const coord_t support_line_width, - const coord_t wall_line_count, - const bool unionAll = false, - const coord_t custom_line_distance = 0, - EFillMethod custom_pattern = EFillMethod::NONE); }; class SupportStorage diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index f117989199..80edce252b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3471,7 +3471,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool skip_stitching = false; const bool fill_gaps = density_idx == 0; // Only fill gaps for one of the densities. Infill infill_comp( - part.custom_line_pattern == EFillMethod::NONE ? support_pattern : part.custom_line_pattern, + part.custom_line_pattern_ == EFillMethod::NONE ? support_pattern : part.custom_line_pattern_, zig_zaggify_infill, connect_polygons, area, diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 19db537ecd..981ef9afb6 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -9,13 +9,13 @@ using namespace cura; SupportInfillPart::SupportInfillPart(const PolygonsPart& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern) - : outline(outline) - , outline_boundary_box(outline) - , support_line_width(support_line_width) - , inset_count_to_generate(inset_count_to_generate) - , custom_line_distance(custom_line_distance) - , custom_line_pattern(custom_line_pattern) - , use_fractional_config(use_fractional_config) + : outline_(outline) + , outline_boundary_box_(outline) + , support_line_width_(support_line_width) + , inset_count_to_generate_(inset_count_to_generate) + , custom_line_distance_(custom_line_distance) + , custom_line_pattern_(custom_line_pattern) + , use_fractional_config_(use_fractional_config) { diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index fd7df095eb..6191a574d9 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -69,8 +69,8 @@ TreeModelVolumes::TreeModelVolumes( } // Figure out the rest of the setting(-like variable)s relevant to the class a whole. - current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx]; - const TreeSupportSettings config(layer_outlines_[current_outline_idx].first); + current_outline_idx_ = mesh_to_layeroutline_idx[current_mesh_idx]; + const TreeSupportSettings config(layer_outlines_[current_outline_idx_].first); for (const auto data_pair : layer_outlines_) { @@ -85,7 +85,6 @@ TreeModelVolumes::TreeModelVolumes( // Figure out the rest of the setting(-like variable)s relevant to the class a whole. current_outline_idx_ = mesh_to_layeroutline_idx[current_mesh_idx]; - const TreeSupportSettings config(layer_outlines_[current_outline_idx_].first); if (config.support_overrides == SupportDistPriority::Z_OVERRIDES_XY) { @@ -180,7 +179,7 @@ void TreeModelVolumes::precalculate(LayerIndex max_layer) { const auto t_start = std::chrono::high_resolution_clock::now(); precalculated_ = true; - max_layer = std::min(max_layer + max_cradle_layers, LayerIndex(layer_outlines_[current_outline_idx].second.size() - 1)); + max_layer = std::min(max_layer + max_cradle_layers, LayerIndex(layer_outlines_[current_outline_idx_].second.size() - 1)); // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant. // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex like initial layer diameter are only done in once. @@ -593,7 +592,7 @@ void TreeModelVolumes::precalculateAntiPreferred() const coord_t radius = precalculated_avoidance_radiis[key_idx].first; const LayerIndex max_required_layer = precalculated_avoidance_radiis[key_idx].second; - const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist * 1.9); + const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist_ * 1.9); RadiusLayerPair key(radius, 0); Polygons latest_avoidance; @@ -621,15 +620,15 @@ void TreeModelVolumes::precalculateAntiPreferred() if(!encountered_anti && ! anti.empty()) { encountered_anti = true; - if (support_rest_preference == RestPreference::BUILDPLATE) + if (support_rest_preference_ == RestPreference::BUILDPLATE) { latest_avoidance = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, false, true); } - if(support_rests_on_model) + if(support_rests_on_model_) { latest_avoidance_to_model = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, true, true); } - if(max_layer_idx_without_blocker <= layer && support_rests_on_model) + if(max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { latest_avoidance_collision = getAvoidance(radius,layer,AvoidanceType::COLLISION, true, true); } @@ -646,28 +645,28 @@ void TreeModelVolumes::precalculateAntiPreferred() anti = anti.unionPolygons().offset(radius).unionPolygons(); data_raw_anti[layer] = std::pair(key, anti); - if (support_rest_preference == RestPreference::BUILDPLATE) + if (support_rest_preference_ == RestPreference::BUILDPLATE) { latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons next_latest_avoidance = simplifier.polygon(latest_avoidance); + Polygons next_latest_avoidance = simplifier_.polygon(latest_avoidance); latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); data[layer] = std::pair(key, latest_avoidance); } - if(support_rests_on_model) + if(support_rests_on_model_) { latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons next_latest_avoidance_to_model = simplifier.polygon(latest_avoidance_to_model); + Polygons next_latest_avoidance_to_model = simplifier_.polygon(latest_avoidance_to_model); latest_avoidance_to_model = next_latest_avoidance_to_model.unionPolygons(latest_avoidance_to_model); latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius,layer)); data_to_model[layer] = std::pair(key, latest_avoidance_to_model); } - if(max_layer_idx_without_blocker <= layer && support_rests_on_model) + if(max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { latest_avoidance_collision = safeOffset(latest_avoidance_collision, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius), ClipperLib::jtRound); + Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius_), ClipperLib::jtRound); latest_avoidance_collision = latest_avoidance_collision.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); data_collision[layer] = std::pair(key, latest_avoidance_collision); } @@ -713,7 +712,7 @@ const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, co return empty_polygon; } - if (precalculated) + if (precalculated_) { spdlog::warn("Missing anti preferred area at radius {} and layer {} Returning Empty! Result had area of {}", ceiled_radius, key.second, result.value().get().area()); } @@ -733,7 +732,7 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye if(type == AvoidanceType::COLLISION) { - if(max_layer_idx_without_blocker <= layer_idx && to_model) + if(max_layer_idx_without_blocker_ <= layer_idx && to_model) { cache_ptr = &anti_preferred_cache_collision; } @@ -772,7 +771,7 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } - if (precalculated) + if (precalculated_) { spdlog::warn("Missing anti preferred calculated at radius {} and layer {} and type {} to model {}, but precalculate was called. Returning Empty!", ceiled_radius, key.second, type == AvoidanceType::COLLISION, to_model); } @@ -1408,7 +1407,7 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) start_layer = 1 + getMaxCalculatedLayer(0, avoidance_cache_); } - const coord_t radius_offset = -radius_0; + const coord_t radius_offset = - radius_0_; cura::parallel_for ( start_layer, @@ -1416,7 +1415,7 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) [&](const LayerIndex layer_idx) { RadiusLayerPair key = RadiusLayerPair (0,layer_idx); - if(!precalculated || support_rest_preference == RestPreference::BUILDPLATE) + if(!precalculated_ || support_rest_preference_ == RestPreference::BUILDPLATE) { Polygons smaller_avoidance_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,false,true).offset(radius_offset,ClipperLib::jtRound); Polygons smaller_avoidance_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,false,true).offset(radius_offset,ClipperLib::jtRound); @@ -1435,7 +1434,7 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) } } - if(!precalculated || support_rests_on_model) + if(!precalculated_ || support_rests_on_model_) { Polygons smaller_avoidance_to_model_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,true,true).offset(radius_offset,ClipperLib::jtRound); Polygons smaller_avoidance_to_model_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,true,true).offset(radius_offset,ClipperLib::jtRound); @@ -1453,7 +1452,7 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_ ); avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; } - if (layer_idx > max_layer_idx_without_blocker) + if (layer_idx > max_layer_idx_without_blocker_) { Polygons smaller_avoidance_collision = getAvoidance(1,layer_idx,AvoidanceType::COLLISION,true,true).offset(radius_offset,ClipperLib::jtRound); std::lock_guard critical_section(*critical_avoidance_cache_collision_ ); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 82cbc372cc..8115bd5549 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -713,7 +713,7 @@ std::optional TreeSupport::increaseSingleArea( // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), // but everything compensates for small rounding errors already, so it will be fine. // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. - if(! settings.no_error && ! to_bp_data.empty() && to_bp_data.area()<1) + if(! settings.no_error_ && ! to_bp_data.empty() && to_bp_data.area()<1) { to_bp_data = to_bp_data.unionPolygons(to_bp_data.offsetPolyLine(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); @@ -745,7 +745,7 @@ std::optional TreeSupport::increaseSingleArea( // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), // but everything compensates for small rounding errors already, so it will be fine. // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. - if(! settings.no_error && ! to_model_data.empty() && to_model_data.area()<1) + if(! settings.no_error_ && ! to_model_data.empty() && to_model_data.area()<1) { to_model_data = to_model_data.unionPolygons(to_model_data.offsetPolyLine(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); @@ -758,18 +758,18 @@ std::optional TreeSupport::increaseSingleArea( bool anti_preferred_applied = false; if(!anti_preferred_areas.empty()) { - bool is_fast = settings.increase_speed >= config.maximum_move_distance; + bool is_fast = settings.increase_speed_ >= config.maximum_move_distance; //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. - coord_t anti_radius_extra = std::max(settings.increase_speed-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); + coord_t anti_radius_extra = std::max(settings.increase_speed_-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); if(anti_radius_extra) { anti_preferred_areas = anti_preferred_areas.offset(anti_radius_extra).unionPolygons(); } - if (current_elem.to_buildplate) + if (current_elem.to_buildplate_) { Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); // If already moving fast there is not much to do. The anti preferred with collision radius will then later be subtracted if it is not subtracted here. - if(to_bp_without_anti.area()>EPSILON || (settings.use_anti_preferred &&!is_fast)) + if(to_bp_without_anti.area()>EPSILON || (settings.use_anti_preferred_ &&!is_fast)) { to_bp_data = to_bp_without_anti; Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); @@ -782,7 +782,7 @@ std::optional TreeSupport::increaseSingleArea( else { Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); - if(to_model_data_without_anti.area()>EPSILON || (settings.use_anti_preferred &&!is_fast)) + if(to_model_data_without_anti.area()>EPSILON || (settings.use_anti_preferred_ &&!is_fast)) { to_model_data = to_model_data_without_anti; Polygons increased_without_anti = increased.difference(anti_preferred_areas); @@ -795,10 +795,10 @@ std::optional TreeSupport::increaseSingleArea( // Remove areas where the branch should not be if possible. // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. - if(volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred) + if(volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred_) { - const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance ); - if (current_elem.to_buildplate) + const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_); + if (current_elem.to_buildplate_) { to_bp_data = to_bp_data.difference(anti_preferred); to_model_data = to_model_data.difference(anti_preferred); @@ -813,16 +813,16 @@ std::optional TreeSupport::increaseSingleArea( increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); } - check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; + check_layer_data = current_elem.to_buildplate_ ? to_bp_data : to_model_data; if(check_layer_data.area() > 1) { - current_elem.can_avoid_anti_preferred = true; + current_elem.can_avoid_anti_preferred_ = true; } } if(volumes_.getFirstAntiPreferredLayerIdx() >= layer_idx) { - current_elem.can_avoid_anti_preferred = true; + current_elem.can_avoid_anti_preferred_ = true; } if (settings.increase_radius_ && check_layer_data.area() > 1) @@ -841,10 +841,10 @@ std::optional TreeSupport::increaseSingleArea( to_bp_data_2 = increased; bool avoidance_handled = false; - if(settings.use_anti_preferred && current_elem.can_use_safe_radius) + if(settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { - to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); - avoidance_handled = settings.type != AvoidanceType::SLOW; + to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + avoidance_handled = settings.type_ != AvoidanceType::SLOW; } else if(anti_preferred_applied) { @@ -860,10 +860,10 @@ std::optional TreeSupport::increaseSingleArea( { to_model_data_2 = increased; bool avoidance_handled = false; - if(settings.use_anti_preferred && current_elem.can_use_safe_radius) + if(settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { - to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type, ! current_elem.to_buildplate, settings.use_min_distance)); - avoidance_handled = settings.type != AvoidanceType::SLOW; //There is no slow anti-preferred avoidance. + to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + avoidance_handled = settings.type_ != AvoidanceType::SLOW; //There is no slow anti-preferred avoidance. } else if(anti_preferred_applied) { @@ -1199,21 +1199,21 @@ void TreeSupport::increaseAreas( insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); } - if(!elem.can_avoid_anti_preferred) + if(!elem.can_avoid_anti_preferred_) { std::deque old_order = order; for (AreaIncreaseSettings settings : old_order) { - if(elem.effective_radius_height < config.increase_radius_until_dtt && !settings.increase_radius) + if(elem.effective_radius_height_ < config.increase_radius_until_dtt && !settings.increase_radius_) { continue ; } - if(!settings.move) + if(!settings.move_) { continue ; } - insertSetting(AreaIncreaseSettings(settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, !settings.use_anti_preferred, settings.move),true); + insertSetting(AreaIncreaseSettings(settings.type_, settings.increase_speed_, settings.increase_radius_, settings.no_error_, use_min_radius, !settings.use_anti_preferred_, settings.move_),true); } } @@ -1225,7 +1225,7 @@ void TreeSupport::increaseAreas( for (AreaIncreaseSettings settings : order) { new_order.emplace_back(settings); - new_order.emplace_back(settings.type_, settings.increase_speed_, settings.increase_radius_, settings.no_error_, use_min_radius, settings.use_anti_preferred, settings.move_); + new_order.emplace_back(settings.type_, settings.increase_speed_, settings.increase_radius_, settings.no_error_, use_min_radius, settings.use_anti_preferred_, settings.move_); } order = new_order; } @@ -1369,10 +1369,10 @@ void TreeSupport::increaseAreas( elem.last_area_increase_ = settings; add = true; // Do not merge if the branch should not move or the priority has to be to get farther away from the model. - bypass_merge = ! settings.move - || (settings.use_min_distance && elem.distance_to_top < config.tip_layers) - || !elem.can_avoid_anti_preferred; // todo less aggressive merge prevention? - if (settings.move) + bypass_merge = ! settings.move_ + || (settings.use_min_distance_ && elem.distance_to_top_ < config.tip_layers) + || !elem.can_avoid_anti_preferred_; // todo less aggressive merge prevention? + if (settings.move_) { elem.dont_move_until_ = 0; } @@ -1469,13 +1469,13 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, for (const TreeSupportElement* elem : all_elements_on_layer) { - if(!elem->can_avoid_anti_preferred || config.getCollisionRadius(*elem) != config.getRadius(*elem)) + if(!elem->can_avoid_anti_preferred_ || config.getCollisionRadius(*elem) != config.getRadius(*elem)) { - const coord_t safe_movement_distance = (elem->use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) + const coord_t safe_movement_distance = (elem->use_min_xy_dist_ ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); - bool immutable = elem->area != nullptr; - bool to_bp = elem->to_buildplate; + bool immutable = elem->area_ != nullptr; + bool to_bp = elem->to_buildplate_; Polygons relevant_influence; Polygons full_influence; @@ -1486,21 +1486,21 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, } else { - relevant_influence = elem->area->difference(volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,elem->use_min_xy_dist)); + relevant_influence = elem->area_->difference(volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,elem->use_min_xy_dist_)); full_influence = relevant_influence; } AABB relevant_influence_aabb = AABB(relevant_influence); for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base && !removed_lines_idx.contains(cradle_idx)) + if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base_ && !removed_lines_idx.contains(cradle_idx)) { //The branch created by the influence area cant lag though the model... So the offset needs to be safe... - AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area); + AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area_); cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); if(cradle_area_aabb.hit(relevant_influence_aabb)) { - Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area, + Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area_, config.getRadius(*elem) + config.xy_distance, volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,true), safe_movement_distance, @@ -1520,9 +1520,9 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, { //todo Check if non remove options are available eg shortening cradle line... removed_lines_idx.emplace(cradle_idx); - cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line); - cradle.getCradleLine()->line.clear(); - spdlog::debug("Flagging to remove cradle line {} {} ", cradle.layer_idx, cradle.line_idx); + cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line_); + cradle.getCradleLine()->line_.clear(); + spdlog::debug("Flagging to remove cradle line {} {} ", cradle.layer_idx_, cradle.line_idx_); } } } @@ -1539,7 +1539,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, { if(cradle.cradleLineExists()) { - cradle.cradle->verifyLines(); + cradle.cradle_->verifyLines(); } } @@ -1549,10 +1549,10 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, next_layer.insert(next_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); for(TreeSupportElement* elem:next_layer) { - if(elem->cradle_line != nullptr && !elem->cradle_line->cradleLineExists()) + if(elem->cradle_line_ != nullptr && !elem->cradle_line_->cradleLineExists()) { move_bounds[layer_idx].erase(elem); - delete elem->area; + delete elem->area_; delete elem; } } @@ -1584,11 +1584,11 @@ void TreeSupport::createLayerPathing(std::vector>& { for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) { - LayerIndex cradle_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + LayerIndex cradle_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; if(cradle_layer_idx == 84) { printf(""); @@ -2286,9 +2286,9 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - if(cradle_data[layer_idx][cradle_idx]->is_roof) + if(cradle_data[layer_idx][cradle_idx]->is_roof_) { std::lock_guard critical_section_cradle(critical_support_roof_storage); support_roof_storage[layer_idx-base_idx].add(base); @@ -2301,20 +2301,20 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } } - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) { - Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].area; - bool is_roof = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_roof; - LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; + LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; if(is_roof) { std::lock_guard critical_section_cradle(critical_support_roof_storage); if(support_roof_storage.size()<=layer_idx) { - support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); } support_roof_storage[cradle_line_layer_idx].add(line_area); } @@ -2324,11 +2324,11 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora if(cradle_support_line_areas.size()<=layer_idx) { - cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); } cradle_support_line_areas[cradle_line_layer_idx].add(line_area); } - if(!cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_base) + if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) { const coord_t radius = config.getRadius(0); Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, @@ -2351,9 +2351,9 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - if(cradle_data[layer_idx][cradle_idx]->is_roof) + if(cradle_data[layer_idx][cradle_idx]->is_roof_) { support_roof_storage[layer_idx-base_idx].add(base); } @@ -2364,18 +2364,18 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } } - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); line_idx++) + for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines[line_idx].size(); height_idx++) + for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) { - Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].area; - bool is_roof = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_roof; - LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].layer_idx; + Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; + LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; if(is_roof) { if(support_roof_storage.size()<=layer_idx) { - support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); } support_roof_storage[cradle_line_layer_idx].add(line_area); } @@ -2383,11 +2383,11 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { if(cradle_support_line_areas.size()<=layer_idx) { - cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines[line_idx].size()-height_idx); + cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); } cradle_support_line_areas[cradle_line_layer_idx].add(line_area); } - if(!cradle_data[layer_idx][cradle_idx]->lines[line_idx][height_idx].is_base) + if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) { const coord_t radius = config.getRadius(0); Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, @@ -2504,16 +2504,16 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { bool has_parent_roof = false; - if (data_pair.first->supports_roof) + if (data_pair.first->supports_roof_) { - for (auto parent : data_pair.first->parents) + for (auto parent : data_pair.first->parents_) { - has_parent_roof |= (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top); + has_parent_roof |= (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_); } } bool element_viable_for_skin = has_parent_roof; - element_viable_for_skin |= data_pair.first->parents.empty() && (config.getRadius(*data_pair.first) >= config.support_tree_skin_for_large_tips_radius_threshold); + element_viable_for_skin |= data_pair.first->parents_.empty() && (config.getRadius(*data_pair.first) >= config.support_tree_skin_for_large_tips_radius_threshold); if (element_viable_for_skin) { @@ -2595,7 +2595,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora for (auto sub_part : part.splitIntoParts()) { // Prevent small slivers of a branch to generate as support. The heuristic to detect if a part is too small or thin could maybe be improved. - if ((sub_part.area() < part_area / 5 && sub_part.area() * 2 < M_PI * pow(config.branch_radius,2)) + if ((sub_part.area() < part_area / 5 && sub_part.area() * 2 < std::numbers::pi * pow(config.branch_radius,2)) || sub_part.offset(-config.support_line_width).area() < 1) { partial_skin_area = partial_skin_area.unionPolygons(sub_part); @@ -2920,11 +2920,17 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor const Polygons all_next_layer = support_layer_storage.size()("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config_.layer_height) : use_fake_roof_ ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0) - , connect_length( - (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config.min_radius - 1.0 * config.support_line_width, 0.0)) - , support_tree_branch_distance((config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length) - , support_roof_line_distance( - use_fake_roof ? (config.support_pattern == EFillMethod::TRIANGLES ? 3 : (config.support_pattern == EFillMethod::GRID ? 2 : 1)) - * (config.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + , connect_length_( + (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config_.min_radius - 1.0 * config_.support_line_width, 0.0)) + , support_tree_branch_distance_((config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length_) + , support_roof_line_distance_( + use_fake_roof_ ? (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) + * (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) : mesh.settings.get("support_roof_line_distance")) - , support_outset(0) + , support_outset_(0) , // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") - roof_outset(use_fake_roof ? support_outset : mesh.settings.get("support_roof_offset")) - , force_tip_to_roof((config.min_radius * config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && support_roof_layers && ! use_fake_roof) - , support_tree_limit_branch_reach(mesh.settings.get("support_tree_limit_branch_reach")) - , support_tree_branch_reach_limit(support_tree_limit_branch_reach ? mesh.settings.get("support_tree_branch_reach_limit") : 0) - , z_distance_delta(std::min(config.z_distance_top_layers + 1, mesh.overhang_areas.size())) - , xy_overrides(config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z) - , tip_roof_size(force_tip_to_roof ? config.min_radius * config.min_radius * M_PI : 0) - , already_inserted(mesh.overhang_areas.size()) - , support_roof_drawn(mesh.overhang_areas.size(), Polygons()) - , roof_tips_drawn(mesh.overhang_areas.size(), Polygons()) - , cradle_data(mesh.overhang_areas.size()) - , volumes_(volumes_s) - , force_minimum_roof_area(use_fake_roof || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) - , cradle_layers(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config.layer_height) - , cradle_layers_min(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config.layer_height) - , cradle_line_count(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) - , cradle_length(retrieveSetting(mesh.settings, "support_tree_cradle_length")) - , cradle_length_min(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) - , cradle_line_width(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) - , cradle_lines_roof(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") - , cradle_base_roof( - ! use_fake_roof + roof_outset_(use_fake_roof_ ? support_outset_ : mesh.settings.get("support_roof_offset")) + , force_tip_to_roof_((config_.min_radius * config_.min_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && support_roof_layers_ && ! use_fake_roof_) + , support_tree_limit_branch_reach_(mesh.settings.get("support_tree_limit_branch_reach")) + , support_tree_branch_reach_limit_(support_tree_limit_branch_reach_ ? mesh.settings.get("support_tree_branch_reach_limit") : 0) + , z_distance_delta_(std::min(config_.z_distance_top_layers + 1, mesh.overhang_areas.size())) + , xy_overrides_(config_.support_overrides == SupportDistPriority::XY_OVERRIDES_Z) + , tip_roof_size_(force_tip_to_roof_ ? config_.min_radius * config_.min_radius * std::numbers::pi : 0) + , already_inserted_(mesh.overhang_areas.size()) + , support_roof_drawn_(mesh.overhang_areas.size(), Polygons()) + , roof_tips_drawn_(mesh.overhang_areas.size(), Polygons()) + , cradle_data_(mesh.overhang_areas.size()) + , force_minimum_roof_area_(use_fake_roof_ || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) + , cradle_layers_(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config_.layer_height) + , cradle_layers_min_(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config_.layer_height) + , cradle_line_count_(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) + , cradle_length_(retrieveSetting(mesh.settings, "support_tree_cradle_length")) + , cradle_length_min_(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) + , cradle_line_width_(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) + , cradle_lines_roof_(! use_fake_roof_ && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") + , cradle_base_roof_( + ! use_fake_roof_ && (retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base")) - , large_cradle_base(! use_fake_roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") - , cradle_area_threshold(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) - , cradle_tip_dtt(config.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) - , large_cradle_line_tips(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_z_distance_layers(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance") , config.layer_height)) + , large_cradle_base_(! use_fake_roof_ && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") + , cradle_area_threshold_(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) + , cradle_tip_dtt_(config_.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) + , large_cradle_line_tips_(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) + , cradle_z_distance_layers_(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance") , config_.layer_height)) { - if(cradle_layers) + if(cradle_layers_) { - cradle_layers += cradle_z_distance_layers; + cradle_layers_ += cradle_z_distance_layers_; } - if(cradle_length-cradle_line_width>0) + if(cradle_length_ - cradle_line_width_ >0) { - cradle_length-= cradle_line_width; - cradle_length_min -= cradle_line_width; + cradle_length_ -= cradle_line_width_; + cradle_length_min_ -= cradle_line_width_; } const double support_overhang_angle = mesh.settings.get("support_angle"); @@ -131,9 +130,9 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T support_supporting_branch_distance_ = 2 * config_.getRadius(dtt_when_tips_can_merge) + config_.support_line_width + FUDGE_LENGTH; - for(size_t cradle_z_dist_ctr=0; cradle_z_dist_ctr < cradle_z_distance_layers + 1; cradle_z_dist_ctr++) + for(size_t cradle_z_dist_ctr=0; cradle_z_dist_ctr < cradle_z_distance_layers_ + 1; cradle_z_dist_ctr++) { - cradle_xy_distance.emplace_back(config.xy_min_distance); + cradle_xy_distance_.emplace_back(config_.xy_min_distance); } for (int i = 0; i < 9; i++) @@ -151,15 +150,15 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T break; } - for (int layer_delta = 0; layer_delta < round_up_divide(next_cradle_xy_dist_height, config.layer_height); layer_delta++) + for (int layer_delta = 0; layer_delta < round_up_divide(next_cradle_xy_dist_height, config_.layer_height); layer_delta++) { - cradle_xy_distance.emplace_back(next_cradle_xy_dist); + cradle_xy_distance_.emplace_back(next_cradle_xy_dist); } } - for (int cradle_xy_dist_fill = cradle_xy_distance.size(); cradle_xy_dist_fill < cradle_layers + 1; cradle_xy_dist_fill++) + for (int cradle_xy_dist_fill = cradle_xy_distance_.size(); cradle_xy_dist_fill < cradle_layers_ + 1; cradle_xy_dist_fill++) { - cradle_xy_distance.emplace_back(config.xy_min_distance); + cradle_xy_distance_.emplace_back(config_.xy_min_distance); } } @@ -477,7 +476,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes max_layer = layer_idx; } } - max_layer = std::min(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); + max_layer = std::min(max_layer + cradle_layers_, LayerIndex(mesh.overhang_areas.size() - 1)); LayerIndex start_layer = 1; floating_parts_cache_.resize(max_layer+1); @@ -503,7 +502,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes AABB part_aabb(part); bool has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); - if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) + if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold_)) { std::lock_guard critical_section_add(critical_sections); next_completely_supported.add(part); @@ -511,7 +510,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes } Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); - coord_t overhang_area = std::max(overhang.area(), M_PI * config.min_wall_line_width * config.min_wall_line_width); + coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * config_.min_wall_line_width * config_.min_wall_line_width); if (!has_support_below) { std::lock_guard critical_section_add(critical_sections); @@ -526,7 +525,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes std::vector idx_of_floating_below; for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) { - if(layer_idx > 1 && floating.height < cradle_layers - 1 && !floating.area.intersection(part).empty()) + if(layer_idx > 1 && floating.height < cradle_layers_ - 1 && !floating.area.intersection(part).empty()) { idx_of_floating_below.emplace_back(idx); supported_overhang_area += floating.accumulated_supportable_overhang; @@ -535,7 +534,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes } } - if(min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) + if(min_resting_on_layers < cradle_layers_ && add && overhang_area + supported_overhang_area < cradle_area_threshold_) { std::lock_guard critical_section_add(critical_sections); for(size_t idx: idx_of_floating_below) @@ -639,29 +638,29 @@ std::vector>> TreeSupportTipGenerator::generat std::vector>> shadows(mesh.overhang_areas.size()); cura::parallel_for( 1, - mesh.overhang_areas.size() - (z_distance_delta + 1), + mesh.overhang_areas.size() - (z_distance_delta_ + 1), [&](const LayerIndex layer_idx) { - if (mesh.overhang_areas[layer_idx + z_distance_delta].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta).empty()) + if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta_).empty()) { return; } - for (auto pointy_info : getFullyUnsupportedArea(layer_idx + z_distance_delta)) + for (auto pointy_info : getFullyUnsupportedArea(layer_idx + z_distance_delta_)) { - AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta]); - if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta], overhang_aabb).intersection(pointy_info.area).empty()) + AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta_]); + if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta_], overhang_aabb).intersection(pointy_info.area).empty()) { // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. continue; } - std::vector accumulated_model(std::min(cradle_layers + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector accumulated_model(std::min(cradle_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); std::vector all_pointy_idx{ pointy_info.index }; - Point center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); - std::vector additional_centers; - TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof, cradle_layers_min, cradle_length_min); + Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); + std::vector additional_centers; + TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_); Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. bool aborted = false; bool contacted_other_pointy = false; @@ -680,7 +679,7 @@ std::vector>> TreeSupportTipGenerator::generat { for (size_t pointy_idx : all_pointy_idx) { - for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta, pointy_idx)) + for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx)) { if (next_pointy_data.height != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it @@ -712,7 +711,7 @@ std::vector>> TreeSupportTipGenerator::generat if (model_outline.empty()) { - if (cradle_up_layer < cradle_layers_min) + if (cradle_up_layer < cradle_layers_min_) { aborted = true; break; @@ -726,7 +725,7 @@ std::vector>> TreeSupportTipGenerator::generat // To reduce the impact an area is estimated where the cradle should be for these areas. Polygons previous_area = shadow; for (size_t cradle_up_layer_z_distance = cradle_up_layer; - cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta - 1, accumulated_model.size()); + cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size()); cradle_up_layer_z_distance++) { accumulated_model[cradle_up_layer_z_distance] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); @@ -736,16 +735,16 @@ std::vector>> TreeSupportTipGenerator::generat } model_outline = model_outline.unionPolygons(); - shadow = shadow.offset(-config.maximum_move_distance).unionPolygons(model_outline); + shadow = shadow.offset(-config_.maximum_move_distance).unionPolygons(model_outline); accumulated_model[cradle_up_layer] = shadow; if(cradle_up_layer > 0) { - Point shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - coord_t center_move_distance = std::min(config.maximum_move_distance_slow, cradle_line_width/3); + Point2LL shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + coord_t center_move_distance = std::min(config_.maximum_move_distance_slow, cradle_line_width_ /3); center_move_distance = std::min(center_move_distance, vSize(shadow_center-center_prev)); center_prev = center_prev + normal(shadow_center-center_prev, center_move_distance); - cradle_main->centers.emplace_back(center_prev); + cradle_main->centers_.emplace_back(center_prev); } } @@ -761,7 +760,7 @@ std::vector>> TreeSupportTipGenerator::generat } else { - cradle_data[layer_idx].emplace_back(cradle_main); + cradle_data_[layer_idx].emplace_back(cradle_main); } shadows[layer_idx].emplace_back(accumulated_model); } @@ -772,100 +771,100 @@ std::vector>> TreeSupportTipGenerator::generat void TreeSupportTipGenerator::generateCradleLines(std::vector>>& shadow_data) { - const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance.begin(), cradle_xy_distance.end()); + const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance_.begin(), cradle_xy_distance_.end()); cura::parallel_for( 1, - cradle_data.size(), + cradle_data_.size(), [&](const LayerIndex layer_idx) { - for (auto [center_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) + for (auto [center_idx, cradle] : cradle_data_[layer_idx] | ranges::views::enumerate) { - std::vector removed_directions; - const auto& accumulated_model = shadow_data[layer_idx][cradle->shadow_idx]; + std::vector removed_directions; + const auto& accumulated_model = shadow_data[layer_idx][cradle->shadow_idx_]; for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { - Point center = cradle->getCenter(layer_idx + idx); - const coord_t current_cradle_xy_distance = cradle_xy_distance[idx]; - const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; + Point2LL center = cradle->getCenter(layer_idx + idx); + const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; + const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; - if(cradle->lines.empty()) + if(cradle->lines_.empty()) { - cradle->lines.resize(cradle_line_count); + cradle->lines_.resize(cradle_line_count_); } - if (idx > cradle_z_distance_layers && ! model_shadow.empty()) + if (idx > cradle_z_distance_layers_ && ! model_shadow.empty()) { Polygons relevant_forbidden = volumes_.getAvoidance( 0, layer_idx + idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, true); - Polygons this_part_influence = model_shadow.offset(config.xy_min_distance); + Polygons this_part_influence = model_shadow.offset(config_.xy_min_distance); - for (size_t layer_offset = 1; layer_offset <= config.z_distance_bottom_layers && layer_offset <= idx; layer_offset++) + for (size_t layer_offset = 1; layer_offset <= config_.z_distance_bottom_layers && layer_offset <= idx; layer_offset++) { this_part_influence.add(accumulated_model[idx - layer_offset]); } - for (coord_t layer_offset = 1; layer_offset <= config.z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) + for (coord_t layer_offset = 1; layer_offset <= config_.z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) { const coord_t required_range_x = coord_t( - config.xy_min_distance - ((layer_offset - (config.z_distance_top_layers == 1 ? 0.5 : 0)) * config.xy_min_distance / config.z_distance_top_layers)); + config_.xy_min_distance - ((layer_offset - (config_.z_distance_top_layers == 1 ? 0.5 : 0)) * config_.xy_min_distance / config_.z_distance_top_layers)); this_part_influence.add(accumulated_model[idx + layer_offset].offset(required_range_x)); } this_part_influence = this_part_influence.unionPolygons().offset(FUDGE_LENGTH, ClipperLib::jtRound).unionPolygons(); - coord_t cradle_min_xy_distance_delta = std::max(config.xy_min_distance - current_cradle_xy_distance, coord_t(0)); + coord_t cradle_min_xy_distance_delta = std::max(config_.xy_min_distance - current_cradle_xy_distance, coord_t(0)); // Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. // To prevent this offset my the delta before removing the influence of the model. relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta) .difference(this_part_influence) .offset(cradle_min_xy_distance_delta) - .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2)) - .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width / 2)); + .unionPolygons(model_shadow.offset(current_cradle_xy_distance + cradle_line_width_ / 2)) + .unionPolygons(volumes_.getSupportBlocker(layer_idx).offset(cradle_line_width_ / 2)); coord_t max_distance2 = 0; for (auto line : model_shadow) { - for (Point p : line) + for (Point2LL p : line) { max_distance2 = std::max(max_distance2, vSize2(center - p)); } } - Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + current_cradle_length * 2.0, (2.0 * M_PI) / double(cradle_line_count)); + Polygon max_outer_points = PolygonUtils::makeCircle(center, sqrt(max_distance2) + current_cradle_length * 2.0, (2.0 * std::numbers::pi) / double(cradle_line_count_)); // create lines that go from the furthest possible location to the center Polygons lines_to_center; - for (Point p : max_outer_points) + for (Point2LL p : max_outer_points) { - Point direction = p - center; - lines_to_center.addLine(p, center + normal(direction,config.support_line_width)); + Point2LL direction = p - center; + lines_to_center.addLine(p, center + normal(direction,config_.support_line_width)); } // Subtract the model shadow up until this layer from the lines. if (idx > 0) { - lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width / 2).unionPolygons().differencePolyLines(lines_to_center, false); + lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width_ / 2).unionPolygons().differencePolyLines(lines_to_center, false); } // Store valid distances from the center in relation to the direction of the line. // Used to detect if a line may be intersecting another model part. - std::vector> vector_distance_map; + std::vector> vector_distance_map; // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing Polygons shortened_lines_to_center; for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) { bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); - Point closer = front_closer ? line.front() : line.back(); - Point further = front_closer ? line.back() : line.front(); + Point2LL closer = front_closer ? line.front() : line.back(); + Point2LL further = front_closer ? line.back() : line.front(); coord_t cradle_line_length = Polygon(line).polylineLength(); - if (cradle_line_length < cradle_length_min) + if (cradle_line_length < cradle_length_min_) { continue; } @@ -876,7 +875,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector= cradle_z_distance_layers + 1) + if(idx >= cradle_z_distance_layers_ + 1) { const Polygons actually_forbidden = volumes_.getAvoidance( - config.getRadius(0), - layer_idx + idx - (cradle_z_distance_layers + 1) , - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, + config_.getRadius(0), + layer_idx + idx - (cradle_z_distance_layers_ + 1) , + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, true); - too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config.support_line_width; + too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config_.support_line_width; } if (! too_short) { @@ -924,7 +923,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector distance_from_center; + bool found_close_line = direction.second + config_.xy_min_distance + config_.min_feature_size > distance_from_center; if (found_close_line) { keep_line = true; @@ -972,44 +971,44 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines[angle_idx].empty()) + if(idx <= cradle_z_distance_layers_ + 1 && !cradle->lines_[angle_idx].empty()) { - cradle->lines[angle_idx][0] = TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof); + cradle->lines_[angle_idx][0] = TreeSupportCradleLine(line,layer_idx+idx, cradle_lines_roof_); } else { - cradle->lines[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx,cradle_lines_roof)); + cradle->lines_[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx, cradle_lines_roof_)); } } } } // enlarge cradle lines below to minimize overhang of cradle lines. - for (auto [line_idx, cradle_lines] : cradle->lines | ranges::views::enumerate) + for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) { if(!cradle_lines.empty()) { - Point line_end = cradle_lines.back().line.back(); + Point2LL line_end = cradle_lines.back().line_.back(); for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { - Point center = cradle->getCenter(line.layer_idx); - if(vSize2(line_end - center) > vSize2(line.line.back() - center)) + Point2LL center = cradle->getCenter(line.layer_idx_); + if(vSize2(line_end - center) > vSize2(line.line_.back() - center)) { Polygons line_extension; - line_extension.addLine(line.line.back(),line_end); + line_extension.addLine(line.line_.back(),line_end); coord_t line_length_before = line_extension.polyLineLength(); Polygons actually_forbidden = volumes_.getAvoidance( 0, - line.layer_idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, + line.layer_idx_, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, true); line_extension = actually_forbidden.differencePolyLines(line_extension); @@ -1018,18 +1017,18 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector> all_cradles_per_layer(cradle_data.size() + cradle_layers); - for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + const coord_t min_distance_between_lines = cradle_line_width_ + + std::max(config_.xy_distance, (config_.fill_outline_gaps ? config_.min_feature_size/ 2 - 5 : config_.min_wall_line_width/ 2 - 5)); + const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance_.begin(), cradle_xy_distance_.end()); + std::vector> all_cradles_per_layer(cradle_data_.size() + cradle_layers_); + for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) { - for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - for (size_t cradle_line_idx = 0; cradle_line_idx < cradle_data[layer_idx][cradle_idx]->lines.size(); cradle_line_idx++) + for (size_t cradle_line_idx = 0; cradle_line_idx < cradle_data_[layer_idx][cradle_idx]->lines_.size(); cradle_line_idx++) { - for (size_t height = 0; height < cradle_data[layer_idx][cradle_idx]->lines[cradle_line_idx].size(); height++) + for (size_t height = 0; height < cradle_data_[layer_idx][cradle_idx]->lines_[cradle_line_idx].size(); height++) { - TreeSupportCradleLine* result = &cradle_data[layer_idx][cradle_idx]->lines[cradle_line_idx][height]; + TreeSupportCradleLine* result = &cradle_data_[layer_idx][cradle_idx]->lines_[cradle_line_idx][height]; all_cradles_per_layer[layer_idx + height].emplace_back(result); } } } } - std::vector removed_lines(cradle_data.size()); + std::vector removed_lines(cradle_data_.size()); cura::parallel_for( 1, @@ -1069,36 +1068,36 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() { std::vector all_cradles_on_layer = all_cradles_per_layer[layer_idx]; - std::function handleNewEnd = [&](size_t cradle_line_idx, Point new_end) + std::function handleNewEnd = [&](size_t cradle_line_idx, Point2LL new_end) { auto& line = (*all_cradles_on_layer[cradle_line_idx]); - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line.front(), line.line.back()) - || vSize(new_end - line.line.front()) < cradle_length_min) + if(LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line_.front(), line.line_.back()) + || vSize(new_end - line.line_.front()) < cradle_length_min_) { - all_cradles_on_layer[cradle_line_idx]->addLineToRemoved(line.line); - all_cradles_on_layer[cradle_line_idx]->line.clear(); + all_cradles_on_layer[cradle_line_idx]->addLineToRemoved(line.line_); + all_cradles_on_layer[cradle_line_idx]->line_.clear(); } else { - all_cradles_on_layer[cradle_line_idx]->line.back() = new_end; + all_cradles_on_layer[cradle_line_idx]->line_.back() = new_end; } }; for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) { - AABB bounding_box_current = AABB(all_cradles_on_layer[cradle_idx]->line); + AABB bounding_box_current = AABB(all_cradles_on_layer[cradle_idx]->line_); bounding_box_current.expand(min_distance_between_lines); for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) { - if(all_cradles_on_layer[cradle_idx_inner]->line.empty()||all_cradles_on_layer[cradle_idx]->line.empty()) + if(all_cradles_on_layer[cradle_idx_inner]->line_.empty()||all_cradles_on_layer[cradle_idx]->line_.empty()) { continue ; } - if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line))) + if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line_))) { - Polygon& outer_line = (*all_cradles_on_layer[cradle_idx]).line; - Polygon& inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line; - Point intersect; + Polygon& outer_line = (*all_cradles_on_layer[cradle_idx]).line_; + Polygon& inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line_; + Point2LL intersect; if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) @@ -1109,13 +1108,13 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if (inner_intersect_dist > outer_intersect_dist) { //this does not ensure that the line ends will not touch. Line ends not touching is handled later - Point new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); + Point2LL new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); handleNewEnd(cradle_idx_inner,new_end_inner); } if (outer_intersect_dist > inner_intersect_dist) { - Point new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); + Point2LL new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); handleNewEnd(cradle_idx,new_end_outer); } } @@ -1123,8 +1122,8 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if(!outer_line.empty() && !inner_line.empty()) { // Touching lines have the same issue Lines touch if the end is to close to another line - Point inner_direction = inner_line.back() - inner_line.front(); - Point outer_direction = outer_line.back() - outer_line.front(); + Point2LL inner_direction = inner_line.back() - inner_line.front(); + Point2LL outer_direction = outer_line.back() - outer_line.front(); double cosine = std::abs((dot(inner_direction, outer_direction)) / double(vSize(outer_direction) * vSize(inner_direction))); // If both lines point in the same/opposite direction check that them being to close is not the end line of one to the start of the other if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) @@ -1132,7 +1131,7 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); if(inner_end_to_outer_distance < min_distance_between_lines) { - Point new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),min_distance_between_lines - inner_end_to_outer_distance); + Point2LL new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),min_distance_between_lines - inner_end_to_outer_distance); double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); double error_correction_factor = 1.0 + error/double(min_distance_between_lines - inner_end_to_outer_distance); new_end_inner = inner_line.back() + @@ -1144,7 +1143,7 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); if(outer_end_to_inner_distance < min_distance_between_lines) { - Point new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),min_distance_between_lines - outer_end_to_inner_distance); + Point2LL new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),min_distance_between_lines - outer_end_to_inner_distance); double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back())); double error_correction_factor = 1.0 + error/double(min_distance_between_lines - outer_end_to_inner_distance); new_end_outer = outer_line.back() + @@ -1161,45 +1160,45 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() cura::parallel_for( 1, - cradle_data.size(), + cradle_data_.size(), [&](const LayerIndex layer_idx) { - for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - auto& cradle = cradle_data[layer_idx][cradle_idx]; + auto& cradle = cradle_data_[layer_idx][cradle_idx]; cradle->verifyLines(); // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. - for (auto [line_idx, cradle_lines] : cradle_data[layer_idx][cradle_idx]->lines | ranges::views::enumerate) + for (auto [line_idx, cradle_lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) { if(!cradle_lines.empty()) { - Point line_end = cradle_lines.back().line.back(); - Point line_front_uppermost = cradle_lines.back().line.front(); + Point2LL line_end = cradle_lines.back().line_.back(); + Point2LL line_front_uppermost = cradle_lines.back().line_.front(); - if(vSize2(line_end - cradle_lines.back().line.front()) > cradle_length * cradle_length) + if(vSize2(line_end - cradle_lines.back().line_.front()) > cradle_length_ * cradle_length_) { - cradle_lines.back().line.back() = line_front_uppermost + normal(line_end - line_front_uppermost, cradle_length); + cradle_lines.back().line_.back() = line_front_uppermost + normal(line_end - line_front_uppermost, cradle_length_); for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { - Point center = cradle->getCenter(line.layer_idx); - Point line_back_inner = line.line.back(); - Point line_front_inner = line.line.front(); - if(vSize2(line_back_inner - line_front_inner) > cradle_length * cradle_length) + Point2LL center = cradle->getCenter(line.layer_idx_); + Point2LL line_back_inner = line.line_.back(); + Point2LL line_front_inner = line.line_.front(); + if(vSize2(line_back_inner - line_front_inner) > cradle_length_ * cradle_length_) { //As the center can move there is no guarantee that the point of the current line lies on the line below. - Point projected_line_end = LinearAlg2D::getClosestOnLine(line_end,line.line.front(),line.line.back()); - const coord_t current_cradle_xy_distance = cradle_xy_distance[line.layer_idx - layer_idx]; - const coord_t current_cradle_length = cradle_length + max_cradle_xy_distance - current_cradle_xy_distance; + Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end,line.line_.front(),line.line_.back()); + const coord_t current_cradle_xy_distance = cradle_xy_distance_[line.layer_idx_ - layer_idx]; + const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; if(vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) { - line.line.back() = projected_line_end; + line.line_.back() = projected_line_end; } else { - line.line.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); + line.line_.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); } } - line_end = line.line.back(); + line_end = line.line_.back(); } } } @@ -1212,33 +1211,33 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas) { - const coord_t min_distance_between_lines = FUDGE_LENGTH + (config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath + const coord_t min_distance_between_lines = FUDGE_LENGTH + (config_.fill_outline_gaps ? config_.min_feature_size/ 2 - 5 : config_.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath // Some angles needed to move cradle lines outwards to prevent them from toughing. - double center_angle = (2.0 * M_PI) / double(cradle_line_count); - double outer_angle = (M_PI - center_angle) / 2; - coord_t outer_radius = (double(min_distance_between_lines + config.support_line_width) / sin(center_angle)) * sin(outer_angle); + double center_angle = (2.0 * std::numbers::pi) / double(cradle_line_count_); + double outer_angle = (std::numbers::pi - center_angle) / 2; + coord_t outer_radius = (double(min_distance_between_lines + config_.support_line_width) / sin(center_angle)) * sin(outer_angle); const coord_t small_hole_size - = EPSILON + (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - const coord_t closing_dist = sqrt(cradle_area_threshold / M_PI) + cradle_length + FUDGE_LENGTH; + = EPSILON + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + const coord_t closing_dist = sqrt(cradle_area_threshold_ / std::numbers::pi) + cradle_length_ + FUDGE_LENGTH; std::mutex critical_support_free_areas_and_cradle_areas; cura::parallel_for( 0, - cradle_data.size(), + cradle_data_.size(), [&](const LayerIndex layer_idx) { std::vector valid_cradles; - for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - TreeSupportCradle& cradle = *cradle_data[layer_idx][cradle_idx]; - for (size_t cradle_height = 0; cradle_height <= cradle_layers; cradle_height++) + TreeSupportCradle& cradle = *cradle_data_[layer_idx][cradle_idx]; + for (size_t cradle_height = 0; cradle_height <= cradle_layers_; cradle_height++) { Polygons line_tips; - std::vector> all_tips_center; + std::vector> all_tips_center; // generate trapezoid line tip with front width of support line width, back cradle_width. - for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) + for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) { std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx+cradle_height,line_idx); @@ -1249,19 +1248,19 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorline.back() - line->line.front(); - Point center_front = line->line.front() - normal(direction, cradle_line_width / 2); + Point2LL direction = line->line_.back() - line->line_.front(); + Point2LL center_front = line->line_.front() - normal(direction, cradle_line_width_ / 2); - Point direction_up_center = normal(rotate(direction, M_PI / 2), config.support_line_width / 2); - Point center_up = center_front + direction_up_center; - Point center_down = center_front - direction_up_center; + Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), config_.support_line_width / 2); + Point2LL center_up = center_front + direction_up_center; + Point2LL center_down = center_front - direction_up_center; for (auto existing_center : all_tips_center) { - Point intersect; + Point2LL intersect; bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); @@ -1270,19 +1269,19 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorlayer_idx))); + center_front = center_front + normal(direction, outer_radius - vSize(center_front - cradle.getCenter(line->layer_idx_))); center_up = center_front + direction_up_center; center_down = center_front - direction_up_center; } } - Point back_center = center_front + normal(direction, triangle_length); - Point direction_up_back = normal(rotate(direction, M_PI / 2), cradle_line_width / 2); + Point2LL back_center = center_front + normal(direction, triangle_length); + Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), cradle_line_width_ / 2); - Point back_up = back_center + direction_up_back; - Point back_down = back_center - direction_up_back; + Point2LL back_up = back_center + direction_up_back; + Point2LL back_down = back_center - direction_up_back; - line->line.front() = back_center + normal(direction, cradle_line_width / 2 - FUDGE_LENGTH / 2); + line->line_.front() = back_center + normal(direction, cradle_line_width_ / 2 - FUDGE_LENGTH / 2); all_tips_center.emplace_back(center_up, center_down); Polygon line_tip; @@ -1294,11 +1293,11 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorarea.add(line_tip); - line->area=line->area.unionPolygons(line->line.offset(0).offsetPolyLine(cradle_line_width / 2, ClipperLib::jtMiter)); - Polygons anti_preferred = line->area.offset(config.xy_distance); + line->area_.add(line_tip); + line->area_=line->area_.unionPolygons(line->line_.offset(0).offsetPolyLine(cradle_line_width_ / 2, ClipperLib::jtMiter)); + Polygons anti_preferred = line->area_.offset(config_.xy_distance); std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for(size_t z_distance_idx = 0; z_distance_idx < config.z_distance_top_layers; z_distance_idx++) + for(size_t z_distance_idx = 0; z_distance_idx < config_.z_distance_top_layers; z_distance_idx++) { volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); } @@ -1306,142 +1305,142 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers_; interface_down++) { support_free_areas[layer_idx - interface_down].add(cut_line_base); volumes_.addAreaToAntiPreferred(cradle_base, layer_idx - interface_down); } } - if (large_cradle_base) + if (large_cradle_base_) { Polygons forbidden_here = - volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); - cradle_base = cradle_base.offset(config.getRadius(cradle_tip_dtt), ClipperLib::jtRound).difference(forbidden_here); + cradle_base = cradle_base.offset(config_.getRadius(cradle_tip_dtt_), ClipperLib::jtRound).difference(forbidden_here); Polygons center_removed = cradle_base.difference(cut_line_base); if(center_removed.area()>1) { cradle_base = center_removed; } } - else if(cradle_base_roof) + else if(cradle_base_roof_) { // collect all inner points and connect to center for thin cradle base Polygons connected_cradle_base; - for(size_t line_idx = 0;line_idx line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers +1, line_idx); + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ +1, line_idx); if(line_opt) { - connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx),line_opt.value()->line.front()); + connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_),line_opt.value()->line_.front()); } } Polygons relevant_forbidden = - volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); - cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width/2).unionPolygons(cradle_base).difference(relevant_forbidden); + volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); + cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ /2).unionPolygons(cradle_base).difference(relevant_forbidden); } } cradle_base = cradle_base.unionPolygons(); - if(cradle_lines_roof) + if(cradle_lines_roof_) { Polygons forbidden_here = - volumes_.getAvoidance(0, layer_idx, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); std::vector> roofs; roofs.emplace_back(cradle_base, -1); - for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) + for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers + 1,line_idx); + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1,line_idx); if(!line_opt) { continue; } - coord_t line_base_offset = large_cradle_base ? std::max(coord_t(0),config.getRadius(cradle_tip_dtt) - cradle_line_width/2) : 0; - roofs.emplace_back(line_opt.value()->area.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); + coord_t line_base_offset = large_cradle_base_ ? std::max(coord_t(0),config_.getRadius(cradle_tip_dtt_) - cradle_line_width_ /2) : 0; + roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); } for(auto roof_area_pair : roofs) { Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( - roof_area_pair.first,roof_outset,forbidden_here, config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); - for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) + roof_area_pair.first,roof_outset_,forbidden_here, config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); + for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) { const Polygons forbidden_next = - volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); Polygons roof_area_before = full_overhang_area; full_overhang_area = full_overhang_area.difference(forbidden_next); - if (full_overhang_area.area()>EPSILON && dtt_roof < support_roof_layers) + if (full_overhang_area.area()>EPSILON && dtt_roof < support_roof_layers_) { if(roof_area_pair.second != -1) //If is_line { - TreeSupportCradleLine roof_base_line(cradle.lines[roof_area_pair.second].front()); - roof_base_line.area = full_overhang_area; - roof_base_line.is_base = true; - roof_base_line.layer_idx = layer_idx - dtt_roof; - cradle.lines[roof_area_pair.second].emplace_front(roof_base_line); + TreeSupportCradleLine roof_base_line(cradle.lines_[roof_area_pair.second].front()); + roof_base_line.area_ = full_overhang_area; + roof_base_line.is_base_ = true; + roof_base_line.layer_idx_ = layer_idx - dtt_roof; + cradle.lines_[roof_area_pair.second].emplace_front(roof_base_line); } else { - if(dtt_roof < cradle.base_below.size()) + if(dtt_roof < cradle.base_below_.size()) { - cradle.base_below[dtt_roof].add(full_overhang_area); + cradle.base_below_[dtt_roof].add(full_overhang_area); } else { - cradle.base_below.emplace_back(full_overhang_area); + cradle.base_below_.emplace_back(full_overhang_area); } } const Polygons forbidden_before = - volumes_.getAvoidance(0, layer_idx - dtt_roof, (only_gracious||!config.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config.support_rests_on_model, ! xy_overrides); + volumes_.getAvoidance(0, layer_idx - dtt_roof, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc(full_overhang_area, - config.maximum_move_distance, + config_.maximum_move_distance, forbidden_before, - config.xy_min_distance + config.min_feature_size, + config_.xy_min_distance + config_.min_feature_size, 0, 1, - config.support_line_distance / 2, - &config.simplifier); + config_.support_line_distance / 2, + &config_.simplifier); Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); if(overhang_part.area()>EPSILON) { - OverhangInformation cradle_overhang(overhang_part,true, cradle_data[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); - cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); + OverhangInformation cradle_overhang(overhang_part,true, cradle_data_[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); + cradle.overhang_[layer_idx-dtt_roof].emplace_back(cradle_overhang); } } else { if(roof_area_before.area()>1) { - LayerIndex line_layer_idx = roof_area_pair.second<0 ? LayerIndex(-1) : cradle_data[layer_idx][cradle_idx]->lines[roof_area_pair.second].front().layer_idx; - OverhangInformation cradle_overhang(roof_area_before, true, cradle_data[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); - cradle.overhang[layer_idx-dtt_roof].emplace_back(cradle_overhang); + LayerIndex line_layer_idx = roof_area_pair.second<0 ? LayerIndex(-1) : cradle_data_[layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; + OverhangInformation cradle_overhang(roof_area_before, true, cradle_data_[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); + cradle.overhang_[layer_idx-dtt_roof].emplace_back(cradle_overhang); } break; } @@ -1451,35 +1450,36 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector1) { - OverhangInformation cradle_overhang(cradle_base, false, cradle_data[layer_idx][cradle_idx]); - cradle.overhang[layer_idx].emplace_back(cradle_overhang); + OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[layer_idx][cradle_idx]); + cradle.overhang_[layer_idx].emplace_back(cradle_overhang); } - for(size_t line_idx = 0;line_idx < cradle.lines.size(); line_idx++) + for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) { - if(!cradle.lines[line_idx].empty()) + if(!cradle.lines_[line_idx].empty()) { - LayerIndex support_cradle_on_layer_idx = cradle.lines[line_idx].front().layer_idx - (cradle_z_distance_layers + 1); - OverhangInformation line_overhang(cradle.lines[line_idx].front().area,false,cradle_data[layer_idx][cradle_idx],cradle.lines[line_idx].front().layer_idx,line_idx); - cradle.overhang[support_cradle_on_layer_idx].emplace_back(line_overhang); + LayerIndex support_cradle_on_layer_idx = cradle.lines_[line_idx].front().layer_idx_ - (cradle_z_distance_layers_ + 1); + OverhangInformation line_overhang(cradle.lines_[line_idx].front().area_,false, + cradle_data_[layer_idx][cradle_idx],cradle.lines_[line_idx].front().layer_idx_,line_idx); + cradle.overhang_[support_cradle_on_layer_idx].emplace_back(line_overhang); } } } - if(!cradle.overhang.empty()) + if(!cradle.overhang_.empty()) { - valid_cradles.emplace_back(cradle_data[layer_idx][cradle_idx]); + valid_cradles.emplace_back(cradle_data_[layer_idx][cradle_idx]); } else { - delete cradle_data[layer_idx][cradle_idx]; + delete cradle_data_[layer_idx][cradle_idx]; } } - cradle_data[layer_idx] = valid_cradles; + cradle_data_[layer_idx] = valid_cradles; }); } @@ -1585,12 +1585,12 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m .getAvoidance( 0, layer_idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - ! xy_overrides) + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_) ; - // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config.support_line_width + // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config_.support_line_width // replaced with another setting. It should still work in most cases, but it should be possible to create a situation where a overhang outset lags though a wall. I will // take a look at this later. Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( @@ -1609,9 +1609,9 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m .getAvoidance( 0, layer_idx - (dtt_roof + 1), - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - ! xy_overrides) + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_) ; full_overhang_area = full_overhang_area.difference(forbidden_next); @@ -1641,7 +1641,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m { // Now, because the avoidance/collision was subtracted above, the overhang parts that are of xy distance were removed, so to merge areas that should have been one // offset by xy_min_distance and then undo it. In a perfect world the offset here would be of a mode that makes sure that - // area.offset(config.xy_min_distance).unionPolygons().offset(-config.xy_min_distance) = area if there is only one polygon in said area. I have not encountered issues + // area.offset(config_.xy_min_distance).unionPolygons().offset(-config_.xy_min_distance) = area if there is only one polygon in said area. I have not encountered issues // with using the default mitered here. Could be that i just have not encountered an issue with it yet though. potential_support_roofs[layer_idx] = potential_support_roofs[layer_idx].unionPolygons().offset(config_.xy_min_distance).unionPolygons().offset(-config_.xy_min_distance).unionPolygons(potential_support_roofs[layer_idx]); @@ -1663,9 +1663,9 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m .getAvoidance( 0, layer_idx, - (only_gracious || ! config.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config.support_rests_on_model, - ! xy_overrides) + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_) ; if (! force_minimum_roof_area_) @@ -1786,7 +1786,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector= 1; dtt_roof_tip++) { @@ -1878,11 +1878,11 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - dtt_roof_tip != 0 || overhang_data.is_roof, overhang_data.is_cradle, disable_ovalization, + dtt_roof_tip != 0 || overhang_data.is_roof_, overhang_data.is_cradle_, disable_ovalization, additional_ovalization_targets); if(elem) { if(overhang_data.isCradleLine()) { - elem->cradle_line = std::make_shared(overhang_data.cradle,overhang_data.cradle_layer_idx,overhang_data.cradle_line_idx); + elem->cradle_line_ = std::make_shared(overhang_data.cradle_,overhang_data.cradle_layer_idx_,overhang_data.cradle_line_idx_); } } } @@ -1914,14 +1914,14 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( { std::vector all_cradle_roofs(storage.support.supportLayers.size()); - for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) + for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) { for(auto cradle:cradles) { - if(cradle->is_roof) + if(cradle->is_roof_) { - for (auto [base_idx, base] : cradle->base_below | ranges::views::enumerate) + for (auto [base_idx, base] : cradle->base_below_ | ranges::views::enumerate) { all_cradle_roofs[layer_idx-base_idx].add(base); } @@ -1937,11 +1937,11 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( if (layer_idx + 1 < storage.support.supportLayers.size()) { std::vector to_be_removed; - Polygons roof_on_layer_above = (use_fake_roof ? support_roof_drawn[layer_idx + 1] + Polygons roof_on_layer_above = (use_fake_roof_ ? support_roof_drawn_[layer_idx + 1] : storage.support.supportLayers[layer_idx + 1].support_roof).unionPolygons(additional_support_areas[layer_idx + 1]); roof_on_layer_above = roof_on_layer_above.unionPolygons(all_cradle_roofs[layer_idx+1]); Polygons roof_on_layer - = (use_fake_roof ? support_roof_drawn[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); + = (use_fake_roof_ ? support_roof_drawn_[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); for (TreeSupportElement* elem : move_bounds[layer_idx]) { @@ -1949,16 +1949,16 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( { to_be_removed.emplace_back(elem); } - else if (elem->supports_roof && elem->cradle_line.get() == nullptr) + else if (elem->supports_roof_ && elem->cradle_line_.get() == nullptr) { Point2LL from = elem->result_on_layer_; PolygonUtils::moveInside(roof_on_layer_above, from); // Remove branches should have interface above them, but don't. Should never happen. if (roof_on_layer_above.empty() - || (!roof_on_layer_above.inside(elem->result_on_layer) && vSize2(from-elem->result_on_layer) > std::pow(config.getRadius(0) + FUDGE_LENGTH,2))) + || (!roof_on_layer_above.inside(elem->result_on_layer_) && vSize2(from-elem->result_on_layer_) > std::pow(config_.getRadius(0) + FUDGE_LENGTH,2))) { to_be_removed.emplace_back(elem); - spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}.",vSize(from-elem->result_on_layer)); + spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}.",vSize(from-elem->result_on_layer_)); } } } @@ -1997,7 +1997,7 @@ void TreeSupportTipGenerator::generateTips( // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is // better than not supporting it at all. - if(cradle_layers>0) + if(cradle_layers_ >0) { generateCradle(mesh,support_free_areas); } @@ -2009,23 +2009,23 @@ void TreeSupportTipGenerator::generateTips( std::vector> all_cradles_requiring_support(move_bounds.size()); std::vector all_cradle_areas(move_bounds.size()); - for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + for(LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) { - for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for(size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - for(auto overhang_pair:cradle_data[layer_idx][cradle_idx]->overhang) + for(auto overhang_pair: cradle_data_[layer_idx][cradle_idx]->overhang_) { - all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data[layer_idx][cradle_idx]); + all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data_[layer_idx][cradle_idx]); } - for (auto [line_idx, lines] : cradle_data[layer_idx][cradle_idx]->lines | ranges::views::enumerate) + for (auto [line_idx, lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) { for (auto [height_idx, line] : lines | ranges::views::enumerate) { - all_cradle_areas[line.layer_idx].add(line.area); + all_cradle_areas[line.layer_idx_].add(line.area_); } } - for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below | ranges::views::enumerate) + for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { all_cradle_areas[layer_idx-base_idx].add(base); } @@ -2071,8 +2071,8 @@ void TreeSupportTipGenerator::generateTips( roof && ! use_fake_roof_, generate_layer_idx, line_distance, - cross_fill_provider, - (roof && ! use_fake_roof) || cradle, + cross_fill_provider_, + (roof && ! use_fake_roof_) || cradle, use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; @@ -2090,7 +2090,7 @@ void TreeSupportTipGenerator::generateTips( { core_overhang = core_overhang.difference(support_roof_drawn_[layer_idx]); for (Polygons roof_part : support_roof_drawn_[layer_idx + 1] - .difference(support_roof_drawn_[layer_idx].offset(config.maximum_move_distance_slow)) + .difference(support_roof_drawn_[layer_idx].offset(config_.maximum_move_distance_slow)) .splitIntoParts(true)) // If there is a roof, the roof will be one layer above the tips. { //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors @@ -2104,9 +2104,9 @@ void TreeSupportTipGenerator::generateTips( for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) { - for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx].size(); cradle_overhang_idx++) + for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx].size(); cradle_overhang_idx++) { - overhang_processing.emplace_back(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang[layer_idx][cradle_overhang_idx]); + overhang_processing.emplace_back(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx][cradle_overhang_idx]); } } @@ -2129,20 +2129,20 @@ void TreeSupportTipGenerator::generateTips( // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. { - Polygons already_supported = support_roof_drawn[layer_idx]; + Polygons already_supported = support_roof_drawn_[layer_idx]; already_supported.add(all_cradle_areas[layer_idx]); already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. already_supported = already_supported.unionPolygons(); - while (extra_total_offset_acc + config.support_line_width / 8 < extra_outset) //+mesh_config.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. + while (extra_total_offset_acc + config_.support_line_width / 8 < extra_outset) //+mesh_config_.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. { coord_t offset_current_step = - extra_total_offset_acc + 2 * config.support_line_width > config.min_radius - ? std::min(config.support_line_width / 8, extra_outset - extra_total_offset_acc) + extra_total_offset_acc + 2 * config_.support_line_width > config_.min_radius + ? std::min(config_.support_line_width / 8, extra_outset - extra_total_offset_acc) : std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); extra_total_offset_acc += offset_current_step; - Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config_.xy_min_distance + config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(already_supported.offset(1.5 * extra_total_offset_acc))).unionPolygons(); //overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. - Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config.xy_min_distance + config.support_line_width, 0, 1, config.support_line_distance / 2, &config.simplifier); + Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config_.xy_min_distance + config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); } } @@ -2197,13 +2197,13 @@ void TreeSupportTipGenerator::generateTips( return relevant_forbidden_below.inside(p.first, true); }; - Polygons already_supported = support_roof_drawn[layer_idx-lag_ctr]; + Polygons already_supported = support_roof_drawn_[layer_idx-lag_ctr]; already_supported.add(support_free_areas[layer_idx-lag_ctr]); // While point there are not supported, there may be no support anyway. already_supported=already_supported.unionPolygons(); //Remove all points that are for some reason are already supported - std::function)> evaluateAlreadySupported = - [&](std::pair p) { return already_supported.inside(p.first, true); + std::function)> evaluateAlreadySupported = + [&](std::pair p) { return already_supported.inside(p.first, true); }; overhang_lines = splitLines(overhang_lines, evaluateAlreadySupported).second; @@ -2215,8 +2215,8 @@ void TreeSupportTipGenerator::generateTips( // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. OverhangInformation dummy_overhang_info(remaining_overhang_part,false); - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers) ? support_roof_layers : 0, - layer_idx - lag_ctr, dummy_overhang_info, support_roof_layers, false); + addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof_ && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, + layer_idx - lag_ctr, dummy_overhang_info, support_roof_layers_, false); } } } @@ -2230,7 +2230,7 @@ void TreeSupportTipGenerator::generateTips( for (OverhangInformation overhang_data : overhang_processing) { - Polygons overhang_outset = overhang_data.overhang; + Polygons overhang_outset = overhang_data.overhang_; const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length_)); std::vector overhang_lines; @@ -2239,11 +2239,11 @@ void TreeSupportTipGenerator::generateTips( // The tip positions are determined here. if(overhang_data.isCradleLine()) { - std::optional cradle_line_opt = overhang_data.cradle->getCradleLineOfIndex(overhang_data.cradle_layer_idx,overhang_data.cradle_line_idx); - Polygons line = cradle_line_opt.value()->line.offset(0); + std::optional cradle_line_opt = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_,overhang_data.cradle_line_idx_); + Polygons line = cradle_line_opt.value()->line_.offset(0); polylines = ensureMaximumDistancePolyline( line, - ! overhang_data.is_roof ? config.min_radius * 2 : support_supporting_branch_distance, + ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, 1, false); } @@ -2251,8 +2251,8 @@ void TreeSupportTipGenerator::generateTips( { // todo can cause inconsistent support density if a line exactly aligns with the model polylines = ensureMaximumDistancePolyline( - generateLines(overhang_outset, overhang_data.is_roof, overhang_data.is_cradle, layer_idx + overhang_data.is_roof), - ! overhang_data.is_roof ? config_.min_radius * 2 + generateLines(overhang_outset, overhang_data.is_roof_, overhang_data.is_cradle_, layer_idx + overhang_data.is_roof_), + ! overhang_data.is_roof_ ? config_.min_radius * 2 : use_fake_roof_ ? support_supporting_branch_distance_ : connect_length_, 1, @@ -2286,9 +2286,9 @@ void TreeSupportTipGenerator::generateTips( } } - if (overhang_data.is_roof || overhang_data.is_cradle) // Some roof may only be supported by a part of a tip + if (overhang_data.is_roof_ || overhang_data.is_cradle_) // Some roof may only be supported by a part of a tip { - polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.is_cradle?2:1) * config.getRadius(0) + FUDGE_LENGTH / 2); + polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.is_cradle_?2:1) * config_.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); @@ -2306,11 +2306,11 @@ void TreeSupportTipGenerator::generateTips( else { spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} Was Line {} On Layer: {} Area size was {}", - overhang_data.is_roof, overhang_data.is_cradle,overhang_data.isCradleLine() ,layer_idx, overhang_data.overhang.area()); + overhang_data.is_roof_, overhang_data.is_cradle_,overhang_data.isCradleLine() ,layer_idx, overhang_data.overhang_.area()); } } - size_t dont_move_for_layers = support_roof_layers_ ? (force_tip_to_roof_ ? support_roof_layers_ : (overhang_data.is_roof ? 0 : support_roof_layers_)) : 0; + size_t dont_move_for_layers = support_roof_layers_ ? (force_tip_to_roof_ ? support_roof_layers_ : (overhang_data.is_roof_ ? 0 : support_roof_layers_)) : 0; addLinesAsInfluenceAreas( new_tips, overhang_lines, @@ -2364,7 +2364,7 @@ void TreeSupportTipGenerator::generateTips( config_.maximum_move_distance, false, support_roof_line_distance_); - placed_fake_roof_areas[layer_idx].add(support_roof_drawn[layer_idx]); + placed_fake_roof_areas[layer_idx].add(support_roof_drawn_[layer_idx]); placed_support_lines_support_areas[layer_idx].add( // todo Only save the area and add to storage at the end to enable correct handling of Support Interface Priority of fake roofs. TreeSupportUtils::generateSupportInfillLines(support_roof_drawn_[layer_idx], config_, false, layer_idx, support_roof_line_distance_, cross_fill_provider_, false).offsetPolyLine(config_.support_line_width / 2)); } @@ -2373,11 +2373,11 @@ void TreeSupportTipGenerator::generateTips( storage.support.supportLayers[layer_idx].support_roof.add(support_roof_drawn_[layer_idx]); storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(roof_tips_drawn_[layer_idx]); - if (layer_idx + 1 < support_roof_drawn.size() && layer_idx > 0) + if (layer_idx + 1 < support_roof_drawn_.size() && layer_idx > 0) { - Polygons all_roof = support_roof_drawn[layer_idx].unionPolygons(roof_tips_drawn[layer_idx]); - Polygons all_roof_above = support_roof_drawn[layer_idx + 1] - .unionPolygons(roof_tips_drawn[layer_idx + 1]) + Polygons all_roof = support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]); + Polygons all_roof_above = support_roof_drawn_[layer_idx + 1] + .unionPolygons(roof_tips_drawn_[layer_idx + 1]) .offset(FUDGE_LENGTH) .unionPolygons(); storage.support.supportLayers[layer_idx].support_fractional_roof.add(all_roof.difference(all_roof_above)); @@ -2385,9 +2385,9 @@ void TreeSupportTipGenerator::generateTips( } } }); - cradle_data_export.resize(cradle_data.size()); + cradle_data_export.resize(cradle_data_.size()); - for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) + for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) { cradle_data_export[layer_idx] = cradles; } From aa00d60768b7b6ec079f8a2cdafef7f8340afe6a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:24:57 +0100 Subject: [PATCH 046/101] Fix wrong type name. --- src/utils/polygon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index e62248b357..19edb4c1c4 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -351,7 +351,7 @@ Polygons Polygons::differencePolyLines(const Polygons& polylines, bool restitch, { Polygons result_lines, result_polygons; const coord_t snap_distance = 10_mu; - PolylineStitcher::stitch(ret, result_lines, result_polygons, max_stitch_distance, snap_distance); + PolylineStitcher::stitch(ret, result_lines, result_polygons, max_stitch_distance, snap_distance); ret = result_lines; // if polylines got stitched into polygons, split them back up into a polyline again, because the result only admits polylines for (PolygonRef poly : result_polygons) From 27dd92c5c8d35e50e279b08243af7ad7b39c7498 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:25:39 +0100 Subject: [PATCH 047/101] Removed performance optimisation causing cradle line tips of removed lines to not be removed --- src/TreeSupport.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 8115bd5549..984d7d6b27 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -1445,7 +1445,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, std::vector>& cradle_data) { - if(cradle_data.size()<=layer_idx || cradle_data[layer_idx].empty()) + if(cradle_data.size()<=layer_idx) { return; } @@ -1549,7 +1549,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, next_layer.insert(next_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); for(TreeSupportElement* elem:next_layer) { - if(elem->cradle_line_ != nullptr && !elem->cradle_line_->cradleLineExists()) + if(elem->cradle_line_ && !elem->cradle_line_->cradleLineExists()) { move_bounds[layer_idx].erase(elem); delete elem->area_; From f08100f89033ae7a689ff8b3a82d0912b9d91831 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 19 Mar 2024 07:51:57 +0100 Subject: [PATCH 048/101] Removed some unused variables --- src/TreeSupport.cpp | 2 -- src/TreeSupportTipGenerator.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 984d7d6b27..c60487cb96 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2330,7 +2330,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) { - const coord_t radius = config.getRadius(0); Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, config.xy_distance, volumes_.getCollision(0,cradle_line_layer_idx), @@ -2389,7 +2388,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) { - const coord_t radius = config.getRadius(0); Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, config.xy_distance, volumes_.getCollision(0,cradle_line_layer_idx), diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 0bc6ad9e7e..11dc79eabe 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -972,7 +972,6 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector( From d7cb1aa222fc397ac14f51eed1cadc5a565dbae4 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 21 Mar 2024 05:52:43 +0100 Subject: [PATCH 049/101] Fix cradle calculation for 1 line not terminating --- src/TreeSupportTipGenerator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 11dc79eabe..502b1a359b 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -837,7 +837,8 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector Date: Mon, 25 Mar 2024 03:42:28 +0100 Subject: [PATCH 050/101] Improving fractional support for tree supports --- include/TreeSupport.h | 28 +++-- include/TreeSupportSettings.h | 10 +- include/TreeSupportTipGenerator.h | 10 +- include/sliceDataStorage.h | 14 +++ src/TreeSupport.cpp | 187 ++++++++++++++++-------------- src/TreeSupportTipGenerator.cpp | 56 +++++---- 6 files changed, 184 insertions(+), 121 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index e8ee1b2c64..ff123c4f0c 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -46,6 +46,20 @@ constexpr coord_t SUPPORT_TREE_COLLISION_RESOLUTION = 500; // Only has an effect using PropertyAreasUnordered = std::unordered_map; using PropertyAreas = std::map; +struct FakeRoofArea +{ + FakeRoofArea(Polygons area, coord_t line_distance, bool fractional): + area_(area) + , line_distance_(line_distance) + , fractional_(fractional) + { + + } + Polygons area_; + coord_t line_distance_; + bool fractional_; +}; + /*! * \brief Generates a tree structure to support your models. */ @@ -304,6 +318,7 @@ class TreeSupport void generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, + std::vector& support_roof_storage_fractional, SliceDataStorage& storage, std::vector>& layer_tree_polygons, std::vector>& cradle_data); @@ -322,7 +337,10 @@ class TreeSupport * \param support_roof_storage[in] Areas where support was replaced with roof. * \param storage[in,out] The storage where the support should be stored. */ - void finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage); + void finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_layer_storage_fractional, + SliceDataStorage& storage); /*! * \brief Draws circles around result_on_layer points of the influence areas and applies some post processing. @@ -344,16 +362,10 @@ class TreeSupport */ std::vector additional_required_support_area; - /*! - * \brief A representation of already placed lines. Required for subtracting from new support areas. - */ - std::vector placed_support_lines_support_areas; - /*! * \brief Areas that use a higher density pattern of regular support to support the model (fake_roof). - * placed_support_lines_support_areas contains the lines placed inside of placed_fake_roof_areas. */ - std::vector placed_fake_roof_areas; + std::vector> fake_roof_areas; /*! * \brief Areas where no support may be. Areas will be subtracted from support areas. diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 38d475d21e..87a7f8592f 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -68,7 +68,8 @@ struct TreeSupportSettings diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40° or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. support_overrides(mesh_group_settings.get("support_xy_overrides_z")), xy_min_distance(support_overrides == SupportDistPriority::Z_OVERRIDES_XY ? mesh_group_settings.get("support_xy_distance_overhang") : xy_distance), - z_distance_top_layers(round_up_divide(mesh_group_settings.get("support_top_distance"), layer_height)), + z_distance_top(mesh_group_settings.get("support_top_distance")), + z_distance_top_layers(round_up_divide(z_distance_top, layer_height)), z_distance_bottom_layers(round_up_divide(mesh_group_settings.get("support_bottom_distance"), layer_height)), support_infill_angles(mesh_group_settings.get>("support_infill_angles")), support_roof_angles(mesh_group_settings.get>("support_roof_angles")), @@ -272,6 +273,11 @@ struct TreeSupportSettings */ coord_t xy_min_distance; + /*! + * \brief Distance required the top of the support to the model + */ + coord_t z_distance_top; + /*! * \brief Amount of layers distance required the top of the support to the model */ @@ -427,7 +433,7 @@ struct TreeSupportSettings && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless. support_roof_angles == other.support_roof_angles && support_infill_angles == other.support_infill_angles && increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height - && z_distance_top_layers == other.z_distance_top_layers && maximum_deviation == other.maximum_deviation && // Infill generation depends on deviation and resolution. + && z_distance_top == other.z_distance_top && maximum_deviation == other.maximum_deviation && // Infill generation depends on deviation and resolution. maximum_resolution == other.maximum_resolution && support_roof_line_distance == other.support_roof_line_distance && skip_some_zags == other.skip_some_zags && zag_skip_count == other.zag_skip_count && connect_zigzags == other.connect_zigzags && interface_preference == other.interface_preference && min_feature_size == other.min_feature_size && // interface_preference should be identical to ensure the tree will correctly interact with the roof. diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 6d57b50673..486e60db8e 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -21,6 +21,8 @@ namespace cura { + + class TreeSupportTipGenerator { public: @@ -43,8 +45,7 @@ class TreeSupportTipGenerator const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, - std::vector& placed_support_lines_support_areas, - std::vector& placed_fake_roof_areas, + std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, std::vector>& cradle_data_export); @@ -370,6 +371,11 @@ class TreeSupportTipGenerator */ std::vector support_roof_drawn_; + /*! + * \brief Areas that will be saved as support roof + */ + std::vector support_roof_drawn_fractional_; + /*! * \brief Areas that will be saved as support roof, originating from tips being replaced with roof areas. */ diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 4d8e26a763..3f25087cc8 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -227,6 +227,20 @@ class SupportLayer */ void excludeAreasFromSupportInfillAreas(const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box); + void fillInfillParts(const Polygons& area, + const coord_t support_line_width, + const coord_t wall_line_count, + const bool use_fractional_config = false, + const bool unionAll = false, + const coord_t custom_line_distance = 0, + EFillMethod custom_pattern = EFillMethod::NONE) + { + for (const PolygonsPart& island_outline : area.splitIntoParts(unionAll)) + { + support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count, custom_line_distance, custom_pattern); + } + } + /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height * support layers. * diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index c60487cb96..f6e6178187 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -94,8 +94,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) mesh.first.setActualZ(known_z); } - placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(),Polygons()); - placed_fake_roof_areas = std::vector(storage.support.supportLayers.size(),Polygons()); + fake_roof_areas = std::vector>(storage.support.supportLayers.size(),std::vector()); support_free_areas = std::vector(storage.support.supportLayers.size(), Polygons()); } @@ -272,7 +271,7 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector>& cradle_data_model) { TreeSupportTipGenerator tip_gen(mesh, volumes_); - tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas, placed_fake_roof_areas , support_free_areas, cradle_data_model); + tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, fake_roof_areas, support_free_areas, cradle_data_model); } void TreeSupport::mergeHelper( @@ -2259,6 +2258,7 @@ void TreeSupport::dropNonGraciousAreas( void TreeSupport::generateSupportSkin(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, + std::vector& support_roof_storage_fractional, SliceDataStorage& storage, std::vector>& layer_tree_polygons, std::vector>& cradle_data) @@ -2292,6 +2292,10 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { std::lock_guard critical_section_cradle(critical_support_roof_storage); support_roof_storage[layer_idx-base_idx].add(base); + if(base_idx == 0 && layer_idx + 1 < support_roof_storage_fractional.size()) + { + support_roof_storage_fractional[layer_idx+1].add(base); + } } else { @@ -2346,63 +2350,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } }); - for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) - { - for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) - { - for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) - { - if(cradle_data[layer_idx][cradle_idx]->is_roof_) - { - support_roof_storage[layer_idx-base_idx].add(base); - } - else - { - cradle_support_base_areas[layer_idx-base_idx].add(base); - support_layer_storage[layer_idx-base_idx].add(base); - } - } - - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) - { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) - { - Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; - bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; - LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; - if(is_roof) - { - if(support_roof_storage.size()<=layer_idx) - { - support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); - } - support_roof_storage[cradle_line_layer_idx].add(line_area); - } - else - { - if(cradle_support_line_areas.size()<=layer_idx) - { - cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); - } - cradle_support_line_areas[cradle_line_layer_idx].add(line_area); - } - if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) - { - Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, - config.xy_distance, - volumes_.getCollision(0,cradle_line_layer_idx), - config.xy_min_distance + config.min_feature_size, - 0, - 0, - config.min_feature_size, - &config.simplifier); - cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); - } - } - } - } - } - + std::vector fake_roofs(fake_roof_areas.size()); cura::parallel_for ( @@ -2410,14 +2358,30 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_layer_storage.size(), [&](const LayerIndex layer_idx) { + + Polygons fake_roof; + + for(FakeRoofArea& f_roof:fake_roof_areas[layer_idx]) + { + fake_roof.add(f_roof.area_); + } + + fake_roof = fake_roof.unionPolygons(); + fake_roofs[layer_idx] = fake_roof; + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx].unionPolygons()); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(fake_roof); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx].unionPolygons()); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(cradle_line_xy_distance_areas[layer_idx]); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - placed_fake_roof_areas[layer_idx] = placed_fake_roof_areas[layer_idx].unionPolygons(); additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx].unionPolygons(); + // Fractional roof is a modifier applied to a roof area, which means if only the fractional roof area is set, there will be nothing as there is no roof to modify. + // Because of that the fractional roof has ALSO to be added to the roof. + Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx]); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(fractional_roof); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(cradle_line_xy_distance_areas[layer_idx]); + storage.support.supportLayers[layer_idx].support_fractional_roof = storage.support.supportLayers[layer_idx].support_fractional_roof.unionPolygons(fractional_roof); + //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (!storage.support.supportLayers[layer_idx].support_roof.empty()) @@ -2481,7 +2445,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora if(storage.support.supportLayers.size() > layer_idx + 1) { needs_supporting.add(storage.support.supportLayers[layer_idx + 1].support_roof.difference(support_shell_capable_of_supporting_roof)); - needs_supporting.add(placed_fake_roof_areas[layer_idx + 1].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(additional_required_support_area[layer_idx + 1]); // E.g. cradle needs_supporting.add(cradle_support_line_areas[layer_idx+1]); @@ -2492,7 +2456,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora Polygons already_supports; already_supports.add(storage.support.supportLayers[layer_idx].support_roof); // roof already_supports.add(additional_required_support_area[layer_idx]); // E.g. cradle - already_supports.add(placed_fake_roof_areas[layer_idx]); + already_supports.add(fake_roofs[layer_idx]); already_supports.add(support_layer_storage[layer_idx].getOutsidePolygons().tubeShape(config.support_line_width*config.support_wall_count,0)); already_supports = already_supports.unionPolygons().offset(FUDGE_LENGTH).unionPolygons(); @@ -2810,7 +2774,10 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora dur_hole_removal); } -void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_skin_storage, std::vector& support_roof_storage, SliceDataStorage& storage) +void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_layer_storage_fractional, + SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS @@ -2823,8 +2790,6 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage.size(), [&](const LayerIndex layer_idx) { - support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(placed_support_lines_support_areas[layer_idx]); - if (!storage.support.supportLayers[layer_idx].support_roof.empty()) { switch (interface_pref) @@ -2914,31 +2879,65 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor [&](const LayerIndex layer_idx) { constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. - const Polygons all_this_layer = support_skin_storage[layer_idx].unionPolygons(support_layer_storage[layer_idx]); - const Polygons all_next_layer = support_layer_storage.size() 0) + { + fractional_support = support_layer_storage_fractional[layer_idx].intersection(support_layer_storage[layer_idx - 1]); + fractional_skin = support_layer_storage_fractional[layer_idx].intersection(support_skin_storage[layer_idx - 1]); + } + else + { + fractional_support = support_layer_storage_fractional[layer_idx]; + } + storage.support.supportLayers[layer_idx].fillInfillParts( - layer_idx, - support_layer_storage, - config.layer_height, - storage.meshes, + fractional_support, config.support_line_width, config.support_wall_count, - config.maximum_move_distance, + true, convert_every_part); + + storage.support.supportLayers[layer_idx].fillInfillParts( + fractional_skin, + config.support_line_width, + std::max(config.support_wall_count - 1,0), + true, + convert_every_part, + config.support_skin_line_distance, + EFillMethod::ZIG_ZAG); + + for(FakeRoofArea& fake_roof : fake_roof_areas[layer_idx]) + { + storage.support.supportLayers[layer_idx].fillInfillParts( + fake_roof.area_, + config.support_line_width, + 0, + fake_roof.fractional_, + convert_every_part, + fake_roof.line_distance_); + } + { std::lock_guard critical_section_progress(critical_sections); progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size(); @@ -2958,6 +2957,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data) { std::vector support_layer_storage(move_bounds.size()); + std::vector support_layer_storage_fractional(move_bounds.size()); + std::vector support_roof_storage_fractional(move_bounds.size()); + std::vector support_skin_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); std::map @@ -3051,11 +3053,28 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { + if(data_pair.first->parents_.empty() && !data_pair.first->supports_roof_ && !data_pair.first->supports_cradle_ && layer_idx + 1< support_roof_storage_fractional.size()) + { + if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) + { + support_roof_storage_fractional[layer_idx+1].add(data_pair.second); + } + else + { + support_layer_storage_fractional[layer_idx+1].add(data_pair.second); + } + + } ((data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) ? support_roof_storage : support_layer_storage)[layer_idx].add(data_pair.second); } + if(layer_idx + 1< support_roof_storage_fractional.size()) + { + support_roof_storage_fractional[layer_idx+1] = support_roof_storage_fractional[layer_idx+1].unionPolygons(); + support_layer_storage_fractional[layer_idx+1] = support_layer_storage_fractional[layer_idx+1].unionPolygons(); + } } - generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,storage,layer_tree_polygons,cradle_data); + generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,support_roof_storage_fractional,storage,layer_tree_polygons,cradle_data); // cradle being added before skin causes cradle lines to become skin. for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) @@ -3071,7 +3090,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou filterFloatingLines(support_layer_storage,support_skin_storage); const auto t_filter = std::chrono::high_resolution_clock::now(); - finalizeInterfaceAndSupportAreas(support_layer_storage, support_skin_storage, support_roof_storage, storage); + finalizeInterfaceAndSupportAreas(support_layer_storage, support_skin_storage, support_layer_storage_fractional, storage); const auto t_end = std::chrono::high_resolution_clock::now(); const auto dur_gen_tips = 0.001 * std::chrono::duration_cast(t_generate - t_start).count(); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 502b1a359b..5b12ce8db1 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -56,6 +56,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T , tip_roof_size_(force_tip_to_roof_ ? config_.min_radius * config_.min_radius * std::numbers::pi : 0) , already_inserted_(mesh.overhang_areas.size()) , support_roof_drawn_(mesh.overhang_areas.size(), Polygons()) + , support_roof_drawn_fractional_(mesh.overhang_areas.size(), Polygons()) , roof_tips_drawn_(mesh.overhang_areas.size(), Polygons()) , cradle_data_(mesh.overhang_areas.size()) , force_minimum_roof_area_(use_fake_roof_ || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) @@ -1625,6 +1626,12 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m { std::lock_guard critical_section_potential_support_roofs(critical_potential_support_roofs); potential_support_roofs[layer_idx - dtt_roof].add((full_overhang_area)); + + if(dtt_roof == 0) + { + support_roof_drawn_fractional_[layer_idx+1].add(full_overhang_area); + } + } else { @@ -1754,7 +1761,7 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( roof, cradle, safe_radius, - force_tip_to_roof_, + !roof && force_tip_to_roof_, skip_ovalisation, support_tree_limit_branch_reach_, support_tree_branch_reach_limit_); @@ -1978,8 +1985,7 @@ void TreeSupportTipGenerator::generateTips( const SliceMeshStorage& mesh, std::vector>& move_bounds, std::vector& additional_support_areas, - std::vector& placed_support_lines_support_areas, - std::vector& placed_fake_roof_areas, + std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, std::vector>& cradle_data_export) { @@ -2062,7 +2068,7 @@ void TreeSupportTipGenerator::generateTips( coord_t upper_line_distance = support_supporting_branch_distance_; coord_t line_distance = std::max(roof ? support_roof_line_distance_ : support_tree_branch_distance_, upper_line_distance); - bool use_grid = line_distance == upper_line_distance; + bool use_grid = line_distance == upper_line_distance; return TreeSupportUtils::generateSupportInfillLines( area, @@ -2087,9 +2093,9 @@ void TreeSupportTipGenerator::generateTips( if (support_roof_layers_ && layer_idx + 1 < support_roof_drawn_.size()) { - core_overhang = core_overhang.difference(support_roof_drawn_[layer_idx]); + core_overhang = core_overhang.difference(support_roof_drawn_[layer_idx]); // todo Rounding errors again. for (Polygons roof_part : support_roof_drawn_[layer_idx + 1] - .difference(support_roof_drawn_[layer_idx].offset(config_.maximum_move_distance_slow)) + .difference(support_roof_drawn_[layer_idx].offset(config_.maximum_move_distance_slow).unionPolygons()) .splitIntoParts(true)) // If there is a roof, the roof will be one layer above the tips. { //^^^Technically one should also subtract the avoidance of radius 0 (similarly how calculated in calculateRoofArea), as there can be some rounding errors @@ -2353,33 +2359,33 @@ void TreeSupportTipGenerator::generateTips( { if (use_fake_roof_) { - storage.support.supportLayers[layer_idx].fillInfillParts( - layer_idx, - support_roof_drawn_, - config_.layer_height, - storage.meshes, - config_.support_line_width, - 0, - config_.maximum_move_distance, - false, - support_roof_line_distance_); - placed_fake_roof_areas[layer_idx].add(support_roof_drawn_[layer_idx]); - placed_support_lines_support_areas[layer_idx].add( // todo Only save the area and add to storage at the end to enable correct handling of Support Interface Priority of fake roofs. - TreeSupportUtils::generateSupportInfillLines(support_roof_drawn_[layer_idx], config_, false, layer_idx, support_roof_line_distance_, cross_fill_provider_, false).offsetPolyLine(config_.support_line_width / 2)); + + placed_fake_roof_areas[layer_idx].emplace_back(support_roof_drawn_[layer_idx],support_roof_line_distance_, false); + + + if (config_.z_distance_top % config_.layer_height != 0) + { + Polygons all_roof = support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]); + Polygons valid_fractional_roof = support_roof_drawn_fractional_[layer_idx].intersection(all_roof); + placed_fake_roof_areas[layer_idx].emplace_back(support_roof_drawn_fractional_[layer_idx], support_roof_line_distance_, true); + } } else { storage.support.supportLayers[layer_idx].support_roof.add(support_roof_drawn_[layer_idx]); storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(roof_tips_drawn_[layer_idx]); - if (layer_idx + 1 < support_roof_drawn_.size() && layer_idx > 0) + if (config_.z_distance_top % config_.layer_height != 0) { Polygons all_roof = support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]); - Polygons all_roof_above = support_roof_drawn_[layer_idx + 1] - .unionPolygons(roof_tips_drawn_[layer_idx + 1]) - .offset(FUDGE_LENGTH) - .unionPolygons(); - storage.support.supportLayers[layer_idx].support_fractional_roof.add(all_roof.difference(all_roof_above)); + Polygons valid_fractional_roof = support_roof_drawn_fractional_[layer_idx].intersection(all_roof); + storage.support.supportLayers[layer_idx].support_fractional_roof = + storage.support.supportLayers[layer_idx].support_fractional_roof.unionPolygons(valid_fractional_roof); + + // Fractional roof is a modifier applied to a roof area, which means if only the fractional roof area is set, there will be nothing as there is no roof to modify. + // Because of that the fractional roof has ALSO to be added to the roof. + storage.support.supportLayers[layer_idx].support_roof = + storage.support.supportLayers[layer_idx].support_roof.unionPolygons(valid_fractional_roof); } } } From 25acb8cd971a70f49bee31fb7687ee035edd059a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 25 Mar 2024 03:43:17 +0100 Subject: [PATCH 051/101] Fix bug causing roof to not properly generate when using tree supports --- include/TreeSupportTipGenerator.h | 2 +- src/TreeSupportTipGenerator.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 486e60db8e..4f3a8f87a7 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -336,7 +336,7 @@ class TreeSupportTipGenerator size_t max_overhang_insert_lag_; /*! - * \brief Area of a tip. + * \brief Area of a tip in mm2. */ const double tip_roof_size_; diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 5b12ce8db1..50e295f5b5 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -53,7 +53,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T , support_tree_branch_reach_limit_(support_tree_limit_branch_reach_ ? mesh.settings.get("support_tree_branch_reach_limit") : 0) , z_distance_delta_(std::min(config_.z_distance_top_layers + 1, mesh.overhang_areas.size())) , xy_overrides_(config_.support_overrides == SupportDistPriority::XY_OVERRIDES_Z) - , tip_roof_size_(force_tip_to_roof_ ? config_.min_radius * config_.min_radius * std::numbers::pi : 0) + , tip_roof_size_(force_tip_to_roof_ ? INT2MM2(config_.min_radius * config_.min_radius) * std::numbers::pi : 0) , already_inserted_(mesh.overhang_areas.size()) , support_roof_drawn_(mesh.overhang_areas.size(), Polygons()) , support_roof_drawn_fractional_(mesh.overhang_areas.size(), Polygons()) From 74c8bd417275790b67134c8155bc1fb05601babe Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 4 Apr 2024 06:28:46 +0200 Subject: [PATCH 052/101] Fix issues with cradle lines including issues occurring when the cradle line width is large. --- include/TreeSupportCradle.h | 6 +++ src/TreeSupportTipGenerator.cpp | 69 ++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 1fada75d38..299f3a435d 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -165,6 +165,12 @@ struct TreeSupportCradle if(!lines_[line_idx][up_idx].is_base_) { previous_layer_idx = lines_[line_idx][up_idx].layer_idx_; + if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || + lines_[line_idx][up_idx].line_.size() < 2 || + lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) + { + lines_[line_idx].clear(); + } break; } } diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 94f233e048..7eb6ca796a 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -974,9 +974,9 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines_[angle_idx].empty()) @@ -1130,7 +1130,7 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) { coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); - if(inner_end_to_outer_distance < min_distance_between_lines) + if(inner_end_to_outer_distance < min_distance_between_lines && inner_end_to_outer_distance < vSize(outer_line.front() - inner_line.front())) { Point2LL new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),min_distance_between_lines - inner_end_to_outer_distance); double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); @@ -1142,7 +1142,7 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() else { coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); - if(outer_end_to_inner_distance < min_distance_between_lines) + if(outer_end_to_inner_distance < min_distance_between_lines && outer_end_to_inner_distance < vSize(outer_line.front() - inner_line.front())) { Point2LL new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),min_distance_between_lines - outer_end_to_inner_distance); double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back())); @@ -1161,15 +1161,15 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() cura::parallel_for( 1, - cradle_data_.size(), + cradle_data_.size(), [&](const LayerIndex layer_idx) { for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - auto& cradle = cradle_data_[layer_idx][cradle_idx]; + TreeSupportCradle* cradle = cradle_data_[layer_idx][cradle_idx]; cradle->verifyLines(); // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. - for (auto [line_idx, cradle_lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) + for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) { if(!cradle_lines.empty()) { @@ -1178,7 +1178,9 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if(vSize2(line_end - cradle_lines.back().line_.front()) > cradle_length_ * cradle_length_) { - cradle_lines.back().line_.back() = line_front_uppermost + normal(line_end - line_front_uppermost, cradle_length_); + coord_t current_cradle_xy_distance = cradle_xy_distance_[cradle_lines.back().layer_idx_ - layer_idx]; + coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; + cradle_lines.back().line_.back() = line_front_uppermost + normal(line_end - line_front_uppermost, current_cradle_length); for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { Point2LL center = cradle->getCenter(line.layer_idx_); @@ -1188,8 +1190,8 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() { //As the center can move there is no guarantee that the point of the current line lies on the line below. Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end,line.line_.front(),line.line_.back()); - const coord_t current_cradle_xy_distance = cradle_xy_distance_[line.layer_idx_ - layer_idx]; - const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; + current_cradle_xy_distance = cradle_xy_distance_[line.layer_idx_ - layer_idx]; + current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; if(vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) { line.line_.back() = projected_line_end; @@ -1245,19 +1247,32 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorline_; + + coord_t current_cradle_line_width = cradle_line_width_; - // 2*cradle_line_count would be distance between cradle lines stays the same - coord_t triangle_length = (cradle_line_width_ - config_.support_line_width) / 2 * tan(std::numbers::pi / 2 - std::numbers::pi / double(3 * cradle_line_count_)); + double assumed_half_center_angle = std::numbers::pi / (1.5 * cradle_line_count_); + coord_t triangle_length = cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - config_.support_line_width) / 2) * + tan(std::numbers::pi / 2 - assumed_half_center_angle); - Point2LL direction = line->line_.back() - line->line_.front(); - Point2LL center_front = line->line_.front() - normal(direction, cradle_line_width_ / 2); + const coord_t line_length = line.polylineLength(); + if(triangle_length >= line_length + cradle_line_width_) + { + triangle_length = line_length + cradle_line_width_; + current_cradle_line_width = config_.support_line_width + + 2 * triangle_length * tan(assumed_half_center_angle); + } + + Point2LL direction = line.back() - line.front(); + Point2LL center_front = line.front() - normal(direction, cradle_line_width_ / 2); Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), config_.support_line_width / 2); Point2LL center_up = center_front + direction_up_center; Point2LL center_down = center_front - direction_up_center; + coord_t tip_shift = 0; for (auto existing_center : all_tips_center) { Point2LL intersect; @@ -1268,24 +1283,34 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorlayer_idx_))); + // Move it back todo If line gets smaller than min length => abort + coord_t tip_shift_here = outer_radius - vSize(center_front - cradle.getCenter(cradle_line->layer_idx_)); + tip_shift += tip_shift_here; + center_front = center_front + normal(direction, tip_shift_here); center_up = center_front + direction_up_center; center_down = center_front - direction_up_center; } } Point2LL back_center = center_front + normal(direction, triangle_length); - Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), cradle_line_width_ / 2); + Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), current_cradle_line_width / 2); Point2LL back_up = back_center + direction_up_back; Point2LL back_down = back_center - direction_up_back; - line->line_.front() = back_center + normal(direction, cradle_line_width_ / 2 - FUDGE_LENGTH / 2); + line.front() = back_center + normal(direction, current_cradle_line_width / 2 - FUDGE_LENGTH / 2); all_tips_center.emplace_back(center_up, center_down); Polygon line_tip; line_tip.add(back_down); + if(current_cradle_line_width == cradle_line_width_) + { + coord_t distance_end_front = line_length - triangle_length + cradle_line_width_ - tip_shift; + Point2LL line_end_down = back_down + normal(direction, distance_end_front); + Point2LL line_end_up = back_up + normal(direction, distance_end_front); + line_tip.add(line_end_down); + line_tip.add(line_end_up); + } line_tip.add(back_up); line_tip.add(center_up); line_tip.add(center_down); @@ -1293,9 +1318,9 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorarea_.add(line_tip); - line->area_=line->area_.unionPolygons(line->line_.offset(0).offsetPolyLine(cradle_line_width_ / 2, ClipperLib::jtMiter)); - Polygons anti_preferred = line->area_.offset(config_.xy_distance); + cradle_line->area_.add(line_tip); + + Polygons anti_preferred = cradle_line->area_.offset(config_.xy_distance); std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); for(size_t z_distance_idx = 0; z_distance_idx < config_.z_distance_top_layers; z_distance_idx++) { From 5f57b8ec8fd271de9034b80dff2c138579ddb188 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 6 Apr 2024 06:17:00 +0200 Subject: [PATCH 053/101] Fix cradle line distance being wrong sometimes for angled parts of the model. --- src/TreeSupportTipGenerator.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index b1b081148a..c284de2e01 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -656,12 +656,17 @@ std::vector>> TreeSupportTipGenerator::generat continue; } - std::vector accumulated_model(std::min(cradle_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector accumulated_model(std::min(cradle_layers_ + z_distance_delta_, mesh.overhang_areas.size() - layer_idx), Polygons()); std::vector all_pointy_idx{ pointy_info.index }; Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); std::vector additional_centers; TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_); + for(size_t z_distance = 0; z_distance < config_.z_distance_top_layers; z_distance++) + { + accumulated_model[z_distance] = pointy_info.area; + cradle_main->centers_.emplace_back(center_prev); + } Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. bool aborted = false; bool contacted_other_pointy = false; @@ -726,10 +731,10 @@ std::vector>> TreeSupportTipGenerator::generat // To reduce the impact an area is estimated where the cradle should be for these areas. Polygons previous_area = shadow; for (size_t cradle_up_layer_z_distance = cradle_up_layer; - cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size()); + cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size() - config_.z_distance_top_layers); cradle_up_layer_z_distance++) { - accumulated_model[cradle_up_layer_z_distance] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); + accumulated_model[cradle_up_layer_z_distance + config_.z_distance_top_layers] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); } } break; @@ -737,7 +742,7 @@ std::vector>> TreeSupportTipGenerator::generat model_outline = model_outline.unionPolygons(); shadow = shadow.offset(-config_.maximum_move_distance).unionPolygons(model_outline); - accumulated_model[cradle_up_layer] = shadow; + accumulated_model[cradle_up_layer + config_.z_distance_top_layers] = shadow; if(cradle_up_layer > 0) { @@ -804,31 +809,31 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector Date: Sat, 6 Apr 2024 06:50:04 +0200 Subject: [PATCH 054/101] Fix fractional support bug related to tree support caused by merging said changes into the cradle branch. --- src/TreeSupport.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 89b8371d20..0ce18d2f7b 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -3062,7 +3062,11 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - if(data_pair.first->parents_.empty() && !data_pair.first->supports_roof_ && !data_pair.first->supports_cradle_ && layer_idx + 1< support_roof_storage_fractional.size()) + if(data_pair.first->parents_.empty() && + !data_pair.first->supports_roof_ && + !data_pair.first->supports_cradle_ && + layer_idx + 1< support_roof_storage_fractional.size() && + config.z_distance_top % config.layer_height > 0) { if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) { From 96d023a1d1cd54830f7fd8e09bbcfe8f78915ac1 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:07:33 +0200 Subject: [PATCH 055/101] Fix fractional support skin for cradle base areas. --- src/TreeSupport.cpp | 2 +- src/TreeSupportTipGenerator.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 0ce18d2f7b..bfee0d8a5d 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -3064,7 +3064,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou { if(data_pair.first->parents_.empty() && !data_pair.first->supports_roof_ && - !data_pair.first->supports_cradle_ && + !data_pair.first->cradle_line_ && layer_idx + 1< support_roof_storage_fractional.size() && config.z_distance_top % config.layer_height > 0) { diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index c284de2e01..5ac9fb401c 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -2143,6 +2143,7 @@ void TreeSupportTipGenerator::generateTips( for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx].size(); cradle_overhang_idx++) { overhang_processing.emplace_back(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx][cradle_overhang_idx]); + core_overhang = core_overhang.difference(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx][cradle_overhang_idx].overhang_); } } From 50c57cef734b8f31b6909b44b474de0afc790af5 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:59:17 +0200 Subject: [PATCH 056/101] First refactoring to add setting to ensure initial layer diameter can be reached. Work in Progress. --- include/TreeSupportElement.h | 53 ++++------ include/TreeSupportSettings.h | 8 +- include/TreeSupportTipGenerator.h | 35 ++++--- src/TreeModelVolumes.cpp | 13 ++- src/TreeSupport.cpp | 48 ++++++++- src/TreeSupportTipGenerator.cpp | 167 +++++++++++------------------- 6 files changed, 165 insertions(+), 159 deletions(-) diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index ce201c4bf3..0ef63eba2d 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -82,7 +82,8 @@ struct TreeSupportElement bool force_tips_to_roof, bool skip_ovalisation, bool influence_area_limit_active, - coord_t influence_area_limit_range + coord_t influence_area_limit_range, + double hidden_radius_increase ) : target_height_(target_height), target_position_(target_position), @@ -107,40 +108,18 @@ struct TreeSupportElement skip_ovalisation_(skip_ovalisation), all_tips_({ target_position }), influence_area_limit_active_(influence_area_limit_active), - influence_area_limit_range_(influence_area_limit_range - ) + influence_area_limit_range_(influence_area_limit_range), + hidden_radius_increase_(hidden_radius_increase) { RecreateInfluenceLimitArea(); } - TreeSupportElement(const TreeSupportElement& elem, Polygons* newArea = nullptr) : // copy constructor with possibility to set a new area - target_height_(elem.target_height_), - target_position_(elem.target_position_), - next_position_(elem.next_position_), - next_height_(elem.next_height_), - effective_radius_height_(elem.effective_radius_height_), - to_buildplate_(elem.to_buildplate_), - distance_to_top_(elem.distance_to_top_), - area_(newArea != nullptr ? newArea : elem.area_), - result_on_layer_(elem.result_on_layer_), - increased_to_model_radius_(elem.increased_to_model_radius_), - to_model_gracious_(elem.to_model_gracious_), - buildplate_radius_increases_(elem.buildplate_radius_increases_), - use_min_xy_dist_(elem.use_min_xy_dist_), - supports_roof_(elem.supports_roof_), - supports_cradle_(elem.supports_cradle_), - dont_move_until_(elem.dont_move_until_), - can_use_safe_radius_(elem.can_use_safe_radius_), - can_avoid_anti_preferred_(elem.can_avoid_anti_preferred_), - last_area_increase_(elem.last_area_increase_), - missing_roof_layers_(elem.missing_roof_layers_), - skip_ovalisation_(elem.skip_ovalisation_), - all_tips_(elem.all_tips_), - influence_area_limit_area_(elem.influence_area_limit_area_), - influence_area_limit_range_(elem.influence_area_limit_range_), - influence_area_limit_active_(elem.influence_area_limit_active_) + TreeSupportElement(const TreeSupportElement& elem, Polygons* new_area) : // copy constructor that sets a new area + TreeSupportElement(elem) { - parents_.insert(parents_.begin(), elem.parents_.begin(), elem.parents_.end()); + area_ = new_area; + additional_ovalization_targets_.clear(); + cradle_line_ = nullptr; } @@ -198,6 +177,13 @@ struct TreeSupportElement // 'buildplate_radius_increases' has to be recalculated, as when a smaller tree with a larger buildplate_radius_increases merge with a larger branch, // the buildplate_radius_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value. buildplate_radius_increases_ = foot_increase_radius / (branch_radius * (diameter_scale_bp_radius - diameter_angle_scale_factor)); + + const coord_t hidden_increase_radius = std::abs( + std::max( + getRadius(second.effective_radius_height_, second.buildplate_radius_increases_ + second.hidden_radius_increase_), + getRadius(first.effective_radius_height_, first.buildplate_radius_increases_ + first.hidden_radius_increase_)) + - getRadius(effective_radius_height_, buildplate_radius_increases_)); + hidden_radius_increase_ = hidden_increase_radius / (branch_radius * (diameter_scale_bp_radius - diameter_angle_scale_factor)); } // set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior. @@ -359,8 +345,15 @@ struct TreeSupportElement */ std::vector additional_ovalization_targets_; + /*! + * \brief Pointer to the cradle line it supports if it does support a cradle line. + */ std::shared_ptr cradle_line_; + /*! + * \brief Counter about the times the radius was increased to reach the correct initial radius. Uses logic intended for buildplate_radius_increases_ + */ + double hidden_radius_increase_; bool operator==(const TreeSupportElement& other) const diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 1898aa3b77..4fa713c8a5 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -466,7 +466,9 @@ struct TreeSupportSettings /*! * \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of - * the branch. \param elem[in] The SupportElement one wants to know the effectiveDTT \return The Effective DTT. + * the branch. + * \param elem[in] The SupportElement one wants to know the effectiveDTT + * \return The Effective DTT. */ [[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportElement& elem) const { @@ -486,7 +488,7 @@ struct TreeSupportSettings /* tip */ min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : /* base */ branch_radius + - /* gradual increase */ branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) + /* gradual increase */ branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) + branch_radius * buildplate_radius_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0)); return std::min(uncapped_radius, max_radius); } @@ -498,7 +500,7 @@ struct TreeSupportSettings */ [[nodiscard]] inline coord_t getRadius(const TreeSupportElement& elem) const { - return getRadius(getEffectiveDTT(elem), (elem.isResultOnLayerSet() || ! support_rests_on_model) && elem.to_buildplate_ ? elem.buildplate_radius_increases_ : 0); + return getRadius(getEffectiveDTT(elem), elem.hidden_radius_increase_ + ((elem.isResultOnLayerSet() || ! support_rests_on_model) && elem.to_buildplate_ ? elem.buildplate_radius_increases_ : 0)); } /*! diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index aaacfb9cc1..e770124754 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -130,9 +130,9 @@ class TreeSupportTipGenerator * \param mesh[in] The mesh that is currently processed. * \param line_distance[in] The distance between the infill lines of the resulting infill * \param line_width[in] What is the width of a line used in the infill. - * \return A valid CrossInfillProvider. Has to be freed manually to avoid a memory leak. + * \return A valid CrossInfillProvider. */ - std::shared_ptr generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const; + std::shared_ptr getCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width); /*! * \brief Provides areas that do not have a connection to the buildplate or a certain height. @@ -210,6 +210,7 @@ class TreeSupportTipGenerator * \param move_bounds[out] The storage for the tips. * \param p[in] The point that will be added and its LineStatus. * \param dtt[in] The distance to top the added tip will have. + * \param hidden_radius_increase[in] Additional bp increases hidden from the collision calculation. Used to ensure branches are large enough to reach initial layer diameter. * \param insert_layer[in] The layer the tip will be on. * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param roof[in] Whether the tip supports a roof. @@ -218,8 +219,14 @@ class TreeSupportTipGenerator */ TreeSupportElement* addPointAsInfluenceArea( std::vector>& move_bounds, - std::pair p, size_t dtt, LayerIndex insert_layer, size_t dont_move_until, bool roof, - bool cradle, bool skip_ovalisation, + std::pair p, + size_t dtt, + double hidden_radius_increase, + LayerIndex insert_layer, + size_t dont_move_until, + bool roof, + bool cradle, + bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); @@ -229,13 +236,16 @@ class TreeSupportTipGenerator * \param lines[in] The lines of which points will be added. * \param roof_tip_layers[in] Amount of layers the tip should be drawn as roof. * \param insert_layer_idx[in] The layer the tip will be on. + * \param tip_radius[in] Target radius of the tips. * \param supports_roof[in] Whether the tip supports a roof. * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param connect_points [in] If the points of said line should be connected by ovalization. */ void addLinesAsInfluenceAreas(std::vector>& move_bounds, std::vector lines, - size_t roof_tip_layers, LayerIndex insert_layer_idx, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + coord_t tip_radius, OverhangInformation& overhang_data, size_t dont_move_until, bool connect_points); @@ -279,14 +289,9 @@ class TreeSupportTipGenerator const size_t support_roof_layers_; /*! - * \brief Distance between tips, so that the tips form a line. Is smaller than Tip Diameter. + * \brief Density the tips should have when supporting overhangs */ - const coord_t connect_length_; - - /*! - * \brief Distance between tips, if the tips support an overhang. - */ - const coord_t support_tree_branch_distance_; + const double support_tree_top_rate_; /*! * \brief Distance between support roof lines. Is required for generating roof patterns. @@ -356,9 +361,9 @@ class TreeSupportTipGenerator coord_t support_supporting_branch_distance_; /*! - * \brief Required to generate cross infill patterns + * \brief Required to generate cross infill patterns. Key: Distance between lines */ - std::shared_ptr cross_fill_provider_; + std::unordered_map, std::shared_ptr> cross_fill_providers_; /*! * \brief Map that saves locations of already inserted tips. Used to prevent tips far to close together from being added. @@ -457,6 +462,8 @@ class TreeSupportTipGenerator std::mutex critical_cradle_; std::mutex critical_move_bounds_; std::mutex critical_roof_tips_; + std::mutex critical_cross_fill_; + mutable std::vector> floating_parts_cache_; mutable std::vector>> floating_parts_map_; std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 6191a574d9..1f1551b09f 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -210,7 +210,7 @@ void TreeModelVolumes::precalculate(LayerIndex max_layer) // but as for every branch going towards the bp, the to model avoidance is required to check for possible merges with to model branches, this assumption is in-fact wrong. std::unordered_map radius_until_layer; // while it is possible to calculate, up to which layer the avoidance should be calculated, this simulation is easier to understand, and does not need to be adjusted if - // something of the radius calculation is changed. Tested overhead was neligable (milliseconds for thounds of layers). + // something of the radius calculation is changed. Tested overhead was negligible (milliseconds for thousands of layers). for (LayerIndex simulated_dtt = 0; simulated_dtt <= max_layer; simulated_dtt++) { const LayerIndex current_layer = max_layer - simulated_dtt; @@ -225,9 +225,16 @@ void TreeModelVolumes::precalculate(LayerIndex max_layer) { radius_until_layer[max_min_radius] = std::min(current_layer + max_cradle_dtt, max_layer); } - if (max_initial_layer_diameter_radius > max_min_radius && ! radius_until_layer.count(max_initial_layer_diameter_radius)) + + // all radiis between max_min_radius and max_initial_layer_diameter_radius can also occur + coord_t current_ceil_radius = max_min_radius; + while (current_ceil_radius < max_initial_layer_diameter_radius) { - radius_until_layer[max_initial_layer_diameter_radius] = std::min(current_layer + max_cradle_dtt, max_layer); + current_ceil_radius = ceilRadius(current_ceil_radius + 1); + if (! radius_until_layer.count(current_ceil_radius)) + { + radius_until_layer[current_ceil_radius] = std::min(current_layer + max_cradle_dtt, max_layer); + } } } diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index bfee0d8a5d..2d2875e8da 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -930,6 +930,52 @@ std::optional TreeSupport::increaseSingleArea( } radius = config.getCollisionRadius(current_elem); + //If a hidden radius increase was used, also do some catching up. + + if (current_elem.hidden_radius_increase_ > 0) + { + coord_t target_radius = config.getRadius(current_elem); + coord_t current_ceil_radius = volumes_.getRadiusNextCeil(radius, settings.use_min_distance_); + + while (current_ceil_radius < target_radius && validWithRadius(volumes_.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance_))) + { + current_ceil_radius = volumes_.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance_); + } + double resulting_hidden_increases = current_elem.hidden_radius_increase_; + while (resulting_hidden_increases > 0 + && config.getRadius(current_elem.effective_radius_height_, resulting_hidden_increases + current_elem.buildplate_radius_increases_) <= current_ceil_radius + && config.getRadius(current_elem.effective_radius_height_, resulting_hidden_increases + current_elem.buildplate_radius_increases_) <= config.getRadius(current_elem)) + { + resulting_hidden_increases--; + } + double bp_increases = current_elem.hidden_radius_increase_ - std::max(0.0, resulting_hidden_increases); + current_elem.hidden_radius_increase_ = std::max(0.0, resulting_hidden_increases); + current_elem.buildplate_radius_increases_ += bp_increases; + + if (current_elem.hidden_radius_increase_ > 0) + { + Polygons new_to_bp_data; + Polygons new_to_model_data; + + if (current_elem.to_buildplate_) + { + new_to_bp_data = to_bp_data.difference(volumes_.getCollision(config.getRadius(current_elem), layer_idx - 1, current_elem.use_min_xy_dist_)); + if (new_to_bp_data.area() > EPSILON) + { + to_bp_data = new_to_bp_data; + } + } + if (config.support_rests_on_model && (! current_elem.to_buildplate_ || mergelayer)) + { + new_to_model_data = to_model_data.difference(volumes_.getCollision(config.getRadius(current_elem), layer_idx - 1, current_elem.use_min_xy_dist_)); + if (new_to_model_data.area() > EPSILON) + { + to_model_data = new_to_model_data; + } + } + } + } + const coord_t foot_radius_increase = config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)); const double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase); // ^^^ Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become @@ -946,7 +992,6 @@ std::optional TreeSupport::increaseSingleArea( const bool increase_bp_foot = planned_foot_increase > 0 && (current_elem.to_buildplate_ || (current_elem.to_model_gracious_ && config.support_rest_preference == RestPreference::GRACEFUL)); - if (increase_bp_foot && config.getRadius(current_elem) >= config.branch_radius && config.getRadius(current_elem) >= config.increase_radius_until_radius) { if (validWithRadius(config.getRadius(current_elem.effective_radius_height_, current_elem.buildplate_radius_increases_ + planned_foot_increase))) @@ -3058,6 +3103,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } }); + //todo check if roof actually contains lines todo disable ovalisation for roof tips for (const auto layer_idx : ranges::views::iota(0UL, layer_tree_polygons.size())) { for (std::pair data_pair : layer_tree_polygons[layer_idx]) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 5ac9fb401c..3e25ac6699 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -38,9 +38,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config_.layer_height) : use_fake_roof_ ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0) - , connect_length_( - (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + std::max(2 * config_.min_radius - 1.0 * config_.support_line_width, 0.0)) - , support_tree_branch_distance_((config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length_) + , support_tree_top_rate_( mesh.settings.get("support_tree_top_rate")) , support_roof_line_distance_( use_fake_roof_ ? (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) @@ -104,7 +102,8 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T // would take to travel xy_distance, nothing reasonable will come from it. The 2*z_distance_delta is only a catch for when the support angle is very high. } - cross_fill_provider_ = generateCrossFillProvider(mesh, support_tree_branch_distance_, config_.support_line_width); + coord_t connect_length = (config_.support_line_width * 100 / support_tree_top_rate_) + std::max(2 * config_.min_radius - 1.0 * config_.support_line_width, 0.0); + coord_t support_tree_branch_distance = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; std::vector known_z(mesh.layers.size()); @@ -438,10 +437,17 @@ Polygons TreeSupportTipGenerator::ensureMaximumDistancePolyline(const Polygons& } -std::shared_ptr TreeSupportTipGenerator::generateCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) const +std::shared_ptr TreeSupportTipGenerator::getCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width) { if (config_.support_pattern == EFillMethod::CROSS || config_.support_pattern == EFillMethod::CROSS_3D) { + std::pair key = std::pair(line_distance, line_width); + if(cross_fill_providers_.contains(key)) + { + return cross_fill_providers_[key]; + } + std::lock_guard critical_section_generate_cross_fill(critical_cross_fill_); + AABB3D aabb; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { @@ -791,7 +797,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorgetCenter(layer_idx + idx); - const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; + const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; // todo fix crash out of bounds const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; if(cradle->lines_.empty()) @@ -1750,10 +1756,12 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( std::vector>& move_bounds, std::pair p, size_t dtt, + double hidden_radius_increase, LayerIndex insert_layer, size_t dont_move_until, bool roof, - bool cradle, bool skip_ovalisation, + bool cradle, + bool skip_ovalisation, std::vector additional_ovalization_targets) { const bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; @@ -1784,14 +1792,15 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( to_bp, gracious, ! xy_overrides_, - dont_move_until, + dont_move_until + dtt, roof, cradle, safe_radius, ! roof && force_tip_to_roof_, skip_ovalisation, support_tree_limit_branch_reach_, - support_tree_branch_reach_limit_); + support_tree_branch_reach_limit_, + hidden_radius_increase); elem->area_ = new Polygons(area); for (Point2LL target : additional_ovalization_targets) { @@ -1810,95 +1819,17 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector lines, size_t roof_tip_layers, LayerIndex insert_layer_idx, + coord_t tip_radius, OverhangInformation& overhang_data, size_t dont_move_until, bool connect_points) { - std::vector inserted_elements; - // Add tip area as roof (happens when minimum roof area > minimum tip - // area) if possible. This is required as there is no guarantee that if support_roof_wall_count == 0 that a - // certain roof area will actually have lines. - size_t dtt_roof_tip = 0; - if (config_.support_roof_wall_count == 0 && ! overhang_data.is_cradle_) //todo cradle base maybe - { - for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; dtt_roof_tip++) - { - std::function)> evaluateRoofWillGenerate = [&](std::pair p) - { - Polygon roof_circle; - for (Point2LL corner : TreeSupportBaseCircle::getBaseCircle()) - { - roof_circle.add(p.first + corner * std::max(config_.min_radius / TreeSupportBaseCircle::base_radius, coord_t(1))); - } - Polygons area = roof_circle.offset(0); - return ! TreeSupportUtils::generateSupportInfillLines(area, config_, true, insert_layer_idx - dtt_roof_tip, support_roof_line_distance_, cross_fill_provider_, true) - .empty(); - }; - - std::pair, std::vector> split - = splitLines(lines, getEvaluatePointForNextLayerFunction(insert_layer_idx - dtt_roof_tip)); // Keep all lines that are still valid on the next layer. - - for (LineInformation line : split.second) // Add all points that would not be valid. - { - for (std::pair point_data : line) - { - TreeSupportElement* elem = addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); - if(elem) - { - inserted_elements.emplace_back(elem); - } - } - } - - // Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points. - split = splitLines(split.first, evaluateRoofWillGenerate); - lines = split.first; - - for (LineInformation line : split.second) - { - for (std::pair point_data : line) - { - TreeSupportElement* elem = addPointAsInfluenceArea(move_bounds, point_data, 0, insert_layer_idx - dtt_roof_tip, roof_tip_layers - dtt_roof_tip, dtt_roof_tip != 0, false, false); - if(elem) - { - inserted_elements.emplace_back(elem); - } - } - } - - // Add all tips as roof to the roof storage. - Polygons added_roofs; - for (LineInformation line : lines) - { - for (std::pair p : line) - { - Polygon roof_circle; - for (Point2LL corner : TreeSupportBaseCircle::getBaseCircle()) - { - roof_circle.add(p.first + corner * std::max(config_.min_radius / TreeSupportBaseCircle::base_radius, coord_t(1))); - } - added_roofs.add(roof_circle); - } - } - added_roofs = added_roofs.unionPolygons(); - { - std::lock_guard critical_section_roof(critical_roof_tips_); - roof_tips_drawn_[insert_layer_idx - dtt_roof_tip].add(added_roofs); - - if (dtt_roof_tip == 0) - { - support_roof_drawn_fractional_[insert_layer_idx].add(added_roofs); - } - } - } - } - for (LineInformation line : lines) { // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern. // Ovalisation should be disabled if they may be placed close to each other to prevent tip-areas merging. If the tips has to turn into roof, the area is most likely not - // large enough for this to cause issues. - const bool disable_ovalization = ! connect_points && config_.min_radius < 3 * config_.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0; + // large enough for this to cause issues. todo does this still make sense? + const bool disable_ovalization = ! connect_points && config_.min_radius < 3 * config_.support_line_width && roof_tip_layers == 0; for (auto [idx, point_data] : line | ranges::views::enumerate) { std::vector additional_ovalization_targets; @@ -1914,29 +1845,37 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector= config_.branch_radius ? config_.tip_layers : + (config_.tip_layers * (tip_radius - config_.min_radius))/(config_.branch_radius - config_.min_radius); + + coord_t hidden_radius = tip_radius > config_.branch_radius ? tip_radius - config_.branch_radius : 0; + double hidden_radius_increases = hidden_radius / (config_.branch_radius * (std::max(config_.diameter_scale_bp_radius - config_.diameter_angle_scale_factor, 0.0))); + TreeSupportElement* elem = addPointAsInfluenceArea( move_bounds, point_data, tip_dtt, - insert_layer_idx - dtt_roof_tip, - dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - dtt_roof_tip != 0 || overhang_data.is_roof_, overhang_data.is_cradle_, disable_ovalization, + hidden_radius_increases, + insert_layer_idx, + dont_move_until, + overhang_data.is_roof_, + overhang_data.is_cradle_, + disable_ovalization, additional_ovalization_targets); if(elem) { if(overhang_data.isCradleLine()) { - elem->cradle_line_ = std::make_shared(overhang_data.cradle_,overhang_data.cradle_layer_idx_,overhang_data.cradle_line_idx_); + elem->cradle_line_ = std::make_shared(overhang_data.cradle_, overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); } } } @@ -2081,6 +2020,11 @@ void TreeSupportTipGenerator::generateTips( return; // This is a continue if imagined in a loop context. } + coord_t current_tip_radius = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx)); //todo make setting + coord_t connect_length = (config_.support_line_width * 100 / support_tree_top_rate_) + std::max(2 * current_tip_radius - 1.0 * config_.support_line_width, 0.0); + coord_t support_tree_branch_distance = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; + + Polygons relevant_forbidden = volumes_.getAvoidance( config_.getRadius(0), layer_idx, @@ -2096,8 +2040,11 @@ void TreeSupportTipGenerator::generateTips( std::function generateLines = [&](const Polygons& area, bool roof, bool cradle, LayerIndex generate_layer_idx) { + //todo ensure larger tips have reasonable density. How would one do that though? + // If tips are 7mm thick, does 20% fill mean a distance of 35mm between tips? Does not make sense... coord_t upper_line_distance = support_supporting_branch_distance_; - coord_t line_distance = std::max(roof ? support_roof_line_distance_ : support_tree_branch_distance_, upper_line_distance); + coord_t line_distance = std::max(roof ? support_roof_line_distance_ : support_tree_branch_distance, upper_line_distance); + coord_t current_tip_radius_generate = std::max(config_.min_radius, config_.recommendedMinRadius(generate_layer_idx)); //todo make setting bool use_grid = line_distance == upper_line_distance; @@ -2107,7 +2054,7 @@ void TreeSupportTipGenerator::generateTips( roof && ! use_fake_roof_, generate_layer_idx, line_distance, - cross_fill_provider_, + roof? nullptr : getCrossFillProvider(mesh, line_distance, current_tip_radius_generate), (roof && ! use_fake_roof_) || cradle, use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; @@ -2197,7 +2144,7 @@ void TreeSupportTipGenerator::generateTips( } all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons(); std::vector overhang_lines; - Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, false, layer_idx), config_.min_radius, 1, false); + Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, false, layer_idx), current_tip_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. // Technically this violates branch distance, but not only is this the only reasonable choice, // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. @@ -2205,7 +2152,7 @@ void TreeSupportTipGenerator::generateTips( if (polylines.pointCount() <= 3) { // Add the outer wall to ensure it is correct supported instead. - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(remaining_overhang_part), connect_length_, 3, true); + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(remaining_overhang_part), connect_length, 3, true); } for (auto line : polylines) @@ -2220,6 +2167,9 @@ void TreeSupportTipGenerator::generateTips( for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag_ && ! overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) { + LayerIndex layer_idx_lag = layer_idx - lag_ctr; + coord_t current_tip_radius_lag = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx_lag)); //todo setting + // get least restricted avoidance for layer_idx-lag_ctr Polygons relevant_forbidden_below = volumes_.getAvoidance( config_.getRadius(0), @@ -2253,7 +2203,7 @@ void TreeSupportTipGenerator::generateTips( OverhangInformation dummy_overhang_info(remaining_overhang_part,false); addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof_ && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, - layer_idx - lag_ctr, dummy_overhang_info, support_roof_layers_, false); + layer_idx - lag_ctr, current_tip_radius_lag, dummy_overhang_info, support_roof_layers_, false); } } } @@ -2268,7 +2218,7 @@ void TreeSupportTipGenerator::generateTips( for (OverhangInformation overhang_data : overhang_processing) { Polygons overhang_outset = overhang_data.overhang_; - const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length_)); + const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); std::vector overhang_lines; bool only_lines = true; @@ -2291,7 +2241,7 @@ void TreeSupportTipGenerator::generateTips( generateLines(overhang_outset, overhang_data.is_roof_, overhang_data.is_cradle_, layer_idx + overhang_data.is_roof_), ! overhang_data.is_roof_ ? config_.min_radius * 2 : use_fake_roof_ ? support_supporting_branch_distance_ - : connect_length_, + : connect_length, 1, false); } @@ -2313,13 +2263,13 @@ void TreeSupportTipGenerator::generateTips( // (If this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback, as some support is // better than none.) if (! reduced_overhang_outset.empty() - && overhang_outset.difference(reduced_overhang_outset.offset(std::max(config_.support_line_width, connect_length_))).area() < 1) + && overhang_outset.difference(reduced_overhang_outset.offset(std::max(config_.support_line_width, connect_length))).area() < 1) { - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(reduced_overhang_outset), connect_length_, min_support_points, true); + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(reduced_overhang_outset), connect_length, min_support_points, true); } else { - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(overhang_outset), connect_length_, min_support_points, true); + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(overhang_outset), connect_length, min_support_points, true); } } @@ -2333,7 +2283,7 @@ void TreeSupportTipGenerator::generateTips( if (overhang_lines.empty()) // some error handling and logging { Polygons enlarged_overhang_outset = overhang_outset.offset(config_.getRadius(0) + FUDGE_LENGTH / 2, ClipperLib::jtRound).difference(relevant_forbidden); - polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(enlarged_overhang_outset), connect_length_, min_support_points, true); + polylines = ensureMaximumDistancePolyline(TreeSupportUtils::toPolylines(enlarged_overhang_outset), connect_length, min_support_points, true); overhang_lines = convertLinesToInternal(polylines, layer_idx); if (! overhang_lines.empty()) @@ -2353,6 +2303,7 @@ void TreeSupportTipGenerator::generateTips( overhang_lines, force_tip_to_roof_ ? support_roof_layers_ : 0, layer_idx, + current_tip_radius, overhang_data, dont_move_for_layers, only_lines); @@ -2374,7 +2325,7 @@ void TreeSupportTipGenerator::generateTips( // technically there is no guarantee that a drawn roof tip has lines, as it could be unioned with another roof area that has, but this has to be enough // hopefully. if (layer_idx < additional_support_areas.size() - && TreeSupportUtils::generateSupportInfillLines(roof_area, config_, true, layer_idx, support_roof_line_distance_, cross_fill_provider_, false).empty()) + && TreeSupportUtils::generateSupportInfillLines(roof_area, config_, true, layer_idx, support_roof_line_distance_, nullptr, false).empty()) { additional_support_areas[layer_idx].add(roof_area); } From f9e0bc7260a63c415b0709e6cd9bd24cf7f51718 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 9 Apr 2024 09:00:20 +0200 Subject: [PATCH 057/101] Fix initial layer diameter not working --- include/TreeSupportSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 4fa713c8a5..bd6cd642bb 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -70,7 +70,7 @@ struct TreeSupportSettings : RestPreference::BUILDPLATE) , xy_distance(mesh_group_settings.get("support_xy_distance")) , bp_radius(mesh_group_settings.get("support_tree_bp_diameter") / 2) - , diameter_scale_bp_radius(std::min(sin(0.7) * static_cast(layer_height / branch_radius), 1.0 / (branch_radius / (support_line_width / 2.0)))) + , diameter_scale_bp_radius(std::min(sin(0.7) * static_cast(layer_height) / static_cast(branch_radius), 1.0 / (branch_radius / (support_line_width / 2.0)))) , // Either 40° or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. support_overrides(mesh_group_settings.get("support_xy_overrides_z")) , xy_min_distance(support_overrides == SupportDistPriority::Z_OVERRIDES_XY ? mesh_group_settings.get("support_xy_distance_overhang") : xy_distance) From 6d44415de67ebbcd36c2f5d9d6ba8ab12561aba8 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:46:50 +0200 Subject: [PATCH 058/101] Fix using wrong fillInfillParts to add tree support areas. --- src/TreeSupport.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 2d2875e8da..1defa08a2b 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2933,10 +2933,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. storage.support.supportLayers[layer_idx].fillInfillParts( - layer_idx, - support_layer_storage, - config.layer_height, - storage.meshes, + support_layer_storage[layer_idx], config.support_line_width, config.support_wall_count, false, From 057ed3631ca23bce276cb2536d475b369bf576dd Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 20 Apr 2024 03:02:25 +0200 Subject: [PATCH 059/101] Enable different amount of roof wall counts for tree support. --- include/FffGcodeWriter.h | 5 +- include/TreeSupport.h | 13 +- include/TreeSupportElement.h | 7 + include/TreeSupportSettings.h | 2 +- include/TreeSupportTipGenerator.h | 35 ++-- include/TreeSupportUtils.h | 6 +- include/sliceDataStorage.h | 49 +++++- src/FffGcodeWriter.cpp | 266 +++++++++++++++------------- src/PrimeTower.cpp | 2 +- src/SkirtBrim.cpp | 13 +- src/TreeSupport.cpp | 283 +++++++++++++++++++----------- src/TreeSupportTipGenerator.cpp | 133 +++++++------- src/bridge.cpp | 7 +- src/sliceDataStorage.cpp | 24 +-- src/support.cpp | 13 +- 15 files changed, 496 insertions(+), 362 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 251712c8fc..a1ef36b4d3 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -636,12 +636,11 @@ class FffGcodeWriter : public NoCopy * layer. * * \param[in] storage Where the slice data is stored. - * \param[in] support_roof_outlines which polygons to generate roofs for. - * \param[in] current_roof_config config to be used. + * \param[in] support_roof_outlines Collection of polygons to generate roofs for. * \param gcodeLayer The initial planning of the g-code of the layer. * \return Whether any support skin was added to the layer plan. */ - bool addSupportRoofsToGCode(const SliceDataStorage& storage, const Polygons& support_roof_outlines, const GCodePathConfig& current_roof_config, LayerPlan& gcode_layer) const; + bool addSupportRoofsToGCode(const SliceDataStorage& storage, const std::vector& support_roof_outlines, LayerPlan& gcode_layer) const; /*! * Add the support bottoms to the layer plan \p gcodeLayer of the current diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 41fe0ef164..4a58c60ebf 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -318,10 +318,12 @@ class TreeSupport /*! * \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. - * * \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_roof_storage[in,out] Areas where support was replaced with roof. + * \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 cradle_data[in] All currently existing cradles, with its corresponding cradle lines. @@ -330,7 +332,9 @@ class TreeSupport void generateSupportSkin(std::vector& support_layer_storage, 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, SliceDataStorage& storage, std::vector>& layer_tree_polygons, std::vector>& cradle_data); @@ -369,11 +373,6 @@ class TreeSupport */ std::vector>> grouped_meshes; - /*! - * \brief Areas that should have been support roof, but where the roof settings would not allow any lines to be generated. Can also be other placed lines, e.g. Cradles - */ - std::vector additional_required_support_area; - /*! * \brief Areas that use a higher density pattern of regular support to support the model (fake_roof). */ diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index 0ef63eba2d..f8a386aaf9 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -105,6 +105,7 @@ struct TreeSupportElement can_avoid_anti_preferred_(false), //todo init? last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)), missing_roof_layers_(force_tips_to_roof ? dont_move_until : 0), + roof_with_enforced_walls(false), skip_ovalisation_(skip_ovalisation), all_tips_({ target_position }), influence_area_limit_active_(influence_area_limit_active), @@ -145,6 +146,7 @@ struct TreeSupportElement 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_)), + roof_with_enforced_walls(first.roof_with_enforced_walls && second.roof_with_enforced_walls), skip_ovalisation_(false) { if (first.target_height_ > second.target_height_) @@ -315,6 +317,11 @@ struct TreeSupportElement */ size_t missing_roof_layers_; + /*! + * \brief True if interface with walls has to be used, even though regular interface does not have walls. + */ + bool roof_with_enforced_walls; + /*! * \brief Skip the ovalisation to parent and children when generating the final circles. */ diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index bd6cd642bb..004884e6f7 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -98,7 +98,7 @@ struct TreeSupportSettings , min_feature_size(mesh_group_settings.get("min_feature_size")) , min_wall_line_width(settings.get("min_wall_line_width")) , fill_outline_gaps(settings.get("fill_outline_gaps")) - , support_skin_layers(retrieveSetting(mesh_group_settings,"support_tree_support_skin_height")/layer_height) + , support_skin_layers(round_up_divide(retrieveSetting(mesh_group_settings,"support_tree_support_skin_height"), layer_height)) , support_skin_line_distance(retrieveSetting(mesh_group_settings,"support_tree_support_skin_line_distance")) , support_tree_skin_for_large_tips_radius_threshold(retrieveSetting(mesh_group_settings,"support_tree_skin_for_large_tips_threshold") / 2) , simplifier(Simplify(mesh_group_settings)) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index e770124754..c74bf0514c 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -33,7 +33,6 @@ class TreeSupportTipGenerator * \param storage[in] Background storage, required for adding roofs. * \param mesh[in] The mesh that is currently processed. Contains the overhangs. * \param move_bounds[out] The storage for the tips. - * \param additional_support_areas[out] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. Should already be initialised. * \param placed_support_lines_support_areas[out] Support-lines that were already placed represented as the area the lines will take when printed. * \param support_free_areas[out] Areas where no support (including roof) of any kind is to be drawn. * \param cradle_data_export[out] Generated cradle lines. @@ -43,7 +42,6 @@ class TreeSupportTipGenerator SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector>& move_bounds, - std::vector& additional_support_areas, std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, std::vector>& cradle_data_export); @@ -59,6 +57,13 @@ class TreeSupportTipGenerator TO_BP_SAFE }; + enum class TipRoofType + { + NONE, + SUPPORTS_ROOF, + IS_ROOF + }; + struct UnsupportedAreaInformation { UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) : @@ -224,7 +229,7 @@ class TreeSupportTipGenerator double hidden_radius_increase, LayerIndex insert_layer, size_t dont_move_until, - bool roof, + TipRoofType roof, bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets = std::vector()); @@ -254,9 +259,8 @@ class TreeSupportTipGenerator * \brief Remove tips that should not have been added in the first place. * \param move_bounds[in,out] The already added tips * \param storage[in] Background storage, required for adding roofs. - * \param additional_support_areas[in] Areas that should have been roofs, but are now support, as they would not generate any lines as roof. */ - void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector& additional_support_areas); + void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage); /*! * \brief Contains config settings to avoid loading them in every function. This was done to improve readability of the code. @@ -309,11 +313,6 @@ class TreeSupportTipGenerator */ const coord_t roof_outset_; - /*! - * \brief Whether tips should be printed as roof - */ - const bool force_tip_to_roof_; - /*! * \brief Whether the maximum distance a branch should from a point they support should be limited. Can be violated if required. */ @@ -339,10 +338,6 @@ class TreeSupportTipGenerator */ size_t max_overhang_insert_lag_; - /*! - * \brief Area of a tip in mm2. - */ - const double tip_roof_size_; /*! * \brief Whether only support that can rest on a flat surface should be supported. @@ -355,6 +350,11 @@ class TreeSupportTipGenerator */ const bool force_minimum_roof_area_ = SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT; + /*! + * \brief Whether tips should be larger to enable reaching initial_layer_diameter. + */ + const bool force_initial_layer_radius_; + /*! * \brief Distance between branches when the branches support a support pattern */ @@ -376,16 +376,13 @@ class TreeSupportTipGenerator std::vector support_roof_drawn_; /*! - * \brief Areas that will be saved as support roof + * \brief Areas that should have fractional roof above it. */ std::vector support_roof_drawn_fractional_; /*! - * \brief Areas that will be saved as support roof, originating from tips being replaced with roof areas. + * \brief Representation of all cradles ordered by layer_idx. */ - std::vector roof_tips_drawn_; - - std::vector> cradle_data_; /*! diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 6b36f6c0f7..26318f2b9c 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -92,7 +92,7 @@ class TreeSupportUtils * \param layer_idx[in] The current layer index. * \param support_infill_distance[in] The distance that should be between the infill lines. * \param cross_fill_provider[in] A SierpinskiFillProvider required for cross infill. - * \param include_walls[in] If the result should also contain walls, or only the infill. + * \param wall_count[in] Amount of walls the result should contain. * \param special_pattern[in] Use a different pattern. None means the default pattern as in config will be used. * \param disable_connect[in] If the connecting of Infill lines has to be disabled. * \return A Polygons object that represents the resulting infill lines. @@ -103,7 +103,7 @@ class TreeSupportUtils bool roof, LayerIndex layer_idx, coord_t support_infill_distance, - std::shared_ptr cross_fill_provider, bool include_walls, EFillMethod special_pattern = EFillMethod::NONE, bool disable_connect = false) + std::shared_ptr cross_fill_provider, size_t wall_count, EFillMethod special_pattern = EFillMethod::NONE, bool disable_connect = false) { Polygons gaps; // As we effectively use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. @@ -115,7 +115,7 @@ class TreeSupportUtils constexpr coord_t support_roof_overlap = 0; constexpr size_t infill_multiplier = 1; const int support_shift = roof ? 0 : support_infill_distance / 2; - const size_t wall_line_count = include_walls ? (! roof ? config.support_wall_count : config.support_roof_wall_count) : 0; + const size_t wall_line_count = wall_count; constexpr coord_t narrow_area_width = 0; const Point2LL infill_origin; constexpr bool skip_stitching = false; diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 85cd39dc7f..f26f1f70ab 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -209,28 +209,41 @@ class SliceLayer class SupportLayer { public: + enum class PartsFilter{NoFilter, RegularParts, FractionalParts}; std::vector support_infill_parts; //!< a list of support infill parts Polygons support_bottom; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof. - Polygons support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. - // NOTE: This is _all_ of the support_roof, and as such, overlaps with support_fractional_roof! - Polygons support_fractional_roof; //!< If the support distance is not exactly a multiple of the layer height, - // the first part of support just underneath the model needs to be printed at a fracional layer height. + std::vector support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. Polygons support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support Polygons support_mesh; //!< Areas from support meshes which should NOT be supported by more support Polygons anti_overhang; //!< Areas where no overhang should be detected. + Polygons getTotalAreaFromParts(const std::vector& parts, PartsFilter filter = PartsFilter::NoFilter) const + { + Polygons result; + for (const SupportInfillPart& part : parts) + { + if(filter == PartsFilter::NoFilter || + (part.use_fractional_config_ && filter == PartsFilter::FractionalParts) || + (!part.use_fractional_config_ && filter == PartsFilter::RegularParts)) + { + result.add(part.outline_); + } + } + return result.unionPolygons(); + } + /*! * Exclude the given polygons from the support infill areas and update the SupportInfillParts. * + * \param parts[in,out] The vector of support parts from which the excluded polygons should be removed from. * \param exclude_polygons The polygons to exclude * \param exclude_polygons_boundary_box The boundary box for the polygons to exclude */ - void excludeAreasFromSupportInfillAreas(const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box); + void excludeAreasFromSupportParts(std::vector& parts, const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box); /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. * * \param area The support polygon to fill up with infill parts. - * \param support_fill_per_layer The support polygons to fill up with infill parts. * \param support_line_width Line width of the support extrusions. * \param wall_line_count Wall-line count around the fill. * \param use_fractional_config (optional, default to false) If the area should be added as fractional support. @@ -252,6 +265,30 @@ class SupportLayer } } + /* Fill up the roof parts for the support with the given support polygons. The support polygons will be split into parts. + * + * \param area The support polygon to fill up with infill parts. + * \param support_line_width Line width of the support extrusions. + * \param wall_line_count Wall-line count around the fill. + * \param use_fractional_config (optional, default to false) If the area should be added as fractional support. + * \param unionAll (optional, default to false) Wether to 'union all' for the split into parts bit. + * \param custom_line_distance (optional, default to 0) Distance between lines of the infill pattern. custom_line_distance of 0 means use the default instead. + * \param custom_pattern (optional, default to EFillMethod::NONE) Set if a non default infill pattern should be used + */ + void fillRoofParts(const Polygons& area, + const coord_t support_line_width, + const coord_t wall_line_count, + const bool use_fractional_config = false, + const bool unionAll = false, + const coord_t custom_line_distance = 0, + EFillMethod custom_pattern = EFillMethod::NONE) + { + for (const PolygonsPart& island_outline : area.splitIntoParts(unionAll)) + { + support_roof.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count, custom_line_distance, custom_pattern); + } + } + /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. This also takes into account fractional-height * support layers. * diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9f8c868314..87bcf4c6dd 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2639,10 +2639,11 @@ bool FffGcodeWriter::processInsets( if (! support_layer.support_roof.empty()) { - AABB support_roof_bb(support_layer.support_roof); + Polygons roof = support_layer.getTotalAreaFromParts(support_layer.support_roof); + AABB support_roof_bb(roof); if (boundaryBox.hit(support_roof_bb)) { - outlines_below.add(support_layer.support_roof); + outlines_below.add(roof); } } else @@ -3054,10 +3055,11 @@ void FffGcodeWriter::processTopBottom( if (! support_layer->support_roof.empty()) { - AABB support_roof_bb(support_layer->support_roof); + Polygons roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); + AABB support_roof_bb(roofs); if (skin_bb.hit(support_roof_bb)) { - supported = ! skin_part.skin_fill.intersection(support_layer->support_roof).empty(); + supported = ! skin_part.skin_fill.intersection(roofs).empty(); } } else @@ -3333,20 +3335,12 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, LayerPla if (extruder_nr == support_roof_extruder_nr) { - support_added |= addSupportRoofsToGCode(storage, support_layer.support_fractional_roof, gcode_layer.configs_storage_.support_fractional_roof_config, gcode_layer); + support_added |= addSupportRoofsToGCode(storage, support_layer.support_roof, gcode_layer); } if (extruder_nr == support_infill_extruder_nr) { support_added |= processSupportInfill(storage, gcode_layer); } - if (extruder_nr == support_roof_extruder_nr) - { - support_added |= addSupportRoofsToGCode( - storage, - support_layer.support_roof.difference(support_layer.support_fractional_roof), - gcode_layer.configs_storage_.support_roof_config, - gcode_layer); - } if (extruder_nr == support_bottom_extruder_nr) { support_added |= addSupportBottomsToGCode(storage, gcode_layer); @@ -3665,8 +3659,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer bool FffGcodeWriter::addSupportRoofsToGCode( const SliceDataStorage& storage, - const Polygons& support_roof_outlines, - const GCodePathConfig& current_roof_config, + const std::vector& support_roof_outlines, LayerPlan& gcode_layer) const { const SupportLayer& support_layer = storage.support.supportLayers[std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr())]; @@ -3679,123 +3672,144 @@ bool FffGcodeWriter::addSupportRoofsToGCode( const size_t roof_extruder_nr = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("support_roof_extruder_nr").extruder_nr_; const ExtruderTrain& roof_extruder = Application::getInstance().current_slice_->scene.extruders[roof_extruder_nr]; - const EFillMethod pattern = roof_extruder.settings_.get("support_roof_pattern"); - AngleDegrees fill_angle = 0; - if (! storage.support.support_roof_angles.empty()) + bool added_support = false; + + for(bool fractional : {true,false}) // Add all fractional roofs first. { - // handle negative layer numbers - int divisor = static_cast(storage.support.support_roof_angles.size()); - int index = ((gcode_layer.getLayerNr() % divisor) + divisor) % divisor; - fill_angle = storage.support.support_roof_angles.at(index); - } - const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG; - const bool connect_polygons = false; // connections might happen in mid air in between the infill lines - constexpr coord_t support_roof_overlap = 0; // the roofs should never be expanded outwards - constexpr size_t infill_multiplier = 1; - constexpr coord_t extra_infill_shift = 0; - const auto wall_line_count = roof_extruder.settings_.get("support_roof_wall_count"); - const coord_t small_area_width = roof_extruder.settings_.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. - const Point2LL infill_origin; - constexpr bool skip_stitching = false; - constexpr bool fill_gaps = true; - constexpr bool use_endpieces = true; - constexpr bool connected_zigzags = false; - constexpr bool skip_some_zags = false; - constexpr size_t zag_skip_count = 0; - constexpr coord_t pocket_size = 0; - const coord_t max_resolution = roof_extruder.settings_.get("meshfix_maximum_resolution"); - const coord_t max_deviation = roof_extruder.settings_.get("meshfix_maximum_deviation"); + for(const SupportInfillPart& roof_part: support_roof_outlines) + { - coord_t support_roof_line_distance = roof_extruder.settings_.get("support_roof_line_distance"); - const coord_t support_roof_line_width = roof_extruder.settings_.get("support_roof_line_width"); - if (gcode_layer.getLayerNr() == 0 && support_roof_line_distance < 2 * support_roof_line_width) - { // if roof is dense - support_roof_line_distance *= roof_extruder.settings_.get("initial_layer_line_width_factor"); - } + if(roof_part.use_fractional_config_ != fractional) + { + continue; + } - Polygons infill_outline = support_roof_outlines; - Polygons wall; - // make sure there is a wall if this is on the first layer - if (gcode_layer.getLayerNr() == 0) - { - wall = support_roof_outlines.offset(-support_roof_line_width / 2); - infill_outline = wall.offset(-support_roof_line_width / 2); - } - infill_outline = Simplify(roof_extruder.settings_).polygon(infill_outline); + const GCodePathConfig& current_roof_config = roof_part.use_fractional_config_ ? + gcode_layer.configs_storage_.support_fractional_roof_config : + gcode_layer.configs_storage_.support_roof_config; + const EFillMethod pattern = roof_part.custom_line_pattern_ == EFillMethod::NONE ? roof_extruder.settings_.get("support_roof_pattern") : roof_part.custom_line_pattern_ ; + AngleDegrees fill_angle = 0; + if (! storage.support.support_roof_angles.empty()) + { + // handle negative layer numbers + int divisor = static_cast(storage.support.support_roof_angles.size()); + int index = ((gcode_layer.getLayerNr() % divisor) + divisor) % divisor; + fill_angle = storage.support.support_roof_angles.at(index); + } + const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG; + const bool connect_polygons = false; // connections might happen in mid air in between the infill lines + constexpr coord_t support_roof_overlap = 0; // the roofs should never be expanded outwards + constexpr size_t infill_multiplier = 1; + constexpr coord_t extra_infill_shift = 0; + const auto wall_line_count = roof_part.inset_count_to_generate_; + const coord_t small_area_width = roof_extruder.settings_.get("min_even_wall_line_width") * 2; // Maximum width of a region that can still be filled with one wall. + const Point2LL infill_origin; + constexpr bool skip_stitching = false; + constexpr bool fill_gaps = true; + constexpr bool use_endpieces = true; + constexpr bool connected_zigzags = false; + constexpr bool skip_some_zags = false; + constexpr size_t zag_skip_count = 0; + constexpr coord_t pocket_size = 0; + const coord_t max_resolution = roof_extruder.settings_.get("meshfix_maximum_resolution"); + const coord_t max_deviation = roof_extruder.settings_.get("meshfix_maximum_deviation"); + + coord_t support_roof_line_distance = roof_part.custom_line_distance_ == 0 ? roof_extruder.settings_.get("support_roof_line_distance") : roof_part.custom_line_distance_; + const coord_t support_roof_line_width = roof_extruder.settings_.get("support_roof_line_width"); + if (gcode_layer.getLayerNr() == 0 && support_roof_line_distance < 2 * support_roof_line_width) + { // if roof is dense + support_roof_line_distance *= roof_extruder.settings_.get("initial_layer_line_width_factor"); + } - Infill roof_computation( - pattern, - zig_zaggify_infill, - connect_polygons, - infill_outline, - current_roof_config.getLineWidth(), - support_roof_line_distance, - support_roof_overlap, - infill_multiplier, - fill_angle, - gcode_layer.z_ + current_roof_config.z_offset, - extra_infill_shift, - max_resolution, - max_deviation, - wall_line_count, - small_area_width, - infill_origin, - skip_stitching, - fill_gaps, - connected_zigzags, - use_endpieces, - skip_some_zags, - zag_skip_count, - pocket_size); - Polygons roof_polygons; - std::vector roof_paths; - Polygons roof_lines; - roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings_, gcode_layer.getLayerNr(), SectionType::SUPPORT); - if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) - { - return false; // We didn't create any support roof. - } - gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support - if (gcode_layer.getLayerNr() == 0) - { - gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); - } - if (! roof_polygons.empty()) - { - constexpr bool force_comb_retract = false; - gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); - } - if (! roof_paths.empty()) - { - const GCodePathConfig& config = current_roof_config; - constexpr bool retract_before_outer_wall = false; - constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + Polygons infill_outline = roof_part.getInfillArea(); + Polygons wall; + // make sure there is a wall if this is on the first layer + if (gcode_layer.getLayerNr() == 0) + { + wall = infill_outline.offset(-support_roof_line_width / 2); + infill_outline = wall.offset(-support_roof_line_width / 2); + } + infill_outline = Simplify(roof_extruder.settings_).polygon(infill_outline); - InsetOrderOptimizer wall_orderer( - *this, - storage, - gcode_layer, - roof_extruder.settings_, - roof_extruder_nr, - config, - config, - config, - config, - config, - config, - retract_before_outer_wall, - wipe_dist, - wipe_dist, - roof_extruder_nr, - roof_extruder_nr, - z_seam_config, - roof_paths); - wall_orderer.addToLayer(); + Infill roof_computation( + pattern, + zig_zaggify_infill, + connect_polygons, + infill_outline, + current_roof_config.getLineWidth(), + support_roof_line_distance, + support_roof_overlap, + infill_multiplier, + fill_angle, + gcode_layer.z_ + current_roof_config.z_offset, + extra_infill_shift, + max_resolution, + max_deviation, + wall_line_count, + small_area_width, + infill_origin, + skip_stitching, + fill_gaps, + connected_zigzags, + use_endpieces, + skip_some_zags, + zag_skip_count, + pocket_size); + Polygons roof_polygons; + std::vector roof_paths; + Polygons roof_lines; + roof_computation.generate(roof_paths, roof_polygons, roof_lines, roof_extruder.settings_, gcode_layer.getLayerNr(), SectionType::SUPPORT); + + if ((gcode_layer.getLayerNr() == 0 && wall.empty()) || (gcode_layer.getLayerNr() > 0 && roof_paths.empty() && roof_polygons.empty() && roof_lines.empty())) + { + // We didn't create any support roof. + continue; + } + added_support = true; + + gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support + if (gcode_layer.getLayerNr() == 0) + { + gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); + } + if (! roof_polygons.empty()) + { + constexpr bool force_comb_retract = false; + gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); + gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); + } + if (! roof_paths.empty()) + { + const GCodePathConfig& config = current_roof_config; + constexpr bool retract_before_outer_wall = false; + constexpr coord_t wipe_dist = 0; + const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + + InsetOrderOptimizer wall_orderer( + *this, + storage, + gcode_layer, + roof_extruder.settings_, + roof_extruder_nr, + config, + config, + config, + config, + config, + config, + retract_before_outer_wall, + wipe_dist, + wipe_dist, + roof_extruder_nr, + roof_extruder_nr, + z_seam_config, + roof_paths); + wall_orderer.addToLayer(); + } + gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + } } - gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); - return true; + return added_support; } bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, LayerPlan& gcode_layer) const diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index f9ebcb4f4c..f6c3da26e4 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -604,7 +604,7 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) AABB outside_polygon_boundary_box(outside_polygon); SupportLayer& support_layer = storage.support.supportLayers[layer]; // take the differences of the support infill parts and the prime tower area - support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box); + support_layer.excludeAreasFromSupportParts(support_layer.support_infill_parts, outside_polygon, outside_polygon_boundary_box); } } diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 74935df98a..8e8d3e84a9 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -393,18 +393,15 @@ Polygons SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) } AABB model_brim_covered_area_boundary_box(model_brim_covered_area); - support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box); + support_layer.excludeAreasFromSupportParts(support_layer.support_infill_parts, model_brim_covered_area, model_brim_covered_area_boundary_box); // If the gap between the model and the BP is small enough, support starts with the interface instead, so remove it there as well: - support_layer.support_roof = support_layer.support_roof.difference(model_brim_covered_area); + support_layer.excludeAreasFromSupportParts(support_layer.support_roof, model_brim_covered_area, model_brim_covered_area_boundary_box); } - for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts) - { - first_layer_outline.add(support_infill_part.outline_); - } + first_layer_outline.add(support_layer.getTotalAreaFromParts(support_layer.support_infill_parts)); first_layer_outline.add(support_layer.support_bottom); - first_layer_outline.add(support_layer.support_roof); + first_layer_outline.add(support_layer.getTotalAreaFromParts(support_layer.support_roof)); } } constexpr coord_t join_distance = 20; @@ -685,7 +682,7 @@ void SkirtBrim::generateSupportBrim() support_outline.add(part.outline_); } const Polygons brim_area = support_outline.difference(support_outline.offset(-brim_width)); - support_layer.excludeAreasFromSupportInfillAreas(brim_area, AABB(brim_area)); + support_layer.excludeAreasFromSupportParts(support_layer.support_infill_parts, brim_area, AABB(brim_area)); coord_t offset_distance = brim_line_width / 2; for (size_t skirt_brim_number = 0; skirt_brim_number < line_count; skirt_brim_number++) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 1defa08a2b..333d50927e 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -119,8 +119,6 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) storage.support.supportLayers .size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. - additional_required_support_area = std::vector(storage.support.supportLayers.size(), Polygons()); - spdlog::info("Processing support tree mesh group {} of {} containing {} meshes.", counter + 1, grouped_meshes.size(), grouped_meshes[counter].second.size()); std::vector exclude(storage.support.supportLayers.size()); auto t_start = std::chrono::high_resolution_clock::now(); @@ -133,11 +131,8 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) { Polygons exlude_at_layer; exlude_at_layer.add(storage.support.supportLayers[layer_idx].support_bottom); - exlude_at_layer.add(storage.support.supportLayers[layer_idx].support_roof); - for (auto part : storage.support.supportLayers[layer_idx].support_infill_parts) - { - exlude_at_layer.add(part.outline_); - } + exlude_at_layer.add(storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof)); + exlude_at_layer.add(storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_infill_parts)); exclude[layer_idx] = exlude_at_layer.unionPolygons(); scripta::log("tree_support_exclude", exclude[layer_idx], SectionType::SUPPORT, layer_idx); }); @@ -271,7 +266,7 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector>& cradle_data_model) { TreeSupportTipGenerator tip_gen(mesh, volumes_); - tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, fake_roof_areas, support_free_areas, cradle_data_model); + tip_gen.generateTips(storage, mesh, move_bounds, fake_roof_areas, support_free_areas, cradle_data_model); } void TreeSupport::mergeHelper( @@ -2300,7 +2295,9 @@ void TreeSupport::dropNonGraciousAreas( void TreeSupport::generateSupportSkin(std::vector& support_layer_storage, 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, SliceDataStorage& storage, std::vector>& layer_tree_polygons, std::vector>& cradle_data) @@ -2313,6 +2310,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::vector cradle_support_base_areas(support_layer_storage.size()); std::vector cradle_support_line_areas(support_layer_storage.size()); + std::vector cradle_support_line_roof_areas(support_layer_storage.size()); + std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); std::mutex critical_cradle_line_xy_distance_areas; @@ -2358,11 +2357,11 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { std::lock_guard critical_section_cradle(critical_support_roof_storage); - if(support_roof_storage.size()<=layer_idx) + if(cradle_support_line_roof_areas.size()<=layer_idx) { - support_roof_storage.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); + cradle_support_line_roof_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); } - support_roof_storage[cradle_line_layer_idx].add(line_area); + cradle_support_line_roof_areas[cradle_line_layer_idx].add(line_area); } else { @@ -2413,7 +2412,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora layer_idx, f_roof.line_distance_, storage.support.cross_fill_provider, - false).offsetPolyLine(config.support_line_width / 2)); + 0).offsetPolyLine(config.support_line_width / 2)); } fake_roof_lines = fake_roof_lines.unionPolygons(); fake_roof = fake_roof.unionPolygons(); @@ -2423,15 +2422,14 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(fake_roof_lines); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx].unionPolygons()); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx].unionPolygons(); - // Fractional roof is a modifier applied to a roof area, which means if only the fractional roof area is set, there will be nothing as there is no roof to modify. - // Because of that the fractional roof has ALSO to be added to the roof. - Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx]); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(fractional_roof); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(cradle_line_xy_distance_areas[layer_idx]); - storage.support.supportLayers[layer_idx].support_fractional_roof = storage.support.supportLayers[layer_idx].support_fractional_roof.unionPolygons(fractional_roof); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].unionPolygons(); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].unionPolygons(); + + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].unionPolygons(); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].unionPolygons(); + cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (!storage.support.supportLayers[layer_idx].support_roof.empty()) @@ -2439,31 +2437,59 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora switch (config.interface_preference) { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); - + { + Polygons all_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + all_roof.add(support_roof_storage[layer_idx]); + all_roof.add(support_roof_storage_fractional[layer_idx]); + all_roof.add(support_roof_extra_wall_storage[layer_idx]); + all_roof.add(support_roof_extra_wall_storage_fractional[layer_idx]); + all_roof = all_roof.unionPolygons(); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(all_roof); break; + } case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof - .difference(support_layer_storage[layer_idx]) - .difference(support_skin_storage[layer_idx]) - .difference(additional_required_support_area[layer_idx]); + { + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); + AABB invalid_roof_aabb = AABB(invalid_roof); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_layer_storage[layer_idx]); + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_layer_storage[layer_idx]); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); break; - + } default: break; } } + Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(support_roof_storage[layer_idx]).difference(support_free_areas[layer_idx]); if(!support_free_areas[layer_idx].empty()) { support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(support_free_areas[layer_idx]); + remove_from_next_roof.add(support_free_areas[layer_idx]); } - additional_required_support_area[layer_idx] = additional_required_support_area[layer_idx] - .difference(storage.support.supportLayers[layer_idx].support_roof) - .difference(support_layer_storage[layer_idx]) - .difference(support_skin_storage[layer_idx]); + + + Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof).unionPolygons(cradle_support_line_roof_areas[layer_idx]); + storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); + + Polygons roof = support_roof_storage[layer_idx].difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); + storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); + + remove_from_next_roof.add(roof_extra_wall); + remove_from_next_roof.add(roof); + remove_from_next_roof = remove_from_next_roof.unionPolygons(); + + Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_roof); + storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, config.support_roof_line_width, config.support_wall_count, true); + + Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_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); + } ); @@ -2485,29 +2511,32 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora return; } - const coord_t roof_stable_range_after_contact = config.support_roof_line_width * config.support_roof_wall_count; + const coord_t roof_stable_range_after_contact = config.support_roof_line_width * (config.support_roof_wall_count + 0.5); Polygons support_shell_capable_of_supporting_roof = support_layer_storage[layer_idx] .getOutsidePolygons() - .tubeShape(config.support_line_width * config.support_wall_count + roof_stable_range_after_contact, roof_stable_range_after_contact, ClipperLib::JoinType::jtRound) + .tubeShape(config.support_line_width * (config.support_wall_count + 0.5) + roof_stable_range_after_contact, roof_stable_range_after_contact, ClipperLib::JoinType::jtRound) .unionPolygons() - .offset(-config.support_line_width/4).offset(config.support_line_width/4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. + .offset(-config.support_line_width / 4).offset(config.support_line_width / 4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. Polygons needs_supporting; if(storage.support.supportLayers.size() > layer_idx + 1) { - needs_supporting.add(storage.support.supportLayers[layer_idx + 1].support_roof.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(additional_required_support_area[layer_idx + 1]); // E.g. cradle - needs_supporting.add(cradle_support_line_areas[layer_idx+1]); + 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(cradle_support_line_areas[layer_idx + 1]); } - needs_supporting.add(cradle_support_base_areas[layer_idx]); + needs_supporting.add(cradle_support_base_areas[layer_idx]); // cradle bases should be skin. + needs_supporting = needs_supporting.unionPolygons(); + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons already_supports; - already_supports.add(storage.support.supportLayers[layer_idx].support_roof); // roof - already_supports.add(additional_required_support_area[layer_idx]); // E.g. cradle + already_supports.add(existing_roof); // roof already_supports.add(fake_roofs[layer_idx]); - already_supports.add(support_layer_storage[layer_idx].getOutsidePolygons().tubeShape(config.support_line_width*config.support_wall_count,0)); + 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(); Polygons may_need_skin_area_topmost = needs_supporting.difference(already_supports); @@ -2566,7 +2595,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora layer_idx - support_skin_ctr, config.support_skin_line_distance, nullptr, - false, + 0, EFillMethod::LINES, true); Polygons intersecting_lines; @@ -2847,47 +2876,72 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor switch (interface_pref) { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); - support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); + { + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(existing_roof); + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(existing_roof); break; + } case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]).difference(support_skin_storage[layer_idx]); + { + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx].unionPolygons(support_skin_storage[layer_idx])); + AABB invalid_roof_aabb = AABB(invalid_roof); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); break; + } + + case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + { + Polygons interface_lines; - case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + for(SupportInfillPart& roof_part:storage.support.supportLayers[layer_idx].support_roof) { - Polygons interface_lines = - TreeSupportUtils::generateSupportInfillLines(storage.support.supportLayers[layer_idx].support_roof, config, true, layer_idx, config.support_roof_line_distance, storage.support.cross_fill_provider, true) - .offsetPolyLine(config.support_roof_line_width / 2); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); - support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(interface_lines); + interface_lines.add(TreeSupportUtils::generateSupportInfillLines(roof_part.outline_, + config, + true, + layer_idx, + roof_part.custom_line_distance_ == 0 ? config.support_roof_line_distance : roof_part.custom_line_distance_, + storage.support.cross_fill_provider, + roof_part.inset_count_to_generate_, + roof_part.custom_line_pattern_) + .offsetPolyLine(config.support_roof_line_width / 2)); + } - break; + interface_lines = interface_lines.unionPolygons(); - case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: - { - Polygons tree_lines; - tree_lines = - tree_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_layer_storage[layer_idx], config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, true) + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); + support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(interface_lines); + } + break; + + case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: + { + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + + Polygons tree_lines; + tree_lines = + tree_lines.unionPolygons + ( + TreeSupportUtils::generateSupportInfillLines(support_layer_storage[layer_idx], config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) + .offsetPolyLine(config.support_line_width / 2) + ); + + Polygons support_skin_lines; + support_skin_lines = + support_skin_lines.unionPolygons + ( + TreeSupportUtils::generateSupportInfillLines(support_skin_storage[layer_idx], config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, std::max(0, config.support_wall_count - 1), EFillMethod::LINES) .offsetPolyLine(config.support_line_width / 2) - ); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); - - Polygons support_skin_lines; - support_skin_lines = - support_skin_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_skin_storage[layer_idx], config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, true, EFillMethod::LINES) - .offsetPolyLine(config.support_line_width / 2) - ); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_skin_lines); - - // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. - } - break; + ); + + Polygons invalid_roof = existing_roof.intersection(support_skin_lines.unionPolygons(tree_lines)); + AABB invalid_roof_aabb = AABB(invalid_roof); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. + } + break; case InterfacePreference::NOTHING: break; @@ -2949,7 +3003,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor EFillMethod::ZIG_ZAG); // This only works because fractional support is always just projected upwards regular support or skin. - // Also technically violates skin height, but there is no good way to prevent that. todo document in UI setting + // Also technically violates skin height, but there is no good way to prevent that. Polygons fractional_support; Polygons fractional_skin; @@ -3011,8 +3065,11 @@ void TreeSupport::drawAreas(std::vector>& move_bou std::vector support_layer_storage(move_bounds.size()); std::vector support_layer_storage_fractional(move_bounds.size()); std::vector support_roof_storage_fractional(move_bounds.size()); + std::vector support_roof_extra_wall_storage_fractional(move_bounds.size()); 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::map inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. std::vector> @@ -3078,29 +3135,31 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ - && TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, true).empty()) + if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ && config.support_roof_wall_count == 0) { - std::vector to_disable_roofs; - to_disable_roofs.emplace_back(data_pair.first); - while (! to_disable_roofs.empty()) + Polygons roof_lines = TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, config.support_roof_wall_count); + if(roof_lines.polyLineLength() < data_pair.second.polyLineLength()) // arbitrary threshold to check if the interface pattern is propper. { - std::vector to_disable_roofs_next; - for (TreeSupportElement* elem : to_disable_roofs) + std::vector to_disable_roofs; + to_disable_roofs.emplace_back(data_pair.first); + while (! to_disable_roofs.empty()) { - elem->missing_roof_layers_ = 0; - if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ + 1) + std::vector to_disable_roofs_next; + for (TreeSupportElement* elem : to_disable_roofs) { - to_disable_roofs_next.emplace_back(inverse_tree_order[elem]); + elem->roof_with_enforced_walls = true; + if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ + 1) + { + to_disable_roofs_next.emplace_back(inverse_tree_order[elem]); + } } + to_disable_roofs = to_disable_roofs_next; } - to_disable_roofs = to_disable_roofs_next; } } } }); - //todo check if roof actually contains lines todo disable ovalisation for roof tips for (const auto layer_idx : ranges::views::iota(0UL, layer_tree_polygons.size())) { for (std::pair data_pair : layer_tree_polygons[layer_idx]) @@ -3108,12 +3167,20 @@ void TreeSupport::drawAreas(std::vector>& move_bou if(data_pair.first->parents_.empty() && !data_pair.first->supports_roof_ && !data_pair.first->cradle_line_ && - layer_idx + 1< support_roof_storage_fractional.size() && + layer_idx + 1 < support_roof_storage_fractional.size() && config.z_distance_top % config.layer_height > 0) { if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) { - support_roof_storage_fractional[layer_idx+1].add(data_pair.second); + if(data_pair.first->roof_with_enforced_walls) + { + support_roof_extra_wall_storage_fractional[layer_idx+1].add(data_pair.second); + + } + else + { + support_roof_storage_fractional[layer_idx+1].add(data_pair.second); + } } else { @@ -3121,25 +3188,41 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - ((data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) ? support_roof_storage : support_layer_storage)[layer_idx].add(data_pair.second); + if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) + { + if(data_pair.first->roof_with_enforced_walls) + { + support_roof_extra_wall_storage[layer_idx].add(data_pair.second); + + } + else + { + support_roof_storage[layer_idx].add(data_pair.second); + } + } + else + { + support_layer_storage[layer_idx].add(data_pair.second); + } + } if(layer_idx + 1< support_roof_storage_fractional.size()) { - support_roof_storage_fractional[layer_idx+1] = support_roof_storage_fractional[layer_idx+1].unionPolygons(); - support_layer_storage_fractional[layer_idx+1] = support_layer_storage_fractional[layer_idx+1].unionPolygons(); + support_roof_storage_fractional[layer_idx + 1] = support_roof_storage_fractional[layer_idx + 1].unionPolygons(); + support_layer_storage_fractional[layer_idx + 1] = support_layer_storage_fractional[layer_idx + 1].unionPolygons(); + support_roof_extra_wall_storage_fractional[layer_idx + 1] = support_roof_extra_wall_storage_fractional[layer_idx + 1].unionPolygons(); } } - generateSupportSkin(support_layer_storage,support_skin_storage,support_roof_storage,support_roof_storage_fractional,storage,layer_tree_polygons,cradle_data); + generateSupportSkin(support_layer_storage,support_skin_storage, + support_roof_storage, support_roof_extra_wall_storage, + support_roof_storage_fractional, support_roof_extra_wall_storage_fractional, + storage,layer_tree_polygons,cradle_data); - // cradle being added before skin causes cradle lines to become skin. - for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) + for (const auto layer_idx : ranges::views::iota(0UL, support_layer_storage.size())) { - if(layer_idx < support_layer_storage.size()) - { - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(additional_required_support_area[layer_idx]); - } scripta::log("tree_support_layer_storage", support_layer_storage[layer_idx], SectionType::SUPPORT, layer_idx); + // todo maybe also log support_skin_storage ? } const auto t_skin = std::chrono::high_resolution_clock::now(); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 3e25ac6699..dcf5f8bb41 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -46,18 +46,16 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T , support_outset_(0) , // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") roof_outset_(use_fake_roof_ ? support_outset_ : mesh.settings.get("support_roof_offset")) - , force_tip_to_roof_((config_.min_radius * config_.min_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && support_roof_layers_ && ! use_fake_roof_) , support_tree_limit_branch_reach_(mesh.settings.get("support_tree_limit_branch_reach")) , support_tree_branch_reach_limit_(support_tree_limit_branch_reach_ ? mesh.settings.get("support_tree_branch_reach_limit") : 0) , z_distance_delta_(std::min(config_.z_distance_top_layers + 1, mesh.overhang_areas.size())) , xy_overrides_(config_.support_overrides == SupportDistPriority::XY_OVERRIDES_Z) - , tip_roof_size_(force_tip_to_roof_ ? INT2MM2(config_.min_radius * config_.min_radius) * std::numbers::pi : 0) , already_inserted_(mesh.overhang_areas.size()) , support_roof_drawn_(mesh.overhang_areas.size(), Polygons()) , support_roof_drawn_fractional_(mesh.overhang_areas.size(), Polygons()) - , roof_tips_drawn_(mesh.overhang_areas.size(), Polygons()) , cradle_data_(mesh.overhang_areas.size()) , force_minimum_roof_area_(use_fake_roof_ || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) + , force_initial_layer_radius_(retrieveSetting(mesh.settings, "support_tree_enforce_initial_layer_diameter")) , cradle_layers_(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config_.layer_height) , cradle_layers_min_(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config_.layer_height) , cradle_line_count_(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) @@ -1652,10 +1650,10 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m full_overhang_area = full_overhang_area.difference(forbidden_next); - if(force_minimum_roof_area_) - { - full_overhang_area.removeSmallAreas(minimum_roof_area_); - } + if(force_minimum_roof_area_) + { + full_overhang_area.removeSmallAreas(minimum_roof_area_); + } if (full_overhang_area.area() > EPSILON) { @@ -1721,7 +1719,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m fuzzy_area.add(potential_support_roofs[layer_idx + layer_offset]); } fuzzy_area = fuzzy_area.unionPolygons(); - fuzzy_area.removeSmallAreas(std::max(minimum_roof_area_, tip_roof_size_)); + fuzzy_area.removeSmallAreas(minimum_roof_area_); for (Polygons potential_roof : potential_support_roofs[layer_idx].difference(forbidden_here).splitIntoParts()) { @@ -1734,7 +1732,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m else { Polygons valid_roof = potential_support_roofs[layer_idx].difference(forbidden_here); - valid_roof.removeSmallAreas(std::max(minimum_roof_area_, tip_roof_size_)); + valid_roof.removeSmallAreas(minimum_roof_area_); additional_support_roofs[layer_idx].add(valid_roof); } } @@ -1759,7 +1757,7 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( double hidden_radius_increase, LayerIndex insert_layer, size_t dont_move_until, - bool roof, + TipRoofType roof, bool cradle, bool skip_ovalisation, std::vector additional_ovalization_targets) @@ -1793,10 +1791,10 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( gracious, ! xy_overrides_, dont_move_until + dtt, - roof, + roof == TipRoofType::SUPPORTS_ROOF, cradle, safe_radius, - ! roof && force_tip_to_roof_, + roof == TipRoofType::IS_ROOF && ! use_fake_roof_, skip_ovalisation, support_tree_limit_branch_reach_, support_tree_branch_reach_limit_, @@ -1866,7 +1864,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector 0) ? TipRoofType::IS_ROOF : (overhang_data.is_roof_ ? TipRoofType::SUPPORTS_ROOF : TipRoofType::NONE), overhang_data.is_cradle_, disable_ovalization, additional_ovalization_targets); @@ -1885,10 +1883,8 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, - SliceDataStorage& storage, - std::vector& additional_support_areas) + SliceDataStorage& storage) { - std::vector all_cradle_roofs(storage.support.supportLayers.size()); for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) { @@ -1913,11 +1909,9 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( if (layer_idx + 1 < storage.support.supportLayers.size()) { std::vector to_be_removed; - Polygons roof_on_layer_above = (use_fake_roof_ ? support_roof_drawn_[layer_idx + 1] - : storage.support.supportLayers[layer_idx + 1].support_roof).unionPolygons(additional_support_areas[layer_idx + 1]); + Polygons roof_on_layer_above = support_roof_drawn_[layer_idx + 1]; roof_on_layer_above = roof_on_layer_above.unionPolygons(all_cradle_roofs[layer_idx+1]); - Polygons roof_on_layer - = (use_fake_roof_ ? support_roof_drawn_[layer_idx] : storage.support.supportLayers[layer_idx].support_roof).unionPolygons(additional_support_areas[layer_idx]); + Polygons roof_on_layer = support_roof_drawn_[layer_idx]; for (TreeSupportElement* elem : move_bounds[layer_idx]) { @@ -1954,7 +1948,6 @@ void TreeSupportTipGenerator::generateTips( SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector>& move_bounds, - std::vector& additional_support_areas, std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, std::vector>& cradle_data_export) @@ -2020,10 +2013,10 @@ void TreeSupportTipGenerator::generateTips( return; // This is a continue if imagined in a loop context. } - coord_t current_tip_radius = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx)); //todo make setting + coord_t current_tip_radius = (force_initial_layer_radius_ && config_.recommendedMinRadius(layer_idx) > config_.min_radius) ? config_.recommendedMinRadius(layer_idx) : config_.min_radius; coord_t connect_length = (config_.support_line_width * 100 / support_tree_top_rate_) + std::max(2 * current_tip_radius - 1.0 * config_.support_line_width, 0.0); coord_t support_tree_branch_distance = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; - + bool force_tip_to_roof = (current_tip_radius * current_tip_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && !use_fake_roof_ && support_roof_layers_; Polygons relevant_forbidden = volumes_.getAvoidance( config_.getRadius(0), @@ -2038,13 +2031,13 @@ void TreeSupportTipGenerator::generateTips( - std::function generateLines = [&](const Polygons& area, bool roof, bool cradle, LayerIndex generate_layer_idx) + std::function generateLines = [&](const Polygons& area, bool roof, LayerIndex generate_layer_idx) { //todo ensure larger tips have reasonable density. How would one do that though? // If tips are 7mm thick, does 20% fill mean a distance of 35mm between tips? Does not make sense... coord_t upper_line_distance = support_supporting_branch_distance_; coord_t line_distance = std::max(roof ? support_roof_line_distance_ : support_tree_branch_distance, upper_line_distance); - coord_t current_tip_radius_generate = std::max(config_.min_radius, config_.recommendedMinRadius(generate_layer_idx)); //todo make setting + coord_t current_tip_radius_generate = (force_initial_layer_radius_ && config_.recommendedMinRadius(generate_layer_idx) > config_.min_radius) ? config_.recommendedMinRadius(generate_layer_idx) : config_.min_radius; bool use_grid = line_distance == upper_line_distance; @@ -2055,7 +2048,7 @@ void TreeSupportTipGenerator::generateTips( generate_layer_idx, line_distance, roof? nullptr : getCrossFillProvider(mesh, line_distance, current_tip_radius_generate), - (roof && ! use_fake_roof_) || cradle, + (roof && ! use_fake_roof_) ? config_.support_roof_wall_count : 0, use_grid ? EFillMethod::GRID:EFillMethod::NONE); }; @@ -2071,7 +2064,7 @@ void TreeSupportTipGenerator::generateTips( if (support_roof_layers_ && layer_idx + 1 < support_roof_drawn_.size()) { - core_overhang = core_overhang.difference(support_roof_drawn_[layer_idx]); // todo Rounding errors again. + core_overhang = core_overhang.difference(support_roof_drawn_[layer_idx].offset(config_.support_roof_line_width / 2).unionPolygons()); for (Polygons roof_part : support_roof_drawn_[layer_idx + 1] .difference(support_roof_drawn_[layer_idx].offset(config_.maximum_move_distance_slow).unionPolygons()) .splitIntoParts(true)) // If there is a roof, the roof will be one layer above the tips. @@ -2144,7 +2137,7 @@ void TreeSupportTipGenerator::generateTips( } all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons(); std::vector overhang_lines; - Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, false, layer_idx), current_tip_radius, 1, false); + Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), current_tip_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. // Technically this violates branch distance, but not only is this the only reasonable choice, // but it ensures consistent behavior as some infill patterns generate each line segment as its own polyline part causing a similar line forming behavior. @@ -2202,7 +2195,7 @@ void TreeSupportTipGenerator::generateTips( // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. OverhangInformation dummy_overhang_info(remaining_overhang_part,false); - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof_ && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, + addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, layer_idx - lag_ctr, current_tip_radius_lag, dummy_overhang_info, support_roof_layers_, false); } } @@ -2238,7 +2231,7 @@ void TreeSupportTipGenerator::generateTips( { // todo can cause inconsistent support density if a line exactly aligns with the model polylines = ensureMaximumDistancePolyline( - generateLines(overhang_outset, overhang_data.is_roof_, overhang_data.is_cradle_, layer_idx + overhang_data.is_roof_), + generateLines(overhang_outset, overhang_data.is_roof_, layer_idx + overhang_data.is_roof_), ! overhang_data.is_roof_ ? config_.min_radius * 2 : use_fake_roof_ ? support_supporting_branch_distance_ : connect_length, @@ -2297,11 +2290,11 @@ void TreeSupportTipGenerator::generateTips( } } - size_t dont_move_for_layers = support_roof_layers_ ? (force_tip_to_roof_ ? support_roof_layers_ : (overhang_data.is_roof_ ? 0 : support_roof_layers_)) : 0; + size_t dont_move_for_layers = support_roof_layers_ ? (force_tip_to_roof ? support_roof_layers_ : (overhang_data.is_roof_ ? 0 : support_roof_layers_)) : 0; addLinesAsInfluenceAreas( new_tips, overhang_lines, - force_tip_to_roof_ ? support_roof_layers_ : 0, + (!overhang_data.is_roof_ && force_tip_to_roof) ? support_roof_layers_ : 0, layer_idx, current_tip_radius, overhang_data, @@ -2315,58 +2308,58 @@ void TreeSupportTipGenerator::generateTips( support_roof_drawn_.size(), [&](const LayerIndex layer_idx) { - // Sometimes roofs could be empty as the pattern does not generate lines if the area is narrow enough. - // If there is a roof could have zero lines in its area (as it has no wall), rand a support area would very likely be printed (because there are walls for the support - // areas), replace non printable roofs with support - if (! use_fake_roof_ && config_.support_wall_count > 0 && config_.support_roof_wall_count == 0) + if (use_fake_roof_) { - for (auto roof_area : support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]).splitIntoParts()) + placed_fake_roof_areas[layer_idx].emplace_back(support_roof_drawn_[layer_idx], support_roof_line_distance_, false); + + if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) { - // technically there is no guarantee that a drawn roof tip has lines, as it could be unioned with another roof area that has, but this has to be enough - // hopefully. - if (layer_idx < additional_support_areas.size() - && TreeSupportUtils::generateSupportInfillLines(roof_area, config_, true, layer_idx, support_roof_line_distance_, nullptr, false).empty()) + Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1]; + placed_fake_roof_areas[layer_idx].emplace_back(all_roof_fractional, support_roof_line_distance_, true); + } + } + else + { + if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) + { + Polygons all_roof_below = support_roof_drawn_[layer_idx - 1]; + Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1].intersection(all_roof_below).difference(support_roof_drawn_[layer_idx]); + + if (config_.support_wall_count > 0 && config_.support_roof_wall_count == 0) // Need to check every area whether it has lines { - additional_support_areas[layer_idx].add(roof_area); + for (auto roof_area : all_roof_fractional.splitIntoParts()) + { + // technically there is no guarantee that a drawn roof tip has lines. If it does not add walls + size_t roof_wall_count = 0; + if (TreeSupportUtils::generateSupportInfillLines(roof_area, config_, true, layer_idx, support_roof_line_distance_, nullptr, 0).empty()) + { + roof_wall_count = config_.support_wall_count; + } + storage.support.supportLayers[layer_idx].fillRoofParts(roof_area, config_.support_roof_line_width, roof_wall_count, false); + } } else { - storage.support.supportLayers[layer_idx].support_roof.add(roof_area); + storage.support.supportLayers[layer_idx].fillRoofParts(all_roof_fractional, config_.support_roof_line_width, config_.support_roof_wall_count, true); } } - additional_support_areas[layer_idx] = additional_support_areas[layer_idx].unionPolygons(); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(); - } - else - { - if (use_fake_roof_) + if (config_.support_wall_count > 0 && config_.support_roof_wall_count == 0) { - placed_fake_roof_areas[layer_idx].emplace_back(support_roof_drawn_[layer_idx], support_roof_line_distance_, false); - - if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) + for (auto roof_area : support_roof_drawn_[layer_idx].splitIntoParts()) // Need to check every area whether it has lines { - // Fake roof tips would just be tips, so no need to add them here as all polygons in roof_tips_drawn_ will be empty! - Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1].intersection(support_roof_drawn_[layer_idx - 1]); - placed_fake_roof_areas[layer_idx].emplace_back(all_roof_fractional, support_roof_line_distance_, true); + // technically there is no guarantee that a drawn roof tip has lines. If it does not add walls + size_t roof_wall_count = 0; + if (TreeSupportUtils::generateSupportInfillLines(roof_area, config_, true, layer_idx, support_roof_line_distance_, nullptr, 0).empty()) + { + roof_wall_count = config_.support_wall_count; + } + storage.support.supportLayers[layer_idx].fillRoofParts(roof_area, config_.support_roof_line_width, roof_wall_count, false); } } else { - if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) - { - Polygons all_roof_below = support_roof_drawn_[layer_idx - 1].unionPolygons(roof_tips_drawn_[layer_idx - 1]); - Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1].intersection(all_roof_below); - storage.support.supportLayers[layer_idx].support_fractional_roof - = storage.support.supportLayers[layer_idx].support_fractional_roof.unionPolygons(all_roof_fractional); - - // Fractional roof is a modifier applied to a roof area, which means if only the fractional roof area is set, there will be nothing as there is no roof to - // modify. Because of that the fractional roof has ALSO to be added to the roof. - storage.support.supportLayers[layer_idx].support_roof.add(all_roof_fractional); - } - - Polygons all_roof = support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(all_roof); + storage.support.supportLayers[layer_idx].fillRoofParts(support_roof_drawn_[layer_idx], config_.support_roof_line_width, config_.support_roof_wall_count, false); } } }); @@ -2378,7 +2371,7 @@ void TreeSupportTipGenerator::generateTips( cradle_data_export[layer_idx] = cradles; } - removeUselessAddedPoints(new_tips, storage, additional_support_areas); + removeUselessAddedPoints(new_tips, storage); for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { diff --git a/src/bridge.cpp b/src/bridge.cpp index 554d81d5b1..32e1405320 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -70,12 +70,13 @@ double bridgeAngle( if (! support_layer->support_roof.empty()) { - AABB support_roof_bb(support_layer->support_roof); + Polygons all_roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); + AABB support_roof_bb(all_roofs); if (boundary_box.hit(support_roof_bb)) { - prev_layer_outline.add(support_layer->support_roof); // not intersected with skin + prev_layer_outline.add(all_roofs); // not intersected with skin - Polygons supported_skin(skin_outline.intersection(support_layer->support_roof)); + Polygons supported_skin(skin_outline.intersection(all_roofs)); if (! supported_skin.empty()) { supported_regions.add(supported_skin); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 6a923686b0..e210d2ca29 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -359,7 +359,10 @@ Polygons SliceDataStorage::getLayerOutlines( total.add(support_infill_part.outline_); } total.add(support_layer.support_bottom); - total.add(support_layer.support_roof); + for (const SupportInfillPart& support_infill_part : support_layer.support_roof) + { + total.add(support_infill_part.outline_); + } } } if (include_prime_tower && primeTower.enabled_ && (extruder_nr == -1 || (! primeTower.extruder_order_.empty() && extruder_nr == primeTower.extruder_order_[0]))) @@ -689,16 +692,15 @@ Polygons SliceDataStorage::getMachineBorder(int checking_extruder_nr) const return border; } - -void SupportLayer::excludeAreasFromSupportInfillAreas(const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box) +void SupportLayer::excludeAreasFromSupportParts(std::vector& parts, const Polygons& exclude_polygons, const AABB& exclude_polygons_boundary_box) { // record the indexes that need to be removed and do that after std::list to_remove_part_indices; // LIFO for removing - unsigned int part_count_to_check = support_infill_parts.size(); // note that support_infill_parts.size() changes during the computation below + unsigned int part_count_to_check = parts.size(); // note that parts.size() changes during the computation below for (size_t part_idx = 0; part_idx < part_count_to_check; ++part_idx) { - SupportInfillPart& support_infill_part = support_infill_parts[part_idx]; + SupportInfillPart& support_infill_part = parts[part_idx]; // if the areas don't overlap, do nothing if (! exclude_polygons_boundary_box.hit(support_infill_part.outline_boundary_box_)) @@ -725,13 +727,13 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(const Polygons& exclude_po // there are one or more smaller parts. // we first replace the current part with one of the smaller parts, - // the rest we add to the support_infill_parts (but after part_count_to_check) + // the rest we add to the parts (but after part_count_to_check) support_infill_part.outline_ = smaller_support_islands[0]; for (size_t support_island_idx = 1; support_island_idx < smaller_support_islands.size(); ++support_island_idx) { const PolygonsPart& smaller_island = smaller_support_islands[support_island_idx]; - support_infill_parts.emplace_back(smaller_island, support_infill_part.support_line_width_, support_infill_part.inset_count_to_generate_); + parts.emplace_back(smaller_island, support_infill_part.support_line_width_, support_infill_part.inset_count_to_generate_); } } @@ -740,15 +742,15 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(const Polygons& exclude_po { const size_t remove_idx = to_remove_part_indices.back(); to_remove_part_indices.pop_back(); - if (support_infill_parts.empty()) + if (parts.empty()) { continue; } - if (remove_idx < support_infill_parts.size() - 1) + if (remove_idx < parts.size() - 1) { // move last element to the to-be-removed element so that we can erase the last place in the vector - support_infill_parts[remove_idx] = std::move(support_infill_parts.back()); + parts[remove_idx] = std::move(parts.back()); } - support_infill_parts.pop_back(); // always erase last place in the vector + parts.pop_back(); // always erase last place in the vector } } diff --git a/src/support.cpp b/src/support.cpp index 7488c6832f..b8c5003323 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1712,10 +1712,13 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh const coord_t z_distance_top = round_up_divide(mesh.settings.get("support_top_distance"), layer_height); // Number of layers between support roof and model. const coord_t roof_line_width = mesh_group_settings.get("support_roof_extruder_nr").settings_.get("support_roof_line_width"); const coord_t roof_outline_offset = mesh_group_settings.get("support_roof_extruder_nr").settings_.get("support_roof_offset"); + const coord_t roof_line_distance = mesh.settings.get("support_roof_line_distance"); + const coord_t roof_wall_line_count = mesh.settings.get("support_roof_wall_count"); const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; + Polygons roofs_before; for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ @@ -1728,11 +1731,13 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh } Polygons roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); - support_layers[layer_idx].support_roof.add(roofs); - if (layer_idx > 0 && layer_idx < support_layers.size() - 1) + // If roof is added as fractional, even though non can exist because remaining z distance is 0 it will be regular roof. + support_layers[layer_idx].fillRoofParts(layer_idx > 0 ? roofs.intersection(roofs_before) : roofs, roof_line_width, roof_wall_line_count); + if (layer_idx > 0) { - support_layers[layer_idx].support_fractional_roof.add(roofs.difference(support_layers[layer_idx + 1].support_roof)); + support_layers[layer_idx].fillRoofParts(roofs.difference(roofs_before), roof_line_width, roof_wall_line_count, true); } + roofs_before = roofs; scripta::log("support_interface_roofs", roofs, SectionType::SUPPORT, layer_idx); } @@ -1748,7 +1753,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh int upper = std::min(static_cast(layer_idx + roof_layer_count + z_distance_top + 5), static_cast(global_support_areas_per_layer.size()) - 1); for (Polygons& global_support : global_support_areas_per_layer | ranges::views::slice(lower, upper)) { - global_support = global_support.difference(support_layer.support_roof); + global_support = global_support.difference(support_layer.getTotalAreaFromParts(support_layer.support_roof)); } } } From 8f2ccddd1e0fe895a3a990c2787f2c4e275c4ea8 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 20 Apr 2024 08:23:57 +0200 Subject: [PATCH 060/101] Improve interaction between cradle base and lines if both are roofs and regular roof wall line count is 0. Now the base overwrites lines. Done make fractional roof look better in this case. --- src/TreeSupport.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 333d50927e..b0f2487c66 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2308,11 +2308,11 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora - std::vector cradle_support_base_areas(support_layer_storage.size()); - std::vector cradle_support_line_areas(support_layer_storage.size()); - std::vector cradle_support_line_roof_areas(support_layer_storage.size()); + 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()); + std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); // All cradle lines offset by xy distance. std::mutex critical_cradle_line_xy_distance_areas; std::mutex critical_cradle_support_line_areas; @@ -2332,6 +2332,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora if(cradle_data[layer_idx][cradle_idx]->is_roof_) { std::lock_guard critical_section_cradle(critical_support_roof_storage); + cradle_base_areas[layer_idx-base_idx].add(base); support_roof_storage[layer_idx-base_idx].add(base); if(base_idx == 0 && layer_idx + 1 < support_roof_storage_fractional.size()) { @@ -2341,7 +2342,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora else { std::lock_guard critical_section_cradle(critical_support_layer_storage); - cradle_support_base_areas[layer_idx-base_idx].add(base); + cradle_base_areas[layer_idx-base_idx].add(base); support_layer_storage[layer_idx-base_idx].add(base); } } @@ -2430,7 +2431,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].unionPolygons(); cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); - + cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); + cradle_support_line_roof_areas[layer_idx] = cradle_support_line_roof_areas[layer_idx].unionPolygons(); //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (!storage.support.supportLayers[layer_idx].support_roof.empty()) { @@ -2474,10 +2476,20 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } - Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof).unionPolygons(cradle_support_line_roof_areas[layer_idx]); - storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); + Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); + Polygons roof = support_roof_storage[layer_idx]; + Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].difference(cradle_base_areas[layer_idx]); + if(config.support_roof_wall_count) + { + roof = roof.unionPolygons(cradle_lines_roof); + } + else + { + roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + } - Polygons roof = support_roof_storage[layer_idx].difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); + roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); + storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); remove_from_next_roof.add(roof_extra_wall); @@ -2526,7 +2538,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora needs_supporting.add(fake_roofs[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); needs_supporting.add(cradle_support_line_areas[layer_idx + 1]); } - needs_supporting.add(cradle_support_base_areas[layer_idx]); // cradle bases should be skin. + needs_supporting.add(cradle_base_areas[layer_idx]); // cradle bases should be skin. needs_supporting = needs_supporting.unionPolygons(); From 1309df457a0f691a911ac4f0912ff124e6c9f015 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sat, 20 Apr 2024 08:24:28 +0200 Subject: [PATCH 061/101] Fix possible crash caused by cradle code. --- src/TreeSupportTipGenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index dcf5f8bb41..f598441035 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -154,7 +154,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T } } - for (int cradle_xy_dist_fill = cradle_xy_distance_.size(); cradle_xy_dist_fill < cradle_layers_ + 1; cradle_xy_dist_fill++) + for (int cradle_xy_dist_fill = cradle_xy_distance_.size(); cradle_xy_dist_fill <= cradle_layers_ + 1; cradle_xy_dist_fill++) { cradle_xy_distance_.emplace_back(config_.xy_min_distance); } @@ -795,7 +795,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorgetCenter(layer_idx + idx); - const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; // todo fix crash out of bounds + const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; if(cradle->lines_.empty()) From 6a2f66238d18f2d6e76a45d952fdab4f1f87a351 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 21 Apr 2024 05:59:30 +0200 Subject: [PATCH 062/101] Fix large cradle tip calculations --- src/TreeSupportTipGenerator.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index f598441035..38bba803a7 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1843,13 +1843,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector= config_.branch_radius ? config_.tip_layers : (config_.tip_layers * (tip_radius - config_.min_radius))/(config_.branch_radius - config_.min_radius); @@ -1857,6 +1851,14 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector config_.branch_radius ? tip_radius - config_.branch_radius : 0; double hidden_radius_increases = hidden_radius / (config_.branch_radius * (std::max(config_.diameter_scale_bp_radius - config_.diameter_angle_scale_factor, 0.0))); + if(overhang_data.is_cradle_ && tip_dtt < cradle_tip_dtt_) + { + if((overhang_data.isCradleLine() && large_cradle_line_tips_) || (!overhang_data.isCradleLine() && (! cradle_base_roof_ || large_cradle_line_tips_))) + { + tip_dtt = cradle_tip_dtt_; + } + } + TreeSupportElement* elem = addPointAsInfluenceArea( move_bounds, point_data, From 221436581d3efc4b457cb835dcdf3234bf8ee75b Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 21 Apr 2024 06:00:30 +0200 Subject: [PATCH 063/101] Fix rounding errors with anti-preferred avoidance. --- src/TreeModelVolumes.cpp | 9 ++++++--- src/TreeSupport.cpp | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 1f1551b09f..964db50ee7 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -654,27 +654,30 @@ void TreeModelVolumes::precalculateAntiPreferred() if (support_rest_preference_ == RestPreference::BUILDPLATE) { - latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance = safeOffset(latest_avoidance, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons next_latest_avoidance = simplifier_.polygon(latest_avoidance); latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); + latest_avoidance = latest_avoidance.unionPolygons(getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, false, true)); data[layer] = std::pair(key, latest_avoidance); } if(support_rests_on_model_) { - latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons next_latest_avoidance_to_model = simplifier_.polygon(latest_avoidance_to_model); latest_avoidance_to_model = next_latest_avoidance_to_model.unionPolygons(latest_avoidance_to_model); latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius,layer)); + latest_avoidance_to_model = latest_avoidance_to_model.unionPolygons(getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, true, true)); data_to_model[layer] = std::pair(key, latest_avoidance_to_model); } if(max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { - latest_avoidance_collision = safeOffset(latest_avoidance_collision, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance_collision = safeOffset(latest_avoidance_collision, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius_), ClipperLib::jtRound); latest_avoidance_collision = latest_avoidance_collision.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); + latest_avoidance_collision = latest_avoidance_collision.unionPolygons(getAvoidance(radius,layer,AvoidanceType::COLLISION, true, true)); data_collision[layer] = std::pair(key, latest_avoidance_collision); } } diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index b0f2487c66..980e50601b 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -998,22 +998,48 @@ std::optional TreeSupport::increaseSingleArea( if (ceil_radius_before != volumes_.ceilRadius(radius, settings.use_min_distance_)) { + if(anti_preferred_applied) + { + increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); + } if (current_elem.to_buildplate_) { - to_bp_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, settings.type_, false, settings.use_min_distance_))); + bool avoidance_handled = false; + to_bp_data = increased; + if(settings.use_anti_preferred_) + { + to_bp_data = to_bp_data.difference(volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + avoidance_handled = true; + } + if(! avoidance_handled) + { + to_bp_data = to_bp_data.difference(volumes_.getAvoidance(radius, layer_idx - 1, settings.type_, false, settings.use_min_distance_)); + } + to_bp_data = TreeSupportUtils::safeUnion(to_bp_data); } if (config.support_rests_on_model && (! current_elem.to_buildplate_ || mergelayer)) { - to_model_data = TreeSupportUtils::safeUnion(increased.difference( - volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_))); + bool avoidance_handled = false; + to_model_data = increased; + if(settings.use_anti_preferred_) + { + to_model_data = to_model_data.difference(volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); + avoidance_handled = true; + } + if(! avoidance_handled) + { + to_bp_data = to_bp_data.difference(volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); + } + to_model_data = TreeSupportUtils::safeUnion(to_model_data); } check_layer_data = current_elem.to_buildplate_ ? to_bp_data : to_model_data; if (check_layer_data.area() < 1) { spdlog::error( - "Lost area by doing catch up from {} to radius {}", + "Lost area by doing catch up from {} to radius {} planned increase was {}", ceil_radius_before, - volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance_)); + volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance_), + planned_foot_increase); } } } From 2e5d928c8017e3c2b5bebccb461653b02aea6349 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 21 Apr 2024 06:50:40 +0200 Subject: [PATCH 064/101] Remove support areas around lines missing because of cradle z distance as if they existed. --- src/TreeSupport.cpp | 87 +++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 980e50601b..1ad19b38a9 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2332,8 +2332,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora 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 @@ -2375,43 +2373,70 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) + if (! cradle_data[layer_idx][cradle_idx]->lines_[line_idx].empty()) { - Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; - bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; - LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; - if(is_roof) + Polygons previous_line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().area_; + LayerIndex previous_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().layer_idx_; + for (int64_t height_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - 1; height_idx >= 0; height_idx--) { - std::lock_guard critical_section_cradle(critical_support_roof_storage); + Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; + LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; + bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; + if(!is_base) + { + for(LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) + { + Polygons line_areas = TreeSupportUtils::safeOffsetInc( + previous_line_area, + config.xy_distance, + volumes_.getCollision(0, xy_dist_layer_idx), + config.xy_min_distance + config.min_feature_size, + 0, + 0, + config.min_feature_size, + &config.simplifier); + std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); + cradle_line_xy_distance_areas[xy_dist_layer_idx].add(line_areas); + } + } - if(cradle_support_line_roof_areas.size()<=layer_idx) + if (is_roof) { - cradle_support_line_roof_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); + std::lock_guard critical_section_cradle(critical_support_roof_storage); + + if (cradle_support_line_roof_areas.size() <= layer_idx) + { + cradle_support_line_roof_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + } + cradle_support_line_roof_areas[cradle_line_layer_idx].add(line_area); } - cradle_support_line_roof_areas[cradle_line_layer_idx].add(line_area); - } - else - { - std::lock_guard critical_section_cradle(critical_cradle_support_line_areas); + else + { + std::lock_guard critical_section_cradle(critical_cradle_support_line_areas); - if(cradle_support_line_areas.size()<=layer_idx) + if (cradle_support_line_areas.size() <= layer_idx) + { + cradle_support_line_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + } + cradle_support_line_areas[cradle_line_layer_idx].add(line_area); + } + if (!is_base) { - cradle_support_line_areas.resize(layer_idx+1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size()-height_idx); + Polygons line_areas = TreeSupportUtils::safeOffsetInc( + line_area, + config.xy_distance, + volumes_.getCollision(0, cradle_line_layer_idx), + config.xy_min_distance + config.min_feature_size, + 0, + 0, + config.min_feature_size, + &config.simplifier); + std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); + cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); } - cradle_support_line_areas[cradle_line_layer_idx].add(line_area); - } - if(!cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_) - { - Polygons line_areas = TreeSupportUtils::safeOffsetInc(line_area, - config.xy_distance, - volumes_.getCollision(0,cradle_line_layer_idx), - config.xy_min_distance + config.min_feature_size, - 0, - 0, - config.min_feature_size, - &config.simplifier); - std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); - cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); + previous_layer_idx = cradle_line_layer_idx; + previous_line_area = line_area; } } } From 872e8e7dd1c96067b6ae3293a7e67f392341376f Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 21 Apr 2024 10:30:01 +0200 Subject: [PATCH 065/101] Fix "Removing already placed tip" when a cradle roof base is only valid on the first layer and can't be supported below. Now it will just be a roof tip instead. --- src/TreeSupportTipGenerator.cpp | 54 ++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 38bba803a7..1935309d81 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1366,10 +1366,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector1) { @@ -1388,9 +1385,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorlayer_idx_),line_opt.value()->line_.front()); } } - Polygons relevant_forbidden = - volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); - cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ /2).unionPolygons(cradle_base).difference(relevant_forbidden); + cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ /2).unionPolygons(cradle_base); } } @@ -1403,7 +1398,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector> roofs; roofs.emplace_back(cradle_base, -1); - for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) + for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) { std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1,line_idx); if(!line_opt) @@ -1412,12 +1407,13 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorarea_.offset(line_base_offset,ClipperLib::jtRound).difference(forbidden_here),line_idx); + roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset,ClipperLib::jtRound),line_idx); } for(auto roof_area_pair : roofs) { + Polygons roof_area_before = roof_area_pair.first; Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( roof_area_pair.first,roof_outset_,forbidden_here, config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) @@ -1425,7 +1421,6 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorEPSILON && dtt_roof < support_roof_layers_) @@ -1463,7 +1458,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorEPSILON) { - OverhangInformation cradle_overhang(overhang_part,true, cradle_data_[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); + OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); cradle.overhang_[layer_idx-dtt_roof].emplace_back(cradle_overhang); } } @@ -1851,9 +1846,29 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector config_.branch_radius ? tip_radius - config_.branch_radius : 0; double hidden_radius_increases = hidden_radius / (config_.branch_radius * (std::max(config_.diameter_scale_bp_radius - config_.diameter_angle_scale_factor, 0.0))); + TipRoofType roof_type = TipRoofType::NONE; + if (! overhang_data.is_cradle_ && roof_tip_layers > 0) + { + roof_type = TipRoofType::IS_ROOF; + } + else if(overhang_data.is_roof_) + { + roof_type = TipRoofType::SUPPORTS_ROOF; + } + + if(!overhang_data.is_roof_ && overhang_data.is_cradle_ && !overhang_data.isCradleLine() && cradle_base_roof_) + { + roof_type = TipRoofType::IS_ROOF; + // No information about the amount of missing roof layers for cradle bases is stored. + // So it is only known that there could be one layer of roof. So to keep consistent behaviour only add one roof tip layer. + // todo change that in the future? + dont_move_until = 1; + } + if(overhang_data.is_cradle_ && tip_dtt < cradle_tip_dtt_) { - if((overhang_data.isCradleLine() && large_cradle_line_tips_) || (!overhang_data.isCradleLine() && (! cradle_base_roof_ || large_cradle_line_tips_))) + if((overhang_data.isCradleLine() && large_cradle_line_tips_) || + (!overhang_data.isCradleLine() && (! overhang_data.is_roof_ || large_cradle_line_tips_))) { tip_dtt = cradle_tip_dtt_; } @@ -1866,7 +1881,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector 0) ? TipRoofType::IS_ROOF : (overhang_data.is_roof_ ? TipRoofType::SUPPORTS_ROOF : TipRoofType::NONE), + roof_type, overhang_data.is_cradle_, disable_ovalization, additional_ovalization_targets); @@ -1927,10 +1942,13 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( PolygonUtils::moveInside(roof_on_layer_above, from); // Remove branches should have interface above them, but don't. Should never happen. if (roof_on_layer_above.empty() - || (!roof_on_layer_above.inside(elem->result_on_layer_) && vSize2(from-elem->result_on_layer_) > std::pow(config_.getRadius(0) + FUDGE_LENGTH,2))) + || (!roof_on_layer_above.inside(elem->result_on_layer_) && vSize2(from-elem->result_on_layer_) > std::pow(config_.getRadius(*elem) + FUDGE_LENGTH,2))) { to_be_removed.emplace_back(elem); - spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}.",vSize(from-elem->result_on_layer_)); + spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}. Radius is {} on layer {}.", + vSize(from-elem->result_on_layer_), + config_.getRadius(*elem), + layer_idx); } } } @@ -2268,9 +2286,11 @@ void TreeSupportTipGenerator::generateTips( } } - if (overhang_data.is_roof_ || overhang_data.is_cradle_) // Some roof may only be supported by a part of a tip + // Some roof may only be supported by a part of a tip. + // Cradle lines may move further, because the tips of cradle lines are generated from the (center) line. + if (overhang_data.is_roof_ || overhang_data.is_cradle_) { - polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.is_cradle_?2:1) * config_.getRadius(0) + FUDGE_LENGTH / 2); + polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.isCradleLine() ? cradle_line_width_ / 2 : 0) + config_.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); From 879ecb1a59c00cf09960523ce087da85c668bd30 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:49:36 +0200 Subject: [PATCH 066/101] Prevent roof from generating close to the cradle as any overhang there is already supported. This can prevent cradle lines from being removed as it prevents (some) tips to support said roof. --- src/TreeSupportTipGenerator.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 1935309d81..994c682494 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1597,6 +1597,26 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m dropOverhangAreas(mesh, dropped_overhangs, true); } + std::vector all_cradle_areas(cradle_data_.size()); + for(LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) + { + for(size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + { + + for (auto [line_idx, lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) + { + for (auto [height_idx, line] : lines | ranges::views::enumerate) + { + all_cradle_areas[line.layer_idx_].add(line.area_); + } + } + for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) + { + all_cradle_areas[layer_idx-base_idx].add(base); + } + } + } + cura::parallel_for( 0, mesh.overhang_areas.size() - z_distance_delta_, @@ -1631,6 +1651,9 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m config_.support_line_distance / 2, &config_.simplifier); + //If an area is already supported by cradle don't put roof there. + full_overhang_area = full_overhang_area.difference(all_cradle_areas[layer_idx].unionPolygons().offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); + for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) { const Polygons forbidden_next = volumes_ @@ -2096,7 +2119,7 @@ void TreeSupportTipGenerator::generateTips( } all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons().offset(EPSILON).unionPolygons(); core_overhang = core_overhang.difference(support_free_areas[layer_idx]); - core_overhang = core_overhang.difference(all_cradle_areas[layer_idx]); + core_overhang = core_overhang.difference(all_cradle_areas[layer_idx].offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) { From f5186eb887388d9d788da37a1178fc6afa7c4f25 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 22 Apr 2024 08:44:58 +0200 Subject: [PATCH 067/101] Fix regressions with fractional tree support. Also ensure fractional support correctly handles interface priority. --- include/TreeSupport.h | 1 + src/TreeSupport.cpp | 106 +++++++++++++++++++++++++++++++----------- 2 files changed, 81 insertions(+), 26 deletions(-) diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 4a58c60ebf..0d4a5cb6a5 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -330,6 +330,7 @@ class TreeSupport */ void generateSupportSkin(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, diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 1ad19b38a9..8fd60320d7 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2319,6 +2319,7 @@ void TreeSupport::dropNonGraciousAreas( } void TreeSupport::generateSupportSkin(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, @@ -2470,10 +2471,24 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora fake_roof = fake_roof.unionPolygons(); fake_roofs[layer_idx] = fake_roof; + + Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; + remove_from_support.add(fake_roof_lines); + remove_from_support.add(support_free_areas[layer_idx]); + remove_from_support = remove_from_support.unionPolygons(); + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(fake_roof_lines); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(cradle_line_xy_distance_areas[layer_idx].unionPolygons()); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].unionPolygons(); + Polygons original_fractional = support_layer_storage_fractional[layer_idx]; + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); + //ensure there is at lease one line space for fractional support. Overlap is removed later! + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].offset(config.support_line_width).intersection(original_fractional); + + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(remove_from_support); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(remove_from_support); support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + support_layer_storage_fractional[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].unionPolygons(); support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].unionPolygons(); @@ -2498,19 +2513,21 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora all_roof.add(support_roof_extra_wall_storage_fractional[layer_idx]); all_roof = all_roof.unionPolygons(); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(all_roof); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(all_roof); break; } case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: { Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons support_areas = support_layer_storage[layer_idx].unionPolygons(support_layer_storage_fractional[layer_idx]); Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); AABB invalid_roof_aabb = AABB(invalid_roof); storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); - support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_layer_storage[layer_idx]); - support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_layer_storage[layer_idx]); - support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); - support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_areas); + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_areas); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_areas); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].difference(support_areas); break; } default: @@ -2522,7 +2539,6 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora if(!support_free_areas[layer_idx].empty()) { - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(support_free_areas[layer_idx]); remove_from_next_roof.add(support_free_areas[layer_idx]); } @@ -2943,13 +2959,17 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(existing_roof); support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(existing_roof); - break; + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(existing_roof); + break; } case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: { Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx].unionPolygons(support_skin_storage[layer_idx])); + Polygons support_areas = support_layer_storage[layer_idx]; + support_areas.add(support_skin_storage[layer_idx]); + support_areas.add(support_layer_storage_fractional[layer_idx]); + Polygons invalid_roof = existing_roof.intersection(support_areas.unionPolygons()); AABB invalid_roof_aabb = AABB(invalid_roof); storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); break; @@ -2976,6 +2996,8 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(interface_lines); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(interface_lines); + } break; @@ -3049,37 +3071,69 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor { constexpr bool convert_every_part = true; // Convert every part into a PolygonsPart for the support. - storage.support.supportLayers[layer_idx].fillInfillParts( - support_layer_storage[layer_idx], - config.support_line_width, - config.support_wall_count, - false, - convert_every_part); - - storage.support.supportLayers[layer_idx].fillInfillParts( - support_skin_storage[layer_idx], - config.support_line_width, - std::max(config.support_wall_count - 1,0), - false, - convert_every_part, - config.support_skin_line_distance, - EFillMethod::ZIG_ZAG); - // This only works because fractional support is always just projected upwards regular support or skin. // Also technically violates skin height, but there is no good way to prevent that. Polygons fractional_support; Polygons fractional_skin; + Polygons support_areas = support_layer_storage[layer_idx]; + Polygons skin_areas = support_skin_storage[layer_idx]; if(layer_idx > 0) { fractional_support = support_layer_storage_fractional[layer_idx].intersection(support_layer_storage[layer_idx - 1]); fractional_skin = support_layer_storage_fractional[layer_idx].intersection(support_skin_storage[layer_idx - 1]); + + //To remove the lines it needs to be known what the lines are. This can not be done in the loop above, so it needs to be done here again for fractional support. + // todo deduplicate code + if(interface_pref == InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE) + { + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons tree_lines; + tree_lines = + tree_lines.unionPolygons + ( + TreeSupportUtils::generateSupportInfillLines(fractional_support, config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) + .offsetPolyLine(config.support_line_width / 2) + ); + + Polygons support_skin_lines; + support_skin_lines = + support_skin_lines.unionPolygons + ( + TreeSupportUtils::generateSupportInfillLines(fractional_skin, config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, std::max(0, config.support_wall_count - 1), EFillMethod::LINES) + .offsetPolyLine(config.support_line_width / 2) + ); + Polygons invalid_roof = existing_roof.intersection(tree_lines); + AABB invalid_roof_aabb = AABB(invalid_roof); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + } + + //Remove overlap between fractional and regular support that may have been created in generateSupportSkin. + support_areas = support_areas.difference(support_layer_storage_fractional[layer_idx]); + skin_areas = skin_areas.difference(support_layer_storage_fractional[layer_idx]); } else { fractional_support = support_layer_storage_fractional[layer_idx]; } + storage.support.supportLayers[layer_idx].fillInfillParts( + support_areas, + config.support_line_width, + config.support_wall_count, + false, + convert_every_part); + + storage.support.supportLayers[layer_idx].fillInfillParts( + skin_areas, + config.support_line_width, + std::max(config.support_wall_count - 1,0), + false, + convert_every_part, + config.support_skin_line_distance, + EFillMethod::ZIG_ZAG); + + storage.support.supportLayers[layer_idx].fillInfillParts( fractional_support, config.support_line_width, @@ -3277,7 +3331,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - generateSupportSkin(support_layer_storage,support_skin_storage, + generateSupportSkin(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, storage,layer_tree_polygons,cradle_data); From 14f8f3d403c02d4ff2e580bbdbdc21d00147a6cd Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 22 Apr 2024 08:47:39 +0200 Subject: [PATCH 068/101] Improve anti-preferred handling in increaseSingleArea. --- src/TreeSupport.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 8fd60320d7..776817e7e3 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -840,7 +840,7 @@ std::optional TreeSupport::increaseSingleArea( to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; } - else if(anti_preferred_applied) + else if(anti_preferred_applied && next_radius > actual_radius) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); } @@ -878,6 +878,8 @@ std::optional TreeSupport::increaseSingleArea( return check_layer_data_2.area() > 1; }; coord_t ceil_radius_before = volumes_.ceilRadius(radius, settings.use_min_distance_); + coord_t ceil_actual_radius_before = volumes_.ceilRadius(actual_radius, settings.use_min_distance_); + // If the Collision Radius is smaller than the actual radius, check if it can catch up without violating the avoidance. if (config.getCollisionRadius(current_elem) < config.increase_radius_until_radius && config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) @@ -926,7 +928,6 @@ std::optional TreeSupport::increaseSingleArea( radius = config.getCollisionRadius(current_elem); //If a hidden radius increase was used, also do some catching up. - if (current_elem.hidden_radius_increase_ > 0) { coord_t target_radius = config.getRadius(current_elem); @@ -993,12 +994,13 @@ std::optional TreeSupport::increaseSingleArea( { current_elem.buildplate_radius_increases_ += planned_foot_increase; radius = config.getCollisionRadius(current_elem); + actual_radius = config.getRadius(current_elem); } } if (ceil_radius_before != volumes_.ceilRadius(radius, settings.use_min_distance_)) { - if(anti_preferred_applied) + if(anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_)) { increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); } From 73313c61624bc86d489f1b5e14ac106d617f32f0 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:13:32 +0200 Subject: [PATCH 069/101] Fixes and refactoring related to cradle line generation. --- include/TreeSupportCradle.h | 13 ++- include/TreeSupportTipGenerator.h | 7 +- src/TreeSupportTipGenerator.cpp | 143 ++++++++++++++++-------------- 3 files changed, 93 insertions(+), 70 deletions(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 299f3a435d..f362f2cc5e 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -108,14 +108,17 @@ struct TreeSupportCradle size_t config_cradle_layers_min_; coord_t config_cradle_length_min_; + size_t cradle_line_count_; - TreeSupportCradle(LayerIndex layer_idx, Point2LL center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min) + + TreeSupportCradle(LayerIndex layer_idx, Point2LL center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min, size_t cradle_line_count) : layer_idx_(layer_idx) , centers_({center}) , shadow_idx_(shadow_idx) , is_roof_(roof) , config_cradle_layers_min_(cradle_layers_min) , config_cradle_length_min_(cradle_length_min) + ,cradle_line_count_(cradle_line_count) { } @@ -149,6 +152,14 @@ struct TreeSupportCradle return centers_[index]; } + size_t getIndexForLineEnd(Point2LL line_end, LayerIndex layer_idx_req) + { + Point2LL current_direction = line_end - getCenter(layer_idx_req); + double angle = std::atan2(current_direction.Y, current_direction.X); + size_t angle_idx = size_t(std::round(((angle+std::numbers::pi)/(2.0*std::numbers::pi)) * double(cradle_line_count_))) % cradle_line_count_; + return angle_idx; + } + void verifyLines() { for (size_t line_idx = 0; line_idx < lines_.size(); line_idx++) diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index c74bf0514c..72ed6b67a0 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -142,10 +142,11 @@ class TreeSupportTipGenerator /*! * \brief Provides areas that do not have a connection to the buildplate or a certain height. * \param layer_idx The layer said area is on. - * \param idx_of_area_below The index of the area below. Only areas that rest on this area will be returned + * \param idx_of_area The index of the area. Only areas that either rest on this area or this area rests on (depending on above) will be returned + * \param above Should the areas above it, that rest on this area should be returned (if true) or if areas that this area rests on (if false) should be returned. * \return A vector containing the areas, how many layers of material they have below them and the idx of each area usable to get the next one layer above. */ - std::vector getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area_below); + std::vector getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area, bool above); /*! * \brief Provides areas that do not have a connection to the buildplate or any other non support material below it. @@ -463,6 +464,8 @@ class TreeSupportTipGenerator mutable std::vector> floating_parts_cache_; mutable std::vector>> floating_parts_map_; + mutable std::vector>> floating_parts_map_below_; + std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 994c682494..a140590639 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -486,6 +486,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes LayerIndex start_layer = 1; floating_parts_cache_.resize(max_layer+1); floating_parts_map_.resize(max_layer+1); + floating_parts_map_below_.resize(max_layer+1); std::mutex critical_sections; Polygons completely_supported = volumes_.getCollision(0,0,true); @@ -521,6 +522,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes std::lock_guard critical_section_add(critical_sections); floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0,overhang_area); floating_parts_map_ [layer_idx].emplace_back(std::vector()); + floating_parts_map_below_ [layer_idx].emplace_back(std::vector()); return ; } @@ -539,6 +541,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes } } + if(min_resting_on_layers < cradle_layers_ && add && overhang_area + supported_overhang_area < cradle_area_threshold_) { std::lock_guard critical_section_add(critical_sections); @@ -548,6 +551,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes } floating_parts_map_[layer_idx].emplace_back(std::vector()); + floating_parts_map_below_[layer_idx].emplace_back(idx_of_floating_below); floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1,overhang_area + supported_overhang_area); } else @@ -563,7 +567,7 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes } -std::vector TreeSupportTipGenerator::getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area_below) +std::vector TreeSupportTipGenerator::getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area, bool above) { std::vector result; @@ -582,25 +586,30 @@ std::vector TreeSupportTipG if (has_result) { std::lock_guard critical_section(*critical_floating_parts_cache_); - if (floating_parts_cache_[layer_idx].size()) + if (!floating_parts_cache_[layer_idx].empty() && above) { - for (size_t resting_idx : floating_parts_map_[layer_idx - 1][idx_of_area_below]) + for (size_t resting_idx : floating_parts_map_[layer_idx - 1][idx_of_area]) { result.emplace_back(floating_parts_cache_[layer_idx][resting_idx]); } } + else if (!floating_parts_cache_[layer_idx - 1].empty() && !above) + { + for (size_t resting_idx : floating_parts_map_below_[layer_idx][idx_of_area]) + { + result.emplace_back(floating_parts_cache_[layer_idx - 1][resting_idx]); + } + } } else { spdlog::error("Requested not calculated unsupported area."); return result; } - return result; - - } + std::vector TreeSupportTipGenerator::getFullyUnsupportedArea(LayerIndex layer_idx) { std::vector result; @@ -665,7 +674,7 @@ std::vector>> TreeSupportTipGenerator::generat Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); std::vector additional_centers; - TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_); + TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_, cradle_line_count_); for(size_t z_distance = 0; z_distance < config_.z_distance_top_layers; z_distance++) { accumulated_model[z_distance] = pointy_info.area; @@ -689,7 +698,7 @@ std::vector>> TreeSupportTipGenerator::generat { for (size_t pointy_idx : all_pointy_idx) { - for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx)) + for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx, true)) { if (next_pointy_data.height != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it @@ -710,6 +719,30 @@ std::vector>> TreeSupportTipGenerator::generat { blocked_by_dedupe = true; } + + std::vector all_pointy_idx_below {next_pointy_data.index}; + for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_idx_below.empty(); cradle_down_layer--) + { + std::vector next_all_pointy_idx_below; + + for (size_t pointy_idx_below : all_pointy_idx_below) + { + for (auto prev_pointy_data : getUnsupportedArea(layer_idx + cradle_down_layer - 1 + z_distance_delta_, pointy_idx_below, false)) + { + if(prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) + { + // Only add if area below does not have it's own cradle. + if(prev_pointy_data.height < cradle_layers_min_) + { + accumulated_model[cradle_down_layer].add(prev_pointy_data.area); + next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); + } + } + } + } + all_pointy_idx_below = next_all_pointy_idx_below; + accumulated_model[cradle_down_layer] = accumulated_model[cradle_down_layer].unionPolygons(); + } } } all_pointy_idx = next_pointy_idx; @@ -790,7 +823,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector removed_directions; + std::vector removed_directions(cradle_line_count_); const auto& accumulated_model = shadow_data[layer_idx][cradle->shadow_idx_]; for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) { @@ -835,7 +868,7 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector> vector_distance_map; // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing Polygons shortened_lines_to_center; @@ -890,14 +920,11 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector ordered_lines_to_center(cradle_line_count_); // Evaluate which lines are still valid after the avoidance was subtracted for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) @@ -907,13 +934,17 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorgetIndexForLineEnd(further, layer_idx + idx); + + if(removed_directions[angle_idx]) + { + continue; + } - shortened_lines_to_center[line_idx].clear(); - shortened_lines_to_center[line_idx].add(closer); - shortened_lines_to_center[line_idx].add(further); bool keep_line = false; bool found_candidate = false; bool too_short = (vSize(closer - further)) < cradle_length_min_; + bool too_long_jump = !cradle->lines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > sqrt(cradle_area_threshold_) / 2; // a cradle line should also be removed if there will be no way to support it if(idx >= cradle_z_distance_layers_ + 1) { @@ -925,67 +956,44 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector 0.99) + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); + } + else + { + // Prefer lines not touching the center. Otherwise, prefer the line closest to the center + if(distance_from_center > FUDGE_LENGTH) { - found_candidate = true; - // found line, check if there is one that is closer, if not then check if distance to center is expected - bool found_close_line = direction.second + config_.xy_min_distance + config_.min_feature_size > distance_from_center; - if (found_close_line) + if(vSize(ordered_lines_to_center[angle_idx].front() - center) < FUDGE_LENGTH) + { + ordered_lines_to_center[angle_idx].clear(); + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); + } + else if(distance_from_center < vSize(ordered_lines_to_center[angle_idx].front() - center)) { - keep_line = true; - break; + ordered_lines_to_center[angle_idx].clear(); + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); } } - } - } - bool was_removed = false; - - for (auto [direction_idx, direction] : removed_directions | ranges::views::enumerate) - { - double cosine = (dot(direction, current_direction)) / (vSize(current_direction) * vSize(direction)); - if (cosine > 0.99) - { - was_removed = true; } } - if (too_short || was_removed || (! keep_line && found_candidate)) - { - if (! was_removed) - { - removed_directions.emplace_back(current_direction); - } - shortened_lines_to_center[line_idx].clear(); - } } - size_t line_remove_idx = 0; - - while (line_remove_idx < shortened_lines_to_center.size()) + for (auto [angle_idx, next_line] : ordered_lines_to_center | ranges::views::enumerate) { - if (shortened_lines_to_center[line_remove_idx].empty()) - { - shortened_lines_to_center.remove(line_remove_idx); - } - else + if (next_line.empty()) { - line_remove_idx++; + removed_directions[angle_idx] = true; + continue; } - } - - - for (auto [next_line_idx, next_line] : shortened_lines_to_center | ranges::views::enumerate) - { - Point2LL current_direction = next_line.front() - next_line.back(); - double angle = std::atan2(current_direction.Y, current_direction.X); - - size_t angle_idx = size_t(std::round(((angle+std::numbers::pi)/(2.0*std::numbers::pi)) * double(cradle_line_count_))) % cradle_line_count_; Polygon line(next_line); //Handle cradle_z_distance_layers by overwriting first element in the vector until valid distance is reached. if(idx <= cradle_z_distance_layers_ + 1 && !cradle->lines_[angle_idx].empty()) @@ -1472,6 +1480,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector Date: Tue, 23 Apr 2024 13:11:15 +0200 Subject: [PATCH 070/101] Fix cradle roof type "Cradle and Base" --- src/TreeSupport.cpp | 8 ++++---- src/TreeSupportTipGenerator.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 776817e7e3..82a4ad9a58 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2360,10 +2360,10 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora { std::lock_guard critical_section_cradle(critical_support_roof_storage); cradle_base_areas[layer_idx-base_idx].add(base); - support_roof_storage[layer_idx-base_idx].add(base); - if(base_idx == 0 && layer_idx + 1 < support_roof_storage_fractional.size()) + (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx-base_idx].add(base); + if(base_idx == 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) { - support_roof_storage_fractional[layer_idx+1].add(base); + (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx+1].add(base); } } else @@ -2547,7 +2547,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); Polygons roof = support_roof_storage[layer_idx]; - Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].difference(cradle_base_areas[layer_idx]); + Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; if(config.support_roof_wall_count) { roof = roof.unionPolygons(cradle_lines_roof); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index a140590639..feca820f5e 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1393,7 +1393,7 @@ void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vectorlayer_idx_),line_opt.value()->line_.front()); } } - cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ /2).unionPolygons(cradle_base); + cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); } } From 5153fe05f6f1351104b36281676d7fe140a4e540 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 24 Apr 2024 07:19:04 +0200 Subject: [PATCH 071/101] Improved formatting --- include/TreeModelVolumes.h | 2 - include/TreeSupport.h | 62 +- include/TreeSupportCradle.h | 93 ++- include/TreeSupportElement.h | 130 ++-- include/TreeSupportSettings.h | 26 +- include/TreeSupportTipGenerator.h | 35 +- include/TreeSupportUtils.h | 7 +- src/TreeModelVolumes.cpp | 124 ++-- src/TreeSupport.cpp | 798 +++++++++++----------- src/TreeSupportTipGenerator.cpp | 1037 +++++++++++++++-------------- 10 files changed, 1196 insertions(+), 1118 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 442b19168d..bfa1490098 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -184,8 +184,6 @@ class TreeModelVolumes const Polygons& getSupportBlocker(LayerIndex layer_idx); - - private: /*! * \brief Convenience typedef for the keys to the caches diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 0d4a5cb6a5..b5f6cf8a8f 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -48,12 +48,11 @@ using PropertyAreas = std::map; struct FakeRoofArea { - FakeRoofArea(Polygons area, coord_t line_distance, bool fractional): - area_(area) + FakeRoofArea(Polygons area, coord_t line_distance, bool fractional) + : area_(area) , line_distance_(line_distance) , fractional_(fractional) { - } /*! * \brief Area that should be a fake roof. @@ -117,7 +116,11 @@ class TreeSupport * \param cradle_data_model[out] All generated cradles, with its corresponding cradle lines. */ - void generateInitialAreas(const SliceMeshStorage& mesh, std::vector>& move_bounds, SliceDataStorage& storage,std::vector>& cradle_data_model); + void generateInitialAreas( + const SliceMeshStorage& mesh, + std::vector>& move_bounds, + SliceDataStorage& storage, + std::vector>& cradle_data_model); /*! @@ -233,7 +236,8 @@ class TreeSupport * \param to_bp_areas[in,out] The Elements of the current Layer that will reach the buildplate. * Value is the influence area where the center of a circle of support may be placed. - * \param to_model_areas[in,out] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is + * \param to_model_areas[in,out] The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate + is * not forced to. Value is the influence area where the center of a circle of support may be placed. * \param influence_areas[in,out] The Elements of the current Layer without * avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed. @@ -241,13 +245,14 @@ class TreeSupport * \param move_bounds[in,out] All currently existing influence areas * \param cradle_data[in] Information about all cradle lines on this layer. */ - void handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, - PropertyAreas& to_model_areas, - PropertyAreas& influence_areas, - PropertyAreas& bypass_merge_areas, - LayerIndex layer_idx, - std::vector>& move_bounds, - std::vector>& cradle_data); + void handleCradleLineValidity( + PropertyAreasUnordered& to_bp_areas, + PropertyAreas& to_model_areas, + PropertyAreas& influence_areas, + PropertyAreas& bypass_merge_areas, + LayerIndex layer_idx, + std::vector>& move_bounds, + std::vector>& cradle_data); /*! * \brief Propagates influence downwards, and merges overlapping ones. @@ -317,7 +322,8 @@ class TreeSupport 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 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. * \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_roof_storage[in] Areas where support was replaced with roof. @@ -329,16 +335,17 @@ class TreeSupport * \param cradle_data[in] All currently existing cradles, with its corresponding cradle lines. */ - void generateSupportSkin(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, - SliceDataStorage& storage, - std::vector>& layer_tree_polygons, - std::vector>& cradle_data); + void generateSupportSkin( + 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, + SliceDataStorage& storage, + std::vector>& layer_tree_polygons, + std::vector>& cradle_data); /*! * \brief Filters out holses that would cause support to be printed mid-air. @@ -354,10 +361,11 @@ class TreeSupport * \param support_roof_storage[in] Areas where support was replaced with roof. * \param storage[in,out] The storage where the support should be stored. */ - void finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, - std::vector& support_skin_storage, - std::vector& support_layer_storage_fractional, - SliceDataStorage& storage); + void finalizeInterfaceAndSupportAreas( + std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_layer_storage_fractional, + SliceDataStorage& storage); /*! * \brief Draws circles around result_on_layer points of the influence areas and applies some post processing. diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index f362f2cc5e..d798d27976 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -4,40 +4,38 @@ #ifndef CURAENGINE_TREESUPPORTCRADLE_H #define CURAENGINE_TREESUPPORTCRADLE_H +#include + #include "TreeSupportElement.h" #include "TreeSupportEnums.h" #include "utils/Coord_t.h" #include "utils/polygon.h" -#include namespace cura { -//todo rename file as now general TreeSupportTipDataStructures +// todo rename file as now general TreeSupportTipDataStructures struct TreeSupportCradle; struct OverhangInformation { - - OverhangInformation(Polygons overhang, bool roof): - overhang_(overhang), - is_roof_(roof), - is_cradle_(false), - cradle_layer_idx_(-1), - cradle_line_idx_(-1), - cradle_(nullptr) + OverhangInformation(Polygons overhang, bool roof) + : overhang_(overhang) + , is_roof_(roof) + , is_cradle_(false) + , cradle_layer_idx_(-1) + , cradle_line_idx_(-1) + , cradle_(nullptr) { - } - OverhangInformation(Polygons overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1,int32_t cradle_line_idx = -1): - overhang_(overhang), - is_roof_(roof), - is_cradle_(true), - cradle_layer_idx_(cradle_layer_idx), - cradle_line_idx_(cradle_line_idx), - cradle_(cradle) + OverhangInformation(Polygons overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1, int32_t cradle_line_idx = -1) + : overhang_(overhang) + , is_roof_(roof) + , is_cradle_(true) + , cradle_layer_idx_(cradle_layer_idx) + , cradle_line_idx_(cradle_line_idx) + , cradle_(cradle) { - } Polygons overhang_; @@ -49,13 +47,12 @@ struct OverhangInformation bool isCradleLine() { - return is_cradle_ && cradle_line_idx_ >= 0 && cradle_layer_idx_>=0; + return is_cradle_ && cradle_line_idx_ >= 0 && cradle_layer_idx_ >= 0; } - }; struct TreeSupportCradleLine { - //required to shrink a vector using resize + // required to shrink a vector using resize TreeSupportCradleLine() { spdlog::error("Dummy TreeSupportCradleLine constructor called"); @@ -113,12 +110,12 @@ struct TreeSupportCradle TreeSupportCradle(LayerIndex layer_idx, Point2LL center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min, size_t cradle_line_count) : layer_idx_(layer_idx) - , centers_({center}) + , centers_({ center }) , shadow_idx_(shadow_idx) , is_roof_(roof) , config_cradle_layers_min_(cradle_layers_min) , config_cradle_length_min_(cradle_length_min) - ,cradle_line_count_(cradle_line_count) + , cradle_line_count_(cradle_line_count) { } @@ -140,12 +137,12 @@ struct TreeSupportCradle Point2LL getCenter(LayerIndex layer_idx_req) { - if(layer_idx_req previous_layer_idx + up_idx || - lines_[line_idx][up_idx].line_.size() < 2 || - lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) + if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 + || lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) { lines_[line_idx].clear(); } @@ -187,22 +183,25 @@ struct TreeSupportCradle } for (size_t up_idx = 1; up_idx < lines_[line_idx].size(); up_idx++) { - if(!lines_[line_idx][up_idx].is_base_) + if (! lines_[line_idx][up_idx].is_base_) { - if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || - lines_[line_idx][up_idx].line_.size()<2 || - lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) + if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 + || lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) { if (up_idx <= config_cradle_layers_min_) { - spdlog::debug("Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}",layer_idx_,line_idx,lines_[line_idx][up_idx].layer_idx_); + spdlog::debug( + "Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}", + layer_idx_, + line_idx, + lines_[line_idx][up_idx].layer_idx_); lines_[line_idx].clear(); break; } else { - spdlog::debug("Partially removing cradle line of cradle on layer {} line at {} at height {}",layer_idx_,line_idx,up_idx); - lines_[line_idx].resize(up_idx-1); + spdlog::debug("Partially removing cradle line of cradle on layer {} line at {} at height {}", layer_idx_, line_idx, up_idx); + lines_[line_idx].resize(up_idx - 1); break; } } @@ -214,28 +213,28 @@ struct TreeSupportCradle struct CradlePresenceInformation { - CradlePresenceInformation(TreeSupportCradle* cradle,LayerIndex layer_idx,size_t line_idx): - cradle_(cradle), - layer_idx_(layer_idx), - line_idx_(line_idx) - {} + CradlePresenceInformation(TreeSupportCradle* cradle, LayerIndex layer_idx, size_t line_idx) + : cradle_(cradle) + , layer_idx_(layer_idx) + , line_idx_(line_idx) + { + } TreeSupportCradle* cradle_; LayerIndex layer_idx_; size_t line_idx_; TreeSupportCradleLine* getCradleLine() { - return cradle_->getCradleLineOfIndex(layer_idx_,line_idx_).value(); + return cradle_->getCradleLineOfIndex(layer_idx_, line_idx_).value(); } bool cradleLineExists() { - return cradle_->getCradleLineOfIndex(layer_idx_,line_idx_).has_value(); + return cradle_->getCradleLineOfIndex(layer_idx_, line_idx_).has_value(); } - }; -} +} // namespace cura #endif // CURAENGINE_TREESUPPORTCRADLE_H diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index f8a386aaf9..f013bc6c66 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -20,34 +20,25 @@ namespace cura struct CradlePresenceInformation; struct AreaIncreaseSettings { - AreaIncreaseSettings() : - type_(AvoidanceType::FAST), - increase_speed_(0), - increase_radius_(false), - no_error_(false), - use_min_distance_(false), - use_anti_preferred_(false), - move_(false) + AreaIncreaseSettings() + : type_(AvoidanceType::FAST) + , increase_speed_(0) + , increase_radius_(false) + , no_error_(false) + , use_min_distance_(false) + , use_anti_preferred_(false) + , move_(false) { } - AreaIncreaseSettings - ( - AvoidanceType type, - coord_t increase_speed, - bool increase_radius, - bool simplify, - bool use_min_distance, - bool use_anti_preferred, - bool move - ) : - type_(type), - increase_speed_(increase_speed), - increase_radius_(increase_radius), - no_error_(simplify), - use_min_distance_(use_min_distance), - use_anti_preferred_(use_anti_preferred), - move_(move) + AreaIncreaseSettings(AvoidanceType type, coord_t increase_speed, bool increase_radius, bool simplify, bool use_min_distance, bool use_anti_preferred, bool move) + : type_(type) + , increase_speed_(increase_speed) + , increase_radius_(increase_radius) + , no_error_(simplify) + , use_min_distance_(use_min_distance) + , use_anti_preferred_(use_anti_preferred) + , move_(move) { } @@ -83,39 +74,40 @@ struct TreeSupportElement bool skip_ovalisation, bool influence_area_limit_active, coord_t influence_area_limit_range, - double hidden_radius_increase - ) : - target_height_(target_height), - target_position_(target_position), - next_position_(target_position), - next_height_(target_height), - effective_radius_height_(0), - 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), - buildplate_radius_increases_(0), - use_min_xy_dist_(use_min_xy_dist), - supports_roof_(supports_roof), - supports_cradle_(supports_cradle), - dont_move_until_(dont_move_until), - can_use_safe_radius_(can_use_safe_radius), - can_avoid_anti_preferred_(false), //todo init? - last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)), - missing_roof_layers_(force_tips_to_roof ? dont_move_until : 0), - roof_with_enforced_walls(false), - skip_ovalisation_(skip_ovalisation), - all_tips_({ target_position }), - influence_area_limit_active_(influence_area_limit_active), - influence_area_limit_range_(influence_area_limit_range), - hidden_radius_increase_(hidden_radius_increase) + double hidden_radius_increase) + : target_height_(target_height) + , target_position_(target_position) + , next_position_(target_position) + , next_height_(target_height) + , effective_radius_height_(0) + , 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) + , buildplate_radius_increases_(0) + , use_min_xy_dist_(use_min_xy_dist) + , supports_roof_(supports_roof) + , supports_cradle_(supports_cradle) + , dont_move_until_(dont_move_until) + , can_use_safe_radius_(can_use_safe_radius) + , can_avoid_anti_preferred_(false) + , // todo init? + last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)) + , missing_roof_layers_(force_tips_to_roof ? dont_move_until : 0) + , roof_with_enforced_walls(false) + , skip_ovalisation_(skip_ovalisation) + , all_tips_({ target_position }) + , influence_area_limit_active_(influence_area_limit_active) + , influence_area_limit_range_(influence_area_limit_range) + , hidden_radius_increase_(hidden_radius_increase) { RecreateInfluenceLimitArea(); } - TreeSupportElement(const TreeSupportElement& elem, Polygons* new_area) : // copy constructor that sets a new area + TreeSupportElement(const TreeSupportElement& elem, Polygons* new_area) + : // copy constructor that sets a new area TreeSupportElement(elem) { area_ = new_area; @@ -134,20 +126,19 @@ struct TreeSupportElement const std::function& getRadius, double diameter_scale_bp_radius, coord_t branch_radius, - double diameter_angle_scale_factor - ) : - 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_), - supports_cradle_(first.supports_cradle_ || second.supports_cradle_), - 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_)), - roof_with_enforced_walls(first.roof_with_enforced_walls && second.roof_with_enforced_walls), - skip_ovalisation_(false) + double diameter_angle_scale_factor) + : 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_) + , supports_cradle_(first.supports_cradle_ || second.supports_cradle_) + , 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_)) + , roof_with_enforced_walls(first.roof_with_enforced_walls && second.roof_with_enforced_walls) + , skip_ovalisation_(false) { if (first.target_height_ > second.target_height_) { @@ -196,7 +187,7 @@ struct TreeSupportElement first.last_area_increase_.no_error_ || second.last_area_increase_.no_error_, first.last_area_increase_.use_min_distance_ && second.last_area_increase_.use_min_distance_, first.can_avoid_anti_preferred_ && second.can_avoid_anti_preferred_, - first.last_area_increase_.move_ || second.last_area_increase_.move_); + first.last_area_increase_.move_ || second.last_area_increase_.move_); all_tips_ = first.all_tips_; all_tips_.insert(all_tips_.end(), second.all_tips_.begin(), second.all_tips_.end()); @@ -456,7 +447,6 @@ struct TreeSupportElement result.area_ = nullptr; return result; } - }; } // namespace cura diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 004884e6f7..a1e15a88e8 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -19,17 +19,17 @@ namespace cura template static A retrieveSetting(const Settings& settings, const std::string& key) { - if(settings.has(key)) + if (settings.has(key)) { return settings.get(key); } else { - for(std::string setting_key:settings.getKeys()) + for (std::string setting_key : settings.getKeys()) { - if(setting_key.find(key) != std::string::npos) + if (setting_key.find(key) != std::string::npos) { - return settings.get(setting_key); + return settings.get(setting_key); } } return settings.get(key); // this will cause a crash, but that's the expected behaviour in this case anyway @@ -98,9 +98,9 @@ struct TreeSupportSettings , min_feature_size(mesh_group_settings.get("min_feature_size")) , min_wall_line_width(settings.get("min_wall_line_width")) , fill_outline_gaps(settings.get("fill_outline_gaps")) - , support_skin_layers(round_up_divide(retrieveSetting(mesh_group_settings,"support_tree_support_skin_height"), layer_height)) - , support_skin_line_distance(retrieveSetting(mesh_group_settings,"support_tree_support_skin_line_distance")) - , support_tree_skin_for_large_tips_radius_threshold(retrieveSetting(mesh_group_settings,"support_tree_skin_for_large_tips_threshold") / 2) + , support_skin_layers(round_up_divide(retrieveSetting(mesh_group_settings, "support_tree_support_skin_height"), layer_height)) + , support_skin_line_distance(retrieveSetting(mesh_group_settings, "support_tree_support_skin_line_distance")) + , support_tree_skin_for_large_tips_radius_threshold(retrieveSetting(mesh_group_settings, "support_tree_skin_for_large_tips_threshold") / 2) , simplifier(Simplify(mesh_group_settings)) { layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); @@ -446,10 +446,10 @@ struct TreeSupportSettings && zag_skip_count == other.zag_skip_count && connect_zigzags == other.connect_zigzags && interface_preference == other.interface_preference && min_feature_size == other.min_feature_size && // interface_preference should be identical to ensure the tree will correctly interact with the roof. support_rest_preference == other.support_rest_preference && max_radius == other.max_radius && min_wall_line_width == other.min_wall_line_width - && fill_outline_gaps == other.fill_outline_gaps - && support_skin_layers == other.support_skin_layers && support_skin_line_distance == other.support_skin_line_distance + && fill_outline_gaps == other.fill_outline_gaps && support_skin_layers == other.support_skin_layers && support_skin_line_distance == other.support_skin_line_distance && support_tree_skin_for_large_tips_radius_threshold == other.support_tree_skin_for_large_tips_radius_threshold && - // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for// interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry + // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for// + // interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry (interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || interface_preference == InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE || (settings.get("fill_outline_gaps") == other.settings.get("fill_outline_gaps") && settings.get("min_bead_width") == other.settings.get("min_bead_width") @@ -488,7 +488,7 @@ struct TreeSupportSettings /* tip */ min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : /* base */ branch_radius + - /* gradual increase */ branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) + /* gradual increase */ branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) + branch_radius * buildplate_radius_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0)); return std::min(uncapped_radius, max_radius); } @@ -500,7 +500,9 @@ struct TreeSupportSettings */ [[nodiscard]] inline coord_t getRadius(const TreeSupportElement& elem) const { - return getRadius(getEffectiveDTT(elem), elem.hidden_radius_increase_ + ((elem.isResultOnLayerSet() || ! support_rests_on_model) && elem.to_buildplate_ ? elem.buildplate_radius_increases_ : 0)); + return getRadius( + getEffectiveDTT(elem), + elem.hidden_radius_increase_ + ((elem.isResultOnLayerSet() || ! support_rests_on_model) && elem.to_buildplate_ ? elem.buildplate_radius_increases_ : 0)); } /*! diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index 72ed6b67a0..efcf710415 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -7,6 +7,7 @@ #include "TreeModelVolumes.h" #include "TreeSupport.h" #include "TreeSupportBaseCircle.h" +#include "TreeSupportCradle.h" #include "TreeSupportElement.h" #include "TreeSupportEnums.h" #include "TreeSupportSettings.h" @@ -16,7 +17,6 @@ #include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" -#include "TreeSupportCradle.h" namespace cura { @@ -66,11 +66,11 @@ class TreeSupportTipGenerator struct UnsupportedAreaInformation { - UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) : - area{ area }, - index{ index }, - height{ height }, - accumulated_supportable_overhang { accumulated_supportable_overhang } + UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) + : area{ area } + , index{ index } + , height{ height } + , accumulated_supportable_overhang{ accumulated_supportable_overhang } { } const Polygons area; @@ -223,7 +223,7 @@ class TreeSupportTipGenerator * \param skip_ovalisation[in] Whether the tip may be ovalized when drawn later. * \param additional_ovalization_targets[in] Additional targets the ovalization should reach. */ - TreeSupportElement* addPointAsInfluenceArea( + TreeSupportElement* addPointAsInfluenceArea( std::vector>& move_bounds, std::pair p, size_t dtt, @@ -247,14 +247,15 @@ class TreeSupportTipGenerator * \param dont_move_until[in] Until which dtt the branch should not move if possible. * \param connect_points [in] If the points of said line should be connected by ovalization. */ - void addLinesAsInfluenceAreas(std::vector>& move_bounds, - std::vector lines, - size_t roof_tip_layers, - LayerIndex insert_layer_idx, - coord_t tip_radius, - OverhangInformation& overhang_data, - size_t dont_move_until, - bool connect_points); + void addLinesAsInfluenceAreas( + std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + coord_t tip_radius, + OverhangInformation& overhang_data, + size_t dont_move_until, + bool connect_points); /*! * \brief Remove tips that should not have been added in the first place. @@ -364,7 +365,7 @@ class TreeSupportTipGenerator /*! * \brief Required to generate cross infill patterns. Key: Distance between lines */ - std::unordered_map, std::shared_ptr> cross_fill_providers_; + std::unordered_map, std::shared_ptr> cross_fill_providers_; /*! * \brief Map that saves locations of already inserted tips. Used to prevent tips far to close together from being added. @@ -467,8 +468,6 @@ class TreeSupportTipGenerator mutable std::vector>> floating_parts_map_below_; std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); - - }; } // namespace cura diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 26318f2b9c..c429531987 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -103,14 +103,17 @@ class TreeSupportUtils bool roof, LayerIndex layer_idx, coord_t support_infill_distance, - std::shared_ptr cross_fill_provider, size_t wall_count, EFillMethod special_pattern = EFillMethod::NONE, bool disable_connect = false) + std::shared_ptr cross_fill_provider, + size_t wall_count, + EFillMethod special_pattern = EFillMethod::NONE, + bool disable_connect = false) { Polygons gaps; // As we effectively use lines to place our supportPoints we may use the Infill class for it, while not made for it, it works perfectly. const EFillMethod pattern = (special_pattern != EFillMethod::NONE) ? special_pattern : roof ? config.roof_pattern : config.support_pattern; - const bool zig_zaggify_infill =!disable_connect && (roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support); + const bool zig_zaggify_infill = ! disable_connect && (roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support); const bool connect_polygons = false; constexpr coord_t support_roof_overlap = 0; constexpr size_t infill_multiplier = 1; diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 964db50ee7..5e7abe1068 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -78,9 +78,10 @@ TreeModelVolumes::TreeModelVolumes( min_maximum_deviation = std::min(min_maximum_deviation, data_pair.first.get("meshfix_maximum_deviation")); min_maximum_resolution = std::min(min_maximum_resolution, data_pair.first.get("meshfix_maximum_resolution")); min_maximum_area_deviation = std::min(min_maximum_area_deviation, data_pair.first.get("meshfix_maximum_extrusion_area_deviation")); - const coord_t extra_cradle_distance = round_divide(retrieveSetting(data_pair.first, "support_tree_cradle_z_distance") , config.layer_height); - max_cradle_layers = std::max(coord_t(max_cradle_layers), extra_cradle_distance + retrieveSetting(data_pair.first,"support_tree_cradle_height") / config.layer_height); - max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * retrieveSetting(data_pair.first,"support_tree_cradle_base_tip_percentage") / 100.0)); + const coord_t extra_cradle_distance = round_divide(retrieveSetting(data_pair.first, "support_tree_cradle_z_distance"), config.layer_height); + max_cradle_layers + = std::max(coord_t(max_cradle_layers), extra_cradle_distance + retrieveSetting(data_pair.first, "support_tree_cradle_height") / config.layer_height); + max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * retrieveSetting(data_pair.first, "support_tree_cradle_base_tip_percentage") / 100.0)); } // Figure out the rest of the setting(-like variable)s relevant to the class a whole. @@ -492,7 +493,7 @@ const Polygons& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_ coord_t(type)); } - if(orig_radius == 0) + if (orig_radius == 0) { calculateFake0Avoidances(layer_idx); } @@ -576,25 +577,23 @@ const Polygons& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex } - void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx) { - RadiusLayerPair key(0,layer_idx); + RadiusLayerPair key(0, layer_idx); std::lock_guard critical_section(*critical_anti_preferred_); anti_preferred_[key] = anti_preferred_[key].unionPolygons(area); - //todo calculated all required radiis and invalidate all non 0 radiis - //todo add function to recalculate anti-pref for non 0 radiis + // todo calculated all required radiis and invalidate all non 0 radiis + // todo add function to recalculate anti-pref for non 0 radiis } void TreeModelVolumes::precalculateAntiPreferred() { - cura::parallel_for( 0, precalculated_avoidance_radii.size(), - [&, precalculated_avoidance_radiis=precalculated_avoidance_radii](const size_t key_idx) + [&, precalculated_avoidance_radiis = precalculated_avoidance_radii](const size_t key_idx) { const coord_t radius = precalculated_avoidance_radiis[key_idx].first; const LayerIndex max_required_layer = precalculated_avoidance_radiis[key_idx].second; @@ -621,29 +620,29 @@ void TreeModelVolumes::precalculateAntiPreferred() Polygons anti; { std::lock_guard critical_section(*critical_anti_preferred_); - anti=anti_preferred_[key_0]; + anti = anti_preferred_[key_0]; } - if(!encountered_anti && ! anti.empty()) + if (! encountered_anti && ! anti.empty()) { encountered_anti = true; if (support_rest_preference_ == RestPreference::BUILDPLATE) { - latest_avoidance = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, false, true); + latest_avoidance = getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, false, true); } - if(support_rests_on_model_) + if (support_rests_on_model_) { - latest_avoidance_to_model = getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, true, true); + latest_avoidance_to_model = getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, true, true); } - if(max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) + if (max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { - latest_avoidance_collision = getAvoidance(radius,layer,AvoidanceType::COLLISION, true, true); + latest_avoidance_collision = getAvoidance(radius, layer, AvoidanceType::COLLISION, true, true); } std::lock_guard critical_section(*critical_anti_preferred_); first_anti_preferred_layer_idx = layer; } - if(!encountered_anti) + if (! encountered_anti) { continue; } @@ -654,30 +653,29 @@ void TreeModelVolumes::precalculateAntiPreferred() if (support_rest_preference_ == RestPreference::BUILDPLATE) { - latest_avoidance = safeOffset(latest_avoidance, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons next_latest_avoidance = simplifier_.polygon(latest_avoidance); latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); - latest_avoidance = latest_avoidance.unionPolygons(getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, false, true)); + latest_avoidance = latest_avoidance.unionPolygons(getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, false, true)); data[layer] = std::pair(key, latest_avoidance); } - if(support_rests_on_model_) + if (support_rests_on_model_) { - latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons next_latest_avoidance_to_model = simplifier_.polygon(latest_avoidance_to_model); latest_avoidance_to_model = next_latest_avoidance_to_model.unionPolygons(latest_avoidance_to_model); - latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius,layer)); - latest_avoidance_to_model = latest_avoidance_to_model.unionPolygons(getAvoidance(radius,layer,AvoidanceType::FAST_SAFE, true, true)); + latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius, layer)); + latest_avoidance_to_model = latest_avoidance_to_model.unionPolygons(getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, true, true)); data_to_model[layer] = std::pair(key, latest_avoidance_to_model); - } - if(max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) + if (max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { - latest_avoidance_collision = safeOffset(latest_avoidance_collision, - max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); + latest_avoidance_collision = safeOffset(latest_avoidance_collision, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius_), ClipperLib::jtRound); latest_avoidance_collision = latest_avoidance_collision.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); - latest_avoidance_collision = latest_avoidance_collision.unionPolygons(getAvoidance(radius,layer,AvoidanceType::COLLISION, true, true)); + latest_avoidance_collision = latest_avoidance_collision.unionPolygons(getAvoidance(radius, layer, AvoidanceType::COLLISION, true, true)); data_collision[layer] = std::pair(key, latest_avoidance_collision); } } @@ -697,7 +695,7 @@ void TreeModelVolumes::precalculateAntiPreferred() const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius) { coord_t ceiled_radius = ceilRadius(radius); - RadiusLayerPair key(ceilRadius(ceiled_radius),layer_idx); + RadiusLayerPair key(ceilRadius(ceiled_radius), layer_idx); std::optional> result; std::unordered_map* cache_ptr = &anti_preferred_; @@ -717,7 +715,7 @@ const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, co result = getArea(anti_preferred_, key); } - if(!result || result.value().get().empty()) //todo where and why are empty areas inserted? + if (! result || result.value().get().empty()) // todo where and why are empty areas inserted? { return empty_polygon; } @@ -727,22 +725,20 @@ const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, co spdlog::warn("Missing anti preferred area at radius {} and layer {} Returning Empty! Result had area of {}", ceiled_radius, key.second, result.value().get().area()); } return empty_polygon; - } const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) { - - coord_t ceiled_radius = ceilRadius(radius,min_xy_dist); - RadiusLayerPair key(ceilRadius(ceiled_radius),layer_idx); + coord_t ceiled_radius = ceilRadius(radius, min_xy_dist); + RadiusLayerPair key(ceilRadius(ceiled_radius), layer_idx); std::optional> result; std::unordered_map* cache_ptr = nullptr; - if(type == AvoidanceType::COLLISION) + if (type == AvoidanceType::COLLISION) { - if(max_layer_idx_without_blocker_ <= layer_idx && to_model) + if (max_layer_idx_without_blocker_ <= layer_idx && to_model) { cache_ptr = &anti_preferred_cache_collision; } @@ -751,14 +747,13 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye cache_ptr = &anti_preferred_; } } - else if(to_model) + else if (to_model) { cache_ptr = &anti_preferred_cache_to_model_; } else { cache_ptr = &anti_preferred_cache_; - } { @@ -776,21 +771,26 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye result = getArea(anti_preferred_, key); } - if(!result || result.value().get().empty()) + if (! result || result.value().get().empty()) { return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } if (precalculated_) { - spdlog::warn("Missing anti preferred calculated at radius {} and layer {} and type {} to model {}, but precalculate was called. Returning Empty!", ceiled_radius, key.second, type == AvoidanceType::COLLISION, to_model); + spdlog::warn( + "Missing anti preferred calculated at radius {} and layer {} and type {} to model {}, but precalculate was called. Returning Empty!", + ceiled_radius, + key.second, + type == AvoidanceType::COLLISION, + to_model); } return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) { - if(layer_idx < anti_overhang_.size()) + if (layer_idx < anti_overhang_.size()) { return anti_overhang_[layer_idx]; } @@ -801,7 +801,6 @@ const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) } - coord_t TreeModelVolumes::ceilRadius(coord_t radius, bool min_xy_dist) const { return ceilRadius(radius + (min_xy_dist ? 0 : current_min_xy_dist_delta_)); @@ -1417,55 +1416,54 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) start_layer = 1 + getMaxCalculatedLayer(0, avoidance_cache_); } - const coord_t radius_offset = - radius_0_; - cura::parallel_for - ( + const coord_t radius_offset = -radius_0_; + cura::parallel_for( start_layer, - max_layer+1, + max_layer + 1, [&](const LayerIndex layer_idx) { - RadiusLayerPair key = RadiusLayerPair (0,layer_idx); - if(!precalculated_ || support_rest_preference_ == RestPreference::BUILDPLATE) + RadiusLayerPair key = RadiusLayerPair(0, layer_idx); + if (! precalculated_ || support_rest_preference_ == RestPreference::BUILDPLATE) { - Polygons smaller_avoidance_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,false,true).offset(radius_offset,ClipperLib::jtRound); - Polygons smaller_avoidance_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,false,true).offset(radius_offset,ClipperLib::jtRound); - Polygons smaller_avoidance_fast_safe = getAvoidance(1,layer_idx,AvoidanceType::FAST_SAFE,false,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, false, true).offset(radius_offset, ClipperLib::jtRound); + Polygons smaller_avoidance_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, false, true).offset(radius_offset, ClipperLib::jtRound); + Polygons smaller_avoidance_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, false, true).offset(radius_offset, ClipperLib::jtRound); { - std::lock_guard critical_section(*critical_avoidance_cache_slow_ ); + std::lock_guard critical_section(*critical_avoidance_cache_slow_); avoidance_cache_slow_[key] = smaller_avoidance_slow; } { - std::lock_guard critical_section(*critical_avoidance_cache_ ); + std::lock_guard critical_section(*critical_avoidance_cache_); avoidance_cache_[key] = smaller_avoidance_fast; } { - std::lock_guard critical_section(*critical_avoidance_cache_holefree_ ); + std::lock_guard critical_section(*critical_avoidance_cache_holefree_); avoidance_cache_hole_[key] = smaller_avoidance_fast_safe; } } - if(!precalculated_ || support_rests_on_model_) + if (! precalculated_ || support_rests_on_model_) { - Polygons smaller_avoidance_to_model_slow = getAvoidance(1,layer_idx,AvoidanceType::SLOW,true,true).offset(radius_offset,ClipperLib::jtRound); - Polygons smaller_avoidance_to_model_fast = getAvoidance(1,layer_idx,AvoidanceType::FAST,true,true).offset(radius_offset,ClipperLib::jtRound); - Polygons smaller_avoidance_to_model_fast_safe = getAvoidance(1,layer_idx,AvoidanceType::FAST_SAFE,true,true).offset(radius_offset,ClipperLib::jtRound); + Polygons smaller_avoidance_to_model_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, true, true).offset(radius_offset, ClipperLib::jtRound); + Polygons smaller_avoidance_to_model_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, true, true).offset(radius_offset, ClipperLib::jtRound); + Polygons smaller_avoidance_to_model_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, true, true).offset(radius_offset, ClipperLib::jtRound); { - std::lock_guard critical_section(*critical_avoidance_cache_to_model_slow_ ); + std::lock_guard critical_section(*critical_avoidance_cache_to_model_slow_); avoidance_cache_to_model_slow_[key] = smaller_avoidance_to_model_slow; } { - std::lock_guard critical_section(*critical_avoidance_cache_to_model_ ); + std::lock_guard critical_section(*critical_avoidance_cache_to_model_); avoidance_cache_to_model_[key] = smaller_avoidance_to_model_fast; } { - std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_ ); + std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_); avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; } if (layer_idx > max_layer_idx_without_blocker_) { - Polygons smaller_avoidance_collision = getAvoidance(1,layer_idx,AvoidanceType::COLLISION,true,true).offset(radius_offset,ClipperLib::jtRound); - std::lock_guard critical_section(*critical_avoidance_cache_collision_ ); + Polygons smaller_avoidance_collision = getAvoidance(1, layer_idx, AvoidanceType::COLLISION, true, true).offset(radius_offset, ClipperLib::jtRound); + std::lock_guard critical_section(*critical_avoidance_cache_collision_); avoidance_cache_collision_[key] = smaller_avoidance_collision; } } diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 82a4ad9a58..77faa643c8 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -94,9 +94,8 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) mesh.first.setActualZ(known_z); } - fake_roof_areas = std::vector>(storage.support.supportLayers.size(),std::vector()); + fake_roof_areas = std::vector>(storage.support.supportLayers.size(), std::vector()); support_free_areas = std::vector(storage.support.supportLayers.size(), Polygons()); - } void TreeSupport::generateSupportAreas(SliceDataStorage& storage) @@ -165,20 +164,20 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) { std::vector> cradle_data_mesh; generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage, cradle_data_mesh); - if(cradle_data.size()>& move_bounds, - SliceDataStorage& storage, - std::vector>& cradle_data_model) +void TreeSupport::generateInitialAreas( + const SliceMeshStorage& mesh, + std::vector>& move_bounds, + SliceDataStorage& storage, + std::vector>& cradle_data_model) { TreeSupportTipGenerator tip_gen(mesh, volumes_); tip_gen.generateTips(storage, mesh, move_bounds, fake_roof_areas, support_free_areas, cradle_data_model); @@ -707,12 +707,11 @@ std::optional TreeSupport::increaseSingleArea( // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), // but everything compensates for small rounding errors already, so it will be fine. // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. - if(! settings.no_error_ && ! to_bp_data.empty() && to_bp_data.area()<1) + if (! settings.no_error_ && ! to_bp_data.empty() && to_bp_data.area() < 1) { to_bp_data = to_bp_data.unionPolygons(to_bp_data.offsetPolyLine(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); } - } if (config.support_rests_on_model) { @@ -739,7 +738,7 @@ std::optional TreeSupport::increaseSingleArea( // technically this makes the influence area is larger than it should be (as it overlaps with the avoidance slightly), // but everything compensates for small rounding errors already, so it will be fine. // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. - if(! settings.no_error_ && ! to_model_data.empty() && to_model_data.area()<1) + if (! settings.no_error_ && ! to_model_data.empty() && to_model_data.area() < 1) { to_model_data = to_model_data.unionPolygons(to_model_data.offsetPolyLine(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); @@ -748,14 +747,14 @@ std::optional TreeSupport::increaseSingleArea( coord_t actual_radius = config.getRadius(current_elem); // Removing cradle areas from influence areas if possible. - Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx-1, actual_radius); + Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius); bool anti_preferred_applied = false; - if(!anti_preferred_areas.empty()) + if (! anti_preferred_areas.empty()) { bool is_fast = settings.increase_speed_ >= config.maximum_move_distance; - //Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. - coord_t anti_radius_extra = std::max(settings.increase_speed_-volumes_.ceilRadius(actual_radius*2,true), coord_t(0)); - if(anti_radius_extra) + // Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. + coord_t anti_radius_extra = std::max(settings.increase_speed_ - volumes_.ceilRadius(actual_radius * 2, true), coord_t(0)); + if (anti_radius_extra) { anti_preferred_areas = anti_preferred_areas.offset(anti_radius_extra).unionPolygons(); } @@ -763,23 +762,23 @@ std::optional TreeSupport::increaseSingleArea( { Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); // If already moving fast there is not much to do. The anti preferred with collision radius will then later be subtracted if it is not subtracted here. - if(to_bp_without_anti.area()>EPSILON || (settings.use_anti_preferred_ &&!is_fast)) + if (to_bp_without_anti.area() > EPSILON || (settings.use_anti_preferred_ && ! is_fast)) { to_bp_data = to_bp_without_anti; - Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); to_model_data = to_model_data_without_anti; - Polygons increased_without_anti = increased.difference(anti_preferred_areas); + Polygons increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; anti_preferred_applied = true; } } else { - Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); - if(to_model_data_without_anti.area()>EPSILON || (settings.use_anti_preferred_ &&!is_fast)) + Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + if (to_model_data_without_anti.area() > EPSILON || (settings.use_anti_preferred_ && ! is_fast)) { to_model_data = to_model_data_without_anti; - Polygons increased_without_anti = increased.difference(anti_preferred_areas); + Polygons increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; anti_preferred_applied = true; } @@ -789,32 +788,32 @@ std::optional TreeSupport::increaseSingleArea( // Remove areas where the branch should not be if possible. // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. - if(volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred_) + if (volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred_) { const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_); if (current_elem.to_buildplate_) { to_bp_data = to_bp_data.difference(anti_preferred); - to_model_data = to_model_data.difference(anti_preferred); + to_model_data = to_model_data.difference(anti_preferred); } else { - to_model_data = to_model_data.difference(anti_preferred); + to_model_data = to_model_data.difference(anti_preferred); } - if(!anti_preferred_applied) + if (! anti_preferred_applied) { - increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); + increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, radius)); } check_layer_data = current_elem.to_buildplate_ ? to_bp_data : to_model_data; - if(check_layer_data.area() > 1) + if (check_layer_data.area() > 1) { current_elem.can_avoid_anti_preferred_ = true; } } - if(volumes_.getFirstAntiPreferredLayerIdx() >= layer_idx) + if (volumes_.getFirstAntiPreferredLayerIdx() >= layer_idx) { current_elem.can_avoid_anti_preferred_ = true; } @@ -835,16 +834,17 @@ std::optional TreeSupport::increaseSingleArea( to_bp_data_2 = increased; bool avoidance_handled = false; - if(settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { - to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + to_bp_data_2 = to_bp_data_2.difference( + volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; } - else if(anti_preferred_applied && next_radius > actual_radius) + else if (anti_preferred_applied && next_radius > actual_radius) { - to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); + to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } - if(! avoidance_handled) + if (! avoidance_handled) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type_, false, settings.use_min_distance_)); } @@ -854,16 +854,17 @@ std::optional TreeSupport::increaseSingleArea( { to_model_data_2 = increased; bool avoidance_handled = false; - if(settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { - to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); - avoidance_handled = settings.type_ != AvoidanceType::SLOW; //There is no slow anti-preferred avoidance. + to_model_data_2 = to_model_data_2.difference( + volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } - else if(anti_preferred_applied) + else if (anti_preferred_applied) { - to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx-1, next_radius)); + to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } - if(! avoidance_handled) + if (! avoidance_handled) { to_model_data_2 = to_model_data_2.difference(volumes_.getAvoidance( next_radius, @@ -927,7 +928,7 @@ std::optional TreeSupport::increaseSingleArea( } radius = config.getCollisionRadius(current_elem); - //If a hidden radius increase was used, also do some catching up. + // If a hidden radius increase was used, also do some catching up. if (current_elem.hidden_radius_increase_ > 0) { coord_t target_radius = config.getRadius(current_elem); @@ -940,7 +941,8 @@ std::optional TreeSupport::increaseSingleArea( double resulting_hidden_increases = current_elem.hidden_radius_increase_; while (resulting_hidden_increases > 0 && config.getRadius(current_elem.effective_radius_height_, resulting_hidden_increases + current_elem.buildplate_radius_increases_) <= current_ceil_radius - && config.getRadius(current_elem.effective_radius_height_, resulting_hidden_increases + current_elem.buildplate_radius_increases_) <= config.getRadius(current_elem)) + && config.getRadius(current_elem.effective_radius_height_, resulting_hidden_increases + current_elem.buildplate_radius_increases_) + <= config.getRadius(current_elem)) { resulting_hidden_increases--; } @@ -1000,39 +1002,47 @@ std::optional TreeSupport::increaseSingleArea( if (ceil_radius_before != volumes_.ceilRadius(radius, settings.use_min_distance_)) { - if(anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_)) + if (anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_)) { - increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx-1, radius)); + increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, radius)); } if (current_elem.to_buildplate_) { bool avoidance_handled = false; to_bp_data = increased; - if(settings.use_anti_preferred_) + if (settings.use_anti_preferred_) { - to_bp_data = to_bp_data.difference(volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + to_bp_data = to_bp_data.difference( + volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = true; } - if(! avoidance_handled) + if (! avoidance_handled) { to_bp_data = to_bp_data.difference(volumes_.getAvoidance(radius, layer_idx - 1, settings.type_, false, settings.use_min_distance_)); } - to_bp_data = TreeSupportUtils::safeUnion(to_bp_data); + to_bp_data = TreeSupportUtils::safeUnion(to_bp_data); } if (config.support_rests_on_model && (! current_elem.to_buildplate_ || mergelayer)) { bool avoidance_handled = false; to_model_data = increased; - if(settings.use_anti_preferred_) + if (settings.use_anti_preferred_) { - to_model_data = to_model_data.difference(volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); + to_model_data = to_model_data.difference(volumes_.getAntiPreferredAvoidance( + radius, + layer_idx - 1, + current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, + true, + settings.use_min_distance_)); avoidance_handled = true; } - if(! avoidance_handled) + if (! avoidance_handled) { - to_bp_data = to_bp_data.difference(volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); + to_bp_data = to_bp_data.difference( + volumes_ + .getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); } - to_model_data = TreeSupportUtils::safeUnion(to_model_data); + to_model_data = TreeSupportUtils::safeUnion(to_model_data); } check_layer_data = current_elem.to_buildplate_ ? to_bp_data : to_model_data; if (check_layer_data.area() < 1) @@ -1236,7 +1246,9 @@ void TreeSupport::increaseAreas( { // 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. - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! move), true); // Did we go through the hole. + insertSetting( + AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, ! 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(config.tip_layers, 2)) @@ -1254,30 +1266,43 @@ void TreeSupport::increaseAreas( insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, 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(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); // a (See above.) + insertSetting( + AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), + true); // a (See above.) if (elem.distance_to_top_ < config.tip_layers) { insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, slow_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); // b (See above.) + insertSetting( + AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), + true); // b (See above.) insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); } - if(!elem.can_avoid_anti_preferred_) + if (! elem.can_avoid_anti_preferred_) { std::deque old_order = order; for (AreaIncreaseSettings settings : old_order) { - if(elem.effective_radius_height_ < config.increase_radius_until_dtt && !settings.increase_radius_) + if (elem.effective_radius_height_ < config.increase_radius_until_dtt && ! settings.increase_radius_) { - continue ; + continue; } - if(!settings.move_) + if (! settings.move_) { - continue ; + continue; } - insertSetting(AreaIncreaseSettings(settings.type_, settings.increase_speed_, settings.increase_radius_, settings.no_error_, use_min_radius, !settings.use_anti_preferred_, settings.move_),true); + insertSetting( + AreaIncreaseSettings( + settings.type_, + settings.increase_speed_, + settings.increase_radius_, + settings.no_error_, + use_min_radius, + ! settings.use_anti_preferred_, + settings.move_), + true); } } @@ -1289,13 +1314,20 @@ void TreeSupport::increaseAreas( for (AreaIncreaseSettings settings : order) { new_order.emplace_back(settings); - new_order.emplace_back(settings.type_, settings.increase_speed_, settings.increase_radius_, settings.no_error_, use_min_radius, settings.use_anti_preferred_, settings.move_); + new_order.emplace_back( + settings.type_, + settings.increase_speed_, + settings.increase_radius_, + settings.no_error_, + use_min_radius, + settings.use_anti_preferred_, + settings.move_); } order = new_order; } insertSetting( - AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist_, !use_anti_preferred, move), + AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist_, ! use_anti_preferred, move), true); // simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has // caused issues @@ -1306,7 +1338,7 @@ void TreeSupport::increaseAreas( || (! elem.to_model_gracious_ && (parent->area_->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty()))) // Error case. { // It is normal that we won't be able to find a new area at some point in time if we won't be able to reach layer 0 aka have to connect with the model. - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist_, !use_anti_preferred, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist_, ! use_anti_preferred, 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. { @@ -1433,9 +1465,8 @@ void TreeSupport::increaseAreas( elem.last_area_increase_ = settings; add = true; // Do not merge if the branch should not move or the priority has to be to get farther away from the model. - bypass_merge = ! settings.move_ - || (settings.use_min_distance_ && elem.distance_to_top_ < config.tip_layers) - || !elem.can_avoid_anti_preferred_; // todo less aggressive merge prevention? + bypass_merge = ! settings.move_ || (settings.use_min_distance_ && elem.distance_to_top_ < config.tip_layers) + || ! elem.can_avoid_anti_preferred_; // todo less aggressive merge prevention? if (settings.move_) { elem.dont_move_until_ = 0; @@ -1500,16 +1531,16 @@ void TreeSupport::increaseAreas( }); } -void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, - PropertyAreas& to_model_areas, - PropertyAreas& influence_areas, - PropertyAreas& bypass_merge_areas, - LayerIndex layer_idx, - std::vector>& move_bounds, - std::vector>& cradle_data) +void TreeSupport::handleCradleLineValidity( + PropertyAreasUnordered& to_bp_areas, + PropertyAreas& to_model_areas, + PropertyAreas& influence_areas, + PropertyAreas& bypass_merge_areas, + LayerIndex layer_idx, + std::vector>& move_bounds, + std::vector>& cradle_data) { - - if(cradle_data.size()<=layer_idx) + if (cradle_data.size() <= layer_idx) { return; } @@ -1533,7 +1564,7 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, for (const TreeSupportElement* elem : all_elements_on_layer) { - if(!elem->can_avoid_anti_preferred_ || config.getCollisionRadius(*elem) != config.getRadius(*elem)) + if (! elem->can_avoid_anti_preferred_ || config.getCollisionRadius(*elem) != config.getRadius(*elem)) { const coord_t safe_movement_distance = (elem->use_min_xy_dist_ ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); @@ -1543,46 +1574,46 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, Polygons relevant_influence; Polygons full_influence; - if(!immutable) + if (! immutable) { - relevant_influence = to_bp? to_bp_areas[*elem] : to_model_areas[*elem]; + relevant_influence = to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]; full_influence = bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]; } else { - relevant_influence = elem->area_->difference(volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,elem->use_min_xy_dist_)); + relevant_influence = elem->area_->difference(volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, elem->use_min_xy_dist_)); full_influence = relevant_influence; } AABB relevant_influence_aabb = AABB(relevant_influence); for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base_ && !removed_lines_idx.contains(cradle_idx)) + if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base_ && ! removed_lines_idx.contains(cradle_idx)) { - //The branch created by the influence area cant lag though the model... So the offset needs to be safe... + // The branch created by the influence area cant lag though the model... So the offset needs to be safe... AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area_); cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); - if(cradle_area_aabb.hit(relevant_influence_aabb)) + if (cradle_area_aabb.hit(relevant_influence_aabb)) { - Polygons cradle_influence = TreeSupportUtils::safeOffsetInc(cradle.getCradleLine()->area_, - config.getRadius(*elem) + config.xy_distance, - volumes_.getCollision(config.getCollisionRadius(*elem),layer_idx,true), - safe_movement_distance, - 0, - 1, - config.support_line_distance / 2, - &config.simplifier); + Polygons cradle_influence = TreeSupportUtils::safeOffsetInc( + cradle.getCradleLine()->area_, + config.getRadius(*elem) + config.xy_distance, + volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, true), + safe_movement_distance, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); Polygons next_relevant_influence = relevant_influence.difference(cradle_influence); - if(next_relevant_influence.area()>EPSILON) + if (next_relevant_influence.area() > EPSILON) { relevant_influence = next_relevant_influence; full_influence = full_influence.difference(cradle_influence).unionPolygons(relevant_influence); - } else { - //todo Check if non remove options are available eg shortening cradle line... + // todo Check if non remove options are available eg shortening cradle line... removed_lines_idx.emplace(cradle_idx); cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line_); cradle.getCradleLine()->line_.clear(); @@ -1591,36 +1622,34 @@ void TreeSupport::handleCradleLineValidity(PropertyAreasUnordered& to_bp_areas, } } } - if(!immutable) + if (! immutable) { (bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]) = full_influence; - (to_bp? to_bp_areas[*elem] : to_model_areas[*elem]) = relevant_influence; + (to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]) = relevant_influence; } - } } for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - if(cradle.cradleLineExists()) + if (cradle.cradleLineExists()) { cradle.cradle_->verifyLines(); } } - //todo would be great if removed cradle lines could be eliminated from the avoidance... + // todo would be great if removed cradle lines could be eliminated from the avoidance... std::vector next_layer; next_layer.insert(next_layer.begin(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); - for(TreeSupportElement* elem:next_layer) + for (TreeSupportElement* elem : next_layer) { - if(elem->cradle_line_ && !elem->cradle_line_->cradleLineExists()) + if (elem->cradle_line_ && ! elem->cradle_line_->cradleLineExists()) { move_bounds[layer_idx].erase(elem); delete elem->area_; delete elem; } } - } @@ -1644,20 +1673,20 @@ void TreeSupport::createLayerPathing(std::vector>& size_t merge_every_x_layers = 1; std::vector> all_cradles_with_line_presence(move_bounds.size()); - for(LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { - for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) + for (size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { - for(size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) + for (size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) { LayerIndex cradle_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; - if(cradle_layer_idx == 84) + if (cradle_layer_idx == 84) { printf(""); } - all_cradles_with_line_presence[cradle_layer_idx].emplace_back(cradle_data[layer_idx][cradle_idx],cradle_layer_idx,line_idx); + all_cradles_with_line_presence[cradle_layer_idx].emplace_back(cradle_data[layer_idx][cradle_idx], cradle_layer_idx, line_idx); } } } @@ -1707,9 +1736,9 @@ void TreeSupport::createLayerPathing(std::vector>& const auto time_c = std::chrono::high_resolution_clock::now(); // ### Cradle lines may be removed, causing tips to be removed. - if(layer_idx>0) + if (layer_idx > 0) { - handleCradleLineValidity(to_bp_areas, to_model_areas,influence_areas,bypass_merge_areas,layer_idx-1,move_bounds,all_cradles_with_line_presence); + handleCradleLineValidity(to_bp_areas, to_model_areas, influence_areas, bypass_merge_areas, layer_idx - 1, move_bounds, all_cradles_with_line_presence); } const auto time_d = std::chrono::high_resolution_clock::now(); @@ -1748,7 +1777,11 @@ void TreeSupport::createLayerPathing(std::vector>& Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); } - spdlog::info("Time spent with creating influence areas' subtasks: Increasing areas {} ms merging areas: {} ms CradleLineValidity: {} ms ", dur_inc.count() / 1000000, dur_merge.count() / 1000000, dur_cradle.count() / 1000000); + spdlog::info( + "Time spent with creating influence areas' subtasks: Increasing areas {} ms merging areas: {} ms CradleLineValidity: {} ms ", + dur_inc.count() / 1000000, + dur_merge.count() / 1000000, + dur_cradle.count() / 1000000); } void TreeSupport::setPointsOnAreas(const TreeSupportElement* elem) @@ -2033,7 +2066,7 @@ void TreeSupport::generateBranchAreas( } std::vector linear_inserts(linear_data.size()); - const size_t progress_inserts_check_interval = std::max(linear_data.size() / progress_report_steps,size_t(1)); + const size_t progress_inserts_check_interval = std::max(linear_data.size() / progress_report_steps, size_t(1)); std::mutex critical_sections; cura::parallel_for( @@ -2320,24 +2353,25 @@ void TreeSupport::dropNonGraciousAreas( }); } -void TreeSupport::generateSupportSkin(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, - SliceDataStorage& storage, - std::vector>& layer_tree_polygons, - std::vector>& cradle_data) +void TreeSupport::generateSupportSkin( + 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, + 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 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_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. @@ -2346,35 +2380,34 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::mutex critical_support_roof_storage; std::mutex critical_support_layer_storage; - cura::parallel_for - ( + cura::parallel_for( 0, cradle_data.size(), [&](const LayerIndex layer_idx) { - for(size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - if(cradle_data[layer_idx][cradle_idx]->is_roof_) + if (cradle_data[layer_idx][cradle_idx]->is_roof_) { std::lock_guard critical_section_cradle(critical_support_roof_storage); - cradle_base_areas[layer_idx-base_idx].add(base); - (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx-base_idx].add(base); - if(base_idx == 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) + cradle_base_areas[layer_idx - base_idx].add(base); + (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx - base_idx].add(base); + if (base_idx == 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) { - (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx+1].add(base); + (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx + 1].add(base); } } else { std::lock_guard critical_section_cradle(critical_support_layer_storage); - cradle_base_areas[layer_idx-base_idx].add(base); - support_layer_storage[layer_idx-base_idx].add(base); + cradle_base_areas[layer_idx - base_idx].add(base); + support_layer_storage[layer_idx - base_idx].add(base); } } - for(size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) + for (size_t line_idx = 0; line_idx < cradle_data[layer_idx][cradle_idx]->lines_.size(); line_idx++) { if (! cradle_data[layer_idx][cradle_idx]->lines_[line_idx].empty()) { @@ -2386,9 +2419,9 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; - if(!is_base) + if (! is_base) { - for(LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) + for (LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) { Polygons line_areas = TreeSupportUtils::safeOffsetInc( previous_line_area, @@ -2424,7 +2457,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } cradle_support_line_areas[cradle_line_layer_idx].add(line_area); } - if (!is_base) + if (! is_base) { Polygons line_areas = TreeSupportUtils::safeOffsetInc( line_area, @@ -2448,158 +2481,155 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora std::vector fake_roofs(fake_roof_areas.size()); - cura::parallel_for - ( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - - Polygons fake_roof; - Polygons fake_roof_lines; + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + Polygons fake_roof; + Polygons fake_roof_lines; - for(FakeRoofArea& f_roof:fake_roof_areas[layer_idx]) - { - fake_roof.add(f_roof.area_); - fake_roof_lines.add(TreeSupportUtils::generateSupportInfillLines(f_roof.area_, - config, - false, - layer_idx, - f_roof.line_distance_, - storage.support.cross_fill_provider, - 0).offsetPolyLine(config.support_line_width / 2)); - } - fake_roof_lines = fake_roof_lines.unionPolygons(); - fake_roof = fake_roof.unionPolygons(); - fake_roofs[layer_idx] = fake_roof; + for (FakeRoofArea& f_roof : fake_roof_areas[layer_idx]) + { + fake_roof.add(f_roof.area_); + fake_roof_lines.add( + TreeSupportUtils::generateSupportInfillLines(f_roof.area_, config, false, layer_idx, f_roof.line_distance_, storage.support.cross_fill_provider, 0) + .offsetPolyLine(config.support_line_width / 2)); + } + fake_roof_lines = fake_roof_lines.unionPolygons(); + fake_roof = fake_roof.unionPolygons(); + fake_roofs[layer_idx] = fake_roof; - Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; - remove_from_support.add(fake_roof_lines); - remove_from_support.add(support_free_areas[layer_idx]); - remove_from_support = remove_from_support.unionPolygons(); + Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; + remove_from_support.add(fake_roof_lines); + remove_from_support.add(support_free_areas[layer_idx]); + remove_from_support = remove_from_support.unionPolygons(); - support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].unionPolygons(); - Polygons original_fractional = support_layer_storage_fractional[layer_idx]; - support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); - //ensure there is at lease one line space for fractional support. Overlap is removed later! - support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].offset(config.support_line_width).intersection(original_fractional); + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))) + .offset(-open_close_distance) + .offset(open_close_distance * 2) + .offset(-open_close_distance); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].unionPolygons(); + Polygons original_fractional = support_layer_storage_fractional[layer_idx]; + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); + // ensure there is at lease one line space for fractional support. Overlap is removed later! + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].offset(config.support_line_width).intersection(original_fractional); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(remove_from_support); - support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(remove_from_support); - support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - support_layer_storage_fractional[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(remove_from_support); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(remove_from_support); + support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + support_layer_storage_fractional[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - support_roof_storage[layer_idx] = support_roof_storage[layer_idx].unionPolygons(); - support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].unionPolygons(); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].unionPolygons(); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].unionPolygons(); - support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].unionPolygons(); - support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].unionPolygons(); + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].unionPolygons(); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].unionPolygons(); - cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); - cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); - cradle_support_line_roof_areas[layer_idx] = cradle_support_line_roof_areas[layer_idx].unionPolygons(); - //If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. - if (!storage.support.supportLayers[layer_idx].support_roof.empty()) + cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); + cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); + cradle_support_line_roof_areas[layer_idx] = cradle_support_line_roof_areas[layer_idx].unionPolygons(); + // If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. + if (! storage.support.supportLayers[layer_idx].support_roof.empty()) + { + switch (config.interface_preference) { - switch (config.interface_preference) - { - case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: - { - Polygons all_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - all_roof.add(support_roof_storage[layer_idx]); - all_roof.add(support_roof_storage_fractional[layer_idx]); - all_roof.add(support_roof_extra_wall_storage[layer_idx]); - all_roof.add(support_roof_extra_wall_storage_fractional[layer_idx]); - all_roof = all_roof.unionPolygons(); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(all_roof); - support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(all_roof); - break; - } - - case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - { - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons support_areas = support_layer_storage[layer_idx].unionPolygons(support_layer_storage_fractional[layer_idx]); - Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); - AABB invalid_roof_aabb = AABB(invalid_roof); - storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); - support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_areas); - support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_areas); - support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_areas); - support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].difference(support_areas); - break; - } - default: - break; - } - } - Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); - - if(!support_free_areas[layer_idx].empty()) + case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: { - remove_from_next_roof.add(support_free_areas[layer_idx]); + Polygons all_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + all_roof.add(support_roof_storage[layer_idx]); + all_roof.add(support_roof_storage_fractional[layer_idx]); + all_roof.add(support_roof_extra_wall_storage[layer_idx]); + all_roof.add(support_roof_extra_wall_storage_fractional[layer_idx]); + all_roof = all_roof.unionPolygons(); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(all_roof); + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(all_roof); + break; } - - Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); - Polygons roof = support_roof_storage[layer_idx]; - Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; - if(config.support_roof_wall_count) + case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: { - roof = roof.unionPolygons(cradle_lines_roof); + Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Polygons support_areas = support_layer_storage[layer_idx].unionPolygons(support_layer_storage_fractional[layer_idx]); + Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); + AABB invalid_roof_aabb = AABB(invalid_roof); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_areas); + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_areas); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_areas); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].difference(support_areas); + break; } - else - { - roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + default: + break; } + } + Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); - roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); - storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); - storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); + if (! support_free_areas[layer_idx].empty()) + { + remove_from_next_roof.add(support_free_areas[layer_idx]); + } - remove_from_next_roof.add(roof_extra_wall); - remove_from_next_roof.add(roof); - remove_from_next_roof = remove_from_next_roof.unionPolygons(); - Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_roof); - storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, config.support_roof_line_width, config.support_wall_count, true); + Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); + Polygons roof = support_roof_storage[layer_idx]; + Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; + if (config.support_roof_wall_count) + { + roof = roof.unionPolygons(cradle_lines_roof); + } + else + { + roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + } - Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_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); + roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); + storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); + storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); - } - ); + remove_from_next_roof.add(roof_extra_wall); + remove_from_next_roof.add(roof); + remove_from_next_roof = remove_from_next_roof.unionPolygons(); + + Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_roof); + storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, config.support_roof_line_width, config.support_wall_count, true); + + Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_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(); - if(config.support_skin_layers) + if (config.support_skin_layers) { - - cura::parallel_for - ( + cura::parallel_for( 0, support_layer_storage.size(), [&](const LayerIndex layer_idx) { - - if(support_layer_storage[layer_idx].empty()) + if (support_layer_storage[layer_idx].empty()) { return; } const coord_t roof_stable_range_after_contact = config.support_roof_line_width * (config.support_roof_wall_count + 0.5); - Polygons support_shell_capable_of_supporting_roof = support_layer_storage[layer_idx] - .getOutsidePolygons() - .tubeShape(config.support_line_width * (config.support_wall_count + 0.5) + roof_stable_range_after_contact, roof_stable_range_after_contact, ClipperLib::JoinType::jtRound) - .unionPolygons() - .offset(-config.support_line_width / 4).offset(config.support_line_width / 4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. + Polygons support_shell_capable_of_supporting_roof + = support_layer_storage[layer_idx] + .getOutsidePolygons() + .tubeShape( + config.support_line_width * (config.support_wall_count + 0.5) + roof_stable_range_after_contact, + roof_stable_range_after_contact, + ClipperLib::JoinType::jtRound) + .unionPolygons() + .offset(-config.support_line_width / 4) + .offset(config.support_line_width / 4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. Polygons needs_supporting; - if(storage.support.supportLayers.size() > layer_idx + 1) + if (storage.support.supportLayers.size() > layer_idx + 1) { Polygons roof_above = storage.support.supportLayers[layer_idx + 1].getTotalAreaFromParts(storage.support.supportLayers[layer_idx + 1].support_roof); @@ -2635,7 +2665,8 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } bool element_viable_for_skin = has_parent_roof; - element_viable_for_skin |= data_pair.first->parents_.empty() && (config.getRadius(*data_pair.first) >= config.support_tree_skin_for_large_tips_radius_threshold); + element_viable_for_skin + |= data_pair.first->parents_.empty() && (config.getRadius(*data_pair.first) >= config.support_tree_skin_for_large_tips_radius_threshold); if (element_viable_for_skin) { @@ -2666,7 +2697,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora for (Polygons part : support_on_layer.splitIntoParts()) { Polygons part_outline = part.getOutsidePolygons(); - if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area,AABB(part_outline)).empty()) + if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area, AABB(part_outline)).empty()) { // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. Polygons scan_lines = TreeSupportUtils::generateSupportInfillLines( @@ -2682,28 +2713,25 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora Polygons intersecting_lines; for (auto line : scan_lines) { - bool valid_for_may_need_skin = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) || - may_need_skin_area.inside(line.front()) || - may_need_skin_area.inside(line.back()); - bool valid_for_may_need_skin_area_topmost = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back()) || - may_need_skin_area_topmost.inside(line.front()) || - may_need_skin_area_topmost.inside(line.back()); + bool valid_for_may_need_skin = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) + || may_need_skin_area.inside(line.front()) || may_need_skin_area.inside(line.back()); + bool valid_for_may_need_skin_area_topmost = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area_topmost, line.front(), line.back()) + || may_need_skin_area_topmost.inside(line.front()) || may_need_skin_area_topmost.inside(line.back()); if (valid_for_may_need_skin && valid_for_may_need_skin_area_topmost) { intersecting_lines.addLine(line.front(), line.back()); } - } Polygons partial_skin_area = intersecting_lines.offsetPolyLine(config.support_skin_line_distance + FUDGE_LENGTH).unionPolygons().intersection(part_outline); - // If a some scan lines had contact with two parts of part_outline, but the second part is outside of may_need_skin_area it could cause a separate skin area, that then cuts a branch in half that could have been completely normal. - // This area that does not need to be skin will be filtered out here. + // If a some scan lines had contact with two parts of part_outline, but the second part is outside of may_need_skin_area it could cause a separate skin + // area, that then cuts a branch in half that could have been completely normal. This area that does not need to be skin will be filtered out here. { Polygons filtered_partial_skin_area; - for(auto p_skin:partial_skin_area.splitIntoParts()) + for (auto p_skin : partial_skin_area.splitIntoParts()) { - if(!PolygonUtils::clipPolygonWithAABB(may_need_skin_area,AABB(p_skin)).intersection(p_skin).empty()) + if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area, AABB(p_skin)).intersection(p_skin).empty()) { filtered_partial_skin_area.add(p_skin); } @@ -2717,7 +2745,7 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora for (auto sub_part : part.splitIntoParts()) { // Prevent small slivers of a branch to generate as support. The heuristic to detect if a part is too small or thin could maybe be improved. - if ((sub_part.area() < part_area / 5 && sub_part.area() * 2 < std::numbers::pi * pow(config.branch_radius,2)) + if ((sub_part.area() < part_area / 5 && sub_part.area() * 2 < std::numbers::pi * pow(config.branch_radius, 2)) || sub_part.offset(-config.support_line_width).area() < 1) { partial_skin_area = partial_skin_area.unionPolygons(sub_part); @@ -2737,20 +2765,17 @@ void TreeSupport::generateSupportSkin(std::vector& support_layer_stora } may_need_skin_area = next_skin.unionPolygons(); } - } - ); + }); } - cura::parallel_for - ( + cura::parallel_for( 0, support_layer_storage.size(), [&](const LayerIndex layer_idx) { 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]); - } - ); + }); } void TreeSupport::filterFloatingLines(std::vector& support_layer_storage, std::vector& support_skin_storage) @@ -2766,15 +2791,15 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora }; - std::vector support_holes(support_layer_storage.size(),Polygons()); + std::vector support_holes(support_layer_storage.size(), Polygons()); - //Extract all holes as polygon objects + // Extract all holes as polygon objects cura::parallel_for( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { std::vector parts = support_layer_storage[layer_idx].sortByNesting(); if (parts.size() <= 1) @@ -2822,8 +2847,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } Polygons outer_walls - = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()) - .tubeShape(config.support_line_width*config.support_wall_count,0); + = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(config.support_line_width * config.support_wall_count, 0); Polygons holes_below; @@ -2840,11 +2864,11 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora { 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 + 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 { for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) { @@ -2936,10 +2960,11 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora dur_hole_removal); } -void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, - std::vector& support_skin_storage, - std::vector& support_layer_storage_fractional, - SliceDataStorage& storage) +void TreeSupport::finalizeInterfaceAndSupportAreas( + std::vector& support_layer_storage, + std::vector& support_skin_storage, + std::vector& support_layer_storage_fractional, + SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS @@ -2952,7 +2977,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor support_layer_storage.size(), [&](const LayerIndex layer_idx) { - if (!storage.support.supportLayers[layer_idx].support_roof.empty()) + if (! storage.support.supportLayers[layer_idx].support_roof.empty()) { switch (interface_pref) { @@ -2981,25 +3006,24 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor { Polygons interface_lines; - for(SupportInfillPart& roof_part:storage.support.supportLayers[layer_idx].support_roof) + for (SupportInfillPart& roof_part : storage.support.supportLayers[layer_idx].support_roof) { - interface_lines.add(TreeSupportUtils::generateSupportInfillLines(roof_part.outline_, - config, - true, - layer_idx, - roof_part.custom_line_distance_ == 0 ? config.support_roof_line_distance : roof_part.custom_line_distance_, - storage.support.cross_fill_provider, - roof_part.inset_count_to_generate_, - roof_part.custom_line_pattern_) + interface_lines.add(TreeSupportUtils::generateSupportInfillLines( + roof_part.outline_, + config, + true, + layer_idx, + roof_part.custom_line_distance_ == 0 ? config.support_roof_line_distance : roof_part.custom_line_distance_, + storage.support.cross_fill_provider, + roof_part.inset_count_to_generate_, + roof_part.custom_line_pattern_) .offsetPolyLine(config.support_roof_line_width / 2)); - } interface_lines = interface_lines.unionPolygons(); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(interface_lines); support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(interface_lines); - } break; @@ -3008,20 +3032,27 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); Polygons tree_lines; - tree_lines = - tree_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_layer_storage[layer_idx], config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) - .offsetPolyLine(config.support_line_width / 2) - ); + tree_lines = tree_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + support_layer_storage[layer_idx], + config, + false, + layer_idx, + config.support_line_distance, + storage.support.cross_fill_provider, + config.support_wall_count) + .offsetPolyLine(config.support_line_width / 2)); Polygons support_skin_lines; - support_skin_lines = - support_skin_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_skin_storage[layer_idx], config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, std::max(0, config.support_wall_count - 1), EFillMethod::LINES) - .offsetPolyLine(config.support_line_width / 2) - ); + support_skin_lines = support_skin_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + support_skin_storage[layer_idx], + config, + false, + layer_idx, + config.support_skin_line_distance, + storage.support.cross_fill_provider, + std::max(0, config.support_wall_count - 1), + EFillMethod::LINES) + .offsetPolyLine(config.support_line_width / 2)); Polygons invalid_roof = existing_roof.intersection(support_skin_lines.unionPolygons(tree_lines)); AABB invalid_roof_aabb = AABB(invalid_roof); @@ -3039,7 +3070,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor if (config.support_bottom_layers > 0 && ! (support_layer_storage[layer_idx].empty() || support_skin_storage[layer_idx].empty())) { Polygons floor_layer = storage.support.supportLayers[layer_idx].support_bottom; - Polygons layer_outset = support_layer_storage[layer_idx].unionPolygons(support_skin_storage[layer_idx]).offset(config.support_bottom_offset).difference(volumes_.getCollision(0, layer_idx, false)); + Polygons layer_outset = support_layer_storage[layer_idx] + .unionPolygons(support_skin_storage[layer_idx]) + .offset(config.support_bottom_offset) + .difference(volumes_.getCollision(0, layer_idx, false)); size_t layers_below = 0; while (layers_below <= config.support_bottom_layers) { @@ -3062,7 +3096,6 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor storage.support.supportLayers[layer_idx].support_bottom = storage.support.supportLayers[layer_idx].support_bottom.unionPolygons(floor_layer); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(floor_layer.offset(10)); // Subtract the support floor from the normal support. - } }); @@ -3080,37 +3113,44 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor Polygons support_areas = support_layer_storage[layer_idx]; Polygons skin_areas = support_skin_storage[layer_idx]; - if(layer_idx > 0) + if (layer_idx > 0) { fractional_support = support_layer_storage_fractional[layer_idx].intersection(support_layer_storage[layer_idx - 1]); fractional_skin = support_layer_storage_fractional[layer_idx].intersection(support_skin_storage[layer_idx - 1]); - //To remove the lines it needs to be known what the lines are. This can not be done in the loop above, so it needs to be done here again for fractional support. - // todo deduplicate code - if(interface_pref == InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE) + // To remove the lines it needs to be known what the lines are. This can not be done in the loop above, so it needs to be done here again for fractional support. + // todo deduplicate code + if (interface_pref == InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE) { Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); Polygons tree_lines; - tree_lines = - tree_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(fractional_support, config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) - .offsetPolyLine(config.support_line_width / 2) - ); + tree_lines = tree_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + fractional_support, + config, + false, + layer_idx, + config.support_line_distance, + storage.support.cross_fill_provider, + config.support_wall_count) + .offsetPolyLine(config.support_line_width / 2)); Polygons support_skin_lines; - support_skin_lines = - support_skin_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(fractional_skin, config, false, layer_idx, config.support_skin_line_distance, storage.support.cross_fill_provider, std::max(0, config.support_wall_count - 1), EFillMethod::LINES) - .offsetPolyLine(config.support_line_width / 2) - ); + support_skin_lines = support_skin_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + fractional_skin, + config, + false, + layer_idx, + config.support_skin_line_distance, + storage.support.cross_fill_provider, + std::max(0, config.support_wall_count - 1), + EFillMethod::LINES) + .offsetPolyLine(config.support_line_width / 2)); Polygons invalid_roof = existing_roof.intersection(tree_lines); AABB invalid_roof_aabb = AABB(invalid_roof); storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); } - //Remove overlap between fractional and regular support that may have been created in generateSupportSkin. + // Remove overlap between fractional and regular support that may have been created in generateSupportSkin. support_areas = support_areas.difference(support_layer_storage_fractional[layer_idx]); skin_areas = skin_areas.difference(support_layer_storage_fractional[layer_idx]); } @@ -3119,48 +3159,33 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor fractional_support = support_layer_storage_fractional[layer_idx]; } - storage.support.supportLayers[layer_idx].fillInfillParts( - support_areas, - config.support_line_width, - config.support_wall_count, - false, - convert_every_part); + storage.support.supportLayers[layer_idx].fillInfillParts(support_areas, config.support_line_width, config.support_wall_count, false, convert_every_part); storage.support.supportLayers[layer_idx].fillInfillParts( skin_areas, config.support_line_width, - std::max(config.support_wall_count - 1,0), + std::max(config.support_wall_count - 1, 0), false, convert_every_part, config.support_skin_line_distance, EFillMethod::ZIG_ZAG); - storage.support.supportLayers[layer_idx].fillInfillParts( - fractional_support, - config.support_line_width, - config.support_wall_count, - true, - convert_every_part); + storage.support.supportLayers[layer_idx].fillInfillParts(fractional_support, config.support_line_width, config.support_wall_count, true, convert_every_part); storage.support.supportLayers[layer_idx].fillInfillParts( fractional_skin, config.support_line_width, - std::max(config.support_wall_count - 1,0), + std::max(config.support_wall_count - 1, 0), true, convert_every_part, config.support_skin_line_distance, EFillMethod::ZIG_ZAG); - for(FakeRoofArea& fake_roof : fake_roof_areas[layer_idx]) + for (FakeRoofArea& fake_roof : fake_roof_areas[layer_idx]) { - storage.support.supportLayers[layer_idx].fillInfillParts( - fake_roof.area_, - config.support_line_width, - 0, - fake_roof.fractional_, - convert_every_part, - fake_roof.line_distance_); + storage.support.supportLayers[layer_idx] + .fillInfillParts(fake_roof.area_, config.support_line_width, 0, fake_roof.fractional_, convert_every_part, fake_roof.line_distance_); } { @@ -3256,8 +3281,15 @@ void TreeSupport::drawAreas(std::vector>& move_bou { if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ && config.support_roof_wall_count == 0) { - Polygons roof_lines = TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, config.support_roof_wall_count); - if(roof_lines.polyLineLength() < data_pair.second.polyLineLength()) // arbitrary threshold to check if the interface pattern is propper. + Polygons roof_lines = TreeSupportUtils::generateSupportInfillLines( + data_pair.second, + config, + true, + layer_idx, + config.support_roof_line_distance, + nullptr, + config.support_roof_wall_count); + if (roof_lines.polyLineLength() < data_pair.second.polyLineLength()) // arbitrary threshold to check if the interface pattern is propper. { std::vector to_disable_roofs; to_disable_roofs.emplace_back(data_pair.first); @@ -3283,36 +3315,30 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - if(data_pair.first->parents_.empty() && - !data_pair.first->supports_roof_ && - !data_pair.first->cradle_line_ && - layer_idx + 1 < support_roof_storage_fractional.size() && - config.z_distance_top % config.layer_height > 0) + if (data_pair.first->parents_.empty() && ! data_pair.first->supports_roof_ && ! data_pair.first->cradle_line_ && layer_idx + 1 < support_roof_storage_fractional.size() + && config.z_distance_top % config.layer_height > 0) { - if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) + if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) { - if(data_pair.first->roof_with_enforced_walls) + if (data_pair.first->roof_with_enforced_walls) { - support_roof_extra_wall_storage_fractional[layer_idx+1].add(data_pair.second); - + support_roof_extra_wall_storage_fractional[layer_idx + 1].add(data_pair.second); } else { - support_roof_storage_fractional[layer_idx+1].add(data_pair.second); + support_roof_storage_fractional[layer_idx + 1].add(data_pair.second); } } else { - support_layer_storage_fractional[layer_idx+1].add(data_pair.second); + support_layer_storage_fractional[layer_idx + 1].add(data_pair.second); } - } - if(data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) + if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_) { - if(data_pair.first->roof_with_enforced_walls) + if (data_pair.first->roof_with_enforced_walls) { support_roof_extra_wall_storage[layer_idx].add(data_pair.second); - } else { @@ -3323,9 +3349,8 @@ void TreeSupport::drawAreas(std::vector>& move_bou { support_layer_storage[layer_idx].add(data_pair.second); } - } - if(layer_idx + 1< support_roof_storage_fractional.size()) + if (layer_idx + 1 < support_roof_storage_fractional.size()) { support_roof_storage_fractional[layer_idx + 1] = support_roof_storage_fractional[layer_idx + 1].unionPolygons(); support_layer_storage_fractional[layer_idx + 1] = support_layer_storage_fractional[layer_idx + 1].unionPolygons(); @@ -3333,10 +3358,17 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - generateSupportSkin(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, - storage,layer_tree_polygons,cradle_data); + generateSupportSkin( + 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, + storage, + layer_tree_polygons, + cradle_data); for (const auto layer_idx : ranges::views::iota(0UL, support_layer_storage.size())) { @@ -3345,7 +3377,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } const auto t_skin = std::chrono::high_resolution_clock::now(); - filterFloatingLines(support_layer_storage,support_skin_storage); + filterFloatingLines(support_layer_storage, support_skin_storage); const auto t_filter = std::chrono::high_resolution_clock::now(); finalizeInterfaceAndSupportAreas(support_layer_storage, support_skin_storage, support_layer_storage_fractional, storage); diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index feca820f5e..59d227eb8a 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -38,11 +38,11 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), config_.layer_height) : use_fake_roof_ ? SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS : 0) - , support_tree_top_rate_( mesh.settings.get("support_tree_top_rate")) + , support_tree_top_rate_(mesh.settings.get("support_tree_top_rate")) , support_roof_line_distance_( use_fake_roof_ ? (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) - * (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) - : mesh.settings.get("support_roof_line_distance")) + * (config_.support_line_width * 100 / mesh.settings.get("support_tree_top_rate")) + : mesh.settings.get("support_roof_line_distance")) , support_outset_(0) , // Since we disable support offset when tree support is enabled we use an offset of 0 rather than the setting value mesh.settings.get("support_offset") roof_outset_(use_fake_roof_ ? support_outset_ : mesh.settings.get("support_roof_offset")) @@ -71,14 +71,13 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T , cradle_area_threshold_(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) , cradle_tip_dtt_(config_.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips_(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_z_distance_layers_(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance") , config_.layer_height)) + , cradle_z_distance_layers_(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance"), config_.layer_height)) { - - if(cradle_layers_) + if (cradle_layers_) { cradle_layers_ += cradle_z_distance_layers_; } - if(cradle_length_ - cradle_line_width_ >0) + if (cradle_length_ - cradle_line_width_ > 0) { cradle_length_ -= cradle_line_width_; cradle_length_min_ -= cradle_line_width_; @@ -128,7 +127,7 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T support_supporting_branch_distance_ = 2 * config_.getRadius(dtt_when_tips_can_merge) + config_.support_line_width + FUDGE_LENGTH; - for(size_t cradle_z_dist_ctr=0; cradle_z_dist_ctr < cradle_z_distance_layers_ + 1; cradle_z_dist_ctr++) + for (size_t cradle_z_dist_ctr = 0; cradle_z_dist_ctr < cradle_z_distance_layers_ + 1; cradle_z_dist_ctr++) { cradle_xy_distance_.emplace_back(config_.xy_min_distance); } @@ -439,8 +438,8 @@ std::shared_ptr TreeSupportTipGenerator::getCrossFillPro { if (config_.support_pattern == EFillMethod::CROSS || config_.support_pattern == EFillMethod::CROSS_3D) { - std::pair key = std::pair(line_distance, line_width); - if(cross_fill_providers_.contains(key)) + std::pair key = std::pair(line_distance, line_width); + if (cross_fill_providers_.contains(key)) { return cross_fill_providers_[key]; } @@ -474,9 +473,9 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes { LayerIndex max_layer = 0; - for(LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) { - if(!mesh.overhang_areas[layer_idx].empty()) + if (! mesh.overhang_areas[layer_idx].empty()) { max_layer = layer_idx; } @@ -484,13 +483,13 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes max_layer = std::min(max_layer + cradle_layers_, LayerIndex(mesh.overhang_areas.size() - 1)); LayerIndex start_layer = 1; - floating_parts_cache_.resize(max_layer+1); - floating_parts_map_.resize(max_layer+1); - floating_parts_map_below_.resize(max_layer+1); + floating_parts_cache_.resize(max_layer + 1); + floating_parts_map_.resize(max_layer + 1); + floating_parts_map_below_.resize(max_layer + 1); std::mutex critical_sections; - Polygons completely_supported = volumes_.getCollision(0,0,true); - Polygons layer_below = completely_supported; //technically wrong, but the xy distance error on layer 1 should not matter + Polygons completely_supported = volumes_.getCollision(0, 0, true); + Polygons layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) { Polygons next_completely_supported; @@ -500,78 +499,77 @@ void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mes const std::vector layer_parts = layer.splitIntoParts(); cura::parallel_for( - 0, - layer_parts.size(), - [&](const size_t part_idx) - { + 0, + layer_parts.size(), + [&](const size_t part_idx) + { const PolygonsPart& part = layer_parts[part_idx]; AABB part_aabb(part); - bool has_support_below = !PolygonUtils::clipPolygonWithAABB(layer_below,part_aabb).intersection(part).empty(); + bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(layer_below, part_aabb).intersection(part).empty(); - if(! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold_)) + if (! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold_)) { std::lock_guard critical_section_add(critical_sections); next_completely_supported.add(part); - return ; + return; } Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * config_.min_wall_line_width * config_.min_wall_line_width); - if (!has_support_below) + if (! has_support_below) { std::lock_guard critical_section_add(critical_sections); - floating_parts_cache_[layer_idx].emplace_back(part,floating_parts_cache_[layer_idx].size(),0,overhang_area); - floating_parts_map_ [layer_idx].emplace_back(std::vector()); - floating_parts_map_below_ [layer_idx].emplace_back(std::vector()); - return ; + floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(), 0, overhang_area); + floating_parts_map_[layer_idx].emplace_back(std::vector()); + floating_parts_map_below_[layer_idx].emplace_back(std::vector()); + return; } size_t min_resting_on_layers = 0; coord_t supported_overhang_area = 0; bool add = false; std::vector idx_of_floating_below; - for (auto [idx, floating] : floating_parts_cache_[layer_idx-1] | ranges::views::enumerate) + for (auto [idx, floating] : floating_parts_cache_[layer_idx - 1] | ranges::views::enumerate) { - if(layer_idx > 1 && floating.height < cradle_layers_ - 1 && !floating.area.intersection(part).empty()) + if (layer_idx > 1 && floating.height < cradle_layers_ - 1 && ! floating.area.intersection(part).empty()) { idx_of_floating_below.emplace_back(idx); supported_overhang_area += floating.accumulated_supportable_overhang; - min_resting_on_layers = std::max(min_resting_on_layers,floating.height); + min_resting_on_layers = std::max(min_resting_on_layers, floating.height); add = true; } } - if(min_resting_on_layers < cradle_layers_ && add && overhang_area + supported_overhang_area < cradle_area_threshold_) + if (min_resting_on_layers < cradle_layers_ && add && overhang_area + supported_overhang_area < cradle_area_threshold_) { std::lock_guard critical_section_add(critical_sections); - for(size_t idx: idx_of_floating_below) + for (size_t idx : idx_of_floating_below) { - floating_parts_map_[layer_idx-1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); + floating_parts_map_[layer_idx - 1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); } floating_parts_map_[layer_idx].emplace_back(std::vector()); floating_parts_map_below_[layer_idx].emplace_back(idx_of_floating_below); - floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(),min_resting_on_layers+1,overhang_area + supported_overhang_area); + floating_parts_cache_[layer_idx] + .emplace_back(part, floating_parts_cache_[layer_idx].size(), min_resting_on_layers + 1, overhang_area + supported_overhang_area); } else { std::lock_guard critical_section_add(critical_sections); next_completely_supported.add(part); } - }); + }); layer_below = layer; completely_supported = next_completely_supported; - } - } std::vector TreeSupportTipGenerator::getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area, bool above) { std::vector result; - if(layer_idx == 0) + if (layer_idx == 0) { return result; } @@ -586,14 +584,14 @@ std::vector TreeSupportTipG if (has_result) { std::lock_guard critical_section(*critical_floating_parts_cache_); - if (!floating_parts_cache_[layer_idx].empty() && above) + if (! floating_parts_cache_[layer_idx].empty() && above) { for (size_t resting_idx : floating_parts_map_[layer_idx - 1][idx_of_area]) { result.emplace_back(floating_parts_cache_[layer_idx][resting_idx]); } } - else if (!floating_parts_cache_[layer_idx - 1].empty() && !above) + else if (! floating_parts_cache_[layer_idx - 1].empty() && ! above) { for (size_t resting_idx : floating_parts_map_below_[layer_idx][idx_of_area]) { @@ -614,7 +612,7 @@ std::vector TreeSupportTipG { std::vector result; - if(layer_idx == 0) + if (layer_idx == 0) { return result; } @@ -625,12 +623,12 @@ std::vector TreeSupportTipG has_result = layer_idx < floating_parts_cache_.size(); } - if(has_result) + if (has_result) { std::lock_guard critical_section(*critical_floating_parts_cache_); for (auto [idx, floating_data] : floating_parts_cache_[layer_idx] | ranges::views::enumerate) { - if(floating_data.height == 0) + if (floating_data.height == 0) { result.emplace_back(floating_data); } @@ -642,7 +640,6 @@ std::vector TreeSupportTipG return result; } return result; - } std::vector>> TreeSupportTipGenerator::generateCradleCenters(const SliceMeshStorage& mesh) @@ -674,8 +671,9 @@ std::vector>> TreeSupportTipGenerator::generat Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); std::vector additional_centers; - TreeSupportCradle* cradle_main = new TreeSupportCradle(layer_idx,center_prev,shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_, cradle_line_count_); - for(size_t z_distance = 0; z_distance < config_.z_distance_top_layers; z_distance++) + TreeSupportCradle* cradle_main + = new TreeSupportCradle(layer_idx, center_prev, shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_, cradle_line_count_); + for (size_t z_distance = 0; z_distance < config_.z_distance_top_layers; z_distance++) { accumulated_model[z_distance] = pointy_info.area; cradle_main->centers_.emplace_back(center_prev); @@ -686,7 +684,6 @@ std::vector>> TreeSupportTipGenerator::generat std::vector unsupported_model(accumulated_model.size()); for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) { - // shadow model up => not cradle where model // then drop cradle down // cut into parts => get close to original pointy that are far enough from each other. @@ -720,7 +717,7 @@ std::vector>> TreeSupportTipGenerator::generat blocked_by_dedupe = true; } - std::vector all_pointy_idx_below {next_pointy_data.index}; + std::vector all_pointy_idx_below{ next_pointy_data.index }; for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_idx_below.empty(); cradle_down_layer--) { std::vector next_all_pointy_idx_below; @@ -729,10 +726,10 @@ std::vector>> TreeSupportTipGenerator::generat { for (auto prev_pointy_data : getUnsupportedArea(layer_idx + cradle_down_layer - 1 + z_distance_delta_, pointy_idx_below, false)) { - if(prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) + if (prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) { // Only add if area below does not have it's own cradle. - if(prev_pointy_data.height < cradle_layers_min_) + if (prev_pointy_data.height < cradle_layers_min_) { accumulated_model[cradle_down_layer].add(prev_pointy_data.area); next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); @@ -781,12 +778,12 @@ std::vector>> TreeSupportTipGenerator::generat shadow = shadow.offset(-config_.maximum_move_distance).unionPolygons(model_outline); accumulated_model[cradle_up_layer + config_.z_distance_top_layers] = shadow; - if(cradle_up_layer > 0) + if (cradle_up_layer > 0) { Point2LL shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - coord_t center_move_distance = std::min(config_.maximum_move_distance_slow, cradle_line_width_ /3); - center_move_distance = std::min(center_move_distance, vSize(shadow_center-center_prev)); - center_prev = center_prev + normal(shadow_center-center_prev, center_move_distance); + coord_t center_move_distance = std::min(config_.maximum_move_distance_slow, cradle_line_width_ / 3); + center_move_distance = std::min(center_move_distance, vSize(shadow_center - center_prev)); + center_prev = center_prev + normal(shadow_center - center_prev, center_move_distance); cradle_main->centers_.emplace_back(center_prev); } } @@ -831,14 +828,13 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines_.empty()) + if (cradle->lines_.empty()) { cradle->lines_.resize(cradle_line_count_); } if (idx > cradle_z_distance_layers_ && ! model_shadow.empty()) { - Polygons relevant_forbidden = volumes_.getAvoidance( 0, layer_idx + idx, @@ -856,7 +852,9 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorgetIndexForLineEnd(further, layer_idx + idx); - if(removed_directions[angle_idx]) + if (removed_directions[angle_idx]) { continue; } @@ -944,21 +943,22 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > sqrt(cradle_area_threshold_) / 2; + bool too_long_jump + = ! cradle->lines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > sqrt(cradle_area_threshold_) / 2; // a cradle line should also be removed if there will be no way to support it - if(idx >= cradle_z_distance_layers_ + 1) + if (idx >= cradle_z_distance_layers_ + 1) { const Polygons actually_forbidden = volumes_.getAvoidance( config_.getRadius(0), - layer_idx + idx - (cradle_z_distance_layers_ + 1) , + layer_idx + idx - (cradle_z_distance_layers_ + 1), (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, true); too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config_.support_line_width; } - if (! too_short && !too_long_jump) + if (! too_short && ! too_long_jump) { - if(ordered_lines_to_center[angle_idx].empty()) + if (ordered_lines_to_center[angle_idx].empty()) { ordered_lines_to_center[angle_idx].add(closer); ordered_lines_to_center[angle_idx].add(further); @@ -966,25 +966,23 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector FUDGE_LENGTH) + if (distance_from_center > FUDGE_LENGTH) { - if(vSize(ordered_lines_to_center[angle_idx].front() - center) < FUDGE_LENGTH) + if (vSize(ordered_lines_to_center[angle_idx].front() - center) < FUDGE_LENGTH) { ordered_lines_to_center[angle_idx].clear(); ordered_lines_to_center[angle_idx].add(closer); ordered_lines_to_center[angle_idx].add(further); } - else if(distance_from_center < vSize(ordered_lines_to_center[angle_idx].front() - center)) + else if (distance_from_center < vSize(ordered_lines_to_center[angle_idx].front() - center)) { ordered_lines_to_center[angle_idx].clear(); ordered_lines_to_center[angle_idx].add(closer); ordered_lines_to_center[angle_idx].add(further); } } - } } - } for (auto [angle_idx, next_line] : ordered_lines_to_center | ranges::views::enumerate) @@ -995,14 +993,14 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines_[angle_idx].empty()) + // Handle cradle_z_distance_layers by overwriting first element in the vector until valid distance is reached. + if (idx <= cradle_z_distance_layers_ + 1 && ! cradle->lines_[angle_idx].empty()) { - cradle->lines_[angle_idx][0] = TreeSupportCradleLine(line,layer_idx+idx, cradle_lines_roof_); + cradle->lines_[angle_idx][0] = TreeSupportCradleLine(line, layer_idx + idx, cradle_lines_roof_); } else { - cradle->lines_[angle_idx].emplace_back(TreeSupportCradleLine(line,layer_idx+idx, cradle_lines_roof_)); + cradle->lines_[angle_idx].emplace_back(TreeSupportCradleLine(line, layer_idx + idx, cradle_lines_roof_)); } } } @@ -1011,16 +1009,16 @@ void TreeSupportTipGenerator::generateCradleLines(std::vectorlines_ | ranges::views::enumerate) { - if(!cradle_lines.empty()) + if (! cradle_lines.empty()) { Point2LL line_end = cradle_lines.back().line_.back(); for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) { Point2LL center = cradle->getCenter(line.layer_idx_); - if(vSize2(line_end - center) > vSize2(line.line_.back() - center)) + if (vSize2(line_end - center) > vSize2(line.line_.back() - center)) { Polygons line_extension; - line_extension.addLine(line.line_.back(),line_end); + line_extension.addLine(line.line_.back(), line_end); coord_t line_length_before = line_extension.polyLineLength(); Polygons actually_forbidden = volumes_.getAvoidance( 0, @@ -1030,22 +1028,22 @@ void TreeSupportTipGenerator::generateCradleLines(std::vector> all_cradles_per_layer(cradle_data_.size() + cradle_layers_); for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) @@ -1088,9 +1086,8 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() std::function handleNewEnd = [&](size_t cradle_line_idx, Point2LL new_end) { - auto& line = (*all_cradles_on_layer[cradle_line_idx]); - if(LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line_.front(), line.line_.back()) - || vSize(new_end - line.line_.front()) < cradle_length_min_) + auto& line = (*all_cradles_on_layer[cradle_line_idx]); + if (LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line_.front(), line.line_.back()) || vSize(new_end - line.line_.front()) < cradle_length_min_) { all_cradles_on_layer[cradle_line_idx]->addLineToRemoved(line.line_); all_cradles_on_layer[cradle_line_idx]->line_.clear(); @@ -1107,9 +1104,9 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() bounding_box_current.expand(min_distance_between_lines); for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) { - if(all_cradles_on_layer[cradle_idx_inner]->line_.empty()||all_cradles_on_layer[cradle_idx]->line_.empty()) + if (all_cradles_on_layer[cradle_idx_inner]->line_.empty() || all_cradles_on_layer[cradle_idx]->line_.empty()) { - continue ; + continue; } if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line_))) { @@ -1125,19 +1122,18 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if (inner_intersect_dist > outer_intersect_dist) { - //this does not ensure that the line ends will not touch. Line ends not touching is handled later + // this does not ensure that the line ends will not touch. Line ends not touching is handled later Point2LL new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); - handleNewEnd(cradle_idx_inner,new_end_inner); - + handleNewEnd(cradle_idx_inner, new_end_inner); } if (outer_intersect_dist > inner_intersect_dist) { Point2LL new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); - handleNewEnd(cradle_idx,new_end_outer); + handleNewEnd(cradle_idx, new_end_outer); } } - if(!outer_line.empty() && !inner_line.empty()) + if (! outer_line.empty() && ! inner_line.empty()) { // Touching lines have the same issue Lines touch if the end is to close to another line Point2LL inner_direction = inner_line.back() - inner_line.front(); @@ -1147,26 +1143,30 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) { coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); - if(inner_end_to_outer_distance < min_distance_between_lines && inner_end_to_outer_distance < vSize(outer_line.front() - inner_line.front())) + if (inner_end_to_outer_distance < min_distance_between_lines && inner_end_to_outer_distance < vSize(outer_line.front() - inner_line.front())) { - Point2LL new_end_inner = inner_line.back() + normal(inner_line.front()-inner_line.back(),min_distance_between_lines - inner_end_to_outer_distance); + Point2LL new_end_inner + = inner_line.back() + normal(inner_line.front() - inner_line.back(), min_distance_between_lines - inner_end_to_outer_distance); double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); - double error_correction_factor = 1.0 + error/double(min_distance_between_lines - inner_end_to_outer_distance); - new_end_inner = inner_line.back() + - normal(inner_line.front()-inner_line.back(),(min_distance_between_lines - inner_end_to_outer_distance)*error_correction_factor); - handleNewEnd(cradle_idx_inner,new_end_inner); + double error_correction_factor = 1.0 + error / double(min_distance_between_lines - inner_end_to_outer_distance); + new_end_inner + = inner_line.back() + + normal(inner_line.front() - inner_line.back(), (min_distance_between_lines - inner_end_to_outer_distance) * error_correction_factor); + handleNewEnd(cradle_idx_inner, new_end_inner); } else { coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); - if(outer_end_to_inner_distance < min_distance_between_lines && outer_end_to_inner_distance < vSize(outer_line.front() - inner_line.front())) + if (outer_end_to_inner_distance < min_distance_between_lines && outer_end_to_inner_distance < vSize(outer_line.front() - inner_line.front())) { - Point2LL new_end_outer = outer_line.back() + normal(outer_line.front()-outer_line.back(),min_distance_between_lines - outer_end_to_inner_distance); - double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer,outer_line.front(), outer_line.back())); - double error_correction_factor = 1.0 + error/double(min_distance_between_lines - outer_end_to_inner_distance); - new_end_outer = outer_line.back() + - normal(outer_line.front()-outer_line.back(),(min_distance_between_lines - outer_end_to_inner_distance)*error_correction_factor); - handleNewEnd(cradle_idx,new_end_outer); + Point2LL new_end_outer + = outer_line.back() + normal(outer_line.front() - outer_line.back(), min_distance_between_lines - outer_end_to_inner_distance); + double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer, outer_line.front(), outer_line.back())); + double error_correction_factor = 1.0 + error / double(min_distance_between_lines - outer_end_to_inner_distance); + new_end_outer + = outer_line.back() + + normal(outer_line.front() - outer_line.back(), (min_distance_between_lines - outer_end_to_inner_distance) * error_correction_factor); + handleNewEnd(cradle_idx, new_end_outer); } } } @@ -1177,10 +1177,10 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() }); cura::parallel_for( - 1, - cradle_data_.size(), - [&](const LayerIndex layer_idx) - { + 1, + cradle_data_.size(), + [&](const LayerIndex layer_idx) + { for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { TreeSupportCradle* cradle = cradle_data_[layer_idx][cradle_idx]; @@ -1188,12 +1188,12 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) { - if(!cradle_lines.empty()) + if (! cradle_lines.empty()) { Point2LL line_end = cradle_lines.back().line_.back(); Point2LL line_front_uppermost = cradle_lines.back().line_.front(); - if(vSize2(line_end - cradle_lines.back().line_.front()) > cradle_length_ * cradle_length_) + if (vSize2(line_end - cradle_lines.back().line_.front()) > cradle_length_ * cradle_length_) { coord_t current_cradle_xy_distance = cradle_xy_distance_[cradle_lines.back().layer_idx_ - layer_idx]; coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; @@ -1203,19 +1203,19 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() Point2LL center = cradle->getCenter(line.layer_idx_); Point2LL line_back_inner = line.line_.back(); Point2LL line_front_inner = line.line_.front(); - if(vSize2(line_back_inner - line_front_inner) > cradle_length_ * cradle_length_) + if (vSize2(line_back_inner - line_front_inner) > cradle_length_ * cradle_length_) { - //As the center can move there is no guarantee that the point of the current line lies on the line below. - Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end,line.line_.front(),line.line_.back()); + // As the center can move there is no guarantee that the point of the current line lies on the line below. + Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end, line.line_.front(), line.line_.back()); current_cradle_xy_distance = cradle_xy_distance_[line.layer_idx_ - layer_idx]; current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; - if(vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) + if (vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) { line.line_.back() = projected_line_end; } else { - line.line_.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); + line.line_.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); } } line_end = line.line_.back(); @@ -1224,301 +1224,325 @@ void TreeSupportTipGenerator::cleanCradleLineOverlaps() } } } - }); + }); } void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas) { + const coord_t min_distance_between_lines + = FUDGE_LENGTH + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + // Some angles needed to move cradle lines outwards to prevent them from toughing. + double center_angle = (2.0 * std::numbers::pi) / double(cradle_line_count_); + double outer_angle = (std::numbers::pi - center_angle) / 2; + coord_t outer_radius = (double(min_distance_between_lines + config_.support_line_width) / sin(center_angle)) * sin(outer_angle); + const coord_t small_hole_size + = EPSILON + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath + std::mutex critical_support_free_areas_and_cradle_areas; - const coord_t min_distance_between_lines = FUDGE_LENGTH + (config_.fill_outline_gaps ? config_.min_feature_size/ 2 - 5 : config_.min_wall_line_width/ 2 - 5); // based on calculation in WallToolPath - // Some angles needed to move cradle lines outwards to prevent them from toughing. - double center_angle = (2.0 * std::numbers::pi) / double(cradle_line_count_); - double outer_angle = (std::numbers::pi - center_angle) / 2; - coord_t outer_radius = (double(min_distance_between_lines + config_.support_line_width) / sin(center_angle)) * sin(outer_angle); - const coord_t small_hole_size - = EPSILON + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - std::mutex critical_support_free_areas_and_cradle_areas; - - cura::parallel_for( - 0, - cradle_data_.size(), - [&](const LayerIndex layer_idx) + cura::parallel_for( + 0, + cradle_data_.size(), + [&](const LayerIndex layer_idx) + { + std::vector valid_cradles; + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - std::vector valid_cradles; - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + TreeSupportCradle& cradle = *cradle_data_[layer_idx][cradle_idx]; + for (size_t cradle_height = 0; cradle_height <= cradle_layers_; cradle_height++) { - TreeSupportCradle& cradle = *cradle_data_[layer_idx][cradle_idx]; - for (size_t cradle_height = 0; cradle_height <= cradle_layers_; cradle_height++) - { - Polygons line_tips; + Polygons line_tips; - std::vector> all_tips_center; - // generate trapezoid line tip with front width of support line width, back cradle_width. + std::vector> all_tips_center; + // generate trapezoid line tip with front width of support line width, back cradle_width. - for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) + { + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_height, line_idx); + if (! line_opt) { + continue; + } + TreeSupportCradleLine* cradle_line = line_opt.value(); + Polygon line = cradle_line->line_; - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx+cradle_height,line_idx); - if(!line_opt) - { - continue; - } - TreeSupportCradleLine* cradle_line = line_opt.value(); - Polygon line = cradle_line->line_; - - coord_t current_cradle_line_width = cradle_line_width_; + coord_t current_cradle_line_width = cradle_line_width_; - double assumed_half_center_angle = std::numbers::pi / (1.5 * cradle_line_count_); - coord_t triangle_length = cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - config_.support_line_width) / 2) * - tan(std::numbers::pi / 2 - assumed_half_center_angle); + double assumed_half_center_angle = std::numbers::pi / (1.5 * cradle_line_count_); + coord_t triangle_length + = cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - config_.support_line_width) / 2) * tan(std::numbers::pi / 2 - assumed_half_center_angle); - const coord_t line_length = line.polylineLength(); - if(triangle_length >= line_length + cradle_line_width_) - { - triangle_length = line_length + cradle_line_width_; - current_cradle_line_width = config_.support_line_width + - 2 * triangle_length * tan(assumed_half_center_angle); - } + const coord_t line_length = line.polylineLength(); + if (triangle_length >= line_length + cradle_line_width_) + { + triangle_length = line_length + cradle_line_width_; + current_cradle_line_width = config_.support_line_width + 2 * triangle_length * tan(assumed_half_center_angle); + } - Point2LL direction = line.back() - line.front(); - Point2LL center_front = line.front() - normal(direction, cradle_line_width_ / 2); + Point2LL direction = line.back() - line.front(); + Point2LL center_front = line.front() - normal(direction, cradle_line_width_ / 2); - Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), config_.support_line_width / 2); - Point2LL center_up = center_front + direction_up_center; - Point2LL center_down = center_front - direction_up_center; + Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), config_.support_line_width / 2); + Point2LL center_up = center_front + direction_up_center; + Point2LL center_down = center_front - direction_up_center; - coord_t tip_shift = 0; - for (auto existing_center : all_tips_center) + coord_t tip_shift = 0; + for (auto existing_center : all_tips_center) + { + Point2LL intersect; + bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); + if (centers_touch || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) { - Point2LL intersect; - bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); - if (centers_touch - || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) - { - // This cradle line is to close to another. - // Move it back todo If line gets smaller than min length => abort - coord_t tip_shift_here = outer_radius - vSize(center_front - cradle.getCenter(cradle_line->layer_idx_)); - tip_shift += tip_shift_here; - center_front = center_front + normal(direction, tip_shift_here); - center_up = center_front + direction_up_center; - center_down = center_front - direction_up_center; - } + // This cradle line is to close to another. + // Move it back todo If line gets smaller than min length => abort + coord_t tip_shift_here = outer_radius - vSize(center_front - cradle.getCenter(cradle_line->layer_idx_)); + tip_shift += tip_shift_here; + center_front = center_front + normal(direction, tip_shift_here); + center_up = center_front + direction_up_center; + center_down = center_front - direction_up_center; } + } - Point2LL back_center = center_front + normal(direction, triangle_length); - Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), current_cradle_line_width / 2); - - Point2LL back_up = back_center + direction_up_back; - Point2LL back_down = back_center - direction_up_back; + Point2LL back_center = center_front + normal(direction, triangle_length); + Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), current_cradle_line_width / 2); - line.front() = back_center + normal(direction, current_cradle_line_width / 2 - FUDGE_LENGTH / 2); - all_tips_center.emplace_back(center_up, center_down); + Point2LL back_up = back_center + direction_up_back; + Point2LL back_down = back_center - direction_up_back; - Polygon line_tip; - line_tip.add(back_down); - if(current_cradle_line_width == cradle_line_width_) - { - coord_t distance_end_front = line_length - triangle_length + cradle_line_width_ - tip_shift; - Point2LL line_end_down = back_down + normal(direction, distance_end_front); - Point2LL line_end_up = back_up + normal(direction, distance_end_front); - line_tip.add(line_end_down); - line_tip.add(line_end_up); - } - line_tip.add(back_up); - line_tip.add(center_up); - line_tip.add(center_down); - if (line_tip.area() < 0) - { - line_tip.reverse(); - } - cradle_line->area_.add(line_tip); + line.front() = back_center + normal(direction, current_cradle_line_width / 2 - FUDGE_LENGTH / 2); + all_tips_center.emplace_back(center_up, center_down); - Polygons anti_preferred = cradle_line->area_.offset(config_.xy_distance); - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for(size_t z_distance_idx = 0; z_distance_idx < config_.z_distance_top_layers; z_distance_idx++) - { - volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); - } + Polygon line_tip; + line_tip.add(back_down); + if (current_cradle_line_width == cradle_line_width_) + { + coord_t distance_end_front = line_length - triangle_length + cradle_line_width_ - tip_shift; + Point2LL line_end_down = back_down + normal(direction, distance_end_front); + Point2LL line_end_up = back_up + normal(direction, distance_end_front); + line_tip.add(line_end_down); + line_tip.add(line_end_up); + } + line_tip.add(back_up); + line_tip.add(center_up); + line_tip.add(center_down); + if (line_tip.area() < 0) + { + line_tip.reverse(); } + cradle_line->area_.add(line_tip); + Polygons anti_preferred = cradle_line->area_.offset(config_.xy_distance); + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t z_distance_idx = 0; z_distance_idx < config_.z_distance_top_layers; z_distance_idx++) + { + volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); + } } + } - Polygons shadow = shadow_data[layer_idx][cradle.shadow_idx_][0]; - Polygons cradle_base = shadow; + Polygons shadow = shadow_data[layer_idx][cradle.shadow_idx_][0]; + Polygons cradle_base = shadow; - if (! use_fake_roof_ && support_roof_layers_) + if (! use_fake_roof_ && support_roof_layers_) + { + Polygons cut_line_base; + Polygons first_cradle_areas; + if (large_cradle_base_) { - Polygons cut_line_base; - Polygons first_cradle_areas; - if (large_cradle_base_) - { - // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest - // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just - // the overhang, but that should not cause any issues. - Point2LL min_p = cradle_base.min(); - Point2LL max_p = cradle_base.max(); - Polygons rest_line; - rest_line.addLine(min_p, max_p); - cut_line_base = rest_line.offsetPolyLine(small_hole_size); - } + // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest + // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just + // the overhang, but that should not cause any issues. + Point2LL min_p = cradle_base.min(); + Point2LL max_p = cradle_base.max(); + Polygons rest_line; + rest_line.addLine(min_p, max_p); + cut_line_base = rest_line.offsetPolyLine(small_hole_size); + } + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers_; interface_down++) { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers_; interface_down++) - { - support_free_areas[layer_idx - interface_down].add(cut_line_base); - volumes_.addAreaToAntiPreferred(cradle_base, layer_idx - interface_down); - } + support_free_areas[layer_idx - interface_down].add(cut_line_base); + volumes_.addAreaToAntiPreferred(cradle_base, layer_idx - interface_down); } - if (large_cradle_base_) + } + if (large_cradle_base_) + { + cradle_base = cradle_base.offset(config_.getRadius(cradle_tip_dtt_), ClipperLib::jtRound); + Polygons center_removed = cradle_base.difference(cut_line_base); + if (center_removed.area() > 1) { - cradle_base = cradle_base.offset(config_.getRadius(cradle_tip_dtt_), ClipperLib::jtRound); - Polygons center_removed = cradle_base.difference(cut_line_base); - if(center_removed.area()>1) - { - cradle_base = center_removed; - } + cradle_base = center_removed; } - else if(cradle_base_roof_) + } + else if (cradle_base_roof_) + { + // collect all inner points and connect to center for thin cradle base + Polygons connected_cradle_base; + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { - // collect all inner points and connect to center for thin cradle base - Polygons connected_cradle_base; - for(size_t line_idx = 0;line_idx line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1, line_idx); + if (line_opt) { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ +1, line_idx); - if(line_opt) - { - connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_),line_opt.value()->line_.front()); - } + connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_), line_opt.value()->line_.front()); } - cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); } + cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); } + } - cradle_base = cradle_base.unionPolygons(); + cradle_base = cradle_base.unionPolygons(); - if(cradle_lines_roof_) + if (cradle_lines_roof_) + { + Polygons forbidden_here = volumes_.getAvoidance( + 0, + layer_idx, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); + std::vector> roofs; + roofs.emplace_back(cradle_base, -1); + + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { - Polygons forbidden_here = - volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); - std::vector> roofs; - roofs.emplace_back(cradle_base, -1); - - for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1, line_idx); + if (! line_opt) { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1,line_idx); - if(!line_opt) - { - continue; - } - - coord_t line_base_offset = large_cradle_base_ ? std::max(coord_t(0),config_.getRadius(cradle_tip_dtt_) - cradle_line_width_ /2) : 0; - roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset,ClipperLib::jtRound),line_idx); + continue; } + coord_t line_base_offset = large_cradle_base_ ? std::max(coord_t(0), config_.getRadius(cradle_tip_dtt_) - cradle_line_width_ / 2) : 0; + roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset, ClipperLib::jtRound), line_idx); + } + - for(auto roof_area_pair : roofs) + for (auto roof_area_pair : roofs) + { + Polygons roof_area_before = roof_area_pair.first; + Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( + roof_area_pair.first, + roof_outset_, + forbidden_here, + config_.support_line_width, + 0, + 1, + config_.support_line_distance / 2, + &config_.simplifier); + for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) { - Polygons roof_area_before = roof_area_pair.first; - Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( - roof_area_pair.first,roof_outset_,forbidden_here, config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); - for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) - { - const Polygons forbidden_next = - volumes_.getAvoidance(0, layer_idx - (dtt_roof + 1), (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); + const Polygons forbidden_next = volumes_.getAvoidance( + 0, + layer_idx - (dtt_roof + 1), + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); - full_overhang_area = full_overhang_area.difference(forbidden_next); + full_overhang_area = full_overhang_area.difference(forbidden_next); - if (full_overhang_area.area()>EPSILON && dtt_roof < support_roof_layers_) + if (full_overhang_area.area() > EPSILON && dtt_roof < support_roof_layers_) + { + if (roof_area_pair.second != -1) // If is_line { - if(roof_area_pair.second != -1) //If is_line + TreeSupportCradleLine roof_base_line(cradle.lines_[roof_area_pair.second].front()); + roof_base_line.area_ = full_overhang_area; + roof_base_line.is_base_ = true; + roof_base_line.layer_idx_ = layer_idx - dtt_roof; + cradle.lines_[roof_area_pair.second].emplace_front(roof_base_line); + } + else + { + if (dtt_roof < cradle.base_below_.size()) { - TreeSupportCradleLine roof_base_line(cradle.lines_[roof_area_pair.second].front()); - roof_base_line.area_ = full_overhang_area; - roof_base_line.is_base_ = true; - roof_base_line.layer_idx_ = layer_idx - dtt_roof; - cradle.lines_[roof_area_pair.second].emplace_front(roof_base_line); + cradle.base_below_[dtt_roof].add(full_overhang_area); } else { - if(dtt_roof < cradle.base_below_.size()) - { - cradle.base_below_[dtt_roof].add(full_overhang_area); - } - else - { - cradle.base_below_.emplace_back(full_overhang_area); - } - } - const Polygons forbidden_before = - volumes_.getAvoidance(0, layer_idx - dtt_roof, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); - - Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc(full_overhang_area, - config_.maximum_move_distance, - forbidden_before, - config_.xy_min_distance + config_.min_feature_size, - 0, - 1, - config_.support_line_distance / 2, - &config_.simplifier); - Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); - if(overhang_part.area()>EPSILON) - { - OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[layer_idx][cradle_idx],layer_idx-dtt_roof,roof_area_pair.second); - cradle.overhang_[layer_idx-dtt_roof].emplace_back(cradle_overhang); + cradle.base_below_.emplace_back(full_overhang_area); } } - else + const Polygons forbidden_before = volumes_.getAvoidance( + 0, + layer_idx - dtt_roof, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); + + Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc( + full_overhang_area, + config_.maximum_move_distance, + forbidden_before, + config_.xy_min_distance + config_.min_feature_size, + 0, + 1, + config_.support_line_distance / 2, + &config_.simplifier); + Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); + if (overhang_part.area() > EPSILON) { - if(roof_area_before.area()>1) - { - LayerIndex line_layer_idx = roof_area_pair.second<0 ? LayerIndex(-1) : cradle_data_[layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; - OverhangInformation cradle_overhang(roof_area_before, true, cradle_data_[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); - cradle.overhang_[layer_idx-dtt_roof].emplace_back(cradle_overhang); - } - break; + OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[layer_idx][cradle_idx], layer_idx - dtt_roof, roof_area_pair.second); + cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); } - roof_area_before = full_overhang_area; } - } - } - else - { - Polygons forbidden_here = - volumes_.getAvoidance(0, layer_idx, (only_gracious_||!config_.support_rests_on_model)? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, ! xy_overrides_); - - coord_t offset_radius = config_.getRadius(cradle_tip_dtt_); - if(cradle_base.offset(offset_radius,ClipperLib::jtRound).difference(forbidden_here).area()>1) - { - OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[layer_idx][cradle_idx]); - cradle.overhang_[layer_idx].emplace_back(cradle_overhang); - } - - for(size_t line_idx = 0;line_idx < cradle.lines_.size(); line_idx++) - { - if(!cradle.lines_[line_idx].empty()) + else { - LayerIndex support_cradle_on_layer_idx = cradle.lines_[line_idx].front().layer_idx_ - (cradle_z_distance_layers_ + 1); - OverhangInformation line_overhang(cradle.lines_[line_idx].front().area_,false, - cradle_data_[layer_idx][cradle_idx],cradle.lines_[line_idx].front().layer_idx_,line_idx); - cradle.overhang_[support_cradle_on_layer_idx].emplace_back(line_overhang); + if (roof_area_before.area() > 1) + { + LayerIndex line_layer_idx + = roof_area_pair.second < 0 ? LayerIndex(-1) : cradle_data_[layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; + OverhangInformation cradle_overhang(roof_area_before, true, cradle_data_[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); + cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); + } + break; } + roof_area_before = full_overhang_area; } } - if(!cradle.overhang_.empty()) + } + else + { + Polygons forbidden_here = volumes_.getAvoidance( + 0, + layer_idx, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); + + coord_t offset_radius = config_.getRadius(cradle_tip_dtt_); + if (cradle_base.offset(offset_radius, ClipperLib::jtRound).difference(forbidden_here).area() > 1) { - valid_cradles.emplace_back(cradle_data_[layer_idx][cradle_idx]); + OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[layer_idx][cradle_idx]); + cradle.overhang_[layer_idx].emplace_back(cradle_overhang); } - else + + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { - delete cradle_data_[layer_idx][cradle_idx]; + if (! cradle.lines_[line_idx].empty()) + { + LayerIndex support_cradle_on_layer_idx = cradle.lines_[line_idx].front().layer_idx_ - (cradle_z_distance_layers_ + 1); + OverhangInformation line_overhang( + cradle.lines_[line_idx].front().area_, + false, + cradle_data_[layer_idx][cradle_idx], + cradle.lines_[line_idx].front().layer_idx_, + line_idx); + cradle.overhang_[support_cradle_on_layer_idx].emplace_back(line_overhang); + } } } - cradle_data_[layer_idx] = valid_cradles; - }); - + if (! cradle.overhang_.empty()) + { + valid_cradles.emplace_back(cradle_data_[layer_idx][cradle_idx]); + } + else + { + delete cradle_data_[layer_idx][cradle_idx]; + } + } + cradle_data_[layer_idx] = valid_cradles; + }); } @@ -1533,7 +1557,7 @@ void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std:: std::vector>> shadow_data = generateCradleCenters(mesh); generateCradleLines(shadow_data); cleanCradleLineOverlaps(); - generateCradleLineAreasAndBase(shadow_data,support_free_areas); + generateCradleLineAreasAndBase(shadow_data, support_free_areas); } void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof) @@ -1557,8 +1581,8 @@ void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, st // Technically this also makes support blocker smaller, which is wrong as they do not have a xy_distance, but it should be good enough. Polygons model_outline = volumes_.getCollision(0, layer_idx, ! xy_overrides_).offset(-config_.xy_min_distance, ClipperLib::jtRound); - //Use full_overhang to ensure dropped overhang will overlap with overhang further down. not leaving a small hole between model and roof where support could creep into. - Polygons overhang_full = TreeSupportUtils::safeOffsetInc( + // Use full_overhang to ensure dropped overhang will overlap with overhang further down. not leaving a small hole between model and roof where support could creep into. + Polygons overhang_full = TreeSupportUtils::safeOffsetInc( mesh.full_overhang_areas[layer_idx + z_distance_delta_], roof ? roof_outset_ : support_outset_, relevant_forbidden, @@ -1586,13 +1610,12 @@ void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, st cura::parallel_for( - 0, - result.size(), - [&](const LayerIndex layer_idx) - { - result[layer_idx]=result[layer_idx].unionPolygons(); - } - ); + 0, + result.size(), + [&](const LayerIndex layer_idx) + { + result[layer_idx] = result[layer_idx].unionPolygons(); + }); } void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& mesh) @@ -1607,11 +1630,10 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m } std::vector all_cradle_areas(cradle_data_.size()); - for(LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) { - for(size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - for (auto [line_idx, lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) { for (auto [height_idx, line] : lines | ranges::views::enumerate) @@ -1621,7 +1643,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m } for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - all_cradle_areas[layer_idx-base_idx].add(base); + all_cradle_areas[layer_idx - base_idx].add(base); } } } @@ -1640,12 +1662,11 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. Polygons forbidden_here = volumes_ .getAvoidance( - 0, - layer_idx, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_) - ; + 0, + layer_idx, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); // todo Since arachnea the assumption that an area smaller then line_width is not printed is no longer true all such safeOffset should have config_.support_line_width // replaced with another setting. It should still work in most cases, but it should be possible to create a situation where a overhang outset lags though a wall. I will @@ -1660,24 +1681,22 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m config_.support_line_distance / 2, &config_.simplifier); - //If an area is already supported by cradle don't put roof there. + // If an area is already supported by cradle don't put roof there. full_overhang_area = full_overhang_area.difference(all_cradle_areas[layer_idx].unionPolygons().offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) { - const Polygons forbidden_next = volumes_ - .getAvoidance( - 0, - layer_idx - (dtt_roof + 1), - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_) - ; + const Polygons forbidden_next = volumes_.getAvoidance( + 0, + layer_idx - (dtt_roof + 1), + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); full_overhang_area = full_overhang_area.difference(forbidden_next); - if(force_minimum_roof_area_) + if (force_minimum_roof_area_) { full_overhang_area.removeSmallAreas(minimum_roof_area_); } @@ -1708,9 +1727,13 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // area.offset(config_.xy_min_distance).unionPolygons().offset(-config_.xy_min_distance) = area if there is only one polygon in said area. I have not encountered issues // with using the default mitered here. Could be that i just have not encountered an issue with it yet though. - potential_support_roofs[layer_idx] = potential_support_roofs[layer_idx].unionPolygons().offset(config_.xy_min_distance).unionPolygons().offset(-config_.xy_min_distance).unionPolygons(potential_support_roofs[layer_idx]); - } - ); + potential_support_roofs[layer_idx] = potential_support_roofs[layer_idx] + .unionPolygons() + .offset(config_.xy_min_distance) + .unionPolygons() + .offset(-config_.xy_min_distance) + .unionPolygons(potential_support_roofs[layer_idx]); + }); std::vector additional_support_roofs(mesh.overhang_areas.size(), Polygons()); @@ -1725,12 +1748,11 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. Polygons forbidden_here = volumes_ .getAvoidance( - 0, - layer_idx, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_) - ; + 0, + layer_idx, + (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + config_.support_rests_on_model, + ! xy_overrides_); if (! force_minimum_roof_area_) { @@ -1767,13 +1789,12 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m cura::parallel_for( - 0, - additional_support_roofs.size(), - [&](const LayerIndex layer_idx) - { - support_roof_drawn_[layer_idx] = support_roof_drawn_[layer_idx].unionPolygons(additional_support_roofs[layer_idx]); - } - ); + 0, + additional_support_roofs.size(), + [&](const LayerIndex layer_idx) + { + support_roof_drawn_[layer_idx] = support_roof_drawn_[layer_idx].unionPolygons(additional_support_roofs[layer_idx]); + }); } @@ -1840,14 +1861,15 @@ TreeSupportElement* TreeSupportTipGenerator::addPointAsInfluenceArea( } -void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, - std::vector lines, - size_t roof_tip_layers, - LayerIndex insert_layer_idx, - coord_t tip_radius, - OverhangInformation& overhang_data, - size_t dont_move_until, - bool connect_points) +void TreeSupportTipGenerator::addLinesAsInfluenceAreas( + std::vector>& move_bounds, + std::vector lines, + size_t roof_tip_layers, + LayerIndex insert_layer_idx, + coord_t tip_radius, + OverhangInformation& overhang_data, + size_t dont_move_until, + bool connect_points) { for (LineInformation line : lines) { @@ -1871,24 +1893,23 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector= config_.branch_radius ? config_.tip_layers : - (config_.tip_layers * (tip_radius - config_.min_radius))/(config_.branch_radius - config_.min_radius); + size_t tip_dtt = tip_radius >= config_.branch_radius ? config_.tip_layers + : (config_.tip_layers * (tip_radius - config_.min_radius)) / (config_.branch_radius - config_.min_radius); coord_t hidden_radius = tip_radius > config_.branch_radius ? tip_radius - config_.branch_radius : 0; - double hidden_radius_increases = hidden_radius / (config_.branch_radius * (std::max(config_.diameter_scale_bp_radius - config_.diameter_angle_scale_factor, 0.0))); + double hidden_radius_increases = hidden_radius / (config_.branch_radius * (std::max(config_.diameter_scale_bp_radius - config_.diameter_angle_scale_factor, 0.0))); TipRoofType roof_type = TipRoofType::NONE; if (! overhang_data.is_cradle_ && roof_tip_layers > 0) { roof_type = TipRoofType::IS_ROOF; } - else if(overhang_data.is_roof_) + else if (overhang_data.is_roof_) { roof_type = TipRoofType::SUPPORTS_ROOF; } - if(!overhang_data.is_roof_ && overhang_data.is_cradle_ && !overhang_data.isCradleLine() && cradle_base_roof_) + if (! overhang_data.is_roof_ && overhang_data.is_cradle_ && ! overhang_data.isCradleLine() && cradle_base_roof_) { roof_type = TipRoofType::IS_ROOF; // No information about the amount of missing roof layers for cradle bases is stored. @@ -1897,12 +1918,11 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vectorcradle_line_ = std::make_shared(overhang_data.cradle_, overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); } @@ -1930,21 +1950,18 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas(std::vector>& move_bounds, - SliceDataStorage& storage) +void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage) { std::vector all_cradle_roofs(storage.support.supportLayers.size()); for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) { - for(auto cradle:cradles) + for (auto cradle : cradles) { - if(cradle->is_roof_) + if (cradle->is_roof_) { - for (auto [base_idx, base] : cradle->base_below_ | ranges::views::enumerate) { - all_cradle_roofs[layer_idx-base_idx].add(base); + all_cradle_roofs[layer_idx - base_idx].add(base); } } } @@ -1959,7 +1976,7 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( { std::vector to_be_removed; Polygons roof_on_layer_above = support_roof_drawn_[layer_idx + 1]; - roof_on_layer_above = roof_on_layer_above.unionPolygons(all_cradle_roofs[layer_idx+1]); + roof_on_layer_above = roof_on_layer_above.unionPolygons(all_cradle_roofs[layer_idx + 1]); Polygons roof_on_layer = support_roof_drawn_[layer_idx]; for (TreeSupportElement* elem : move_bounds[layer_idx]) @@ -1974,13 +1991,15 @@ void TreeSupportTipGenerator::removeUselessAddedPoints( PolygonUtils::moveInside(roof_on_layer_above, from); // Remove branches should have interface above them, but don't. Should never happen. if (roof_on_layer_above.empty() - || (!roof_on_layer_above.inside(elem->result_on_layer_) && vSize2(from-elem->result_on_layer_) > std::pow(config_.getRadius(*elem) + FUDGE_LENGTH,2))) + || (! roof_on_layer_above.inside(elem->result_on_layer_) + && vSize2(from - elem->result_on_layer_) > std::pow(config_.getRadius(*elem) + FUDGE_LENGTH, 2))) { to_be_removed.emplace_back(elem); - spdlog::warn("Removing already placed tip that should have roof above it. Distance from roof is {}. Radius is {} on layer {}.", - vSize(from-elem->result_on_layer_), - config_.getRadius(*elem), - layer_idx); + spdlog::warn( + "Removing already placed tip that should have roof above it. Distance from roof is {}. Radius is {} on layer {}.", + vSize(from - elem->result_on_layer_), + config_.getRadius(*elem), + layer_idx); } } } @@ -2017,9 +2036,9 @@ void TreeSupportTipGenerator::generateTips( // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is // better than not supporting it at all. - if(cradle_layers_ >0) + if (cradle_layers_ > 0) { - generateCradle(mesh,support_free_areas); + generateCradle(mesh, support_free_areas); } if (support_roof_layers_) @@ -2029,11 +2048,11 @@ void TreeSupportTipGenerator::generateTips( std::vector> all_cradles_requiring_support(move_bounds.size()); std::vector all_cradle_areas(move_bounds.size()); - for(LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) + for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) { - for(size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) { - for(auto overhang_pair: cradle_data_[layer_idx][cradle_idx]->overhang_) + for (auto overhang_pair : cradle_data_[layer_idx][cradle_idx]->overhang_) { all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data_[layer_idx][cradle_idx]); } @@ -2047,7 +2066,7 @@ void TreeSupportTipGenerator::generateTips( } for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - all_cradle_areas[layer_idx-base_idx].add(base); + all_cradle_areas[layer_idx - base_idx].add(base); } } } @@ -2057,18 +2076,18 @@ void TreeSupportTipGenerator::generateTips( mesh.overhang_areas.size() - z_distance_delta_, [&](const LayerIndex layer_idx) { - if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() && - (layer_idx + 1 >= support_roof_drawn_.size() || support_roof_drawn_[layer_idx + 1].empty()) && - (layer_idx >= all_cradles_requiring_support.size() || all_cradles_requiring_support[layer_idx].empty()) - ) + if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() && (layer_idx + 1 >= support_roof_drawn_.size() || support_roof_drawn_[layer_idx + 1].empty()) + && (layer_idx >= all_cradles_requiring_support.size() || all_cradles_requiring_support[layer_idx].empty())) { return; // This is a continue if imagined in a loop context. } - coord_t current_tip_radius = (force_initial_layer_radius_ && config_.recommendedMinRadius(layer_idx) > config_.min_radius) ? config_.recommendedMinRadius(layer_idx) : config_.min_radius; + coord_t current_tip_radius + = (force_initial_layer_radius_ && config_.recommendedMinRadius(layer_idx) > config_.min_radius) ? config_.recommendedMinRadius(layer_idx) : config_.min_radius; coord_t connect_length = (config_.support_line_width * 100 / support_tree_top_rate_) + std::max(2 * current_tip_radius - 1.0 * config_.support_line_width, 0.0); - coord_t support_tree_branch_distance = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; - bool force_tip_to_roof = (current_tip_radius * current_tip_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && !use_fake_roof_ && support_roof_layers_; + coord_t support_tree_branch_distance + = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; + bool force_tip_to_roof = (current_tip_radius * current_tip_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && ! use_fake_roof_ && support_roof_layers_; Polygons relevant_forbidden = volumes_.getAvoidance( config_.getRadius(0), @@ -2082,14 +2101,15 @@ void TreeSupportTipGenerator::generateTips( .unionPolygons(); // Prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. - std::function generateLines = [&](const Polygons& area, bool roof, LayerIndex generate_layer_idx) { - //todo ensure larger tips have reasonable density. How would one do that though? - // If tips are 7mm thick, does 20% fill mean a distance of 35mm between tips? Does not make sense... + // todo ensure larger tips have reasonable density. How would one do that though? + // If tips are 7mm thick, does 20% fill mean a distance of 35mm between tips? Does not make sense... coord_t upper_line_distance = support_supporting_branch_distance_; coord_t line_distance = std::max(roof ? support_roof_line_distance_ : support_tree_branch_distance, upper_line_distance); - coord_t current_tip_radius_generate = (force_initial_layer_radius_ && config_.recommendedMinRadius(generate_layer_idx) > config_.min_radius) ? config_.recommendedMinRadius(generate_layer_idx) : config_.min_radius; + coord_t current_tip_radius_generate = (force_initial_layer_radius_ && config_.recommendedMinRadius(generate_layer_idx) > config_.min_radius) + ? config_.recommendedMinRadius(generate_layer_idx) + : config_.min_radius; bool use_grid = line_distance == upper_line_distance; @@ -2099,9 +2119,9 @@ void TreeSupportTipGenerator::generateTips( roof && ! use_fake_roof_, generate_layer_idx, line_distance, - roof? nullptr : getCrossFillProvider(mesh, line_distance, current_tip_radius_generate), + roof ? nullptr : getCrossFillProvider(mesh, line_distance, current_tip_radius_generate), (roof && ! use_fake_roof_) ? config_.support_roof_wall_count : 0, - use_grid ? EFillMethod::GRID:EFillMethod::NONE); + use_grid ? EFillMethod::GRID : EFillMethod::NONE); }; @@ -2128,11 +2148,11 @@ void TreeSupportTipGenerator::generateTips( } all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons().offset(EPSILON).unionPolygons(); core_overhang = core_overhang.difference(support_free_areas[layer_idx]); - core_overhang = core_overhang.difference(all_cradle_areas[layer_idx].offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); + core_overhang = core_overhang.difference(all_cradle_areas[layer_idx].offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); // todo what do if large tips? - for(size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < all_cradles_requiring_support[layer_idx].size(); cradle_idx++) { - for(size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx].size(); cradle_overhang_idx++) + for (size_t cradle_overhang_idx = 0; cradle_overhang_idx < all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx].size(); cradle_overhang_idx++) { overhang_processing.emplace_back(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx][cradle_overhang_idx]); core_overhang = core_overhang.difference(all_cradles_requiring_support[layer_idx][cradle_idx]->overhang_[layer_idx][cradle_overhang_idx].overhang_); @@ -2155,26 +2175,43 @@ void TreeSupportTipGenerator::generateTips( // Offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang coord_t extra_total_offset_acc = 0; - // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. + // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. - { - Polygons already_supported = support_roof_drawn_[layer_idx]; - already_supported.add(all_cradle_areas[layer_idx]); - already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. - already_supported = already_supported.unionPolygons(); - while (extra_total_offset_acc + config_.support_line_width / 8 < extra_outset) //+mesh_config_.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. - { - coord_t offset_current_step = - extra_total_offset_acc + 2 * config_.support_line_width > config_.min_radius - ? std::min(config_.support_line_width / 8, extra_outset - extra_total_offset_acc) - : std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); - extra_total_offset_acc += offset_current_step; - Polygons overhang_offset = TreeSupportUtils::safeOffsetInc(overhang_regular, 1.5 * extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config_.xy_min_distance + config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); - remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(already_supported.offset(1.5 * extra_total_offset_acc))).unionPolygons(); //overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. - Polygons next_overhang = TreeSupportUtils::safeOffsetInc(remaining_overhang, extra_total_offset_acc, volumes_.getCollision(0, layer_idx, true), config_.xy_min_distance + config_.support_line_width, 0, 1, config_.support_line_distance / 2, &config_.simplifier); - overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); - } + { + Polygons already_supported = support_roof_drawn_[layer_idx]; + already_supported.add(all_cradle_areas[layer_idx]); + already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. + already_supported = already_supported.unionPolygons(); + while (extra_total_offset_acc + config_.support_line_width / 8 + < extra_outset) //+mesh_config_.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. + { + coord_t offset_current_step = extra_total_offset_acc + 2 * config_.support_line_width > config_.min_radius + ? std::min(config_.support_line_width / 8, extra_outset - extra_total_offset_acc) + : std::min(circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); + extra_total_offset_acc += offset_current_step; + Polygons overhang_offset = TreeSupportUtils::safeOffsetInc( + overhang_regular, + 1.5 * extra_total_offset_acc, + volumes_.getCollision(0, layer_idx, true), + config_.xy_min_distance + config_.support_line_width, + 0, + 1, + config_.support_line_distance / 2, + &config_.simplifier); + remaining_overhang = remaining_overhang.difference(overhang_offset.unionPolygons(already_supported.offset(1.5 * extra_total_offset_acc))) + .unionPolygons(); // overhang_offset is combined with roof, as all area that has a roof, is already supported by said roof. + Polygons next_overhang = TreeSupportUtils::safeOffsetInc( + remaining_overhang, + extra_total_offset_acc, + volumes_.getCollision(0, layer_idx, true), + config_.xy_min_distance + config_.support_line_width, + 0, + 1, + config_.support_line_distance / 2, + &config_.simplifier); + overhang_regular = overhang_regular.unionPolygons(next_overhang.difference(relevant_forbidden)); } + } // If the xy distance overrides the z distance, some support needs to be inserted further down. //=> Analyze which support points do not fit on this layer and check if they will fit a few layers down @@ -2213,7 +2250,7 @@ void TreeSupportTipGenerator::generateTips( for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag_ && ! overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) { LayerIndex layer_idx_lag = layer_idx - lag_ctr; - coord_t current_tip_radius_lag = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx_lag)); //todo setting + coord_t current_tip_radius_lag = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx_lag)); // todo setting // get least restricted avoidance for layer_idx-lag_ctr Polygons relevant_forbidden_below = volumes_.getAvoidance( @@ -2229,13 +2266,14 @@ void TreeSupportTipGenerator::generateTips( return relevant_forbidden_below.inside(p.first, true); }; - Polygons already_supported = support_roof_drawn_[layer_idx-lag_ctr]; - already_supported.add(support_free_areas[layer_idx-lag_ctr]); // While point there are not supported, there may be no support anyway. - already_supported=already_supported.unionPolygons(); + Polygons already_supported = support_roof_drawn_[layer_idx - lag_ctr]; + already_supported.add(support_free_areas[layer_idx - lag_ctr]); // While point there are not supported, there may be no support anyway. + already_supported = already_supported.unionPolygons(); - //Remove all points that are for some reason are already supported - std::function)> evaluateAlreadySupported = - [&](std::pair p) { return already_supported.inside(p.first, true); + // Remove all points that are for some reason are already supported + std::function)> evaluateAlreadySupported = [&](std::pair p) + { + return already_supported.inside(p.first, true); }; overhang_lines = splitLines(overhang_lines, evaluateAlreadySupported).second; @@ -2246,9 +2284,16 @@ void TreeSupportTipGenerator::generateTips( std::vector fresh_valid_points = convertLinesToInternal(convertInternalToLines(split.second), layer_idx - lag_ctr); // ^^^ Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again. - OverhangInformation dummy_overhang_info(remaining_overhang_part,false); - addLinesAsInfluenceAreas(new_tips,fresh_valid_points, (force_tip_to_roof && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, - layer_idx - lag_ctr, current_tip_radius_lag, dummy_overhang_info, support_roof_layers_, false); + OverhangInformation dummy_overhang_info(remaining_overhang_part, false); + addLinesAsInfluenceAreas( + new_tips, + fresh_valid_points, + (force_tip_to_roof && lag_ctr <= support_roof_layers_) ? support_roof_layers_ : 0, + layer_idx - lag_ctr, + current_tip_radius_lag, + dummy_overhang_info, + support_roof_layers_, + false); } } } @@ -2269,24 +2314,21 @@ void TreeSupportTipGenerator::generateTips( bool only_lines = true; Polygons polylines; // The tip positions are determined here. - if(overhang_data.isCradleLine()) + if (overhang_data.isCradleLine()) { - std::optional cradle_line_opt = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_,overhang_data.cradle_line_idx_); + std::optional cradle_line_opt + = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); Polygons line = cradle_line_opt.value()->line_.offset(0); - polylines = ensureMaximumDistancePolyline( - line, - ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, - 1, - false); + polylines = ensureMaximumDistancePolyline(line, ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, 1, false); } else { // todo can cause inconsistent support density if a line exactly aligns with the model polylines = ensureMaximumDistancePolyline( - generateLines(overhang_outset, overhang_data.is_roof_, layer_idx + overhang_data.is_roof_), + generateLines(overhang_outset, overhang_data.is_roof_, layer_idx + overhang_data.is_roof_), ! overhang_data.is_roof_ ? config_.min_radius * 2 - : use_fake_roof_ ? support_supporting_branch_distance_ - : connect_length, + : use_fake_roof_ ? support_supporting_branch_distance_ + : connect_length, 1, false); } @@ -2322,7 +2364,10 @@ void TreeSupportTipGenerator::generateTips( // Cradle lines may move further, because the tips of cradle lines are generated from the (center) line. if (overhang_data.is_roof_ || overhang_data.is_cradle_) { - polylines = TreeSupportUtils::movePointsOutside(polylines, relevant_forbidden, (overhang_data.isCradleLine() ? cradle_line_width_ / 2 : 0) + config_.getRadius(0) + FUDGE_LENGTH / 2); + polylines = TreeSupportUtils::movePointsOutside( + polylines, + relevant_forbidden, + (overhang_data.isCradleLine() ? cradle_line_width_ / 2 : 0) + config_.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); @@ -2339,8 +2384,13 @@ void TreeSupportTipGenerator::generateTips( } else { - spdlog::warn("Overhang area has no valid tips! Was roof: {} Was Cradle {} Was Line {} On Layer: {} Area size was {}", - overhang_data.is_roof_, overhang_data.is_cradle_,overhang_data.isCradleLine() ,layer_idx, overhang_data.overhang_.area()); + spdlog::warn( + "Overhang area has no valid tips! Was roof: {} Was Cradle {} Was Line {} On Layer: {} Area size was {}", + overhang_data.is_roof_, + overhang_data.is_cradle_, + overhang_data.isCradleLine(), + layer_idx, + overhang_data.overhang_.area()); } } @@ -2348,7 +2398,7 @@ void TreeSupportTipGenerator::generateTips( addLinesAsInfluenceAreas( new_tips, overhang_lines, - (!overhang_data.is_roof_ && force_tip_to_roof) ? support_roof_layers_ : 0, + (! overhang_data.is_roof_ && force_tip_to_roof) ? support_roof_layers_ : 0, layer_idx, current_tip_radius, overhang_data, @@ -2431,7 +2481,6 @@ void TreeSupportTipGenerator::generateTips( { move_bounds[layer_idx].insert(tips_on_layer.begin(), tips_on_layer.end()); } - } } // namespace cura From e3299281b9c1074d72751c789e7c62a7ec2eda92 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:56:53 +0200 Subject: [PATCH 072/101] Fix roof cradle lines missing if roof wall line count is larger than 0 --- src/TreeSupport.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 77faa643c8..9c42f0f6f6 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2573,21 +2573,22 @@ void TreeSupport::generateSupportSkin( { remove_from_next_roof.add(support_free_areas[layer_idx]); } - + remove_from_next_roof = remove_from_next_roof.unionPolygons(); Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); Polygons roof = support_roof_storage[layer_idx]; Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; if (config.support_roof_wall_count) { + roof = roof.difference(remove_from_next_roof); roof = roof.unionPolygons(cradle_lines_roof); } else { roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); } - roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); From abcf5ce1142b6361e707d816c26d2fe78167ed02 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 25 Apr 2024 00:03:43 +0200 Subject: [PATCH 073/101] Improve branches avoiding cradle lines. --- src/TreeModelVolumes.cpp | 4 +++- src/TreeSupport.cpp | 36 ++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 5e7abe1068..52f58b579c 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -590,6 +590,8 @@ void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex la void TreeModelVolumes::precalculateAntiPreferred() { + const TreeSupportSettings config(layer_outlines_[current_outline_idx_].first); + cura::parallel_for( 0, precalculated_avoidance_radii.size(), @@ -648,7 +650,7 @@ void TreeModelVolumes::precalculateAntiPreferred() } Polygons col = getCollisionHolefree(radius, layer, true); - anti = anti.unionPolygons().offset(radius).unionPolygons(); + anti = anti.unionPolygons().offset(std::max(radius, config.branch_radius)).unionPolygons(); data_raw_anti[layer] = std::pair(key, anti); if (support_rest_preference_ == RestPreference::BUILDPLATE) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 9c42f0f6f6..7397474dc8 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -751,7 +751,7 @@ std::optional TreeSupport::increaseSingleArea( bool anti_preferred_applied = false; if (! anti_preferred_areas.empty()) { - bool is_fast = settings.increase_speed_ >= config.maximum_move_distance; + bool is_fast = settings.type_ != AvoidanceType::SLOW; // Ensure that branches can not lag through cradle lines. Proper way to do this would be in the beginning with custom increased areas. coord_t anti_radius_extra = std::max(settings.increase_speed_ - volumes_.ceilRadius(actual_radius * 2, true), coord_t(0)); if (anti_radius_extra) @@ -860,7 +860,7 @@ std::optional TreeSupport::increaseSingleArea( volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } - else if (anti_preferred_applied) + else if (anti_preferred_applied && next_radius > actual_radius) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } @@ -883,7 +883,9 @@ std::optional TreeSupport::increaseSingleArea( // If the Collision Radius is smaller than the actual radius, check if it can catch up without violating the avoidance. - if (config.getCollisionRadius(current_elem) < config.increase_radius_until_radius && config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) + const bool collision_radius_catch_up = config.getCollisionRadius(current_elem) < config.increase_radius_until_radius && + config.getCollisionRadius(current_elem) < config.getRadius(current_elem); + if (collision_radius_catch_up) { coord_t target_radius = std::min(config.getRadius(current_elem), config.increase_radius_until_radius); coord_t current_ceil_radius = volumes_.getRadiusNextCeil(radius, settings.use_min_distance_); @@ -929,7 +931,8 @@ std::optional TreeSupport::increaseSingleArea( radius = config.getCollisionRadius(current_elem); // If a hidden radius increase was used, also do some catching up. - if (current_elem.hidden_radius_increase_ > 0) + const bool hidden_radius_catch_up = current_elem.hidden_radius_increase_ > 0; + if (hidden_radius_catch_up) { coord_t target_radius = config.getRadius(current_elem); coord_t current_ceil_radius = volumes_.getRadiusNextCeil(radius, settings.use_min_distance_); @@ -950,7 +953,8 @@ std::optional TreeSupport::increaseSingleArea( current_elem.hidden_radius_increase_ = std::max(0.0, resulting_hidden_increases); current_elem.buildplate_radius_increases_ += bp_increases; - if (current_elem.hidden_radius_increase_ > 0) + //Try to ensure the branch stays away from potential walls if possible. + if (config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) { Polygons new_to_bp_data; Polygons new_to_model_data; @@ -1004,17 +1008,17 @@ std::optional TreeSupport::increaseSingleArea( { if (anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_)) { - increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, radius)); + increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius)); } if (current_elem.to_buildplate_) { bool avoidance_handled = false; to_bp_data = increased; - if (settings.use_anti_preferred_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { to_bp_data = to_bp_data.difference( volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); - avoidance_handled = true; + avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } if (! avoidance_handled) { @@ -1026,7 +1030,7 @@ std::optional TreeSupport::increaseSingleArea( { bool avoidance_handled = false; to_model_data = increased; - if (settings.use_anti_preferred_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) { to_model_data = to_model_data.difference(volumes_.getAntiPreferredAvoidance( radius, @@ -1034,13 +1038,12 @@ std::optional TreeSupport::increaseSingleArea( current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); - avoidance_handled = true; + avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } if (! avoidance_handled) { to_bp_data = to_bp_data.difference( - volumes_ - .getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); + volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); } to_model_data = TreeSupportUtils::safeUnion(to_model_data); } @@ -1048,10 +1051,11 @@ std::optional TreeSupport::increaseSingleArea( if (check_layer_data.area() < 1) { spdlog::error( - "Lost area by doing catch up from {} to radius {} planned increase was {}", + "Lost area by doing catch up from {} to radius {} collision_radius_catch_up: {} hidden_radius_catch_up: {}", ceil_radius_before, volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance_), - planned_foot_increase); + collision_radius_catch_up, + hidden_radius_catch_up); } } } @@ -1682,10 +1686,6 @@ void TreeSupport::createLayerPathing(std::vector>& for (size_t height_idx = 0; height_idx < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size(); height_idx++) { LayerIndex cradle_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; - if (cradle_layer_idx == 84) - { - printf(""); - } all_cradles_with_line_presence[cradle_layer_idx].emplace_back(cradle_data[layer_idx][cradle_idx], cradle_layer_idx, line_idx); } } From aed739c29c055a5b2040b869220f22a36ff01378 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:34:06 +0200 Subject: [PATCH 074/101] Added performance logging to generateInitialAreas --- src/TreeSupportTipGenerator.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 59d227eb8a..7222ea68fd 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1660,8 +1660,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. - Polygons forbidden_here = volumes_ - .getAvoidance( + Polygons forbidden_here = volumes_.getAvoidance( 0, layer_idx, (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, @@ -2023,6 +2022,7 @@ void TreeSupportTipGenerator::generateTips( std::vector& support_free_areas, std::vector>& cradle_data_export) { + const auto t_start = std::chrono::high_resolution_clock::now(); std::vector> new_tips(move_bounds.size()); const coord_t circle_length_to_half_linewidth_change @@ -2040,11 +2040,14 @@ void TreeSupportTipGenerator::generateTips( { generateCradle(mesh, support_free_areas); } + const auto t_cradle = std::chrono::high_resolution_clock::now(); if (support_roof_layers_) { calculateRoofAreas(mesh); } + const auto t_roof = std::chrono::high_resolution_clock::now(); + std::vector> all_cradles_requiring_support(move_bounds.size()); std::vector all_cradle_areas(move_bounds.size()); @@ -2406,6 +2409,7 @@ void TreeSupportTipGenerator::generateTips( only_lines); } }); + const auto t_influenced = std::chrono::high_resolution_clock::now(); cura::parallel_for( 0, @@ -2481,6 +2485,19 @@ void TreeSupportTipGenerator::generateTips( { move_bounds[layer_idx].insert(tips_on_layer.begin(), tips_on_layer.end()); } + + const auto t_end = std::chrono::high_resolution_clock::now(); + const auto dur_cradle = 0.001 * std::chrono::duration_cast(t_cradle - t_start).count(); + const auto dur_roof = 0.001 * std::chrono::duration_cast(t_roof - t_cradle).count(); + const auto dur_inf = 0.001 * std::chrono::duration_cast(t_influenced - t_roof).count(); + const auto dur_clean = 0.001 * std::chrono::duration_cast(t_end - t_influenced).count(); + spdlog::info( + "Subtasks of generateInitialAreas: Generating cradle: {} ms Generating roof: {} ms Creating Influence Areas: {} ms Checking result validity: {} ms ", + dur_cradle, + dur_roof, + dur_inf, + dur_clean); + } } // namespace cura From 1a7db83708adebf936a2b0ade04afdfc81480ef6 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:19:09 +0200 Subject: [PATCH 075/101] Improved performance and fixed regression that could cause branches being removed wrongly. --- include/TreeModelVolumes.h | 2 +- src/TreeModelVolumes.cpp | 9 ++++----- src/TreeSupport.cpp | 29 +++++++++++++++++------------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index bfa1490098..93679cf82a 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -519,7 +519,7 @@ class TreeModelVolumes */ size_t max_cradle_dtt = 0; - LayerIndex first_anti_preferred_layer_idx = 0; + LayerIndex first_anti_preferred_layer_idx_ = 0; /*! * \brief radii for which avoidance was already precalculated. Used to calculate anti preferred avoidance. diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 52f58b579c..85dee55b66 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -36,6 +36,7 @@ TreeModelVolumes::TreeModelVolumes( , progress_offset_{ progress_offset } , machine_border_{ calculateMachineBorderCollision(storage.getMachineBorder()) } , machine_area_{ storage.getMachineBorder() } + , first_anti_preferred_layer_idx_{ storage.support.supportLayers.size() } { anti_overhang_ = std::vector(storage.support.supportLayers.size(), Polygons()); std::unordered_map mesh_to_layeroutline_idx; @@ -582,9 +583,7 @@ void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex la RadiusLayerPair key(0, layer_idx); std::lock_guard critical_section(*critical_anti_preferred_); anti_preferred_[key] = anti_preferred_[key].unionPolygons(area); - - // todo calculated all required radiis and invalidate all non 0 radiis - // todo add function to recalculate anti-pref for non 0 radiis + first_anti_preferred_layer_idx_ = std::min(first_anti_preferred_layer_idx_, layer_idx); } @@ -642,7 +641,7 @@ void TreeModelVolumes::precalculateAntiPreferred() } std::lock_guard critical_section(*critical_anti_preferred_); - first_anti_preferred_layer_idx = layer; + first_anti_preferred_layer_idx_ = layer; } if (! encountered_anti) { @@ -815,7 +814,7 @@ coord_t TreeModelVolumes::getRadiusNextCeil(coord_t radius, bool min_xy_dist) co LayerIndex TreeModelVolumes::getFirstAntiPreferredLayerIdx() { - return first_anti_preferred_layer_idx; + return first_anti_preferred_layer_idx_; } diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 7397474dc8..8401bdf8a4 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -749,6 +749,7 @@ std::optional TreeSupport::increaseSingleArea( // Removing cradle areas from influence areas if possible. Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius); bool anti_preferred_applied = false; + bool anti_preferred_exists = volumes_.getFirstAntiPreferredLayerIdx() < layer_idx; if (! anti_preferred_areas.empty()) { bool is_fast = settings.type_ != AvoidanceType::SLOW; @@ -788,7 +789,7 @@ std::optional TreeSupport::increaseSingleArea( // Remove areas where the branch should not be if possible. // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. - if (volumes_.getFirstAntiPreferredLayerIdx() < layer_idx && settings.use_anti_preferred_) + if (anti_preferred_exists && settings.use_anti_preferred_) { const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_); if (current_elem.to_buildplate_) @@ -813,7 +814,7 @@ std::optional TreeSupport::increaseSingleArea( } } - if (volumes_.getFirstAntiPreferredLayerIdx() >= layer_idx) + if (!anti_preferred_exists) { current_elem.can_avoid_anti_preferred_ = true; } @@ -834,13 +835,13 @@ std::optional TreeSupport::increaseSingleArea( to_bp_data_2 = increased; bool avoidance_handled = false; - if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_ && anti_preferred_exists) { to_bp_data_2 = to_bp_data_2.difference( volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; } - else if (anti_preferred_applied && next_radius > actual_radius) + else if (anti_preferred_applied && next_radius > actual_radius && anti_preferred_exists) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } @@ -854,13 +855,17 @@ std::optional TreeSupport::increaseSingleArea( { to_model_data_2 = increased; bool avoidance_handled = false; - if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_ && anti_preferred_exists) { to_model_data_2 = to_model_data_2.difference( - volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); + volumes_.getAntiPreferredAvoidance(next_radius, + layer_idx - 1, + current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, + true, + settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } - else if (anti_preferred_applied && next_radius > actual_radius) + else if (anti_preferred_applied && next_radius > actual_radius && anti_preferred_exists) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } @@ -1006,7 +1011,7 @@ std::optional TreeSupport::increaseSingleArea( if (ceil_radius_before != volumes_.ceilRadius(radius, settings.use_min_distance_)) { - if (anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_)) + if (anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_) && anti_preferred_exists) { increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius)); } @@ -1014,7 +1019,7 @@ std::optional TreeSupport::increaseSingleArea( { bool avoidance_handled = false; to_bp_data = increased; - if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_ && anti_preferred_exists) { to_bp_data = to_bp_data.difference( volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); @@ -1030,7 +1035,7 @@ std::optional TreeSupport::increaseSingleArea( { bool avoidance_handled = false; to_model_data = increased; - if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_) + if (settings.use_anti_preferred_ && current_elem.can_use_safe_radius_ && anti_preferred_exists) { to_model_data = to_model_data.difference(volumes_.getAntiPreferredAvoidance( radius, @@ -1042,7 +1047,7 @@ std::optional TreeSupport::increaseSingleArea( } if (! avoidance_handled) { - to_bp_data = to_bp_data.difference( + to_model_data = to_model_data.difference( volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious_ ? settings.type_ : AvoidanceType::COLLISION, true, settings.use_min_distance_)); } to_model_data = TreeSupportUtils::safeUnion(to_model_data); @@ -1283,7 +1288,7 @@ void TreeSupport::increaseAreas( insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, use_anti_preferred, move), true); } - if (! elem.can_avoid_anti_preferred_) + if (! elem.can_avoid_anti_preferred_ && layer_idx > volumes_.getFirstAntiPreferredLayerIdx() ) { std::deque old_order = order; for (AreaIncreaseSettings settings : old_order) From 93f53f1f7841ca847224dc69087ceb36f0d40e07 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:51:27 +0200 Subject: [PATCH 076/101] Fix issue causing force initial layer diameter to be always applied for tips placed further down because of xy overrides z --- src/TreeSupportTipGenerator.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 7222ea68fd..f45d042e9a 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -2227,7 +2227,6 @@ void TreeSupportTipGenerator::generateTips( { continue; } - all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons(); std::vector overhang_lines; Polygons polylines = ensureMaximumDistancePolyline(generateLines(remaining_overhang_part, false, layer_idx), current_tip_radius, 1, false); // ^^^ Support_line_width to form a line here as otherwise most will be unsupported. @@ -2253,8 +2252,8 @@ void TreeSupportTipGenerator::generateTips( for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag_ && ! overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) { LayerIndex layer_idx_lag = layer_idx - lag_ctr; - coord_t current_tip_radius_lag = std::max(config_.min_radius, config_.recommendedMinRadius(layer_idx_lag)); // todo setting - + coord_t current_tip_radius_lag + = (force_initial_layer_radius_ && config_.recommendedMinRadius(layer_idx_lag) > config_.min_radius) ? config_.recommendedMinRadius(layer_idx_lag) : config_.min_radius; // get least restricted avoidance for layer_idx-lag_ctr Polygons relevant_forbidden_below = volumes_.getAvoidance( config_.getRadius(0), @@ -2321,8 +2320,11 @@ void TreeSupportTipGenerator::generateTips( { std::optional cradle_line_opt = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); - Polygons line = cradle_line_opt.value()->line_.offset(0); - polylines = ensureMaximumDistancePolyline(line, ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, 1, false); + if(cradle_line_opt) + { + Polygons line = cradle_line_opt.value()->line_.offset(0); + polylines = ensureMaximumDistancePolyline(line, ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, 1, false); + } } else { From ba8208d59910f1529804790340af064d8a22496a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 1 May 2024 22:38:15 +0200 Subject: [PATCH 077/101] Refactoring to enable adding cradle to regular support later --- CMakeLists.txt | 2 +- include/TreeModelVolumes.h | 1 + include/TreeSupportCradle.h | 313 +++++++- include/TreeSupportElement.h | 12 +- include/TreeSupportTipGenerator.h | 160 +--- src/TreeModelVolumes.cpp | 2 +- src/TreeSupport.cpp | 188 +++-- src/TreeSupportCradle.cpp | 1221 ++++++++++++++++++++++++++++ src/TreeSupportTipGenerator.cpp | 1233 +---------------------------- 9 files changed, 1681 insertions(+), 1451 deletions(-) create mode 100644 src/TreeSupportCradle.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 210d8b5c57..13b4ae6d97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,7 @@ set(engine_SRCS # Except main.cpp. src/utils/ToolpathVisualizer.cpp src/utils/VoronoiUtils.cpp src/utils/VoxelUtils.cpp -) + src/TreeSupportCradle.cpp) add_library(_CuraEngine STATIC ${engine_SRCS} ${engine_PB_SRCS}) if (ENABLE_THREADING) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 93679cf82a..9046299f4e 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -9,6 +9,7 @@ #include #include +#include "TreeSupportEnums.h" #include "TreeSupportSettings.h" #include "settings/EnumSettings.h" //To store whether X/Y or Z distance gets priority. #include "settings/types/LayerIndex.h" //Part of the RadiusLayerPair. diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index d798d27976..54e141908f 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -1,15 +1,15 @@ -// -// Created by Thomas on 04/03/2024. -// - #ifndef CURAENGINE_TREESUPPORTCRADLE_H #define CURAENGINE_TREESUPPORTCRADLE_H #include -#include "TreeSupportElement.h" -#include "TreeSupportEnums.h" +#include "TreeSupportSettings.h" +#include "polyclipping/clipper.hpp" +#include "settings/types/LayerIndex.h" +#include "sliceDataStorage.h" #include "utils/Coord_t.h" #include "utils/polygon.h" +#include "TreeModelVolumes.h" +#include "TreeSupportEnums.h" namespace cura { @@ -93,6 +93,151 @@ struct TreeSupportCradleLine } }; +struct CradleConfig +{ + CradleConfig(const SliceMeshStorage& mesh, bool roof): + cradle_layers_(retrieveSetting(mesh.settings, "support_tree_cradle_height") / mesh.settings.get("layer_height")) + , cradle_layers_min_(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / mesh.settings.get("layer_height")) + , cradle_line_count_(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) + , cradle_length_(retrieveSetting(mesh.settings, "support_tree_cradle_length")) + , cradle_length_min_(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) + , cradle_line_width_(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) + , cradle_lines_roof_(roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") + , cradle_base_roof_(roof && ( + retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" || + retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base" + )) + , large_cradle_base_(roof && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") + , large_cradle_line_tips_(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) + , cradle_z_distance_layers_(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance"), mesh.settings.get("layer_height"))) + + { + TreeSupportSettings config(mesh.settings); //todo replace with gathering settings manually + if (cradle_layers_) + { + cradle_layers_ += cradle_z_distance_layers_; + } + if (cradle_length_ - cradle_line_width_ > 0) + { + cradle_length_ -= cradle_line_width_; + cradle_length_min_ -= cradle_line_width_; + } + + cradle_z_distance_layers_ = round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance"), config.layer_height); + cradle_support_base_area_radius_ = retrieveSetting(mesh.settings, "support_tree_cradle_base_diameter") / 2; + + for (size_t cradle_z_dist_ctr = 0; cradle_z_dist_ctr < cradle_z_distance_layers_ + 1; cradle_z_dist_ctr++) + { + cradle_xy_distance_.emplace_back(config.xy_min_distance); + } + + for (int i = 0; i < 9; i++) + { + std::stringstream setting_name_dist; + setting_name_dist << "support_tree_cradle_xy_dist_"; + setting_name_dist << i; + coord_t next_cradle_xy_dist = retrieveSetting(mesh.settings, setting_name_dist.str()); + std::stringstream setting_name_height; + setting_name_height << "support_tree_cradle_xy_height_"; + setting_name_height << i; + coord_t next_cradle_xy_dist_height = retrieveSetting(mesh.settings, setting_name_height.str()); + if (next_cradle_xy_dist_height == 0) + { + break; + } + + for (int layer_delta = 0; layer_delta < round_up_divide(next_cradle_xy_dist_height, config.layer_height); layer_delta++) + { + cradle_xy_distance_.emplace_back(next_cradle_xy_dist); + } + } + + for (int cradle_xy_dist_fill = cradle_xy_distance_.size(); cradle_xy_dist_fill <= cradle_layers_ + 1; cradle_xy_dist_fill++) + { + cradle_xy_distance_.emplace_back(config.xy_min_distance); + } + + min_distance_between_lines_areas_ = (config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5); + cradle_line_distance_ = min_distance_between_lines_areas_ + config.xy_distance; + } + + /*! + * \brief Amount of layers of the cradle to support pointy overhangs. + */ + size_t cradle_layers_; + + /*! + * \brief Minimum amount of layers of the cradle to support pointy overhangs. + */ + size_t cradle_layers_min_; + + /*! + * \brief Amount of lines used for the cradle. + */ + size_t cradle_line_count_; + + /*! + * \brief Length of lines used for the cradle. + */ + coord_t cradle_length_; + + /*! + * \brief Minimum length of lines used for the cradle. TODO Width is effectively added to length ... fix or document? + */ + coord_t cradle_length_min_; + + /*! + * \brief Width of lines used for the cradle. + */ + coord_t cradle_line_width_; + + /*! + * \brief If cradle lines should be drawn as roof. + */ + bool cradle_lines_roof_; + + /*! + * \brief If the cradle base should be drawn as roof. + */ + bool cradle_base_roof_; + + /*! + * \brief Distances the cradle lines should be from the model. First value corresponds to cradle line on the same layer as the first model line. + */ + std::vector cradle_xy_distance_; + + /*! + * \brief If the (roof) cradle base should contain all cradle lines at cradle_tip_dtt size. + */ + bool large_cradle_base_; + + /*! + * \brief If the cradle lines should also be supported by larger tips. + */ + bool large_cradle_line_tips_; + + /*! + * \brief Distances in lines between the cradle and the support they are supported by. + */ + size_t cradle_z_distance_layers_; + + /*! + * \brief Distance to top of tips that support either the pointy overhang or the cradle lines at the bottom-most layer. + */ + coord_t cradle_support_base_area_radius_; + + /*! + * \brief Distance between line areas to prevent them from fusing + */ + coord_t min_distance_between_lines_areas_; + + /*! + * \brief Targeted distance between line areas + */ + coord_t cradle_line_distance_; + +}; + struct TreeSupportCradle { std::vector> lines_; @@ -100,22 +245,19 @@ struct TreeSupportCradle LayerIndex layer_idx_; std::vector base_below_; std::vector centers_; - size_t shadow_idx_; + std::vector shadow_; std::unordered_map> overhang_; - size_t config_cradle_layers_min_; - coord_t config_cradle_length_min_; - size_t cradle_line_count_; + const std::shared_ptr config_; + size_t mesh_idx_; - TreeSupportCradle(LayerIndex layer_idx, Point2LL center, size_t shadow_idx, bool roof, size_t cradle_layers_min, coord_t cradle_length_min, size_t cradle_line_count) + TreeSupportCradle(LayerIndex layer_idx, Point2LL center, bool roof, std::shared_ptr config, size_t mesh_idx) : layer_idx_(layer_idx) , centers_({ center }) - , shadow_idx_(shadow_idx) , is_roof_(roof) - , config_cradle_layers_min_(cradle_layers_min) - , config_cradle_length_min_(cradle_length_min) - , cradle_line_count_(cradle_line_count) + , config_ (config) + , mesh_idx_(mesh_idx) { } @@ -153,7 +295,7 @@ struct TreeSupportCradle { Point2LL current_direction = line_end - getCenter(layer_idx_req); double angle = std::atan2(current_direction.Y, current_direction.X); - size_t angle_idx = size_t(std::round(((angle + std::numbers::pi) / (2.0 * std::numbers::pi)) * double(cradle_line_count_))) % cradle_line_count_; + size_t angle_idx = size_t(std::round(((angle + std::numbers::pi) / (2.0 * std::numbers::pi)) * double(config_->cradle_line_count_))) % config_->cradle_line_count_; return angle_idx; } @@ -161,7 +303,7 @@ struct TreeSupportCradle { for (size_t line_idx = 0; line_idx < lines_.size(); line_idx++) { - if (lines_[line_idx].size() < config_cradle_layers_min_) + if (lines_[line_idx].size() < config_->cradle_layers_min_) { lines_[line_idx].clear(); continue; @@ -174,7 +316,7 @@ struct TreeSupportCradle { previous_layer_idx = lines_[line_idx][up_idx].layer_idx_; if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 - || lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) + || lines_[line_idx][up_idx].line_.polylineLength() < config_->cradle_length_min_) { lines_[line_idx].clear(); } @@ -186,9 +328,9 @@ struct TreeSupportCradle if (! lines_[line_idx][up_idx].is_base_) { if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 - || lines_[line_idx][up_idx].line_.polylineLength() < config_cradle_length_min_) + || lines_[line_idx][up_idx].line_.polylineLength() < config_->cradle_length_min_) { - if (up_idx <= config_cradle_layers_min_) + if (up_idx <= config_->cradle_layers_min_) { spdlog::debug( "Removing cradle line of cradle on layer {} line at {}. Invalid line was on layer {}", @@ -234,6 +376,137 @@ struct CradlePresenceInformation } }; +class SupportCradleGeneration +{ +public: + + + /*! + * \brief Add meshes to generate cradles and generate cradle centers. Not threadsafe! + * \param mesh[in] The mesh that is currently processed. + * \param mesh_idx[in] The idx of the mesh that is currently processed. + */ + void addMeshToCradleCalculation(const SliceMeshStorage& mesh, size_t mesh_idx); + + /*! + * \brief Generate all cradle areas and line areas for the previously added meshes. Not threadsafe! Should only called once. + * \param storage[in] The storage that contains all meshes. Used to access mesh specific settings + */ + + void generate(const SliceDataStorage& storage); + + /*! + * \brief Retrieve cradles generated for a certain mesh. + * \param target[out] Data storage for the cradles. + * \param support_free_areas[out] Areas where support should be removed to ensure the pointy overhang to supported. + * \param mesh_idx[in] The idx of the mesh for which the cradles are retrieved. + */ + void pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx); + + + SupportCradleGeneration(const SliceDataStorage& storage, TreeModelVolumes& volumes_); +private: + + struct UnsupportedAreaInformation + { + UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) + : area{ area } + , index{ index } + , height{ height } + , accumulated_supportable_overhang{ accumulated_supportable_overhang } + { + } + const Polygons area; + size_t index; + size_t height; + coord_t accumulated_supportable_overhang; + }; + + +/*! + * \brief Provides areas that do not have a connection to the buildplate or a certain height. + * \param mesh_idx[in] The idx of the mesh. + * \param layer_idx The layer said area is on. + * \param idx_of_area The index of the area. Only areas that either rest on this area or this area rests on (depending on above) will be returned + * \param above Should the areas above it, that rest on this area should be returned (if true) or if areas that this area rests on (if false) should be returned. + * \return A vector containing the areas, how many layers of material they have below them and the idx of each area usable to get the next one layer above. + */ +std::vector getUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx, size_t idx_of_area, bool above); + +/*! + * \brief Provides areas that do not have a connection to the buildplate or any other non support material below it. + * \param mesh_idx[in] The idx of the mesh. + * \param layer_idx The layer said area is on. + * \return A vector containing the areas, how many layers of material they have below them (always 0) and the idx of each area usable to get the next one layer above. + */ +std::vector getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx); + +/*! + * \brief Calculates which parts of the model to not connect with the buildplate and how many layers of material is below them (height). + * Results are stored in a cache. + * Only area up to the required maximum height are stored. + * \param mesh[in] The mesh that is currently processed. + * \param mesh_idx[in] The idx of the mesh. + */ +void calculateFloatingParts(const SliceMeshStorage& mesh, size_t mesh_idx); + +/*! + * \brief Generate the center points of all generated cradles. + * \param mesh[in] The mesh that is currently processed. + * \param mesh_idx[in] The idx of the mesh. + * \returns The calculated cradle centers for the mesh. + */ +std::vector> generateCradleCenters(const SliceMeshStorage& mesh, size_t mesh_idx); + +/*! + * \brief Generate lines from center and model information + * Only area up to the required maximum height are stored. + * \param cradle_data_mesh[in] The calculated cradle centers for the mesh. + * \param mesh[in] The mesh that is currently processed. + */ +void generateCradleLines(std::vector>& cradle_data_mesh, const SliceMeshStorage& mesh); + +/*! + * \brief Ensures cradle-lines do not intersect with each other. + */ +void cleanCradleLineOverlaps(); + +/*! + * \brief Finishes the cradle areas that represent cradle base and lines and calculates overhang for them. + * \param storage[in] The storage that contains all meshes. Used to access mesh specific settings + */ +void generateCradleLineAreasAndBase(const SliceDataStorage& storage); + + + +/*! + * \brief Representation of all cradles ordered by mesh_idx and layer_idx. + */ +std::vector>> cradle_data_; + +/*! + * \brief Representation of areas that have to be removed to ensure lines below the pointy overhang. + */ +std::vector support_free_areas_; + +/*! + * \brief Generator for model collision, avoidance and internal guide volumes. + */ +TreeModelVolumes& volumes_; + +/*! + * \brief Whether only support that can rest on a flat surface should be supported. todo + */ +const bool only_gracious_ = false; + +mutable std::vector>> floating_parts_cache_; +mutable std::vector>>> floating_parts_map_; +mutable std::vector>>> floating_parts_map_below_; + +std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); + +}; + } // namespace cura diff --git a/include/TreeSupportElement.h b/include/TreeSupportElement.h index f013bc6c66..360b87581c 100644 --- a/include/TreeSupportElement.h +++ b/include/TreeSupportElement.h @@ -93,8 +93,8 @@ struct TreeSupportElement , dont_move_until_(dont_move_until) , can_use_safe_radius_(can_use_safe_radius) , can_avoid_anti_preferred_(false) - , // todo init? - last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)) + , ensure_valid_anti_preferred_(false) + , last_area_increase_(AreaIncreaseSettings(AvoidanceType::FAST, 0, false, false, false, false, false)) , missing_roof_layers_(force_tips_to_roof ? dont_move_until : 0) , roof_with_enforced_walls(false) , skip_ovalisation_(skip_ovalisation) @@ -136,6 +136,8 @@ struct TreeSupportElement , supports_cradle_(first.supports_cradle_ || second.supports_cradle_) , 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_) + , can_avoid_anti_preferred_(first.can_avoid_anti_preferred_ || second.can_avoid_anti_preferred_) + , ensure_valid_anti_preferred_(first.ensure_valid_anti_preferred_ || second.ensure_valid_anti_preferred_) , missing_roof_layers_(std::min(first.missing_roof_layers_, second.missing_roof_layers_)) , roof_with_enforced_walls(first.roof_with_enforced_walls && second.roof_with_enforced_walls) , skip_ovalisation_(false) @@ -298,6 +300,11 @@ struct TreeSupportElement */ bool can_avoid_anti_preferred_; + /*! + * \brief If the influence area ensures no collision with anti preferred on this layer. + */ + bool ensure_valid_anti_preferred_; + /*! * \brief Settings used to increase the influence area to its current state. */ @@ -445,6 +452,7 @@ struct TreeSupportElement result.skip_ovalisation_ = false; result.result_on_layer_ = Point2LL(-1, -1); result.area_ = nullptr; + result.ensure_valid_anti_preferred_ = false; return result; } }; diff --git a/include/TreeSupportTipGenerator.h b/include/TreeSupportTipGenerator.h index efcf710415..eb540e467d 100644 --- a/include/TreeSupportTipGenerator.h +++ b/include/TreeSupportTipGenerator.h @@ -35,7 +35,7 @@ class TreeSupportTipGenerator * \param move_bounds[out] The storage for the tips. * \param placed_support_lines_support_areas[out] Support-lines that were already placed represented as the area the lines will take when printed. * \param support_free_areas[out] Areas where no support (including roof) of any kind is to be drawn. - * \param cradle_data_export[out] Generated cradle lines. + * \param cradle_data[in] Generated cradle lines. * \return All lines of the \p polylines object, with information for each point regarding in which avoidance it is currently valid in. */ void generateTips( @@ -44,7 +44,7 @@ class TreeSupportTipGenerator std::vector>& move_bounds, std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, - std::vector>& cradle_data_export); + std::vector>& cradle_data); private: enum class LineStatus @@ -64,20 +64,6 @@ class TreeSupportTipGenerator IS_ROOF }; - struct UnsupportedAreaInformation - { - UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) - : area{ area } - , index{ index } - , height{ height } - , accumulated_supportable_overhang{ accumulated_supportable_overhang } - { - } - const Polygons area; - size_t index; - size_t height; - coord_t accumulated_supportable_overhang; - }; using LineInformation = std::vector>; @@ -139,64 +125,6 @@ class TreeSupportTipGenerator */ std::shared_ptr getCrossFillProvider(const SliceMeshStorage& mesh, coord_t line_distance, coord_t line_width); - /*! - * \brief Provides areas that do not have a connection to the buildplate or a certain height. - * \param layer_idx The layer said area is on. - * \param idx_of_area The index of the area. Only areas that either rest on this area or this area rests on (depending on above) will be returned - * \param above Should the areas above it, that rest on this area should be returned (if true) or if areas that this area rests on (if false) should be returned. - * \return A vector containing the areas, how many layers of material they have below them and the idx of each area usable to get the next one layer above. - */ - std::vector getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area, bool above); - - /*! - * \brief Provides areas that do not have a connection to the buildplate or any other non support material below it. - * \param layer_idx The layer said area is on. - * \return A vector containing the areas, how many layers of material they have below them (always 0) and the idx of each area usable to get the next one layer above. - */ - std::vector getFullyUnsupportedArea(LayerIndex layer_idx); - - /*! - * \brief Calculates which parts of the model to not connect with the buildplate and how many layers of material is below them (height). - * Results are stored in a cache. - * Only area up to the required maximum height are stored. - * \param mesh[in] The mesh that is currently processed. - */ - void calculateFloatingParts(const SliceMeshStorage& mesh); - - /*! - * \brief Generate the center points of all generated cradles. - * \param shadow_data[in] The accumulated model of given pointy overhangs - * \returns The accumulated model of given pointy overhangs - */ - std::vector>> generateCradleCenters(const SliceMeshStorage& mesh); - - /*! - * \brief Generate lines from center and model information - * Only area up to the required maximum height are stored. - * \param shadow_data[in] The accumulated model of given pointy overhangs - */ - void generateCradleLines(std::vector>>& shadow_data); - - /*! - * \brief Ensures cradle-lines do not intersect with each other. - */ - void cleanCradleLineOverlaps(); - - /*! - * \brief Finishes the cradle areas that represent cradle base and lines and calculates overhang for them. - * \param shadow_data[in] The accumulated model of given pointy overhangs - * \param support_free_areas[out] Areas where support will have to be removed, to guarantee that a line is around it. - */ - void generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas); - - - /*! - * \brief Generate a cradle to stabilize pointy overhang - * \param mesh[in] The mesh that is currently processed. - * \param support_free_areas[out] Areas where no support may be. - */ - void generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas); - /*! * \brief Drops overhang areas further down until they are valid (at most max_overhang_insert_lag layers) * \param mesh[in] The mesh that is currently processed. @@ -208,8 +136,9 @@ class TreeSupportTipGenerator /*! * \brief Calculates which areas should be supported with roof, and saves these in roof support_roof_drawn * \param mesh[in] The mesh that is currently processed. + * \param cradle_data[in] Cradles for this mesh if applicable */ - void calculateRoofAreas(const SliceMeshStorage& mesh); + void calculateRoofAreas(const SliceMeshStorage& mesh, std::vector>& cradle_data); /*! * \brief Add a point as a tip @@ -261,8 +190,9 @@ class TreeSupportTipGenerator * \brief Remove tips that should not have been added in the first place. * \param move_bounds[in,out] The already added tips * \param storage[in] Background storage, required for adding roofs. + * \param cradle_data[in] Cradles for this mesh if applicable */ - void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage); + void removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data); /*! * \brief Contains config settings to avoid loading them in every function. This was done to improve readability of the code. @@ -382,92 +312,14 @@ class TreeSupportTipGenerator */ std::vector support_roof_drawn_fractional_; - /*! - * \brief Representation of all cradles ordered by layer_idx. - */ - std::vector> cradle_data_; - - /*! - * \brief Amount of layers of the cradle to support pointy overhangs. - */ - size_t cradle_layers_; - - /*! - * \brief Minimum amount of layers of the cradle to support pointy overhangs. - */ - size_t cradle_layers_min_; - - /*! - * \brief Amount of lines used for the cradle. - */ - size_t cradle_line_count_; - - /*! - * \brief Length of lines used for the cradle. - */ - coord_t cradle_length_; - - /*! - * \brief Minimum length of lines used for the cradle. TODO Width is effectively added to length ... fix or document? - */ - coord_t cradle_length_min_; - - /*! - * \brief Width of lines used for the cradle. - */ - coord_t cradle_line_width_; - - /*! - * \brief If cradle lines should be drawn as roof. - */ - bool cradle_lines_roof_; - - /*! - * \brief If the cradle base should be drawn as roof. - */ - bool cradle_base_roof_; - - /*! - * \brief If the (roof) cradle base should contain all cradle lines at cradle_tip_dtt size. - */ - bool large_cradle_base_; - - - /*! - * \brief Maximum area of an overhang to still receive a cradle. Unit is square-microns! - */ - double cradle_area_threshold_; - - /*! - * \brief Distance to top of tips that support either the pointy overhang or the cradle lines at the bottom-most layer. - */ - size_t cradle_tip_dtt_; - /*! * \brief If the cradle lines should also be supported by larger tips. */ bool large_cradle_line_tips_; - /*! - * \brief Distances the cradle lines should be from the model. First value corresponds to cradle line on the same layer as the first model line. - */ - std::vector cradle_xy_distance_; - - /*! - * \brief Distances in lines between the cradle and the support they are supported by. - */ - size_t cradle_z_distance_layers_; - - std::mutex critical_cradle_; std::mutex critical_move_bounds_; - std::mutex critical_roof_tips_; std::mutex critical_cross_fill_; - mutable std::vector> floating_parts_cache_; - mutable std::vector>> floating_parts_map_; - mutable std::vector>> floating_parts_map_below_; - - std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); }; } // namespace cura diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 85dee55b66..2f50d51785 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -82,7 +82,7 @@ TreeModelVolumes::TreeModelVolumes( const coord_t extra_cradle_distance = round_divide(retrieveSetting(data_pair.first, "support_tree_cradle_z_distance"), config.layer_height); max_cradle_layers = std::max(coord_t(max_cradle_layers), extra_cradle_distance + retrieveSetting(data_pair.first, "support_tree_cradle_height") / config.layer_height); - max_cradle_dtt = std::max(max_cradle_dtt, size_t(config.tip_layers * retrieveSetting(data_pair.first, "support_tree_cradle_base_tip_percentage") / 100.0)); + max_cradle_dtt = std::max(max_cradle_dtt, config.tip_layers); // todo better estimation } // Figure out the rest of the setting(-like variable)s relevant to the class a whole. diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 8401bdf8a4..64ce955889 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -159,10 +159,20 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) const auto t_precalc = std::chrono::high_resolution_clock::now(); std::vector> cradle_data; + + SupportCradleGeneration cradle_gen(storage, volumes_); + for (size_t mesh_idx : processing.second) + { + cradle_gen.addMeshToCradleCalculation(*storage.meshes[mesh_idx], mesh_idx); + } + cradle_gen.generate(storage); + const auto t_cradle = std::chrono::high_resolution_clock::now(); + // ### Place tips of the support tree for (size_t mesh_idx : processing.second) { - std::vector> cradle_data_mesh; + std::vector> cradle_data_mesh(move_bounds.size()); + cradle_gen.pushCradleData(cradle_data_mesh, support_free_areas, mesh_idx); // todo the support free areas here are HORRIBLE generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage, cradle_data_mesh); if (cradle_data.size() < cradle_data_mesh.size()) { @@ -189,17 +199,20 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) const auto t_draw = std::chrono::high_resolution_clock::now(); const auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); - const auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_precalc).count(); + const auto dur_cradle = 0.001 * std::chrono::duration_cast(t_cradle - t_precalc).count(); + + const auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_cradle).count(); const auto dur_path = 0.001 * std::chrono::duration_cast(t_path - t_gen).count(); const auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); const auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); const auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); spdlog::info( "Total time used creating Tree support for the currently grouped meshes: {} ms. Different subtasks:\n" - "Calculating Avoidance: {} ms Creating initial influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " + "Calculating Avoidance: {} ms Calculating Cradle: {} ms Creating initial influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " "support {} ms", dur_total, dur_pre_gen, + dur_cradle, dur_gen, dur_path, dur_place, @@ -748,7 +761,6 @@ std::optional TreeSupport::increaseSingleArea( coord_t actual_radius = config.getRadius(current_elem); // Removing cradle areas from influence areas if possible. Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius); - bool anti_preferred_applied = false; bool anti_preferred_exists = volumes_.getFirstAntiPreferredLayerIdx() < layer_idx; if (! anti_preferred_areas.empty()) { @@ -770,7 +782,7 @@ std::optional TreeSupport::increaseSingleArea( to_model_data = to_model_data_without_anti; Polygons increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; - anti_preferred_applied = true; + current_elem.ensure_valid_anti_preferred_ = true; } } else @@ -781,10 +793,14 @@ std::optional TreeSupport::increaseSingleArea( to_model_data = to_model_data_without_anti; Polygons increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; - anti_preferred_applied = true; + current_elem.ensure_valid_anti_preferred_ = true; } } } + else + { + current_elem.ensure_valid_anti_preferred_ = true; + } check_layer_data = current_elem.to_buildplate_ ? to_bp_data : to_model_data; // Remove areas where the branch should not be if possible. @@ -802,7 +818,7 @@ std::optional TreeSupport::increaseSingleArea( to_model_data = to_model_data.difference(anti_preferred); } - if (! anti_preferred_applied) + if (! current_elem.ensure_valid_anti_preferred_) { increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, radius)); } @@ -817,6 +833,7 @@ std::optional TreeSupport::increaseSingleArea( if (!anti_preferred_exists) { current_elem.can_avoid_anti_preferred_ = true; + current_elem.ensure_valid_anti_preferred_ = true; } if (settings.increase_radius_ && check_layer_data.area() > 1) @@ -841,7 +858,7 @@ std::optional TreeSupport::increaseSingleArea( volumes_.getAntiPreferredAvoidance(next_radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; } - else if (anti_preferred_applied && next_radius > actual_radius && anti_preferred_exists) + else if (current_elem.ensure_valid_anti_preferred_ && next_radius > actual_radius && anti_preferred_exists) { to_bp_data_2 = to_bp_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } @@ -865,7 +882,7 @@ std::optional TreeSupport::increaseSingleArea( settings.use_min_distance_)); avoidance_handled = settings.type_ != AvoidanceType::SLOW; // There is no slow anti-preferred avoidance. } - else if (anti_preferred_applied && next_radius > actual_radius && anti_preferred_exists) + else if (current_elem.ensure_valid_anti_preferred_ && next_radius > actual_radius && anti_preferred_exists) { to_model_data_2 = to_model_data_2.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, next_radius)); } @@ -1011,7 +1028,7 @@ std::optional TreeSupport::increaseSingleArea( if (ceil_radius_before != volumes_.ceilRadius(radius, settings.use_min_distance_)) { - if (anti_preferred_applied && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_) && anti_preferred_exists) + if (current_elem.ensure_valid_anti_preferred_ && ceil_actual_radius_before < volumes_.ceilRadius(actual_radius, settings.use_min_distance_) && anti_preferred_exists) { increased = increased.difference(volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius)); } @@ -1549,100 +1566,103 @@ void TreeSupport::handleCradleLineValidity( std::vector>& move_bounds, std::vector>& cradle_data) { + // cant skip just because cradle_data is empty as there may be tips that have to be removed as the line they support was removed further up if (cradle_data.size() <= layer_idx) { return; } - std::unordered_set removed_lines_idx; - // Evaluate which lines have to be removed for all influence areas to be valid. - // Goal is to remove as few lines as possible - // Correctly solving this is very hard. - // So for now any solution will do. Todo find a better way. Also parallelize - - std::vector all_elements_on_layer; - all_elements_on_layer.insert(all_elements_on_layer.end(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); - for (auto& elem_influence_pair : influence_areas) - { - all_elements_on_layer.emplace_back(&elem_influence_pair.first); - } - for (auto& elem_influence_pair : bypass_merge_areas) + if(!cradle_data[layer_idx].empty()) { - all_elements_on_layer.emplace_back(&elem_influence_pair.first); - } + std::unordered_set removed_lines_idx; + // Evaluate which lines have to be removed for all influence areas to be valid. + // Goal is to remove as few lines as possible + // Correctly solving this is very hard. + // So for now any solution will do. Todo find a better way. Also parallelize - for (const TreeSupportElement* elem : all_elements_on_layer) - { - if (! elem->can_avoid_anti_preferred_ || config.getCollisionRadius(*elem) != config.getRadius(*elem)) + std::vector all_elements_on_layer; + all_elements_on_layer.insert(all_elements_on_layer.end(), move_bounds[layer_idx].begin(), move_bounds[layer_idx].end()); + for (auto& elem_influence_pair : influence_areas) { - const coord_t safe_movement_distance = (elem->use_min_xy_dist_ ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) - + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + all_elements_on_layer.emplace_back(&elem_influence_pair.first); + } + for (auto& elem_influence_pair : bypass_merge_areas) + { + all_elements_on_layer.emplace_back(&elem_influence_pair.first); + } + for (const TreeSupportElement* elem : all_elements_on_layer) + { + if (! elem->ensure_valid_anti_preferred_) + { + const coord_t safe_movement_distance = (elem->use_min_xy_dist_ ? config.xy_min_distance : config.xy_distance) + config.getCollisionRadius(*elem) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); - bool immutable = elem->area_ != nullptr; - bool to_bp = elem->to_buildplate_; + bool immutable = elem->area_ != nullptr; + bool to_bp = elem->to_buildplate_; - Polygons relevant_influence; - Polygons full_influence; - if (! immutable) - { - relevant_influence = to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]; - full_influence = bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]; - } - else - { - relevant_influence = elem->area_->difference(volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, elem->use_min_xy_dist_)); - full_influence = relevant_influence; - } - AABB relevant_influence_aabb = AABB(relevant_influence); + Polygons relevant_influence; + Polygons full_influence; + if (! immutable) + { + relevant_influence = to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]; + full_influence = bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]; + } + else + { + relevant_influence = elem->area_->difference(volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, elem->use_min_xy_dist_)); + full_influence = relevant_influence; + } + AABB relevant_influence_aabb = AABB(relevant_influence); - for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) - { - if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base_ && ! removed_lines_idx.contains(cradle_idx)) + for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - // The branch created by the influence area cant lag though the model... So the offset needs to be safe... - AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area_); - cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); - if (cradle_area_aabb.hit(relevant_influence_aabb)) + if (cradle.cradleLineExists() && ! cradle.getCradleLine()->is_base_ && ! removed_lines_idx.contains(cradle_idx)) { - Polygons cradle_influence = TreeSupportUtils::safeOffsetInc( - cradle.getCradleLine()->area_, - config.getRadius(*elem) + config.xy_distance, - volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, true), - safe_movement_distance, - 0, - 1, - config.support_line_distance / 2, - &config.simplifier); - Polygons next_relevant_influence = relevant_influence.difference(cradle_influence); - - if (next_relevant_influence.area() > EPSILON) - { - relevant_influence = next_relevant_influence; - full_influence = full_influence.difference(cradle_influence).unionPolygons(relevant_influence); - } - else + // The branch created by the influence area cant lag though the model... So the offset needs to be safe... + AABB cradle_area_aabb = AABB(cradle.getCradleLine()->area_); + cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); + if (cradle_area_aabb.hit(relevant_influence_aabb)) { - // todo Check if non remove options are available eg shortening cradle line... - removed_lines_idx.emplace(cradle_idx); - cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line_); - cradle.getCradleLine()->line_.clear(); - spdlog::debug("Flagging to remove cradle line {} {} ", cradle.layer_idx_, cradle.line_idx_); + Polygons cradle_influence = TreeSupportUtils::safeOffsetInc( + cradle.getCradleLine()->area_, + config.getRadius(*elem) + config.xy_distance, + volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, true), + safe_movement_distance, + 0, + 1, + config.support_line_distance / 2, + &config.simplifier); + Polygons next_relevant_influence = relevant_influence.difference(cradle_influence); + + if (next_relevant_influence.area() > EPSILON) + { + relevant_influence = next_relevant_influence; + full_influence = full_influence.difference(cradle_influence).unionPolygons(relevant_influence); + } + else + { + // todo Check if non remove options are available eg shortening cradle line... + removed_lines_idx.emplace(cradle_idx); + cradle.getCradleLine()->addLineToRemoved(cradle.getCradleLine()->line_); + cradle.getCradleLine()->line_.clear(); + spdlog::debug("Flagging to remove cradle line {} {} ", cradle.layer_idx_, cradle.line_idx_); + } } } } - } - if (! immutable) - { - (bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]) = full_influence; - (to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]) = relevant_influence; + if (! immutable) + { + (bypass_merge_areas.contains(*elem) ? bypass_merge_areas[*elem] : influence_areas[*elem]) = full_influence; + (to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]) = relevant_influence; + } } } - } - for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) - { - if (cradle.cradleLineExists()) + for (auto [cradle_idx, cradle] : cradle_data[layer_idx] | ranges::views::enumerate) { - cradle.cradle_->verifyLines(); + if (cradle.cradleLineExists()) + { + cradle.cradle_->verifyLines(); + } } } diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp new file mode 100644 index 0000000000..c17db56f61 --- /dev/null +++ b/src/TreeSupportCradle.cpp @@ -0,0 +1,1221 @@ + +#include "TreeSupportCradle.h" + +#include "TreeSupportUtils.h" +#include "utils/ThreadPool.h" +#include "utils/linearAlg2D.h" +#include "utils/math.h" //For round_up_divide and PI. +#include "utils/polygonUtils.h" //For moveInside. + +namespace cura +{ + + +SupportCradleGeneration::SupportCradleGeneration(const SliceDataStorage& storage, TreeModelVolumes& volumes_s) + : volumes_(volumes_s) + , cradle_data_(storage.meshes.size(), std::vector>(storage.print_layer_count)) + , floating_parts_cache_(storage.meshes.size()) + , floating_parts_map_(storage.meshes.size()) + , floating_parts_map_below_(storage.meshes.size()) + , support_free_areas_(storage.print_layer_count) +{ + +} + + +void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mesh, size_t mesh_idx) +{ + + const coord_t layer_height = mesh.settings.get("layer_height"); + const coord_t min_wall_line_width = mesh.settings.get("min_wall_line_width"); + const size_t cradle_layers = retrieveSetting(mesh.settings, "support_tree_cradle_height") / layer_height; + const double cradle_area_threshold = 1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area"); + LayerIndex max_layer = 0; + + for (LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) + { + if (! mesh.overhang_areas[layer_idx].empty()) + { + max_layer = layer_idx; + } + } + max_layer = std::min(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); + + LayerIndex start_layer = 1; + floating_parts_cache_[mesh_idx].resize(max_layer + 1); + floating_parts_map_[mesh_idx].resize(max_layer + 1); + floating_parts_map_below_[mesh_idx].resize(max_layer + 1); + std::mutex critical_sections; + + Polygons completely_supported = volumes_.getCollision(0, 0, true); + Polygons layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter + for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) + { + Polygons next_completely_supported; + + // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. + Polygons layer = mesh.layers[layer_idx].getOutlines(); + + const std::vector layer_parts = layer.splitIntoParts(); + cura::parallel_for( + 0, + layer_parts.size(), + [&](const size_t part_idx) + { + const PolygonsPart& part = layer_parts[part_idx]; + AABB part_aabb(part); + bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(layer_below, part_aabb).intersection(part).empty(); + + if (! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) + { + std::lock_guard critical_section_add(critical_sections); + next_completely_supported.add(part); + return; + } + + Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); + coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * min_wall_line_width * min_wall_line_width); + if (! has_support_below) + { + std::lock_guard critical_section_add(critical_sections); + floating_parts_cache_[mesh_idx][layer_idx].emplace_back(part, floating_parts_cache_[mesh_idx][layer_idx].size(), 0, overhang_area); + floating_parts_map_[mesh_idx][layer_idx].emplace_back(std::vector()); + floating_parts_map_below_[mesh_idx][layer_idx].emplace_back(std::vector()); + return; + } + + size_t min_resting_on_layers = 0; + coord_t supported_overhang_area = 0; + bool add = false; + std::vector idx_of_floating_below; + for (auto [idx, floating] : floating_parts_cache_[mesh_idx][layer_idx - 1] | ranges::views::enumerate) + { + if (layer_idx > 1 && floating.height < cradle_layers - 1 && ! floating.area.intersection(part).empty()) + { + idx_of_floating_below.emplace_back(idx); + supported_overhang_area += floating.accumulated_supportable_overhang; + min_resting_on_layers = std::max(min_resting_on_layers, floating.height); + add = true; + } + } + + + if (min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) + { + std::lock_guard critical_section_add(critical_sections); + for (size_t idx : idx_of_floating_below) + { + floating_parts_map_[mesh_idx][layer_idx - 1][idx].emplace_back(floating_parts_cache_[mesh_idx][layer_idx].size()); + } + + floating_parts_map_[mesh_idx][layer_idx].emplace_back(std::vector()); + floating_parts_map_below_[mesh_idx][layer_idx].emplace_back(idx_of_floating_below); + floating_parts_cache_[mesh_idx][layer_idx] + .emplace_back(part, floating_parts_cache_[mesh_idx][layer_idx].size(), min_resting_on_layers + 1, overhang_area + supported_overhang_area); + } + else + { + std::lock_guard critical_section_add(critical_sections); + next_completely_supported.add(part); + } + }); + layer_below = layer; + completely_supported = next_completely_supported; + } +} + +std::vector SupportCradleGeneration::getUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx, size_t idx_of_area, bool above) +{ + std::vector result; + + if (layer_idx == 0) + { + return result; + } + + bool has_result = false; + + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + has_result = layer_idx < floating_parts_cache_[mesh_idx].size(); + } + + if (has_result) + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + if (! floating_parts_cache_[mesh_idx][layer_idx].empty() && above) + { + for (size_t resting_idx : floating_parts_map_[mesh_idx][layer_idx - 1][idx_of_area]) + { + result.emplace_back(floating_parts_cache_[mesh_idx][layer_idx][resting_idx]); + } + } + else if (! floating_parts_cache_[mesh_idx][layer_idx - 1].empty() && ! above) + { + for (size_t resting_idx : floating_parts_map_below_[mesh_idx][layer_idx][idx_of_area]) + { + result.emplace_back(floating_parts_cache_[mesh_idx][layer_idx - 1][resting_idx]); + } + } + } + else + { + spdlog::error("Requested not calculated unsupported area."); + return result; + } + return result; +} + + +std::vector SupportCradleGeneration::getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx) +{ + std::vector result; + + if (layer_idx == 0) + { + return result; + } + + bool has_result = false; + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + has_result = layer_idx < floating_parts_cache_[mesh_idx].size(); + } + + if (has_result) + { + std::lock_guard critical_section(*critical_floating_parts_cache_); + for (auto [idx, floating_data] : floating_parts_cache_[mesh_idx][layer_idx] | ranges::views::enumerate) + { + if (floating_data.height == 0) + { + result.emplace_back(floating_data); + } + } + } + else + { + spdlog::error("Requested not calculated unsupported area.", layer_idx); + return result; + } + return result; +} + +std::vector> SupportCradleGeneration::generateCradleCenters(const SliceMeshStorage& mesh, size_t mesh_idx) +{ + std::shared_ptr cradle_config = std::make_shared(mesh, mesh.settings.get("support_roof_enable")); + + const size_t z_distance_top_layers = round_up_divide(mesh.settings.get("support_top_distance"), mesh.settings.get("layer_height")); + const size_t z_distance_delta_(std::min(z_distance_top_layers + 1, mesh.overhang_areas.size())); + const bool support_moves = mesh.settings.get("support_structure") != ESupportStructure::NORMAL; + coord_t maximum_move_distance = 0; + coord_t maximum_move_distance_slow = 0; + if(support_moves) + { + TreeSupportSettings config(mesh.settings); + maximum_move_distance = config.maximum_move_distance; + maximum_move_distance_slow = config.maximum_move_distance_slow; + } + std::mutex critical_dedupe; + std::vector> dedupe(mesh.overhang_areas.size()); + std::vector> result(mesh.overhang_areas.size()); + cura::parallel_for( + 1, + mesh.overhang_areas.size() - (z_distance_delta_ + 1), + [&](const LayerIndex layer_idx) + { + if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() || getFullyUnsupportedArea(mesh_idx, layer_idx + z_distance_delta_).empty()) + { + return; + } + + for (auto pointy_info : getFullyUnsupportedArea(mesh_idx, layer_idx + z_distance_delta_)) + { + AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta_]); + if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta_], overhang_aabb).intersection(pointy_info.area).empty()) + { + // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. + continue; + } + + std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + z_distance_delta_, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector all_pointy_idx{ pointy_info.index }; + + Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); + std::vector additional_centers; + TreeSupportCradle* cradle_main + = new TreeSupportCradle(layer_idx, center_prev, cradle_config->cradle_base_roof_, cradle_config, mesh_idx); + for (size_t z_distance = 0; z_distance < z_distance_top_layers; z_distance++) + { + accumulated_model[z_distance] = pointy_info.area; + cradle_main->centers_.emplace_back(center_prev); + } + Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. + bool aborted = false; + bool contacted_other_pointy = false; + std::vector unsupported_model(accumulated_model.size()); + for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size() - z_distance_top_layers; cradle_up_layer++) + { + // shadow model up => not cradle where model + // then drop cradle down + // cut into parts => get close to original pointy that are far enough from each other. + std::vector next_pointy_idx; + Polygons model_outline; + bool blocked_by_dedupe = false; + // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up + if (cradle_up_layer > 1) + { + for (size_t pointy_idx : all_pointy_idx) + { + for (auto next_pointy_data : getUnsupportedArea(mesh_idx, layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx, true)) + { + if (next_pointy_data.height + != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it + { + contacted_other_pointy = true; + continue; + } + unsupported_model[cradle_up_layer].add(next_pointy_data.area); + // Ensure each area is only handles once + std::lock_guard critical_section_cradle(critical_dedupe); + if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) + { + dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data.index); + model_outline.add(next_pointy_data.area); + next_pointy_idx.emplace_back(next_pointy_data.index); + } + else + { + blocked_by_dedupe = true; + } + + std::vector all_pointy_idx_below{ next_pointy_data.index }; + for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_idx_below.empty(); cradle_down_layer--) + { + std::vector next_all_pointy_idx_below; + + for (size_t pointy_idx_below : all_pointy_idx_below) + { + for (auto prev_pointy_data : getUnsupportedArea(mesh_idx, layer_idx + cradle_down_layer - 1 + z_distance_delta_, pointy_idx_below, false)) + { + if (prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) + { + // Only add if area below does not have it's own cradle. + if (prev_pointy_data.height < cradle_config->cradle_layers_min_) + { + accumulated_model[cradle_down_layer].add(prev_pointy_data.area); + next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); + } + } + } + } + all_pointy_idx_below = next_all_pointy_idx_below; + accumulated_model[cradle_down_layer] = accumulated_model[cradle_down_layer].unionPolygons(); + } + } + } + all_pointy_idx = next_pointy_idx; + } + else + { + model_outline.add(pointy_info.area); + } + + if (model_outline.empty()) + { + if (cradle_up_layer < cradle_config->cradle_layers_min_) + { + aborted = true; + break; + } + + if (! blocked_by_dedupe) + { + // The model is surrounded with cradle based on the area above (z distance). + // When an area that should have a cradle merges with a buildplate supported area above, it will no longer exist for a cradle. + // But if the cradle stops there will be z distance layer between the end of the cradle and said merge. + // To reduce the impact an area is estimated where the cradle should be for these areas. + Polygons previous_area = shadow; + for (size_t cradle_up_layer_z_distance = cradle_up_layer; + cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size() - z_distance_top_layers); + cradle_up_layer_z_distance++) + { + accumulated_model[cradle_up_layer_z_distance + z_distance_top_layers] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); + } + } + break; + } + + model_outline = model_outline.unionPolygons(); + shadow = shadow.offset(-maximum_move_distance).unionPolygons(model_outline); + accumulated_model[cradle_up_layer + z_distance_top_layers] = shadow; + + if (cradle_up_layer > 0) + { + Point2LL shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); + coord_t center_move_distance = support_moves ? std::min(maximum_move_distance_slow, cradle_config->cradle_line_width_ / 3) : cradle_config->cradle_line_width_ / 3; + center_move_distance = std::min(center_move_distance, vSize(shadow_center - center_prev)); + center_prev = center_prev + normal(shadow_center - center_prev, center_move_distance); + cradle_main->centers_.emplace_back(center_prev); + } + } + + if (aborted) + { + // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface + // base. todo reimplement that + + Polygons cradle_0 = accumulated_model[0]; + accumulated_model.clear(); + accumulated_model.emplace_back(cradle_0); + delete cradle_main; + } + else + { + cradle_main->shadow_ = accumulated_model; + result[layer_idx].emplace_back(cradle_main); + } + } + }); + return result; +} + +void SupportCradleGeneration::generateCradleLines(std::vector>& cradle_data_mesh, const SliceMeshStorage& mesh) +{ + + const size_t z_distance_top_layers = round_up_divide(mesh.settings.get("support_top_distance"), mesh.settings.get("layer_height")); + const size_t z_distance_bottom_layers = round_up_divide(mesh.settings.get("support_bottom_distance"), mesh.settings.get("layer_height")); + const bool support_rests_on_model = mesh.settings.get("support_type") == ESupportType::EVERYWHERE; + const coord_t support_line_width = mesh.settings.get("support_line_width"); + const coord_t xy_distance = mesh.settings.get("support_xy_distance"); + const bool xy_overrides = mesh.settings.get("support_xy_overrides_z") == SupportDistPriority::XY_OVERRIDES_Z; + const coord_t xy_min_distance = !xy_overrides ? mesh.settings.get("support_xy_distance_overhang") : xy_distance; + const bool support_moves = mesh.settings.get("support_structure") != ESupportStructure::NORMAL; + + const coord_t minimum_area_to_be_supportable = support_moves ? mesh.settings.get("support_tree_tip_diameter") / 2 : mesh.settings.get("support_line_distance"); + cura::parallel_for( + 1, + cradle_data_mesh.size(), + [&](const LayerIndex layer_idx) + { + for (auto [center_idx, cradle] : cradle_data_mesh[layer_idx] | ranges::views::enumerate) + { + const coord_t max_cradle_xy_distance = *std::max_element(cradle->config_->cradle_xy_distance_.begin(), cradle->config_->cradle_xy_distance_.end()); + std::vector removed_directions(cradle->config_->cradle_line_count_); + const auto& accumulated_model = cradle->shadow_; + for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) + { + Point2LL center = cradle->getCenter(layer_idx + idx); + const coord_t current_cradle_xy_distance = cradle->config_->cradle_xy_distance_[idx]; + const coord_t current_cradle_length = cradle->config_->cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; + + if (cradle->lines_.empty()) + { + cradle->lines_.resize(cradle->config_->cradle_line_count_); + } + + if (idx > cradle->config_->cradle_z_distance_layers_ && ! model_shadow.empty()) + { + Polygons relevant_forbidden = volumes_.getAvoidance( + 0, + layer_idx + idx, + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + true); + + Polygons this_part_influence = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2); + + for (size_t layer_offset = 1; layer_offset <= z_distance_bottom_layers && layer_offset <= idx; layer_offset++) + { + this_part_influence.add(accumulated_model[idx - layer_offset].offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2)); + } + + for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) + { + const coord_t required_range_x = coord_t( + (current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2) + - ((layer_offset - (z_distance_top_layers == 1 ? 0.5 : 0)) * (current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2) + / z_distance_top_layers)); + this_part_influence.add(accumulated_model[idx + layer_offset].offset(required_range_x)); + } + + this_part_influence = this_part_influence.unionPolygons(); + + coord_t cradle_min_xy_distance_delta = std::max(xy_min_distance - current_cradle_xy_distance, coord_t(0)); + + // Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. + // To prevent this offset my the delta before removing the influence of the model. + relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta) + .difference(this_part_influence.offset(cradle_min_xy_distance_delta + EPSILON).unionPolygons()) + .offset(cradle_min_xy_distance_delta + cradle->config_->cradle_line_width_ / 2 + FUDGE_LENGTH) + .unionPolygons(this_part_influence) + .unionPolygons(volumes_.getSupportBlocker(layer_idx + idx).offset(cradle->config_->cradle_line_width_ / 2)); + coord_t max_distance2 = 0; + for (auto line : model_shadow) + { + for (Point2LL p : line) + { + max_distance2 = std::max(max_distance2, vSize2(center - p)); + } + } + + Polygon max_outer_points = PolygonUtils::makeCircle( + center, + sqrt(max_distance2) + current_cradle_length * 2.0, + std::min((2.0 * std::numbers::pi) / double(cradle->config_->cradle_line_count_), 1.9 * std::numbers::pi)); + + // create lines that go from the furthest possible location to the center + Polygons lines_to_center; + for (Point2LL p : max_outer_points) + { + Point2LL direction = p - center; + lines_to_center.addLine(p, center + normal(direction, support_line_width)); + } + + // Subtract the model shadow up until this layer from the lines. + if (idx > 0) + { + lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2).unionPolygons().differencePolyLines(lines_to_center, false); + } + + // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing + Polygons shortened_lines_to_center; + for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); + Point2LL closer = front_closer ? line.front() : line.back(); + Point2LL further = front_closer ? line.back() : line.front(); + coord_t cradle_line_length = Polygon(line).polylineLength(); + if (cradle_line_length < cradle->config_->cradle_length_min_) + { + continue; + } + if (Polygon(line).polylineLength() <= current_cradle_length) + { + shortened_lines_to_center.add(line); + } + else + { + double scale = (double(current_cradle_length) / double(vSize(further - closer))); + Point2LL correct_length = closer + (further - closer) * scale; + shortened_lines_to_center.addLine(correct_length, closer); + } + } + // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent + // this. + shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); + std::vector ordered_lines_to_center(cradle->config_->cradle_line_count_); + + // Evaluate which lines are still valid after the avoidance was subtracted + for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) + { + bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); + Point2LL closer = front_closer ? line.front() : line.back(); + Point2LL further = front_closer ? line.back() : line.front(); + Point2LL current_direction = further - closer; + coord_t distance_from_center = vSize(closer - center); + size_t angle_idx = cradle->getIndexForLineEnd(further, layer_idx + idx); + + if (removed_directions[angle_idx]) + { + continue; + } + + bool keep_line = false; + bool found_candidate = false; + bool too_short = (vSize(closer - further)) < cradle->config_->cradle_length_min_; + bool too_long_jump + = ! cradle->lines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > cradle->config_->cradle_length_ / 3; //todo better non arbitrary limit + // a cradle line should also be removed if there will be no way to support it + if (idx >= cradle->config_->cradle_z_distance_layers_ + 1) + { + const Polygons actually_forbidden = volumes_.getAvoidance( + minimum_area_to_be_supportable, + layer_idx + idx - (cradle->config_->cradle_z_distance_layers_ + 1), + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + true); + too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < support_line_width; + } + if (! too_short && ! too_long_jump) + { + if (ordered_lines_to_center[angle_idx].empty()) + { + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); + } + else + { + // Prefer lines not touching the center. Otherwise, prefer the line closest to the center + if (distance_from_center > FUDGE_LENGTH) + { + if (vSize(ordered_lines_to_center[angle_idx].front() - center) < FUDGE_LENGTH) + { + ordered_lines_to_center[angle_idx].clear(); + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); + } + else if (distance_from_center < vSize(ordered_lines_to_center[angle_idx].front() - center)) + { + ordered_lines_to_center[angle_idx].clear(); + ordered_lines_to_center[angle_idx].add(closer); + ordered_lines_to_center[angle_idx].add(further); + } + } + } + } + } + + for (auto [angle_idx, next_line] : ordered_lines_to_center | ranges::views::enumerate) + { + if (next_line.empty()) + { + removed_directions[angle_idx] = true; + continue; + } + Polygon line(next_line); + // Handle cradle_z_distance_layers by overwriting first element in the vector until valid distance is reached. + if (idx <= cradle->config_->cradle_z_distance_layers_ + 1 && ! cradle->lines_[angle_idx].empty()) + { + cradle->lines_[angle_idx][0] = TreeSupportCradleLine(line, layer_idx + idx, cradle->config_->cradle_lines_roof_); + } + else + { + cradle->lines_[angle_idx].emplace_back(TreeSupportCradleLine(line, layer_idx + idx, cradle->config_->cradle_lines_roof_)); + } + } + } + } + + // enlarge cradle lines below to minimize overhang of cradle lines. + for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) + { + if (! cradle_lines.empty()) + { + Point2LL line_end = cradle_lines.back().line_.back(); + for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) + { + Point2LL center = cradle->getCenter(line.layer_idx_); + if (vSize2(line_end - center) > vSize2(line.line_.back() - center)) + { + Polygons line_extension; + line_extension.addLine(line.line_.back(), line_end); + coord_t line_length_before = line_extension.polyLineLength(); + Polygons actually_forbidden = volumes_.getAvoidance( + 0, + line.layer_idx_, + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + true); + line_extension = actually_forbidden.differencePolyLines(line_extension); + + if (line_extension.polyLineLength() + EPSILON < line_length_before) + { + for (auto line_part : line_extension) + { + bool front_closer = vSize2(line_part.front() - center) < vSize2(line_part.back() - center); + Point2LL closer = front_closer ? line_part.front() : line_part.back(); + Point2LL further = front_closer ? line_part.back() : line_part.front(); + + if (vSize2(closer - line.line_.back() < EPSILON * EPSILON)) + { + line_end = further; + } + } + } + // As the center can move there is no guarantee that the point of the current line lies on the line below. + line_end = LinearAlg2D::getClosestOnLine(line_end, line.line_.front(), line.line_.back()); + line.line_.back() = line_end; + } + } + } + } + } + }); +} + + +void SupportCradleGeneration::cleanCradleLineOverlaps() +{ + std::vector> all_cradles_per_layer(cradle_data_.front().size()); + for(size_t mesh_idx = 0; mesh_idx < cradle_data_.size(); mesh_idx++) + { + for (LayerIndex layer_idx = 0; layer_idx < cradle_data_[mesh_idx].size(); layer_idx++) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[mesh_idx][layer_idx].size(); cradle_idx++) + { + for (size_t cradle_line_idx = 0; cradle_line_idx < cradle_data_[mesh_idx][layer_idx][cradle_idx]->lines_.size(); cradle_line_idx++) + { + for (size_t height = 0; height < cradle_data_[mesh_idx][layer_idx][cradle_idx]->lines_[cradle_line_idx].size(); height++) + { + LayerIndex cradle_line_layer_idx = cradle_data_[mesh_idx][layer_idx][cradle_idx]->lines_[cradle_line_idx][height].layer_idx_; + if(all_cradles_per_layer.size() <= layer_idx + height) + { + all_cradles_per_layer.resize(cradle_data_[mesh_idx][layer_idx][cradle_idx]->config_->cradle_layers_ + 1); + } + all_cradles_per_layer[layer_idx + height].emplace_back(cradle_data_[mesh_idx][layer_idx][cradle_idx], cradle_line_layer_idx, cradle_line_idx); + } + } + } + } + } + + cura::parallel_for( + 1, + all_cradles_per_layer.size(), + [&](const LayerIndex layer_idx) + { + std::vector& all_cradles_on_layer = all_cradles_per_layer[layer_idx]; + + std::function handleNewEnd = [&](size_t cradle_line_idx, Point2LL new_end) + { + TreeSupportCradleLine* cradle_line = all_cradles_on_layer[cradle_line_idx].getCradleLine(); + if (LinearAlg2D::pointIsProjectedBeyondLine(new_end, cradle_line->line_.front(), cradle_line->line_.back()) || vSize(new_end - cradle_line->line_.front()) < all_cradles_on_layer[cradle_line_idx].cradle_->config_->cradle_length_min_) + { + cradle_line->addLineToRemoved(cradle_line->line_); + cradle_line->line_.clear(); + } + else + { + cradle_line->line_.back() = new_end; + } + }; + + for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) + { + TreeSupportCradleLine* cradle_line = all_cradles_on_layer[cradle_idx].getCradleLine(); + + AABB bounding_box_outer = AABB(cradle_line->line_); + for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) + { + TreeSupportCradleLine* cradle_line_inner = all_cradles_on_layer[cradle_idx_inner].getCradleLine(); + + const coord_t min_distance_between_lines + = std::max(all_cradles_on_layer[cradle_idx].cradle_->config_->cradle_line_width_, all_cradles_on_layer[cradle_idx_inner].cradle_->config_->cradle_line_width_) + + std::max(all_cradles_on_layer[cradle_idx].cradle_->config_->cradle_line_distance_, all_cradles_on_layer[cradle_idx_inner].cradle_->config_->cradle_line_distance_); + + AABB bounding_box_current = bounding_box_outer; + bounding_box_current.expand(min_distance_between_lines); + + if (cradle_line_inner->line_.empty() || cradle_line->line_.empty()) + { + continue; + } + if (bounding_box_current.hit(AABB(cradle_line_inner->line_))) + { + Polygon& outer_line = cradle_line->line_; + Polygon& inner_line = cradle_line_inner->line_; + Point2LL intersect; + if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) + { + coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); + coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); + + if (inner_intersect_dist > outer_intersect_dist) + { + // this does not ensure that the line ends will not touch. Line ends not touching is handled later + Point2LL new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); + handleNewEnd(cradle_idx_inner, new_end_inner); + } + if (outer_intersect_dist > inner_intersect_dist) + { + Point2LL new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); + handleNewEnd(cradle_idx, new_end_outer); + } + } + + if (! outer_line.empty() && ! inner_line.empty()) + { + // Touching lines have the same issue Lines touch if the end is to close to another line + Point2LL inner_direction = inner_line.back() - inner_line.front(); + Point2LL outer_direction = outer_line.back() - outer_line.front(); + double cosine = std::abs((dot(inner_direction, outer_direction)) / double(vSize(outer_direction) * vSize(inner_direction))); + // If both lines point in the same/opposite direction check that them being to close is not the end line of one to the start of the other + if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) + { + coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); + if (inner_end_to_outer_distance < min_distance_between_lines && inner_end_to_outer_distance < vSize(outer_line.front() - inner_line.front())) + { + Point2LL new_end_inner + = inner_line.back() + normal(inner_line.front() - inner_line.back(), min_distance_between_lines - inner_end_to_outer_distance); + double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); + double error_correction_factor = 1.0 + error / double(min_distance_between_lines - inner_end_to_outer_distance); + new_end_inner + = inner_line.back() + + normal(inner_line.front() - inner_line.back(), (min_distance_between_lines - inner_end_to_outer_distance) * error_correction_factor); + handleNewEnd(cradle_idx_inner, new_end_inner); + } + else + { + coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); + if (outer_end_to_inner_distance < min_distance_between_lines && outer_end_to_inner_distance < vSize(outer_line.front() - inner_line.front())) + { + Point2LL new_end_outer + = outer_line.back() + normal(outer_line.front() - outer_line.back(), min_distance_between_lines - outer_end_to_inner_distance); + double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer, outer_line.front(), outer_line.back())); + double error_correction_factor = 1.0 + error / double(min_distance_between_lines - outer_end_to_inner_distance); + new_end_outer + = outer_line.back() + + normal(outer_line.front() - outer_line.back(), (min_distance_between_lines - outer_end_to_inner_distance) * error_correction_factor); + handleNewEnd(cradle_idx, new_end_outer); + } + } + } + } + } + } + } + }); + for(size_t mesh_idx = 0; mesh_idx < cradle_data_.size(); mesh_idx++) + { + cura::parallel_for( + 1, + cradle_data_[mesh_idx].size(), + [&](const LayerIndex layer_idx) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[mesh_idx][layer_idx].size(); cradle_idx++) + { + TreeSupportCradle* cradle = cradle_data_[mesh_idx][layer_idx][cradle_idx]; + const coord_t max_cradle_xy_distance = *std::max_element(cradle->config_->cradle_xy_distance_.begin(), cradle->config_->cradle_xy_distance_.end()); + + cradle->verifyLines(); + // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. + for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) + { + if (! cradle_lines.empty()) + { + Point2LL line_end = cradle_lines.back().line_.back(); + Point2LL line_front_uppermost = cradle_lines.back().line_.front(); + + if (vSize2(line_end - cradle_lines.back().line_.front()) > cradle->config_->cradle_length_ * cradle->config_->cradle_length_) + { + coord_t current_cradle_xy_distance = cradle->config_->cradle_xy_distance_[cradle_lines.back().layer_idx_ - layer_idx]; + coord_t current_cradle_length = cradle->config_->cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; + cradle_lines.back().line_.back() = line_front_uppermost + normal(line_end - line_front_uppermost, current_cradle_length); + for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) + { + Point2LL center = cradle->getCenter(line.layer_idx_); + Point2LL line_back_inner = line.line_.back(); + Point2LL line_front_inner = line.line_.front(); + if (vSize2(line_back_inner - line_front_inner) > cradle->config_->cradle_length_ * cradle->config_->cradle_length_) + { + // As the center can move there is no guarantee that the point of the current line lies on the line below. + Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end, line.line_.front(), line.line_.back()); + current_cradle_xy_distance = cradle->config_->cradle_xy_distance_[line.layer_idx_ - layer_idx]; + current_cradle_length = cradle->config_->cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; + if (vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) + { + line.line_.back() = projected_line_end; + } + else + { + line.line_.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); + } + } + line_end = line.line_.back(); + } + } + } + } + } + }); + } +} + + +void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStorage& storage) +{ + + std::mutex critical_support_free_areas_and_cradle_areas; + + for(size_t mesh_idx = 0; mesh_idx < cradle_data_.size(); mesh_idx++) + { + + if(cradle_data_[mesh_idx].empty()) + { + continue; + + } + + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; + const coord_t layer_height = mesh.settings.get("layer_height"); + const size_t z_distance_top_layers = round_up_divide(mesh.settings.get("support_top_distance"), layer_height); + const bool support_rests_on_model = mesh.settings.get("support_type") == ESupportType::EVERYWHERE; + const coord_t support_line_width = mesh.settings.get("support_line_width"); + const size_t support_roof_layers = mesh.settings.get("support_roof_enable") ? round_divide(mesh.settings.get("support_roof_height"), layer_height) : 0; + const bool xy_overrides = mesh.settings.get("support_xy_overrides_z") == SupportDistPriority::XY_OVERRIDES_Z; + const coord_t xy_distance = mesh.settings.get("support_xy_distance"); + const coord_t xy_min_distance = !xy_overrides ? mesh.settings.get("support_xy_distance_overhang") : xy_distance; + const coord_t roof_outset = support_roof_layers > 0 ? mesh.settings.get("support_roof_offset") : 0; + const bool support_moves = mesh.settings.get("support_structure") != ESupportStructure::NORMAL; + + const coord_t max_roof_movement = support_moves ? TreeSupportSettings(mesh.settings).maximum_move_distance_slow : 0; //todo without TreeSupportSettings + Simplify simplifyer(mesh.settings); + + cura::parallel_for( + 0, + cradle_data_[mesh_idx].size(), + [&](const LayerIndex layer_idx) + { + std::vector valid_cradles; + for (size_t cradle_idx = 0; cradle_idx < cradle_data_[mesh_idx][layer_idx].size(); cradle_idx++) + { + TreeSupportCradle& cradle = *cradle_data_[mesh_idx][layer_idx][cradle_idx]; + const coord_t min_distance_between_lines = FUDGE_LENGTH + cradle.config_->min_distance_between_lines_areas_; + // Some angles needed to move cradle lines outwards to prevent them from toughing. + double center_angle = (2.0 * std::numbers::pi) / double(cradle.config_->cradle_line_count_); + double outer_angle = (std::numbers::pi - center_angle) / 2; + coord_t outer_radius = (double(min_distance_between_lines + support_line_width) / sin(center_angle)) * sin(outer_angle); + const coord_t small_hole_size = EPSILON + cradle.config_->min_distance_between_lines_areas_; // based on calculation in WallToolPath + + for (size_t cradle_height = 0; cradle_height <= cradle.config_->cradle_layers_; cradle_height++) + { + Polygons line_tips; + + std::vector> all_tips_center; + // generate trapezoid line tip with front width of support line width, back cradle_width. + + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) + { + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_height, line_idx); + if (! line_opt) + { + continue; + } + TreeSupportCradleLine* cradle_line = line_opt.value(); + Polygon line = cradle_line->line_; + + coord_t current_cradle_line_width = cradle.config_->cradle_line_width_; + + double assumed_half_center_angle = std::numbers::pi / (1.5 * cradle.config_->cradle_line_count_); + coord_t triangle_length + = cradle.config_->cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - support_line_width) / 2) * tan(std::numbers::pi / 2 - assumed_half_center_angle); + + const coord_t line_length = line.polylineLength(); + if (triangle_length >= line_length + cradle.config_->cradle_line_width_) + { + triangle_length = line_length + cradle.config_->cradle_line_width_; + current_cradle_line_width = support_line_width + 2 * triangle_length * tan(assumed_half_center_angle); + } + + Point2LL direction = line.back() - line.front(); + Point2LL center_front = line.front() - normal(direction, cradle.config_->cradle_line_width_ / 2); + + Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), support_line_width / 2); + Point2LL center_up = center_front + direction_up_center; + Point2LL center_down = center_front - direction_up_center; + + coord_t tip_shift = 0; + for (auto existing_center : all_tips_center) + { + Point2LL intersect; + bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) + && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); + if (centers_touch || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) + { + // This cradle line is to close to another. + // Move it back todo If line gets smaller than min length => abort + coord_t tip_shift_here = outer_radius - vSize(center_front - cradle.getCenter(cradle_line->layer_idx_)); + tip_shift += tip_shift_here; + center_front = center_front + normal(direction, tip_shift_here); + center_up = center_front + direction_up_center; + center_down = center_front - direction_up_center; + } + } + + Point2LL back_center = center_front + normal(direction, triangle_length); + Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), current_cradle_line_width / 2); + + Point2LL back_up = back_center + direction_up_back; + Point2LL back_down = back_center - direction_up_back; + + line.front() = back_center + normal(direction, current_cradle_line_width / 2 - FUDGE_LENGTH / 2); + all_tips_center.emplace_back(center_up, center_down); + + Polygon line_tip; + line_tip.add(back_down); + if (current_cradle_line_width == cradle.config_->cradle_line_width_) + { + coord_t distance_end_front = line_length - triangle_length + cradle.config_->cradle_line_width_ - tip_shift; + Point2LL line_end_down = back_down + normal(direction, distance_end_front); + Point2LL line_end_up = back_up + normal(direction, distance_end_front); + line_tip.add(line_end_down); + line_tip.add(line_end_up); + } + line_tip.add(back_up); + line_tip.add(center_up); + line_tip.add(center_down); + if (line_tip.area() < 0) + { + line_tip.reverse(); + } + cradle_line->area_.add(line_tip); + + Polygons anti_preferred = cradle_line->area_.offset(xy_distance); + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t z_distance_idx = 0; z_distance_idx < z_distance_top_layers; z_distance_idx++) + { + volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); + } + } + } + + Polygons shadow = cradle.shadow_[0]; + Polygons cradle_base = shadow; + + if (support_roof_layers) + { + Polygons cut_line_base; + Polygons first_cradle_areas; + if (cradle.config_->large_cradle_base_) + { + // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest + // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just + // the overhang, but that should not cause any issues. + Point2LL min_p = cradle_base.min(); + Point2LL max_p = cradle_base.max(); + Polygons rest_line; + rest_line.addLine(min_p, max_p); + cut_line_base = rest_line.offsetPolyLine(small_hole_size); + } + { + std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); + for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) + { + support_free_areas_[layer_idx - interface_down].add(cut_line_base); + } + } + if (cradle.config_->large_cradle_base_) + { + cradle_base = cradle_base.offset(cradle.config_->cradle_support_base_area_radius_, ClipperLib::jtRound); + Polygons center_removed = cradle_base.difference(cut_line_base); + if (center_removed.area() > 1) + { + cradle_base = center_removed; + } + } + else if (cradle.config_->cradle_base_roof_) + { + // collect all inner points and connect to center for thin cradle base + Polygons connected_cradle_base; + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) + { + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle.config_->cradle_z_distance_layers_ + 1, line_idx); + if (line_opt) + { + connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_), line_opt.value()->line_.front()); + } + } + cradle_base = connected_cradle_base.offsetPolyLine(cradle.config_->cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); + } + } + + cradle_base = cradle_base.unionPolygons(); + + if (cradle.config_->cradle_lines_roof_) + { + Polygons forbidden_here = volumes_.getAvoidance( + 0, + layer_idx, + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + ! xy_overrides); + std::vector> roofs; + roofs.emplace_back(cradle_base, -1); + + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) + { + std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle.config_->cradle_z_distance_layers_ + 1, line_idx); + if (! line_opt) + { + continue; + } + + coord_t line_base_offset = cradle.config_->large_cradle_base_ ? std::max(coord_t(0), cradle.config_->cradle_support_base_area_radius_ - cradle.config_->cradle_line_width_ / 2) : 0; + roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset, ClipperLib::jtRound), line_idx); + } + + + for (auto roof_area_pair : roofs) + { + Polygons roof_area_before = roof_area_pair.first; + Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( + roof_area_pair.first, + roof_outset, + forbidden_here, + xy_min_distance * 2, + 0, + 1, + support_line_width, + &simplifyer); + for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) + { + const Polygons forbidden_next = volumes_.getAvoidance( + 0, + layer_idx - (dtt_roof + 1), + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + ! xy_overrides); + + full_overhang_area = full_overhang_area.difference(forbidden_next); + + if (full_overhang_area.area() > EPSILON && dtt_roof < support_roof_layers) + { + if (roof_area_pair.second != -1) // If is_line + { + TreeSupportCradleLine roof_base_line(cradle.lines_[roof_area_pair.second].front()); + roof_base_line.area_ = full_overhang_area; + roof_base_line.is_base_ = true; + roof_base_line.layer_idx_ = layer_idx - dtt_roof; + cradle.lines_[roof_area_pair.second].emplace_front(roof_base_line); + } + else + { + if (dtt_roof < cradle.base_below_.size()) + { + cradle.base_below_[dtt_roof].add(full_overhang_area); + } + else + { + cradle.base_below_.emplace_back(full_overhang_area); + } + } + const Polygons forbidden_before = volumes_.getAvoidance( + 0, + layer_idx - dtt_roof, + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + ! xy_overrides); + + Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc( //todo better safeStepOffset values to improve performance + full_overhang_area, + max_roof_movement, + forbidden_before, + 2 * xy_min_distance, + 0, + 1, + support_line_width, + &simplifyer); + Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); + if (overhang_part.area() > EPSILON) + { + OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[mesh_idx][layer_idx][cradle_idx], layer_idx - dtt_roof, roof_area_pair.second); + cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); + } + } + else + { + if (roof_area_before.area() > 1) + { + LayerIndex line_layer_idx + = roof_area_pair.second < 0 ? LayerIndex(-1) : cradle_data_[mesh_idx][layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; + OverhangInformation cradle_overhang(roof_area_before, true, cradle_data_[mesh_idx][layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); + cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); + } + break; + } + roof_area_before = full_overhang_area; + } + } + } + else + { + Polygons forbidden_here = volumes_.getAvoidance( + 0, + layer_idx, + (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, + support_rests_on_model, + ! xy_overrides); + + coord_t offset_radius = cradle.config_->cradle_support_base_area_radius_; + if (cradle_base.offset(offset_radius, ClipperLib::jtRound).difference(forbidden_here).area() > 1) + { + OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[mesh_idx][layer_idx][cradle_idx]); + cradle.overhang_[layer_idx].emplace_back(cradle_overhang); + } + + for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) + { + if (! cradle.lines_[line_idx].empty()) + { + LayerIndex support_cradle_on_layer_idx = cradle.lines_[line_idx].front().layer_idx_ - (cradle.config_->cradle_z_distance_layers_ + 1); + OverhangInformation line_overhang( + cradle.lines_[line_idx].front().area_, + false, + cradle_data_[mesh_idx][layer_idx][cradle_idx], + cradle.lines_[line_idx].front().layer_idx_, + line_idx); + cradle.overhang_[support_cradle_on_layer_idx].emplace_back(line_overhang); + } + } + } + if (! cradle.overhang_.empty()) + { + valid_cradles.emplace_back(cradle_data_[mesh_idx][layer_idx][cradle_idx]); + } + else + { + delete cradle_data_[mesh_idx][layer_idx][cradle_idx]; + } + } + cradle_data_[mesh_idx][layer_idx] = valid_cradles; + }); + + + } +} + + +void SupportCradleGeneration::addMeshToCradleCalculation(const SliceMeshStorage& mesh, size_t mesh_idx) +{ + if(retrieveSetting(mesh.settings, "support_tree_cradle_height") < mesh.settings.get("layer_height")) + { + return; + } + calculateFloatingParts(mesh, mesh_idx); + std::vector> cradle_data_mesh = generateCradleCenters(mesh, mesh_idx); + generateCradleLines(cradle_data_mesh, mesh); + if(cradle_data_[mesh_idx].size() < cradle_data_mesh.size()) + { + cradle_data_[mesh_idx].resize(cradle_data_mesh.size()); + } + for (auto [layer_idx, cradles_on_layer] :cradle_data_mesh | ranges::views::enumerate) + { + cradle_data_[mesh_idx][layer_idx].insert(cradle_data_[mesh_idx][layer_idx].end(), cradles_on_layer.begin(), cradles_on_layer.end()); + } + +} + +void SupportCradleGeneration::generate(const SliceDataStorage& storage) +{ + cleanCradleLineOverlaps(); + generateCradleLineAreasAndBase(storage); +} + +void SupportCradleGeneration::pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx) +{ + + if(target.size() < cradle_data_[mesh_idx].size()) + { + target.resize(cradle_data_[mesh_idx].size()); + } + + for (auto [layer_idx, cradles_on_layer] :cradle_data_[mesh_idx] | ranges::views::enumerate) + { + target[layer_idx].insert(target[layer_idx].end(), cradles_on_layer.begin(), cradles_on_layer.end()); + } + + if(support_free_areas.size() < support_free_areas_.size()) + { + support_free_areas.resize(support_free_areas_.size()); + } + for (auto [layer_idx, support_free_on_layer] : support_free_areas_ | ranges::views::enumerate) + { + support_free_areas_[layer_idx].add(support_free_on_layer); + } +} + +} //End Namespace \ No newline at end of file diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index f45d042e9a..422d8d6a01 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -53,35 +53,10 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T , already_inserted_(mesh.overhang_areas.size()) , support_roof_drawn_(mesh.overhang_areas.size(), Polygons()) , support_roof_drawn_fractional_(mesh.overhang_areas.size(), Polygons()) - , cradle_data_(mesh.overhang_areas.size()) , force_minimum_roof_area_(use_fake_roof_ || SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT) , force_initial_layer_radius_(retrieveSetting(mesh.settings, "support_tree_enforce_initial_layer_diameter")) - , cradle_layers_(retrieveSetting(mesh.settings, "support_tree_cradle_height") / config_.layer_height) - , cradle_layers_min_(retrieveSetting(mesh.settings, "support_tree_cradle_min_height") / config_.layer_height) - , cradle_line_count_(retrieveSetting(mesh.settings, "support_tree_cradle_line_count")) - , cradle_length_(retrieveSetting(mesh.settings, "support_tree_cradle_length")) - , cradle_length_min_(retrieveSetting(mesh.settings, "support_tree_min_cradle_length")) - , cradle_line_width_(retrieveSetting(mesh.settings, "support_tree_cradle_line_width")) - , cradle_lines_roof_(! use_fake_roof_ && retrieveSetting(mesh.settings, "support_tree_roof_cradle") != "none") - , cradle_base_roof_( - ! use_fake_roof_ - && (retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "cradle_and_base" - || retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base")) - , large_cradle_base_(! use_fake_roof_ && retrieveSetting(mesh.settings, "support_tree_roof_cradle") == "large_cradle_and_base") - , cradle_area_threshold_(1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area")) - , cradle_tip_dtt_(config_.tip_layers * retrieveSetting(mesh.settings, "support_tree_cradle_base_tip_percentage") / 100.0) , large_cradle_line_tips_(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) - , cradle_z_distance_layers_(round_divide(retrieveSetting(mesh.settings, "support_tree_cradle_z_distance"), config_.layer_height)) { - if (cradle_layers_) - { - cradle_layers_ += cradle_z_distance_layers_; - } - if (cradle_length_ - cradle_line_width_ > 0) - { - cradle_length_ -= cradle_line_width_; - cradle_length_min_ -= cradle_line_width_; - } const double support_overhang_angle = mesh.settings.get("support_angle"); const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * config_.layer_height) : std::numeric_limits::max(); @@ -125,38 +100,6 @@ TreeSupportTipGenerator::TreeSupportTipGenerator(const SliceMeshStorage& mesh, T dtt_when_tips_can_merge = config_.tip_layers; // arbitrary default for when there is no guarantee that the while loop above will terminate } support_supporting_branch_distance_ = 2 * config_.getRadius(dtt_when_tips_can_merge) + config_.support_line_width + FUDGE_LENGTH; - - - for (size_t cradle_z_dist_ctr = 0; cradle_z_dist_ctr < cradle_z_distance_layers_ + 1; cradle_z_dist_ctr++) - { - cradle_xy_distance_.emplace_back(config_.xy_min_distance); - } - - for (int i = 0; i < 9; i++) - { - std::stringstream setting_name_dist; - setting_name_dist << "support_tree_cradle_xy_dist_"; - setting_name_dist << i; - coord_t next_cradle_xy_dist = retrieveSetting(mesh.settings, setting_name_dist.str()); - std::stringstream setting_name_height; - setting_name_height << "support_tree_cradle_xy_height_"; - setting_name_height << i; - coord_t next_cradle_xy_dist_height = retrieveSetting(mesh.settings, setting_name_height.str()); - if (next_cradle_xy_dist_height == 0) - { - break; - } - - for (int layer_delta = 0; layer_delta < round_up_divide(next_cradle_xy_dist_height, config_.layer_height); layer_delta++) - { - cradle_xy_distance_.emplace_back(next_cradle_xy_dist); - } - } - - for (int cradle_xy_dist_fill = cradle_xy_distance_.size(); cradle_xy_dist_fill <= cradle_layers_ + 1; cradle_xy_dist_fill++) - { - cradle_xy_distance_.emplace_back(config_.xy_min_distance); - } } @@ -469,1097 +412,6 @@ std::shared_ptr TreeSupportTipGenerator::getCrossFillPro return nullptr; } -void TreeSupportTipGenerator::calculateFloatingParts(const SliceMeshStorage& mesh) -{ - LayerIndex max_layer = 0; - - for (LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) - { - if (! mesh.overhang_areas[layer_idx].empty()) - { - max_layer = layer_idx; - } - } - max_layer = std::min(max_layer + cradle_layers_, LayerIndex(mesh.overhang_areas.size() - 1)); - - LayerIndex start_layer = 1; - floating_parts_cache_.resize(max_layer + 1); - floating_parts_map_.resize(max_layer + 1); - floating_parts_map_below_.resize(max_layer + 1); - std::mutex critical_sections; - - Polygons completely_supported = volumes_.getCollision(0, 0, true); - Polygons layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter - for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) - { - Polygons next_completely_supported; - - // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. - Polygons layer = mesh.layers[layer_idx].getOutlines(); - - const std::vector layer_parts = layer.splitIntoParts(); - cura::parallel_for( - 0, - layer_parts.size(), - [&](const size_t part_idx) - { - const PolygonsPart& part = layer_parts[part_idx]; - AABB part_aabb(part); - bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(layer_below, part_aabb).intersection(part).empty(); - - if (! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold_)) - { - std::lock_guard critical_section_add(critical_sections); - next_completely_supported.add(part); - return; - } - - Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); - coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * config_.min_wall_line_width * config_.min_wall_line_width); - if (! has_support_below) - { - std::lock_guard critical_section_add(critical_sections); - floating_parts_cache_[layer_idx].emplace_back(part, floating_parts_cache_[layer_idx].size(), 0, overhang_area); - floating_parts_map_[layer_idx].emplace_back(std::vector()); - floating_parts_map_below_[layer_idx].emplace_back(std::vector()); - return; - } - - size_t min_resting_on_layers = 0; - coord_t supported_overhang_area = 0; - bool add = false; - std::vector idx_of_floating_below; - for (auto [idx, floating] : floating_parts_cache_[layer_idx - 1] | ranges::views::enumerate) - { - if (layer_idx > 1 && floating.height < cradle_layers_ - 1 && ! floating.area.intersection(part).empty()) - { - idx_of_floating_below.emplace_back(idx); - supported_overhang_area += floating.accumulated_supportable_overhang; - min_resting_on_layers = std::max(min_resting_on_layers, floating.height); - add = true; - } - } - - - if (min_resting_on_layers < cradle_layers_ && add && overhang_area + supported_overhang_area < cradle_area_threshold_) - { - std::lock_guard critical_section_add(critical_sections); - for (size_t idx : idx_of_floating_below) - { - floating_parts_map_[layer_idx - 1][idx].emplace_back(floating_parts_cache_[layer_idx].size()); - } - - floating_parts_map_[layer_idx].emplace_back(std::vector()); - floating_parts_map_below_[layer_idx].emplace_back(idx_of_floating_below); - floating_parts_cache_[layer_idx] - .emplace_back(part, floating_parts_cache_[layer_idx].size(), min_resting_on_layers + 1, overhang_area + supported_overhang_area); - } - else - { - std::lock_guard critical_section_add(critical_sections); - next_completely_supported.add(part); - } - }); - layer_below = layer; - completely_supported = next_completely_supported; - } -} - -std::vector TreeSupportTipGenerator::getUnsupportedArea(LayerIndex layer_idx, size_t idx_of_area, bool above) -{ - std::vector result; - - if (layer_idx == 0) - { - return result; - } - - bool has_result = false; - - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - has_result = layer_idx < floating_parts_cache_.size(); - } - - if (has_result) - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - if (! floating_parts_cache_[layer_idx].empty() && above) - { - for (size_t resting_idx : floating_parts_map_[layer_idx - 1][idx_of_area]) - { - result.emplace_back(floating_parts_cache_[layer_idx][resting_idx]); - } - } - else if (! floating_parts_cache_[layer_idx - 1].empty() && ! above) - { - for (size_t resting_idx : floating_parts_map_below_[layer_idx][idx_of_area]) - { - result.emplace_back(floating_parts_cache_[layer_idx - 1][resting_idx]); - } - } - } - else - { - spdlog::error("Requested not calculated unsupported area."); - return result; - } - return result; -} - - -std::vector TreeSupportTipGenerator::getFullyUnsupportedArea(LayerIndex layer_idx) -{ - std::vector result; - - if (layer_idx == 0) - { - return result; - } - - bool has_result = false; - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - has_result = layer_idx < floating_parts_cache_.size(); - } - - if (has_result) - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - for (auto [idx, floating_data] : floating_parts_cache_[layer_idx] | ranges::views::enumerate) - { - if (floating_data.height == 0) - { - result.emplace_back(floating_data); - } - } - } - else - { - spdlog::error("Requested not calculated unsupported area.", layer_idx); - return result; - } - return result; -} - -std::vector>> TreeSupportTipGenerator::generateCradleCenters(const SliceMeshStorage& mesh) -{ - std::mutex critical_dedupe; - std::vector> dedupe(mesh.overhang_areas.size()); - std::vector>> shadows(mesh.overhang_areas.size()); - cura::parallel_for( - 1, - mesh.overhang_areas.size() - (z_distance_delta_ + 1), - [&](const LayerIndex layer_idx) - { - if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() || getFullyUnsupportedArea(layer_idx + z_distance_delta_).empty()) - { - return; - } - - for (auto pointy_info : getFullyUnsupportedArea(layer_idx + z_distance_delta_)) - { - AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta_]); - if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta_], overhang_aabb).intersection(pointy_info.area).empty()) - { - // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. - continue; - } - - std::vector accumulated_model(std::min(cradle_layers_ + z_distance_delta_, mesh.overhang_areas.size() - layer_idx), Polygons()); - std::vector all_pointy_idx{ pointy_info.index }; - - Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); - std::vector additional_centers; - TreeSupportCradle* cradle_main - = new TreeSupportCradle(layer_idx, center_prev, shadows[layer_idx].size(), cradle_base_roof_, cradle_layers_min_, cradle_length_min_, cradle_line_count_); - for (size_t z_distance = 0; z_distance < config_.z_distance_top_layers; z_distance++) - { - accumulated_model[z_distance] = pointy_info.area; - cradle_main->centers_.emplace_back(center_prev); - } - Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. - bool aborted = false; - bool contacted_other_pointy = false; - std::vector unsupported_model(accumulated_model.size()); - for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size(); cradle_up_layer++) - { - // shadow model up => not cradle where model - // then drop cradle down - // cut into parts => get close to original pointy that are far enough from each other. - std::vector next_pointy_idx; - Polygons model_outline; - bool blocked_by_dedupe = false; - // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up - if (cradle_up_layer > 1) - { - for (size_t pointy_idx : all_pointy_idx) - { - for (auto next_pointy_data : getUnsupportedArea(layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx, true)) - { - if (next_pointy_data.height - != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it - { - contacted_other_pointy = true; - continue; - } - unsupported_model[cradle_up_layer].add(next_pointy_data.area); - // Ensure each area is only handles once - std::lock_guard critical_section_cradle(critical_dedupe); - if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) - { - dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data.index); - model_outline.add(next_pointy_data.area); - next_pointy_idx.emplace_back(next_pointy_data.index); - } - else - { - blocked_by_dedupe = true; - } - - std::vector all_pointy_idx_below{ next_pointy_data.index }; - for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_idx_below.empty(); cradle_down_layer--) - { - std::vector next_all_pointy_idx_below; - - for (size_t pointy_idx_below : all_pointy_idx_below) - { - for (auto prev_pointy_data : getUnsupportedArea(layer_idx + cradle_down_layer - 1 + z_distance_delta_, pointy_idx_below, false)) - { - if (prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) - { - // Only add if area below does not have it's own cradle. - if (prev_pointy_data.height < cradle_layers_min_) - { - accumulated_model[cradle_down_layer].add(prev_pointy_data.area); - next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); - } - } - } - } - all_pointy_idx_below = next_all_pointy_idx_below; - accumulated_model[cradle_down_layer] = accumulated_model[cradle_down_layer].unionPolygons(); - } - } - } - all_pointy_idx = next_pointy_idx; - } - else - { - model_outline.add(pointy_info.area); - } - - if (model_outline.empty()) - { - if (cradle_up_layer < cradle_layers_min_) - { - aborted = true; - break; - } - - if (! blocked_by_dedupe) - { - // The model is surrounded with cradle based on the area above (z distance). - // When an area that should have a cradle merges with a buildplate supported area above, it will no longer exist for a cradle. - // But if the cradle stops there will be z distance layer between the end of the cradle and said merge. - // To reduce the impact an area is estimated where the cradle should be for these areas. - Polygons previous_area = shadow; - for (size_t cradle_up_layer_z_distance = cradle_up_layer; - cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size() - config_.z_distance_top_layers); - cradle_up_layer_z_distance++) - { - accumulated_model[cradle_up_layer_z_distance + config_.z_distance_top_layers] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); - } - } - break; - } - - model_outline = model_outline.unionPolygons(); - shadow = shadow.offset(-config_.maximum_move_distance).unionPolygons(model_outline); - accumulated_model[cradle_up_layer + config_.z_distance_top_layers] = shadow; - - if (cradle_up_layer > 0) - { - Point2LL shadow_center = Polygon(shadow.getOutsidePolygons()[0]).centerOfMass(); - coord_t center_move_distance = std::min(config_.maximum_move_distance_slow, cradle_line_width_ / 3); - center_move_distance = std::min(center_move_distance, vSize(shadow_center - center_prev)); - center_prev = center_prev + normal(shadow_center - center_prev, center_move_distance); - cradle_main->centers_.emplace_back(center_prev); - } - } - - if (aborted) - { - // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface - // base. todo reimplement that - - Polygons cradle_0 = accumulated_model[0]; - accumulated_model.clear(); - accumulated_model.emplace_back(cradle_0); - delete cradle_main; - } - else - { - cradle_data_[layer_idx].emplace_back(cradle_main); - } - shadows[layer_idx].emplace_back(accumulated_model); - } - }); - - return shadows; -} - -void TreeSupportTipGenerator::generateCradleLines(std::vector>>& shadow_data) -{ - const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance_.begin(), cradle_xy_distance_.end()); - - cura::parallel_for( - 1, - cradle_data_.size(), - [&](const LayerIndex layer_idx) - { - for (auto [center_idx, cradle] : cradle_data_[layer_idx] | ranges::views::enumerate) - { - std::vector removed_directions(cradle_line_count_); - const auto& accumulated_model = shadow_data[layer_idx][cradle->shadow_idx_]; - for (auto [idx, model_shadow] : accumulated_model | ranges::views::enumerate) - { - Point2LL center = cradle->getCenter(layer_idx + idx); - const coord_t current_cradle_xy_distance = cradle_xy_distance_[idx]; - const coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; - - if (cradle->lines_.empty()) - { - cradle->lines_.resize(cradle_line_count_); - } - - if (idx > cradle_z_distance_layers_ && ! model_shadow.empty()) - { - Polygons relevant_forbidden = volumes_.getAvoidance( - 0, - layer_idx + idx, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - true); - - Polygons this_part_influence = model_shadow.offset(current_cradle_xy_distance + cradle_line_width_ / 2); - - for (size_t layer_offset = 1; layer_offset <= config_.z_distance_bottom_layers && layer_offset <= idx; layer_offset++) - { - this_part_influence.add(accumulated_model[idx - layer_offset].offset(current_cradle_xy_distance + cradle_line_width_ / 2)); - } - - for (coord_t layer_offset = 1; layer_offset <= config_.z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) - { - const coord_t required_range_x = coord_t( - (current_cradle_xy_distance + cradle_line_width_ / 2) - - ((layer_offset - (config_.z_distance_top_layers == 1 ? 0.5 : 0)) * (current_cradle_xy_distance + cradle_line_width_ / 2) - / config_.z_distance_top_layers)); - this_part_influence.add(accumulated_model[idx + layer_offset].offset(required_range_x)); - } - - this_part_influence = this_part_influence.unionPolygons(); - - coord_t cradle_min_xy_distance_delta = std::max(config_.xy_min_distance - current_cradle_xy_distance, coord_t(0)); - - // Somewhere, Somehow there is a small rounding error which causes small slivers of collision of the model to remain. - // To prevent this offset my the delta before removing the influence of the model. - relevant_forbidden = relevant_forbidden.offset(-cradle_min_xy_distance_delta) - .difference(this_part_influence.offset(cradle_min_xy_distance_delta + EPSILON).unionPolygons()) - .offset(cradle_min_xy_distance_delta + cradle_line_width_ / 2 + FUDGE_LENGTH) - .unionPolygons(this_part_influence) - .unionPolygons(volumes_.getSupportBlocker(layer_idx + idx).offset(cradle_line_width_ / 2)); - coord_t max_distance2 = 0; - for (auto line : model_shadow) - { - for (Point2LL p : line) - { - max_distance2 = std::max(max_distance2, vSize2(center - p)); - } - } - - Polygon max_outer_points = PolygonUtils::makeCircle( - center, - sqrt(max_distance2) + current_cradle_length * 2.0, - std::min((2.0 * std::numbers::pi) / double(cradle_line_count_), 1.9 * std::numbers::pi)); - - // create lines that go from the furthest possible location to the center - Polygons lines_to_center; - for (Point2LL p : max_outer_points) - { - Point2LL direction = p - center; - lines_to_center.addLine(p, center + normal(direction, config_.support_line_width)); - } - - // Subtract the model shadow up until this layer from the lines. - if (idx > 0) - { - lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle_line_width_ / 2).unionPolygons().differencePolyLines(lines_to_center, false); - } - - // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing - Polygons shortened_lines_to_center; - for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) - { - bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); - Point2LL closer = front_closer ? line.front() : line.back(); - Point2LL further = front_closer ? line.back() : line.front(); - coord_t cradle_line_length = Polygon(line).polylineLength(); - if (cradle_line_length < cradle_length_min_) - { - continue; - } - if (Polygon(line).polylineLength() <= current_cradle_length) - { - shortened_lines_to_center.add(line); - } - else - { - double scale = (double(current_cradle_length) / double(vSize(further - closer))); - Point2LL correct_length = closer + (further - closer) * scale; - shortened_lines_to_center.addLine(correct_length, closer); - } - } - // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent - // this. - shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); - std::vector ordered_lines_to_center(cradle_line_count_); - - // Evaluate which lines are still valid after the avoidance was subtracted - for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) - { - bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); - Point2LL closer = front_closer ? line.front() : line.back(); - Point2LL further = front_closer ? line.back() : line.front(); - Point2LL current_direction = further - closer; - coord_t distance_from_center = vSize(closer - center); - size_t angle_idx = cradle->getIndexForLineEnd(further, layer_idx + idx); - - if (removed_directions[angle_idx]) - { - continue; - } - - bool keep_line = false; - bool found_candidate = false; - bool too_short = (vSize(closer - further)) < cradle_length_min_; - bool too_long_jump - = ! cradle->lines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > sqrt(cradle_area_threshold_) / 2; - // a cradle line should also be removed if there will be no way to support it - if (idx >= cradle_z_distance_layers_ + 1) - { - const Polygons actually_forbidden = volumes_.getAvoidance( - config_.getRadius(0), - layer_idx + idx - (cradle_z_distance_layers_ + 1), - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - true); - too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < config_.support_line_width; - } - if (! too_short && ! too_long_jump) - { - if (ordered_lines_to_center[angle_idx].empty()) - { - ordered_lines_to_center[angle_idx].add(closer); - ordered_lines_to_center[angle_idx].add(further); - } - else - { - // Prefer lines not touching the center. Otherwise, prefer the line closest to the center - if (distance_from_center > FUDGE_LENGTH) - { - if (vSize(ordered_lines_to_center[angle_idx].front() - center) < FUDGE_LENGTH) - { - ordered_lines_to_center[angle_idx].clear(); - ordered_lines_to_center[angle_idx].add(closer); - ordered_lines_to_center[angle_idx].add(further); - } - else if (distance_from_center < vSize(ordered_lines_to_center[angle_idx].front() - center)) - { - ordered_lines_to_center[angle_idx].clear(); - ordered_lines_to_center[angle_idx].add(closer); - ordered_lines_to_center[angle_idx].add(further); - } - } - } - } - } - - for (auto [angle_idx, next_line] : ordered_lines_to_center | ranges::views::enumerate) - { - if (next_line.empty()) - { - removed_directions[angle_idx] = true; - continue; - } - Polygon line(next_line); - // Handle cradle_z_distance_layers by overwriting first element in the vector until valid distance is reached. - if (idx <= cradle_z_distance_layers_ + 1 && ! cradle->lines_[angle_idx].empty()) - { - cradle->lines_[angle_idx][0] = TreeSupportCradleLine(line, layer_idx + idx, cradle_lines_roof_); - } - else - { - cradle->lines_[angle_idx].emplace_back(TreeSupportCradleLine(line, layer_idx + idx, cradle_lines_roof_)); - } - } - } - } - - // enlarge cradle lines below to minimize overhang of cradle lines. - for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) - { - if (! cradle_lines.empty()) - { - Point2LL line_end = cradle_lines.back().line_.back(); - for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) - { - Point2LL center = cradle->getCenter(line.layer_idx_); - if (vSize2(line_end - center) > vSize2(line.line_.back() - center)) - { - Polygons line_extension; - line_extension.addLine(line.line_.back(), line_end); - coord_t line_length_before = line_extension.polyLineLength(); - Polygons actually_forbidden = volumes_.getAvoidance( - 0, - line.layer_idx_, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - true); - line_extension = actually_forbidden.differencePolyLines(line_extension); - - if (line_extension.polyLineLength() + EPSILON < line_length_before) - { - for (auto line_part : line_extension) - { - bool front_closer = vSize2(line_part.front() - center) < vSize2(line_part.back() - center); - Point2LL closer = front_closer ? line_part.front() : line_part.back(); - Point2LL further = front_closer ? line_part.back() : line_part.front(); - - if (vSize2(closer - line.line_.back() < EPSILON * EPSILON)) - { - line_end = further; - } - } - } - // As the center can move there is no guarantee that the point of the current line lies on the line below. - line_end = LinearAlg2D::getClosestOnLine(line_end, line.line_.front(), line.line_.back()); - line.line_.back() = line_end; - } - } - } - } - } - }); -} - - -void TreeSupportTipGenerator::cleanCradleLineOverlaps() -{ - const coord_t min_distance_between_lines - = cradle_line_width_ + std::max(config_.xy_distance, (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5)); - const coord_t max_cradle_xy_distance = *std::max_element(cradle_xy_distance_.begin(), cradle_xy_distance_.end()); - std::vector> all_cradles_per_layer(cradle_data_.size() + cradle_layers_); - for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) - { - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) - { - for (size_t cradle_line_idx = 0; cradle_line_idx < cradle_data_[layer_idx][cradle_idx]->lines_.size(); cradle_line_idx++) - { - for (size_t height = 0; height < cradle_data_[layer_idx][cradle_idx]->lines_[cradle_line_idx].size(); height++) - { - TreeSupportCradleLine* result = &cradle_data_[layer_idx][cradle_idx]->lines_[cradle_line_idx][height]; - all_cradles_per_layer[layer_idx + height].emplace_back(result); - } - } - } - } - - std::vector removed_lines(cradle_data_.size()); - - cura::parallel_for( - 1, - all_cradles_per_layer.size(), - [&](const LayerIndex layer_idx) - { - std::vector all_cradles_on_layer = all_cradles_per_layer[layer_idx]; - - std::function handleNewEnd = [&](size_t cradle_line_idx, Point2LL new_end) - { - auto& line = (*all_cradles_on_layer[cradle_line_idx]); - if (LinearAlg2D::pointIsProjectedBeyondLine(new_end, line.line_.front(), line.line_.back()) || vSize(new_end - line.line_.front()) < cradle_length_min_) - { - all_cradles_on_layer[cradle_line_idx]->addLineToRemoved(line.line_); - all_cradles_on_layer[cradle_line_idx]->line_.clear(); - } - else - { - all_cradles_on_layer[cradle_line_idx]->line_.back() = new_end; - } - }; - - for (size_t cradle_idx = 0; cradle_idx < all_cradles_on_layer.size(); cradle_idx++) - { - AABB bounding_box_current = AABB(all_cradles_on_layer[cradle_idx]->line_); - bounding_box_current.expand(min_distance_between_lines); - for (size_t cradle_idx_inner = cradle_idx + 1; cradle_idx_inner < all_cradles_on_layer.size(); cradle_idx_inner++) - { - if (all_cradles_on_layer[cradle_idx_inner]->line_.empty() || all_cradles_on_layer[cradle_idx]->line_.empty()) - { - continue; - } - if (bounding_box_current.hit(AABB(all_cradles_on_layer[cradle_idx_inner]->line_))) - { - Polygon& outer_line = (*all_cradles_on_layer[cradle_idx]).line_; - Polygon& inner_line = (*all_cradles_on_layer[cradle_idx_inner]).line_; - Point2LL intersect; - if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, inner_line.front(), inner_line.back())) - { - coord_t inner_intersect_dist = vSize(inner_line.front() - intersect); - coord_t outer_intersect_dist = vSize(outer_line.front() - intersect); - - if (inner_intersect_dist > outer_intersect_dist) - { - // this does not ensure that the line ends will not touch. Line ends not touching is handled later - Point2LL new_end_inner = intersect + normal((inner_line.front() - intersect), min_distance_between_lines); - handleNewEnd(cradle_idx_inner, new_end_inner); - } - if (outer_intersect_dist > inner_intersect_dist) - { - Point2LL new_end_outer = intersect + normal((outer_line.front() - intersect), min_distance_between_lines); - handleNewEnd(cradle_idx, new_end_outer); - } - } - - if (! outer_line.empty() && ! inner_line.empty()) - { - // Touching lines have the same issue Lines touch if the end is to close to another line - Point2LL inner_direction = inner_line.back() - inner_line.front(); - Point2LL outer_direction = outer_line.back() - outer_line.front(); - double cosine = std::abs((dot(inner_direction, outer_direction)) / double(vSize(outer_direction) * vSize(inner_direction))); - // If both lines point in the same/opposite direction check that them being to close is not the end line of one to the start of the other - if (cosine < 0.99 || vSize2(outer_line.back() - inner_line.back()) + EPSILON * EPSILON < vSize2(outer_line.front() - inner_line.front())) - { - coord_t inner_end_to_outer_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), inner_line.back(), outer_line.back())); - if (inner_end_to_outer_distance < min_distance_between_lines && inner_end_to_outer_distance < vSize(outer_line.front() - inner_line.front())) - { - Point2LL new_end_inner - = inner_line.back() + normal(inner_line.front() - inner_line.back(), min_distance_between_lines - inner_end_to_outer_distance); - double error = min_distance_between_lines - sqrt(LinearAlg2D::getDist2FromLineSegment(outer_line.front(), new_end_inner, outer_line.back())); - double error_correction_factor = 1.0 + error / double(min_distance_between_lines - inner_end_to_outer_distance); - new_end_inner - = inner_line.back() - + normal(inner_line.front() - inner_line.back(), (min_distance_between_lines - inner_end_to_outer_distance) * error_correction_factor); - handleNewEnd(cradle_idx_inner, new_end_inner); - } - else - { - coord_t outer_end_to_inner_distance = sqrt(LinearAlg2D::getDist2FromLineSegment(inner_line.front(), outer_line.back(), inner_line.back())); - if (outer_end_to_inner_distance < min_distance_between_lines && outer_end_to_inner_distance < vSize(outer_line.front() - inner_line.front())) - { - Point2LL new_end_outer - = outer_line.back() + normal(outer_line.front() - outer_line.back(), min_distance_between_lines - outer_end_to_inner_distance); - double error = min_distance_between_lines - sqrt(LinearAlg2D::getDistFromLine(new_end_outer, outer_line.front(), outer_line.back())); - double error_correction_factor = 1.0 + error / double(min_distance_between_lines - outer_end_to_inner_distance); - new_end_outer - = outer_line.back() - + normal(outer_line.front() - outer_line.back(), (min_distance_between_lines - outer_end_to_inner_distance) * error_correction_factor); - handleNewEnd(cradle_idx, new_end_outer); - } - } - } - } - } - } - } - }); - - cura::parallel_for( - 1, - cradle_data_.size(), - [&](const LayerIndex layer_idx) - { - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) - { - TreeSupportCradle* cradle = cradle_data_[layer_idx][cradle_idx]; - cradle->verifyLines(); - // As cradle lines (causing lines below to be longer) may have been removed to prevent them intersecting, all cradle lines are now shortened again if required. - for (auto [line_idx, cradle_lines] : cradle->lines_ | ranges::views::enumerate) - { - if (! cradle_lines.empty()) - { - Point2LL line_end = cradle_lines.back().line_.back(); - Point2LL line_front_uppermost = cradle_lines.back().line_.front(); - - if (vSize2(line_end - cradle_lines.back().line_.front()) > cradle_length_ * cradle_length_) - { - coord_t current_cradle_xy_distance = cradle_xy_distance_[cradle_lines.back().layer_idx_ - layer_idx]; - coord_t current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; - cradle_lines.back().line_.back() = line_front_uppermost + normal(line_end - line_front_uppermost, current_cradle_length); - for (auto [up_idx, line] : cradle_lines | ranges::views::enumerate | ranges::views::reverse) - { - Point2LL center = cradle->getCenter(line.layer_idx_); - Point2LL line_back_inner = line.line_.back(); - Point2LL line_front_inner = line.line_.front(); - if (vSize2(line_back_inner - line_front_inner) > cradle_length_ * cradle_length_) - { - // As the center can move there is no guarantee that the point of the current line lies on the line below. - Point2LL projected_line_end = LinearAlg2D::getClosestOnLine(line_end, line.line_.front(), line.line_.back()); - current_cradle_xy_distance = cradle_xy_distance_[line.layer_idx_ - layer_idx]; - current_cradle_length = cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; - if (vSize2(line_front_inner - projected_line_end) > current_cradle_length * current_cradle_length) - { - line.line_.back() = projected_line_end; - } - else - { - line.line_.back() = line_front_inner + normal(line_back_inner - line_front_inner, current_cradle_length); - } - } - line_end = line.line_.back(); - } - } - } - } - } - }); -} - - -void TreeSupportTipGenerator::generateCradleLineAreasAndBase(std::vector>>& shadow_data, std::vector& support_free_areas) -{ - const coord_t min_distance_between_lines - = FUDGE_LENGTH + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - // Some angles needed to move cradle lines outwards to prevent them from toughing. - double center_angle = (2.0 * std::numbers::pi) / double(cradle_line_count_); - double outer_angle = (std::numbers::pi - center_angle) / 2; - coord_t outer_radius = (double(min_distance_between_lines + config_.support_line_width) / sin(center_angle)) * sin(outer_angle); - const coord_t small_hole_size - = EPSILON + (config_.fill_outline_gaps ? config_.min_feature_size / 2 - 5 : config_.min_wall_line_width / 2 - 5); // based on calculation in WallToolPath - std::mutex critical_support_free_areas_and_cradle_areas; - - cura::parallel_for( - 0, - cradle_data_.size(), - [&](const LayerIndex layer_idx) - { - std::vector valid_cradles; - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) - { - TreeSupportCradle& cradle = *cradle_data_[layer_idx][cradle_idx]; - for (size_t cradle_height = 0; cradle_height <= cradle_layers_; cradle_height++) - { - Polygons line_tips; - - std::vector> all_tips_center; - // generate trapezoid line tip with front width of support line width, back cradle_width. - - for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) - { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_height, line_idx); - if (! line_opt) - { - continue; - } - TreeSupportCradleLine* cradle_line = line_opt.value(); - Polygon line = cradle_line->line_; - - coord_t current_cradle_line_width = cradle_line_width_; - - double assumed_half_center_angle = std::numbers::pi / (1.5 * cradle_line_count_); - coord_t triangle_length - = cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - config_.support_line_width) / 2) * tan(std::numbers::pi / 2 - assumed_half_center_angle); - - const coord_t line_length = line.polylineLength(); - if (triangle_length >= line_length + cradle_line_width_) - { - triangle_length = line_length + cradle_line_width_; - current_cradle_line_width = config_.support_line_width + 2 * triangle_length * tan(assumed_half_center_angle); - } - - Point2LL direction = line.back() - line.front(); - Point2LL center_front = line.front() - normal(direction, cradle_line_width_ / 2); - - Point2LL direction_up_center = normal(rotate(direction, std::numbers::pi / 2), config_.support_line_width / 2); - Point2LL center_up = center_front + direction_up_center; - Point2LL center_down = center_front - direction_up_center; - - coord_t tip_shift = 0; - for (auto existing_center : all_tips_center) - { - Point2LL intersect; - bool centers_touch = LinearAlg2D::lineLineIntersection(center_up, center_down, existing_center.first, existing_center.second, intersect) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, existing_center.first, existing_center.second) - && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, center_up, center_down); - if (centers_touch || std::min(vSize(center_down - existing_center.first), vSize(center_up - existing_center.second)) < min_distance_between_lines) - { - // This cradle line is to close to another. - // Move it back todo If line gets smaller than min length => abort - coord_t tip_shift_here = outer_radius - vSize(center_front - cradle.getCenter(cradle_line->layer_idx_)); - tip_shift += tip_shift_here; - center_front = center_front + normal(direction, tip_shift_here); - center_up = center_front + direction_up_center; - center_down = center_front - direction_up_center; - } - } - - Point2LL back_center = center_front + normal(direction, triangle_length); - Point2LL direction_up_back = normal(rotate(direction, std::numbers::pi / 2), current_cradle_line_width / 2); - - Point2LL back_up = back_center + direction_up_back; - Point2LL back_down = back_center - direction_up_back; - - line.front() = back_center + normal(direction, current_cradle_line_width / 2 - FUDGE_LENGTH / 2); - all_tips_center.emplace_back(center_up, center_down); - - Polygon line_tip; - line_tip.add(back_down); - if (current_cradle_line_width == cradle_line_width_) - { - coord_t distance_end_front = line_length - triangle_length + cradle_line_width_ - tip_shift; - Point2LL line_end_down = back_down + normal(direction, distance_end_front); - Point2LL line_end_up = back_up + normal(direction, distance_end_front); - line_tip.add(line_end_down); - line_tip.add(line_end_up); - } - line_tip.add(back_up); - line_tip.add(center_up); - line_tip.add(center_down); - if (line_tip.area() < 0) - { - line_tip.reverse(); - } - cradle_line->area_.add(line_tip); - - Polygons anti_preferred = cradle_line->area_.offset(config_.xy_distance); - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t z_distance_idx = 0; z_distance_idx < config_.z_distance_top_layers; z_distance_idx++) - { - volumes_.addAreaToAntiPreferred(anti_preferred, layer_idx + cradle_height + z_distance_idx); - } - } - } - - Polygons shadow = shadow_data[layer_idx][cradle.shadow_idx_][0]; - Polygons cradle_base = shadow; - - if (! use_fake_roof_ && support_roof_layers_) - { - Polygons cut_line_base; - Polygons first_cradle_areas; - if (large_cradle_base_) - { - // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest - // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just - // the overhang, but that should not cause any issues. - Point2LL min_p = cradle_base.min(); - Point2LL max_p = cradle_base.max(); - Polygons rest_line; - rest_line.addLine(min_p, max_p); - cut_line_base = rest_line.offsetPolyLine(small_hole_size); - } - { - std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); - for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers_; interface_down++) - { - support_free_areas[layer_idx - interface_down].add(cut_line_base); - volumes_.addAreaToAntiPreferred(cradle_base, layer_idx - interface_down); - } - } - if (large_cradle_base_) - { - cradle_base = cradle_base.offset(config_.getRadius(cradle_tip_dtt_), ClipperLib::jtRound); - Polygons center_removed = cradle_base.difference(cut_line_base); - if (center_removed.area() > 1) - { - cradle_base = center_removed; - } - } - else if (cradle_base_roof_) - { - // collect all inner points and connect to center for thin cradle base - Polygons connected_cradle_base; - for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) - { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1, line_idx); - if (line_opt) - { - connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_), line_opt.value()->line_.front()); - } - } - cradle_base = connected_cradle_base.offsetPolyLine(cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); - } - } - - cradle_base = cradle_base.unionPolygons(); - - if (cradle_lines_roof_) - { - Polygons forbidden_here = volumes_.getAvoidance( - 0, - layer_idx, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_); - std::vector> roofs; - roofs.emplace_back(cradle_base, -1); - - for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) - { - std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle_z_distance_layers_ + 1, line_idx); - if (! line_opt) - { - continue; - } - - coord_t line_base_offset = large_cradle_base_ ? std::max(coord_t(0), config_.getRadius(cradle_tip_dtt_) - cradle_line_width_ / 2) : 0; - roofs.emplace_back(line_opt.value()->area_.offset(line_base_offset, ClipperLib::jtRound), line_idx); - } - - - for (auto roof_area_pair : roofs) - { - Polygons roof_area_before = roof_area_pair.first; - Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( - roof_area_pair.first, - roof_outset_, - forbidden_here, - config_.support_line_width, - 0, - 1, - config_.support_line_distance / 2, - &config_.simplifier); - for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) - { - const Polygons forbidden_next = volumes_.getAvoidance( - 0, - layer_idx - (dtt_roof + 1), - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_); - - full_overhang_area = full_overhang_area.difference(forbidden_next); - - if (full_overhang_area.area() > EPSILON && dtt_roof < support_roof_layers_) - { - if (roof_area_pair.second != -1) // If is_line - { - TreeSupportCradleLine roof_base_line(cradle.lines_[roof_area_pair.second].front()); - roof_base_line.area_ = full_overhang_area; - roof_base_line.is_base_ = true; - roof_base_line.layer_idx_ = layer_idx - dtt_roof; - cradle.lines_[roof_area_pair.second].emplace_front(roof_base_line); - } - else - { - if (dtt_roof < cradle.base_below_.size()) - { - cradle.base_below_[dtt_roof].add(full_overhang_area); - } - else - { - cradle.base_below_.emplace_back(full_overhang_area); - } - } - const Polygons forbidden_before = volumes_.getAvoidance( - 0, - layer_idx - dtt_roof, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_); - - Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc( - full_overhang_area, - config_.maximum_move_distance, - forbidden_before, - config_.xy_min_distance + config_.min_feature_size, - 0, - 1, - config_.support_line_distance / 2, - &config_.simplifier); - Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); - if (overhang_part.area() > EPSILON) - { - OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[layer_idx][cradle_idx], layer_idx - dtt_roof, roof_area_pair.second); - cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); - } - } - else - { - if (roof_area_before.area() > 1) - { - LayerIndex line_layer_idx - = roof_area_pair.second < 0 ? LayerIndex(-1) : cradle_data_[layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; - OverhangInformation cradle_overhang(roof_area_before, true, cradle_data_[layer_idx][cradle_idx], line_layer_idx, roof_area_pair.second); - cradle.overhang_[layer_idx - dtt_roof].emplace_back(cradle_overhang); - } - break; - } - roof_area_before = full_overhang_area; - } - } - } - else - { - Polygons forbidden_here = volumes_.getAvoidance( - 0, - layer_idx, - (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, - config_.support_rests_on_model, - ! xy_overrides_); - - coord_t offset_radius = config_.getRadius(cradle_tip_dtt_); - if (cradle_base.offset(offset_radius, ClipperLib::jtRound).difference(forbidden_here).area() > 1) - { - OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[layer_idx][cradle_idx]); - cradle.overhang_[layer_idx].emplace_back(cradle_overhang); - } - - for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) - { - if (! cradle.lines_[line_idx].empty()) - { - LayerIndex support_cradle_on_layer_idx = cradle.lines_[line_idx].front().layer_idx_ - (cradle_z_distance_layers_ + 1); - OverhangInformation line_overhang( - cradle.lines_[line_idx].front().area_, - false, - cradle_data_[layer_idx][cradle_idx], - cradle.lines_[line_idx].front().layer_idx_, - line_idx); - cradle.overhang_[support_cradle_on_layer_idx].emplace_back(line_overhang); - } - } - } - if (! cradle.overhang_.empty()) - { - valid_cradles.emplace_back(cradle_data_[layer_idx][cradle_idx]); - } - else - { - delete cradle_data_[layer_idx][cradle_idx]; - } - } - cradle_data_[layer_idx] = valid_cradles; - }); -} - - -void TreeSupportTipGenerator::generateCradle(const SliceMeshStorage& mesh, std::vector& support_free_areas) -{ - calculateFloatingParts(mesh); - - - // todo move up cradle if no line contacts model - // todo generate additional overhang if model may bend... Is this a TreeSupport Feature though? - - std::vector>> shadow_data = generateCradleCenters(mesh); - generateCradleLines(shadow_data); - cleanCradleLineOverlaps(); - generateCradleLineAreasAndBase(shadow_data, support_free_areas); -} - void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, std::vector& result, bool roof) { std::mutex critical; @@ -1618,7 +470,7 @@ void TreeSupportTipGenerator::dropOverhangAreas(const SliceMeshStorage& mesh, st }); } -void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& mesh) +void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& mesh, std::vector>& cradle_data) { std::vector potential_support_roofs(mesh.overhang_areas.size(), Polygons()); std::mutex critical_potential_support_roofs; @@ -1629,19 +481,19 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m dropOverhangAreas(mesh, dropped_overhangs, true); } - std::vector all_cradle_areas(cradle_data_.size()); - for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) + std::vector all_cradle_areas(cradle_data.size()); + for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (auto [line_idx, lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) + for (auto [line_idx, lines] : cradle_data[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) { for (auto [height_idx, line] : lines | ranges::views::enumerate) { all_cradle_areas[line.layer_idx_].add(line.area_); } } - for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { all_cradle_areas[layer_idx - base_idx].add(base); } @@ -1891,6 +743,15 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( } } + if (overhang_data.is_cradle_ && tip_radius < overhang_data.cradle_->config_->cradle_support_base_area_radius_) + { + if ((overhang_data.isCradleLine() && large_cradle_line_tips_) || (! overhang_data.isCradleLine() && (! overhang_data.is_roof_ || large_cradle_line_tips_))) + { + tip_radius = overhang_data.cradle_->config_->cradle_support_base_area_radius_; + } + } + + size_t tip_dtt = tip_radius >= config_.branch_radius ? config_.tip_layers : (config_.tip_layers * (tip_radius - config_.min_radius)) / (config_.branch_radius - config_.min_radius); @@ -1908,7 +769,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( roof_type = TipRoofType::SUPPORTS_ROOF; } - if (! overhang_data.is_roof_ && overhang_data.is_cradle_ && ! overhang_data.isCradleLine() && cradle_base_roof_) + if (! overhang_data.is_roof_ && overhang_data.is_cradle_ && ! overhang_data.isCradleLine() && overhang_data.cradle_->is_roof_) { roof_type = TipRoofType::IS_ROOF; // No information about the amount of missing roof layers for cradle bases is stored. @@ -1917,13 +778,6 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( dont_move_until = 1; } - if (overhang_data.is_cradle_ && tip_dtt < cradle_tip_dtt_) - { - if ((overhang_data.isCradleLine() && large_cradle_line_tips_) || (! overhang_data.isCradleLine() && (! overhang_data.is_roof_ || large_cradle_line_tips_))) - { - tip_dtt = cradle_tip_dtt_; - } - } TreeSupportElement* elem = addPointAsInfluenceArea( move_bounds, @@ -1949,10 +803,10 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( } -void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage) +void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data) { std::vector all_cradle_roofs(storage.support.supportLayers.size()); - for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) + for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) { for (auto cradle : cradles) { @@ -2020,7 +874,7 @@ void TreeSupportTipGenerator::generateTips( std::vector>& move_bounds, std::vector>& placed_fake_roof_areas, std::vector& support_free_areas, - std::vector>& cradle_data_export) + std::vector>& cradle_data) { const auto t_start = std::chrono::high_resolution_clock::now(); std::vector> new_tips(move_bounds.size()); @@ -2036,38 +890,47 @@ void TreeSupportTipGenerator::generateTips( // ^^^ Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is // better than not supporting it at all. - if (cradle_layers_ > 0) + + if(cradle_data.size() < mesh.overhang_areas.size()) { - generateCradle(mesh, support_free_areas); + cradle_data.resize(mesh.overhang_areas.size()); } - const auto t_cradle = std::chrono::high_resolution_clock::now(); if (support_roof_layers_) { - calculateRoofAreas(mesh); + calculateRoofAreas(mesh, cradle_data); } const auto t_roof = std::chrono::high_resolution_clock::now(); - std::vector> all_cradles_requiring_support(move_bounds.size()); - std::vector all_cradle_areas(move_bounds.size()); - for (LayerIndex layer_idx = 0; layer_idx < cradle_data_.size(); layer_idx++) + std::vector> all_cradles_requiring_support(cradle_data.size()); + std::vector all_cradle_areas(cradle_data.size()); + for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { - for (size_t cradle_idx = 0; cradle_idx < cradle_data_[layer_idx].size(); cradle_idx++) + for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) { - for (auto overhang_pair : cradle_data_[layer_idx][cradle_idx]->overhang_) + for (auto overhang_pair : cradle_data[layer_idx][cradle_idx]->overhang_) { - all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data_[layer_idx][cradle_idx]); + if(all_cradles_requiring_support.size() <= overhang_pair.first) //should not happen, but just to be sure + { + all_cradles_requiring_support.resize(overhang_pair.first+1); + } + all_cradles_requiring_support[overhang_pair.first].emplace_back(cradle_data[layer_idx][cradle_idx]); } - for (auto [line_idx, lines] : cradle_data_[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) + for (auto [line_idx, lines] : cradle_data[layer_idx][cradle_idx]->lines_ | ranges::views::enumerate) { for (auto [height_idx, line] : lines | ranges::views::enumerate) { + if(all_cradle_areas.size() <= line.layer_idx_) //should not happen, but just to be sure + { + all_cradle_areas.resize(line.layer_idx_+1); + } + all_cradle_areas[line.layer_idx_].add(line.area_); } } - for (auto [base_idx, base] : cradle_data_[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) + for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { all_cradle_areas[layer_idx - base_idx].add(base); } @@ -2372,7 +1235,7 @@ void TreeSupportTipGenerator::generateTips( polylines = TreeSupportUtils::movePointsOutside( polylines, relevant_forbidden, - (overhang_data.isCradleLine() ? cradle_line_width_ / 2 : 0) + config_.getRadius(0) + FUDGE_LENGTH / 2); + (overhang_data.isCradleLine() ? overhang_data.cradle_->config_->cradle_line_width_ / 2 : 0) + config_.getRadius(0) + FUDGE_LENGTH / 2); } overhang_lines = convertLinesToInternal(polylines, layer_idx); @@ -2474,14 +1337,8 @@ void TreeSupportTipGenerator::generateTips( } }); - cradle_data_export.resize(cradle_data_.size()); - - for (auto [layer_idx, cradles] : cradle_data_ | ranges::views::enumerate) - { - cradle_data_export[layer_idx] = cradles; - } - removeUselessAddedPoints(new_tips, storage); + removeUselessAddedPoints(new_tips, storage, cradle_data); for (auto [layer_idx, tips_on_layer] : new_tips | ranges::views::enumerate) { @@ -2489,13 +1346,11 @@ void TreeSupportTipGenerator::generateTips( } const auto t_end = std::chrono::high_resolution_clock::now(); - const auto dur_cradle = 0.001 * std::chrono::duration_cast(t_cradle - t_start).count(); - const auto dur_roof = 0.001 * std::chrono::duration_cast(t_roof - t_cradle).count(); + const auto dur_roof = 0.001 * std::chrono::duration_cast(t_roof - t_start).count(); const auto dur_inf = 0.001 * std::chrono::duration_cast(t_influenced - t_roof).count(); const auto dur_clean = 0.001 * std::chrono::duration_cast(t_end - t_influenced).count(); spdlog::info( - "Subtasks of generateInitialAreas: Generating cradle: {} ms Generating roof: {} ms Creating Influence Areas: {} ms Checking result validity: {} ms ", - dur_cradle, + "Subtasks of generateInitialAreas: Generating roof: {} ms Creating Influence Areas: {} ms Checking result validity: {} ms ", dur_roof, dur_inf, dur_clean); From 2cec5777ce387e86e579fa7effa44369c497b77a Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 2 May 2024 03:30:32 +0200 Subject: [PATCH 078/101] Fix roof cradle base calculation creating non-supportable bases --- src/TreeSupportCradle.cpp | 20 ++++++++++++++++---- src/TreeSupportTipGenerator.cpp | 3 +-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index c17db56f61..595147b441 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -1024,7 +1024,11 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor support_rests_on_model, ! xy_overrides); std::vector> roofs; - roofs.emplace_back(cradle_base, -1); + + if(cradle.is_roof_) + { + roofs.emplace_back(cradle_base, -1); + } for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { @@ -1108,7 +1112,12 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } else { - if (roof_area_before.area() > 1) + + if(dtt_roof == 0 && roof_area_pair.second < 0) // There was no roof base! + { + cradle.is_roof_ = false; //Try a regular base + } + else if (roof_area_before.area() > 1) { LayerIndex line_layer_idx = roof_area_pair.second < 0 ? LayerIndex(-1) : cradle_data_[mesh_idx][layer_idx][cradle_idx]->lines_[roof_area_pair.second].front().layer_idx_; @@ -1121,7 +1130,8 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } } } - else + + if(!cradle.is_roof_) { Polygons forbidden_here = volumes_.getAvoidance( 0, @@ -1136,7 +1146,9 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[mesh_idx][layer_idx][cradle_idx]); cradle.overhang_[layer_idx].emplace_back(cradle_overhang); } - + } + if(!cradle.config_->cradle_lines_roof_) + { for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { if (! cradle.lines_[line_idx].empty()) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 422d8d6a01..ff04554b02 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -597,8 +597,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m { // Roof does not have a radius, so remove it using offset. Note that there is no 0 radius avoidance, and it would not be identical with the avoidance offset with // -radius. This is intentional here, as support roof is still valid if only a part of the tip may reach it. - Polygons forbidden_here = volumes_ - .getAvoidance( + Polygons forbidden_here = volumes_.getAvoidance( 0, layer_idx, (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, From a79c0ded84d70e255cb045e98abeed883e030eb5 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 2 May 2024 03:39:22 +0200 Subject: [PATCH 079/101] Fix rounding errors in handleCradleLineValidity. --- src/TreeSupport.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 64ce955889..697f84ff57 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -1636,8 +1636,8 @@ void TreeSupport::handleCradleLineValidity( if (next_relevant_influence.area() > EPSILON) { - relevant_influence = next_relevant_influence; - full_influence = full_influence.difference(cradle_influence).unionPolygons(relevant_influence); + relevant_influence = TreeSupportUtils::safeUnion(next_relevant_influence); + full_influence = TreeSupportUtils::safeUnion(full_influence.difference(cradle_influence), relevant_influence); } else { From b07e7fd01a721b9cd28323eae8ceb7054dbd2b50 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 2 May 2024 06:24:37 +0200 Subject: [PATCH 080/101] Fix support of cradle lines by fixing the reduction of overshoot of tips below it. --- src/TreeSupportTipGenerator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index ff04554b02..a7f69fabec 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -750,8 +750,6 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( } } - - size_t tip_dtt = tip_radius >= config_.branch_radius ? config_.tip_layers : (config_.tip_layers * (tip_radius - config_.min_radius)) / (config_.branch_radius - config_.min_radius); @@ -1178,14 +1176,15 @@ void TreeSupportTipGenerator::generateTips( bool only_lines = true; Polygons polylines; // The tip positions are determined here. - if (overhang_data.isCradleLine()) + if (overhang_data.isCradleLine() && + overhang_data.cradle_->config_->cradle_line_width_ < std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_)) { std::optional cradle_line_opt = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); if(cradle_line_opt) { Polygons line = cradle_line_opt.value()->line_.offset(0); - polylines = ensureMaximumDistancePolyline(line, ! overhang_data.is_roof_ ? config_.min_radius * 2 : support_supporting_branch_distance_, 1, false); + polylines = ensureMaximumDistancePolyline(line, current_tip_radius * 2, 2, false); } } else @@ -1206,7 +1205,7 @@ void TreeSupportTipGenerator::generateTips( // but it ensures consistent behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. // This is not done when a roof is above as the roof will support the model and the trees only need to support the roof - if (polylines.pointCount() <= min_support_points) + if (polylines.pointCount() <= min_support_points && (!overhang_data.isCradleLine() || polylines.pointCount() < 2)) { only_lines = false; // Add the outer wall (of the overhang) to ensure it is correct supported instead. From 9aa2fad59bbbc27cc8569dc646a118004bd64764 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 2 May 2024 19:05:58 +0200 Subject: [PATCH 081/101] Fix cradle roof base z distance if no fractional roof is present and fix xy distance for missing lines because of cradle z distance. --- src/TreeSupport.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 697f84ff57..460157d0d5 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2399,6 +2399,7 @@ void TreeSupport::generateSupportSkin( 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. + std::vector missing_cradle_line_xy_distance_areas(support_layer_storage.size()); // All missing (because of cradle z distance) cradle lines offset by xy distance. std::mutex critical_cradle_line_xy_distance_areas; std::mutex critical_cradle_support_line_areas; @@ -2419,16 +2420,21 @@ void TreeSupport::generateSupportSkin( std::lock_guard critical_section_cradle(critical_support_roof_storage); cradle_base_areas[layer_idx - base_idx].add(base); (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx - base_idx].add(base); - if (base_idx == 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) + if (base_idx == 0 && config.z_distance_top % config.layer_height != 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) { (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx + 1].add(base); } } else { + // Dead code. Currently, Cradles that are not roofs do not have a base area, just a tip. This is just here for the case that this changes std::lock_guard critical_section_cradle(critical_support_layer_storage); cradle_base_areas[layer_idx - base_idx].add(base); support_layer_storage[layer_idx - base_idx].add(base); + if (base_idx == 0 && config.z_distance_top % config.layer_height != 0 && layer_idx + 1 < support_layer_storage_fractional.size()) + { + support_layer_storage_fractional[layer_idx + 1].add(base); + } } } @@ -2444,7 +2450,9 @@ void TreeSupport::generateSupportSkin( bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; - if (! is_base) + bool was_line_above = height_idx + 1 < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() && + ! cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx + 1].is_base_; + if (was_line_above) { for (LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) { @@ -2458,7 +2466,7 @@ void TreeSupport::generateSupportSkin( config.min_feature_size, &config.simplifier); std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); - cradle_line_xy_distance_areas[xy_dist_layer_idx].add(line_areas); + missing_cradle_line_xy_distance_areas[xy_dist_layer_idx].add(line_areas); } } @@ -2527,6 +2535,7 @@ void TreeSupport::generateSupportSkin( Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; + remove_from_support.add(missing_cradle_line_xy_distance_areas[layer_idx]); remove_from_support.add(fake_roof_lines); remove_from_support.add(support_free_areas[layer_idx]); remove_from_support = remove_from_support.unionPolygons(); @@ -2600,6 +2609,9 @@ void TreeSupport::generateSupportSkin( } remove_from_next_roof = remove_from_next_roof.unionPolygons(); + Polygons remove_from_next_fractional_roof = remove_from_next_roof; + remove_from_next_roof = remove_from_next_roof.unionPolygons(missing_cradle_line_xy_distance_areas[layer_idx]); + Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); Polygons roof = support_roof_storage[layer_idx]; Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; @@ -2617,14 +2629,14 @@ void TreeSupport::generateSupportSkin( storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); - remove_from_next_roof.add(roof_extra_wall); - remove_from_next_roof.add(roof); - remove_from_next_roof = remove_from_next_roof.unionPolygons(); + remove_from_next_fractional_roof.add(roof_extra_wall); + remove_from_next_fractional_roof.add(roof); + remove_from_next_fractional_roof = remove_from_next_fractional_roof.unionPolygons(); - Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_roof); + Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof); storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, config.support_roof_line_width, config.support_wall_count, true); - Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_roof.unionPolygons(fractional_roof_extra_wall)); + 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); }); From 5efba207856a9608a767c36616350bceec85731d Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 2 May 2024 21:11:38 +0200 Subject: [PATCH 082/101] Fix tip density for cradle lines if cradle tips size is small. --- src/TreeSupportTipGenerator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index a7f69fabec..0259eb01a7 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1177,14 +1177,15 @@ void TreeSupportTipGenerator::generateTips( Polygons polylines; // The tip positions are determined here. if (overhang_data.isCradleLine() && - overhang_data.cradle_->config_->cradle_line_width_ < std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_)) + overhang_data.cradle_->config_->cradle_line_width_ / 2 < std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_)) { std::optional cradle_line_opt = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); if(cradle_line_opt) { Polygons line = cradle_line_opt.value()->line_.offset(0); - polylines = ensureMaximumDistancePolyline(line, current_tip_radius * 2, 2, false); + coord_t cradle_line_tip_radius = std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_); + polylines = ensureMaximumDistancePolyline(line, cradle_line_tip_radius, 2, false); } } else @@ -1192,9 +1193,8 @@ void TreeSupportTipGenerator::generateTips( // todo can cause inconsistent support density if a line exactly aligns with the model polylines = ensureMaximumDistancePolyline( generateLines(overhang_outset, overhang_data.is_roof_, layer_idx + overhang_data.is_roof_), - ! overhang_data.is_roof_ ? config_.min_radius * 2 - : use_fake_roof_ ? support_supporting_branch_distance_ - : connect_length, + ! overhang_data.is_roof_ ? current_tip_radius * 2 + : (use_fake_roof_ && ! overhang_data.isCradleLine() ? support_supporting_branch_distance_ : connect_length), 1, false); } From fbbf9fa2bd107d757193412920ef935f830d4a8e Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 3 Jun 2024 02:02:57 +0200 Subject: [PATCH 083/101] Fix roof generating for areas that are handled by cradle bases or lines. Prevents some kind of overlapping roof lines. --- src/TreeSupportTipGenerator.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/TreeSupportTipGenerator.cpp b/src/TreeSupportTipGenerator.cpp index 0259eb01a7..dd99db7e4f 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -481,7 +481,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m dropOverhangAreas(mesh, dropped_overhangs, true); } - std::vector all_cradle_areas(cradle_data.size()); + std::vector all_cradle_areas(cradle_data.size()); // All cradle areas. Later offset by min xy distance. for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) @@ -505,6 +505,10 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m mesh.overhang_areas.size() - z_distance_delta_, [&](const LayerIndex layer_idx) { + + // Needed for fuzzy roof search below. + all_cradle_areas[layer_idx] = all_cradle_areas[layer_idx].unionPolygons().offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons(); + if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty()) { return; // This is a continue if imagined in a loop context. @@ -533,7 +537,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m &config_.simplifier); // If an area is already supported by cradle don't put roof there. - full_overhang_area = full_overhang_area.difference(all_cradle_areas[layer_idx].unionPolygons().offset(config_.xy_distance + FUDGE_LENGTH).unionPolygons()); + full_overhang_area = full_overhang_area.difference(all_cradle_areas[layer_idx]); for (LayerIndex dtt_roof = 0; dtt_roof < support_roof_layers_ && layer_idx - dtt_roof >= 1; dtt_roof++) { @@ -602,7 +606,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m layer_idx, (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, config_.support_rests_on_model, - ! xy_overrides_); + ! xy_overrides_).unionPolygons(all_cradle_areas[layer_idx]); if (! force_minimum_roof_area_) { From 9d1ebf21f6d4ceb8aa34942e534a259ae02f6799 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 3 May 2024 02:18:26 +0200 Subject: [PATCH 084/101] Ensure that cradle lines can not intersect with already added roof. Prevents overlapping roof lines. --- src/TreeSupport.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 460157d0d5..1f4fbc2530 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2600,9 +2600,14 @@ void TreeSupport::generateSupportSkin( break; } } - Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); + Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); + Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); + //Remove only already added roof from line areas. Should not be needed, but better safe than sorry. + cradle_lines_roof = cradle_lines_roof.difference(remove_from_next_roof); + cradle_support_line_areas[layer_idx] = cradle_support_line_areas[layer_idx].unionPolygons().difference(remove_from_next_roof); + //Collect remaining parts that non cradle line roof areas may not intersect with. + remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); if (! support_free_areas[layer_idx].empty()) { remove_from_next_roof.add(support_free_areas[layer_idx]); @@ -2614,7 +2619,6 @@ void TreeSupport::generateSupportSkin( Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); Polygons roof = support_roof_storage[layer_idx]; - Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx]; if (config.support_roof_wall_count) { roof = roof.difference(remove_from_next_roof); From 60d8b9585bcbf7133e38902f3511a32269eafbd9 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 3 May 2024 02:54:56 +0200 Subject: [PATCH 085/101] Fix guaranteed line below large cradle base. --- src/TreeSupport.cpp | 8 ++++---- src/TreeSupportCradle.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 1f4fbc2530..c67f8efe22 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2602,16 +2602,16 @@ void TreeSupport::generateSupportSkin( } Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); + if (! support_free_areas[layer_idx].empty()) + { + remove_from_next_roof.add(support_free_areas[layer_idx]); + } //Remove only already added roof from line areas. Should not be needed, but better safe than sorry. cradle_lines_roof = cradle_lines_roof.difference(remove_from_next_roof); cradle_support_line_areas[layer_idx] = cradle_support_line_areas[layer_idx].unionPolygons().difference(remove_from_next_roof); //Collect remaining parts that non cradle line roof areas may not intersect with. remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); - if (! support_free_areas[layer_idx].empty()) - { - remove_from_next_roof.add(support_free_areas[layer_idx]); - } remove_from_next_roof = remove_from_next_roof.unionPolygons(); Polygons remove_from_next_fractional_roof = remove_from_next_roof; diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 595147b441..5feb9ea519 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -1226,7 +1226,7 @@ void SupportCradleGeneration::pushCradleData(std::vector Date: Fri, 24 May 2024 16:17:32 +0200 Subject: [PATCH 086/101] Fix missing cradle lines if cradle line count is odd --- include/TreeSupportCradle.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 54e141908f..f7ab09819d 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -295,7 +295,12 @@ struct TreeSupportCradle { Point2LL current_direction = line_end - getCenter(layer_idx_req); double angle = std::atan2(current_direction.Y, current_direction.X); - size_t angle_idx = size_t(std::round(((angle + std::numbers::pi) / (2.0 * std::numbers::pi)) * double(config_->cradle_line_count_))) % config_->cradle_line_count_; + if(angle < 0) + { + angle = 2.0 * std::numbers::pi + angle; + } + double step_size = (2.0 * std::numbers::pi) / double(config_->cradle_line_count_); + size_t angle_idx = size_t(std::round(angle / step_size)) % config_->cradle_line_count_; return angle_idx; } From ae2553271a89584285a616413f32092770ef07b8 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 3 Jun 2024 02:22:12 +0200 Subject: [PATCH 087/101] Fix issue causing support lines to overlap with the model --- src/TreeSupport.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index c67f8efe22..3eb56f3a60 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2888,6 +2888,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora 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); @@ -2910,6 +2911,10 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora { 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) From fba4d7b9693ce78ea41fc82376c1c8ff988fc473 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 3 Jun 2024 02:27:11 +0200 Subject: [PATCH 088/101] Fix issue causing support skin lines to overlap with the model --- src/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 3eb56f3a60..e2815e7713 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2738,7 +2738,7 @@ void TreeSupport::generateSupportSkin( for (Polygons part : support_on_layer.splitIntoParts()) { - Polygons part_outline = part.getOutsidePolygons(); + Polygons part_outline = part.getOutsidePolygons().difference(volumes_.getCollision(0, layer_idx - support_skin_ctr, true)); if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area, AABB(part_outline)).empty()) { // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. 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 089/101] 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); From 11b4a76214db2c8cbc66db22f763c979737f0e58 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 4 Jun 2024 03:53:34 +0200 Subject: [PATCH 090/101] Fix regression causing hole removal to not work correctly. --- src/TreeSupport.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 8bf3eab511..514db163ef 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2980,14 +2980,6 @@ void TreeSupport::removeFloatingHoles(std::vector& support_layer_stora 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::unordered_set removed_holes_by_idx; 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 @@ -3046,9 +3038,8 @@ void TreeSupport::removeFloatingHoles(std::vector& support_layer_stora return; } - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); - reversePolygon(valid_holes_areas[layer_idx]); - support_layer_storage[layer_idx].add(valid_holes_areas[layer_idx]); + //Because the support_layer_storage could be modified (e.g. because the holes are now skin), just adding back the reversed holes is no longer working. Need to do a real difference instead. + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons().difference(valid_holes_areas[layer_idx]); }); } From 6664a4f96b0b518b1a2bbfd172c35205d74fe291 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:07:19 +0200 Subject: [PATCH 091/101] Change non-removable hole detection, to prevent potential issues with nested holes. --- src/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 514db163ef..d458219ca1 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2727,7 +2727,7 @@ void TreeSupport::calculateSupportHoles(std::vector& support_layer_sto } else { - if(hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).area() > hole.polygonLength() * EPSILON) + if(!hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).offset(- config.xy_min_distance / 2).empty()) { 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 } From 5e03048e4a78cf9a1d83b22f261be84c2f399de7 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:04:11 +0200 Subject: [PATCH 092/101] Fix crash occurring when cradle z distance is smaller than z distance. --- src/TreeSupportCradle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 5feb9ea519..4eb637a0ee 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -238,7 +238,7 @@ std::vector> SupportCradleGeneration::generateCr continue; } - std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + z_distance_delta_, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + cradle_config->cradle_z_distance_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); std::vector all_pointy_idx{ pointy_info.index }; Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); @@ -337,7 +337,7 @@ std::vector> SupportCradleGeneration::generateCr // To reduce the impact an area is estimated where the cradle should be for these areas. Polygons previous_area = shadow; for (size_t cradle_up_layer_z_distance = cradle_up_layer; - cradle_up_layer_z_distance < std::min(cradle_up_layer + z_distance_delta_ - 1, accumulated_model.size() - z_distance_top_layers); + cradle_up_layer_z_distance < std::min(cradle_up_layer + cradle_config->cradle_z_distance_layers_, accumulated_model.size() - z_distance_top_layers); cradle_up_layer_z_distance++) { accumulated_model[cradle_up_layer_z_distance + z_distance_top_layers] = unsupported_model[cradle_up_layer_z_distance].unionPolygons(); From c0e46cb1776bfe9e0f0e1c92cfd94b9ff2df9473 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:39:13 +0200 Subject: [PATCH 093/101] Adapt to new Shape and LinesSets after merge. Also add back all functions that were lost by the merge (because they were in the poylgons class) --- include/FffGcodeWriter.h | 1 + include/TreeModelVolumes.h | 18 +- include/TreeSupport.h | 40 ++-- include/TreeSupportCradle.h | 37 ++-- include/geometry/LinesSet.h | 3 +- include/geometry/Shape.h | 14 ++ include/sliceDataStorage.h | 12 +- include/utils/AABB.h | 4 + src/FffGcodeWriter.cpp | 4 +- src/PrimeTower/PrimeTower.cpp | 3 +- src/SkirtBrim.cpp | 6 +- src/TreeModelVolumes.cpp | 62 +++--- src/TreeSupport.cpp | 323 ++++++++++++++++---------------- src/TreeSupportCradle.cpp | 179 +++++++++--------- src/TreeSupportTipGenerator.cpp | 47 ++--- src/bridge.cpp | 2 +- src/geometry/LinesSet.cpp | 10 +- src/geometry/Shape.cpp | 47 +++++ src/sliceDataStorage.cpp | 2 +- src/support.cpp | 2 +- src/utils/AABB.cpp | 18 ++ 21 files changed, 457 insertions(+), 377 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index e5dcd5b0a7..45be16e44b 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -11,6 +11,7 @@ #include "FanSpeedLayerTime.h" #include "GCodePathConfig.h" #include "LayerPlanBuffer.h" +#include "SupportInfillPart.h" #include "gcodeExport.h" #include "utils/LayerVector.h" #include "utils/NoCopy.h" diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index e0fd70034f..8236eaab0a 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -159,7 +159,7 @@ class TreeModelVolumes * \param area The area that should be avoided in the future. * \param layer_idx The layer said area is on. */ - void addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx); + void addAreaToAntiPreferred(const Shape area, LayerIndex layer_idx); void precalculateAntiPreferred(); @@ -169,7 +169,7 @@ class TreeModelVolumes * \param radius The radius of the node of interest. * \returns The area that should be avoided */ - const Polygons& getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius); + const Shape& getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius); /*! * \brief Get avoidance areas for areas that were additionally set to be avoided @@ -177,14 +177,14 @@ class TreeModelVolumes * \param radius The radius of the node of interest. * \returns The area that should be avoided */ - const Polygons& getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist); + const Shape& getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist); /*! * \brief Get areas that were are classified as support blocker * \param layer_idx The layer said area is on. * \returns The area should not contain support */ - const Polygons& getSupportBlocker(LayerIndex layer_idx); + const Shape& getSupportBlocker(LayerIndex layer_idx); private: @@ -585,12 +585,12 @@ class TreeModelVolumes mutable std::unordered_map wall_restrictions_cache_min_; std::unique_ptr critical_wall_restrictions_cache_min_ = std::make_unique(); - mutable std::unordered_map anti_preferred_; + mutable std::unordered_map anti_preferred_; std::unique_ptr critical_anti_preferred_ = std::make_unique(); - mutable std::unordered_map anti_preferred_cache_; - mutable std::unordered_map anti_preferred_cache_to_model_; - mutable std::unordered_map anti_preferred_cache_collision; + mutable std::unordered_map anti_preferred_cache_; + mutable std::unordered_map anti_preferred_cache_to_model_; + mutable std::unordered_map anti_preferred_cache_collision; std::unique_ptr critical_anti_preferred_caches = std::make_unique(); @@ -598,7 +598,7 @@ class TreeModelVolumes Simplify simplifier_ = Simplify(0, 0, 0); // a simplifier to simplify polygons. Will be properly initialised in the constructor. - Polygons empty_polygon = Polygons(); + Shape empty_polygon = Shape(); }; } // namespace cura diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 75e477aa70..e89d51f66d 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -337,15 +337,15 @@ class TreeSupport * \param cradle_data[in] All currently existing cradles, with its corresponding cradle lines. */ void prepareSupportAreas( - std::vector& support_layer_storage, - std::vector& support_layer_storage_fractional, - 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, + std::vector& support_layer_storage, + std::vector& support_layer_storage_fractional, + 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>& cradle_data); @@ -357,8 +357,8 @@ class TreeSupport * \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, + 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); @@ -380,17 +380,17 @@ class TreeSupport * \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on. */ 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& 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::vector>& layer_tree_polygons); /*! * \brief Filters out holes that would cause support to be printed mid-air. @@ -401,7 +401,7 @@ class TreeSupport * \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>& hole_parts, std::vector>& valid_holes, std::vector>& non_removable_holes, std::vector>>& hole_rest_map); @@ -442,7 +442,7 @@ class TreeSupport /*! * \brief Areas where no support may be. Areas will be subtracted from support areas. */ - std::vector support_free_areas; + std::vector support_free_areas; /*! * \brief Generator for model collision, avoidance and internal guide volumes. diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index f7ab09819d..4560b77fbd 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -7,7 +7,6 @@ #include "settings/types/LayerIndex.h" #include "sliceDataStorage.h" #include "utils/Coord_t.h" -#include "utils/polygon.h" #include "TreeModelVolumes.h" #include "TreeSupportEnums.h" @@ -18,7 +17,7 @@ struct TreeSupportCradle; struct OverhangInformation { - OverhangInformation(Polygons overhang, bool roof) + OverhangInformation(Shape overhang, bool roof) : overhang_(overhang) , is_roof_(roof) , is_cradle_(false) @@ -28,7 +27,7 @@ struct OverhangInformation { } - OverhangInformation(Polygons overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1, int32_t cradle_line_idx = -1) + OverhangInformation(Shape overhang, bool roof, TreeSupportCradle* cradle, int32_t cradle_layer_idx = -1, int32_t cradle_line_idx = -1) : overhang_(overhang) , is_roof_(roof) , is_cradle_(true) @@ -38,7 +37,7 @@ struct OverhangInformation { } - Polygons overhang_; + Shape overhang_; bool is_roof_; bool is_cradle_; int32_t cradle_layer_idx_; @@ -58,25 +57,25 @@ struct TreeSupportCradleLine spdlog::error("Dummy TreeSupportCradleLine constructor called"); } - TreeSupportCradleLine(Polygon line, LayerIndex layer_idx, bool is_roof) + TreeSupportCradleLine(OpenPolyline line, LayerIndex layer_idx, bool is_roof) : line_(line) , layer_idx_(layer_idx) , is_roof_(is_roof) { } - Polygons area_; - Polygon line_; - Polygon removed_line_; + Shape area_; + OpenPolyline line_; + OpenPolyline removed_line_; LayerIndex layer_idx_; bool is_base_ = false; bool is_roof_; - void addLineToRemoved(Polygon& line_to_add) + void addLineToRemoved(OpenPolyline& line_to_add) { if (removed_line_.empty()) { - removed_line_.add(line_to_add.front()); - removed_line_.add(line_to_add.back()); + removed_line_.push_back(line_to_add.front()); + removed_line_.push_back(line_to_add.back()); } else { @@ -243,9 +242,9 @@ struct TreeSupportCradle std::vector> lines_; bool is_roof_; LayerIndex layer_idx_; - std::vector base_below_; + std::vector base_below_; std::vector centers_; - std::vector shadow_; + std::vector shadow_; std::unordered_map> overhang_; const std::shared_ptr config_; @@ -321,7 +320,7 @@ struct TreeSupportCradle { previous_layer_idx = lines_[line_idx][up_idx].layer_idx_; if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 - || lines_[line_idx][up_idx].line_.polylineLength() < config_->cradle_length_min_) + || lines_[line_idx][up_idx].line_.length() < config_->cradle_length_min_) { lines_[line_idx].clear(); } @@ -333,7 +332,7 @@ struct TreeSupportCradle if (! lines_[line_idx][up_idx].is_base_) { if (lines_[line_idx][up_idx].layer_idx_ > previous_layer_idx + up_idx || lines_[line_idx][up_idx].line_.size() < 2 - || lines_[line_idx][up_idx].line_.polylineLength() < config_->cradle_length_min_) + || lines_[line_idx][up_idx].line_.length() < config_->cradle_length_min_) { if (up_idx <= config_->cradle_layers_min_) { @@ -406,7 +405,7 @@ class SupportCradleGeneration * \param support_free_areas[out] Areas where support should be removed to ensure the pointy overhang to supported. * \param mesh_idx[in] The idx of the mesh for which the cradles are retrieved. */ - void pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx); + void pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx); SupportCradleGeneration(const SliceDataStorage& storage, TreeModelVolumes& volumes_); @@ -414,14 +413,14 @@ class SupportCradleGeneration struct UnsupportedAreaInformation { - UnsupportedAreaInformation(const Polygons area, size_t index, size_t height, coord_t accumulated_supportable_overhang) + UnsupportedAreaInformation(const Shape area, size_t index, size_t height, coord_t accumulated_supportable_overhang) : area{ area } , index{ index } , height{ height } , accumulated_supportable_overhang{ accumulated_supportable_overhang } { } - const Polygons area; + const Shape area; size_t index; size_t height; coord_t accumulated_supportable_overhang; @@ -492,7 +491,7 @@ std::vector>> cradle_data_; /*! * \brief Representation of areas that have to be removed to ensure lines below the pointy overhang. */ -std::vector support_free_areas_; +std::vector support_free_areas_; /*! * \brief Generator for model collision, avoidance and internal guide volumes. diff --git a/include/geometry/LinesSet.h b/include/geometry/LinesSet.h index 7834b821eb..bb444f52cc 100644 --- a/include/geometry/LinesSet.h +++ b/include/geometry/LinesSet.h @@ -255,9 +255,10 @@ class LinesSet * not the shape. * \param outer_offset Offset relative to the original shape-outline towards the outside of the * shape. Comparable to normal offset. + * \param jt JoinType for the offsets. * \return The resulting polygons. */ - [[nodiscard]] Shape createTubeShape(const coord_t inner_offset, const coord_t outer_offset) const; + [[nodiscard]] Shape createTubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt = ClipperLib::jtMiter) const; void translate(const Point2LL& delta); diff --git a/include/geometry/Shape.h b/include/geometry/Shape.h index 552325676f..715e29f96c 100644 --- a/include/geometry/Shape.h +++ b/include/geometry/Shape.h @@ -103,6 +103,20 @@ class Shape : public LinesSet template OpenLinesSet intersection(const LinesSet& polylines, bool restitch = true, const coord_t max_stitch_distance = 10_mu) const; + /*! + * Subtract an area covered by the shape from polylines. + * + * \note Due to a clipper bug with polylines with nearly collinear segments, the polylines are cut up into separate polylines, and restitched back together at the end. + * + * \param polylines The polylines from which the the area of this Polygons object will be removed. + * \param restitch Whether to stitch the resulting segments into longer polylines, or leave every segment as a single segment + * \param max_stitch_distance The maximum distance for two polylines to be stitched together with a segment + * \return The resulting polylines from which the the area of this Polygons object was removed. + * \todo This should technically return a MixedLinesSet, because it can definitely contain open and closed polylines, but that is a heavy change + */ + template + OpenLinesSet difference(const LinesSet& polylines, bool restitch = true, const coord_t max_stitch_distance = 10_mu) const; + [[nodiscard]] Shape xorPolygons(const Shape& other, ClipperLib::PolyFillType pft = ClipperLib::pftEvenOdd) const; [[nodiscard]] Shape execute(ClipperLib::PolyFillType pft = ClipperLib::pftEvenOdd) const; diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 744aa1e5d9..1f01cfceb2 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -217,16 +217,16 @@ class SupportLayer Shape support_mesh; //!< Areas from support meshes which should NOT be supported by more support Shape anti_overhang; //!< Areas where no overhang should be detected. - Polygons getTotalAreaFromParts(const std::vector& parts, PartsFilter filter = PartsFilter::NoFilter) const + Shape getTotalAreaFromParts(const std::vector& parts, PartsFilter filter = PartsFilter::NoFilter) const { - Polygons result; + Shape result; for (const SupportInfillPart& part : parts) { if(filter == PartsFilter::NoFilter || (part.use_fractional_config_ && filter == PartsFilter::FractionalParts) || (!part.use_fractional_config_ && filter == PartsFilter::RegularParts)) { - result.add(part.outline_); + result.push_back(part.outline_); } } return result.unionPolygons(); @@ -239,7 +239,7 @@ class SupportLayer * \param exclude_polygons The polygons to exclude * \param exclude_polygons_boundary_box The boundary box for the polygons to exclude */ - void excludeAreasFromSupportParts(std::vector& parts, const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box); + void excludeAreasFromSupportInfillAreas(std::vector& parts, const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box); /* Fill up the infill parts for the support with the given support polygons. The support polygons will be split into parts. * @@ -251,7 +251,7 @@ class SupportLayer * \param custom_line_distance (optional, default to 0) Distance between lines of the infill pattern. custom_line_distance of 0 means use the default instead. * \param custom_pattern (optional, default to EFillMethod::NONE) Set if a non default infill pattern should be used */ - void fillInfillParts(const Polygons& area, + void fillInfillParts(const Shape& area, const coord_t support_line_width, const coord_t wall_line_count, const bool use_fractional_config = false, @@ -259,7 +259,7 @@ class SupportLayer const coord_t custom_line_distance = 0, EFillMethod custom_pattern = EFillMethod::NONE) { - for (const PolygonsPart& island_outline : area.splitIntoParts(unionAll)) + for (const SingleShape& island_outline : area.splitIntoParts(unionAll)) { support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count, custom_line_distance, custom_pattern); } diff --git a/include/utils/AABB.h b/include/utils/AABB.h index e3a93c678b..f51802042d 100644 --- a/include/utils/AABB.h +++ b/include/utils/AABB.h @@ -11,6 +11,7 @@ namespace cura class Polygon; class Shape; +class Polyline; /* Axis aligned boundary box */ class AABB @@ -22,9 +23,12 @@ class AABB AABB(const Point2LL& min, const Point2LL& max); //!< initializes with given min and max AABB(const Shape& shape); //!< Computes the boundary box for the given shape AABB(const Polygon& poly); //!< Computes the boundary box for the given polygons + AABB(const Polyline& line); //!< Computes the boundary box for the given polyline void calculate(const Shape& shape); //!< Calculates the aabb for the given shape (throws away old min and max data of this aabb) void calculate(const Polygon& poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb) + void calculate(const Polyline& poly); //!< Calculates the aabb for the given polyline (throws away old min and max data of this aabb) + /*! * Whether the bounding box contains the specified point. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 2832961d9e..cacee2e9ac 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2604,7 +2604,7 @@ bool FffGcodeWriter::processInsets( if (! support_layer.support_roof.empty()) { - Polygons roof = support_layer.getTotalAreaFromParts(support_layer.support_roof); + Shape roof = support_layer.getTotalAreaFromParts(support_layer.support_roof); AABB support_roof_bb(roof); if (boundaryBox.hit(support_roof_bb)) { @@ -3022,7 +3022,7 @@ void FffGcodeWriter::processTopBottom( if (! support_layer->support_roof.empty()) { - Polygons roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); + Shape roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); AABB support_roof_bb(roofs); if (skin_bb.hit(support_roof_bb)) { diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 074d1f501a..63d45ef8f1 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -291,7 +291,8 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) AABB outside_polygon_boundary_box(outside_polygon); SupportLayer& support_layer = storage.support.supportLayers[layer]; // take the differences of the support infill parts and the prime tower area - support_layer.excludeAreasFromSupportInfillAreas(Shape(outside_polygon), outside_polygon_boundary_box); + support_layer.excludeAreasFromSupportInfillAreas(support_layer.support_infill_parts, Shape(outside_polygon), outside_polygon_boundary_box); + support_layer.excludeAreasFromSupportInfillAreas(support_layer.support_roof, Shape(outside_polygon), outside_polygon_boundary_box); } } diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 053e30153e..401feb904e 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -386,10 +386,10 @@ Shape SkirtBrim::getFirstLayerOutline(const int extruder_nr /* = -1 */) } AABB model_brim_covered_area_boundary_box(model_brim_covered_area); - support_layer.excludeAreasFromSupportParts(support_layer.support_infill_parts, model_brim_covered_area, model_brim_covered_area_boundary_box); + support_layer.excludeAreasFromSupportInfillAreas(support_layer.support_infill_parts, model_brim_covered_area, model_brim_covered_area_boundary_box); // If the gap between the model and the BP is small enough, support starts with the interface instead, so remove it there as well: - support_layer.excludeAreasFromSupportParts(support_layer.support_roof, model_brim_covered_area, model_brim_covered_area_boundary_box); + support_layer.excludeAreasFromSupportInfillAreas(support_layer.support_roof, model_brim_covered_area, model_brim_covered_area_boundary_box); } first_layer_outline.push_back(support_layer.getTotalAreaFromParts(support_layer.support_infill_parts)); @@ -674,7 +674,7 @@ void SkirtBrim::generateSupportBrim() support_outline.push_back(part.outline_); } const Shape brim_area = support_outline.difference(support_outline.offset(-brim_width)); - support_layer.excludeAreasFromSupportParts(support_layer.support_infill_parts, brim_area, AABB(brim_area)); + support_layer.excludeAreasFromSupportInfillAreas(support_layer.support_infill_parts, brim_area, AABB(brim_area)); coord_t offset_distance = brim_line_width / 2; for (size_t skirt_brim_number = 0; skirt_brim_number < line_count; skirt_brim_number++) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index ddcefa909b..66e657cc03 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -579,7 +579,7 @@ const Shape& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex lay } -void TreeModelVolumes::addAreaToAntiPreferred(const Polygons area, LayerIndex layer_idx) +void TreeModelVolumes::addAreaToAntiPreferred(const Shape area, LayerIndex layer_idx) { RadiusLayerPair key(0, layer_idx); std::lock_guard critical_section(*critical_anti_preferred_); @@ -603,15 +603,15 @@ void TreeModelVolumes::precalculateAntiPreferred() const coord_t max_step_move = std::max(1.9 * radius, current_min_xy_dist_ * 1.9); RadiusLayerPair key(radius, 0); - Polygons latest_avoidance; - Polygons latest_avoidance_to_model; - Polygons latest_avoidance_collision; + Shape latest_avoidance; + Shape latest_avoidance_to_model; + Shape latest_avoidance_collision; LayerIndex start_layer = 0; - std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - std::vector> data_to_model(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - std::vector> data_collision(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - std::vector> data_raw_anti(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); + std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Shape())); + std::vector> data_to_model(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Shape())); + std::vector> data_collision(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Shape())); + std::vector> data_raw_anti(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Shape())); bool encountered_anti = false; // ### main loop doing the calculation @@ -619,7 +619,7 @@ void TreeModelVolumes::precalculateAntiPreferred() { key.second = layer; RadiusLayerPair key_0(0, layer); - Polygons anti; + Shape anti; { std::lock_guard critical_section(*critical_anti_preferred_); anti = anti_preferred_[key_0]; @@ -649,36 +649,36 @@ void TreeModelVolumes::precalculateAntiPreferred() continue; } - Polygons col = getCollisionHolefree(radius, layer, true); + Shape col = getCollisionHolefree(radius, layer, true); anti = anti.unionPolygons().offset(std::max(radius, config.branch_radius)).unionPolygons(); - data_raw_anti[layer] = std::pair(key, anti); + data_raw_anti[layer] = std::pair(key, anti); if (support_rest_preference_ == RestPreference::BUILDPLATE) { latest_avoidance = safeOffset(latest_avoidance, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons next_latest_avoidance = simplifier_.polygon(latest_avoidance); + Shape next_latest_avoidance = simplifier_.polygon(latest_avoidance); latest_avoidance = next_latest_avoidance.unionPolygons(latest_avoidance); latest_avoidance = latest_avoidance.unionPolygons(getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, false, true)); - data[layer] = std::pair(key, latest_avoidance); + data[layer] = std::pair(key, latest_avoidance); } if (support_rests_on_model_) { latest_avoidance_to_model = safeOffset(latest_avoidance_to_model, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons next_latest_avoidance_to_model = simplifier_.polygon(latest_avoidance_to_model); + Shape next_latest_avoidance_to_model = simplifier_.polygon(latest_avoidance_to_model); latest_avoidance_to_model = next_latest_avoidance_to_model.unionPolygons(latest_avoidance_to_model); latest_avoidance_to_model = latest_avoidance_to_model.difference(getPlaceableAreas(radius, layer)); latest_avoidance_to_model = latest_avoidance_to_model.unionPolygons(getAvoidance(radius, layer, AvoidanceType::FAST_SAFE, true, true)); - data_to_model[layer] = std::pair(key, latest_avoidance_to_model); + data_to_model[layer] = std::pair(key, latest_avoidance_to_model); } if (max_layer_idx_without_blocker_ <= layer && support_rests_on_model_) { latest_avoidance_collision = safeOffset(latest_avoidance_collision, -max_move_, ClipperLib::jtRound, -max_step_move, col.unionPolygons(anti)); - Polygons placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius_), ClipperLib::jtRound); + Shape placeable0RadiusCompensated = getAccumulatedPlaceable0(layer).offset(-std::max(radius, increase_until_radius_), ClipperLib::jtRound); latest_avoidance_collision = latest_avoidance_collision.difference(placeable0RadiusCompensated).unionPolygons(getCollision(radius, layer, true)); latest_avoidance_collision = latest_avoidance_collision.unionPolygons(getAvoidance(radius, layer, AvoidanceType::COLLISION, true, true)); - data_collision[layer] = std::pair(key, latest_avoidance_collision); + data_collision[layer] = std::pair(key, latest_avoidance_collision); } } { @@ -694,13 +694,13 @@ void TreeModelVolumes::precalculateAntiPreferred() }); } -const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius) +const Shape& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, coord_t radius) { coord_t ceiled_radius = ceilRadius(radius); RadiusLayerPair key(ceilRadius(ceiled_radius), layer_idx); - std::optional> result; + std::optional> result; - std::unordered_map* cache_ptr = &anti_preferred_; + std::unordered_map* cache_ptr = &anti_preferred_; { std::lock_guard critical_section(*critical_anti_preferred_caches); result = getArea(*cache_ptr, key); @@ -730,13 +730,13 @@ const Polygons& TreeModelVolumes::getAntiPreferredAreas(LayerIndex layer_idx, co } -const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) +const Shape& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) { coord_t ceiled_radius = ceilRadius(radius, min_xy_dist); RadiusLayerPair key(ceilRadius(ceiled_radius), layer_idx); - std::optional> result; + std::optional> result; - std::unordered_map* cache_ptr = nullptr; + std::unordered_map* cache_ptr = nullptr; if (type == AvoidanceType::COLLISION) { @@ -790,7 +790,7 @@ const Polygons& TreeModelVolumes::getAntiPreferredAvoidance(coord_t radius, Laye return getAvoidance(radius, layer_idx, type, to_model, min_xy_dist); } -const Polygons& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) +const Shape& TreeModelVolumes::getSupportBlocker(LayerIndex layer_idx) { if (layer_idx < anti_overhang_.size()) { @@ -1427,9 +1427,9 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) RadiusLayerPair key = RadiusLayerPair(0, layer_idx); if (! precalculated_ || support_rest_preference_ == RestPreference::BUILDPLATE) { - Polygons smaller_avoidance_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, false, true).offset(radius_offset, ClipperLib::jtRound); - Polygons smaller_avoidance_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, false, true).offset(radius_offset, ClipperLib::jtRound); - Polygons smaller_avoidance_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, false, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, false, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, false, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, false, true).offset(radius_offset, ClipperLib::jtRound); { std::lock_guard critical_section(*critical_avoidance_cache_slow_); avoidance_cache_slow_[key] = smaller_avoidance_slow; @@ -1446,9 +1446,9 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) if (! precalculated_ || support_rests_on_model_) { - Polygons smaller_avoidance_to_model_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, true, true).offset(radius_offset, ClipperLib::jtRound); - Polygons smaller_avoidance_to_model_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, true, true).offset(radius_offset, ClipperLib::jtRound); - Polygons smaller_avoidance_to_model_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, true, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_to_model_slow = getAvoidance(1, layer_idx, AvoidanceType::SLOW, true, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_to_model_fast = getAvoidance(1, layer_idx, AvoidanceType::FAST, true, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_to_model_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, true, true).offset(radius_offset, ClipperLib::jtRound); { std::lock_guard critical_section(*critical_avoidance_cache_to_model_slow_); @@ -1464,7 +1464,7 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) } if (layer_idx > max_layer_idx_without_blocker_) { - Polygons smaller_avoidance_collision = getAvoidance(1, layer_idx, AvoidanceType::COLLISION, true, true).offset(radius_offset, ClipperLib::jtRound); + Shape smaller_avoidance_collision = getAvoidance(1, layer_idx, AvoidanceType::COLLISION, true, true).offset(radius_offset, ClipperLib::jtRound); std::lock_guard critical_section(*critical_avoidance_cache_collision_); avoidance_cache_collision_[key] = smaller_avoidance_collision; } diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index f84e984579..74b04b5832 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -95,7 +95,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) } fake_roof_areas = std::vector>(storage.support.supportLayers.size(), std::vector()); - support_free_areas = std::vector(storage.support.supportLayers.size(), Polygons()); + support_free_areas = std::vector(storage.support.supportLayers.size(), Shape()); } void TreeSupport::generateSupportAreas(SliceDataStorage& storage) @@ -721,7 +721,7 @@ std::optional TreeSupport::increaseSingleArea( // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. if (! settings.no_error_ && ! to_bp_data.empty() && to_bp_data.area() < 1) { - to_bp_data = to_bp_data.unionPolygons(to_bp_data.offsetPolyLine(1)); + to_bp_data = to_bp_data.unionPolygons(TreeSupportUtils::toPolylines(to_bp_data).offset(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); } } @@ -752,14 +752,14 @@ std::optional TreeSupport::increaseSingleArea( // Other solution would be to apply a morphological closure for the avoidances, but as this issue occurs very rarely it may not be worth the performance impact. if (! settings.no_error_ && ! to_model_data.empty() && to_model_data.area() < 1) { - to_model_data = to_model_data.unionPolygons(to_model_data.offsetPolyLine(1)); + to_model_data = to_model_data.unionPolygons(TreeSupportUtils::toPolylines(to_model_data).offset(1)); spdlog::warn("Detected very small influence area, possible caused by a small hole in the avoidance. Compensating."); } } coord_t actual_radius = config.getRadius(current_elem); // Removing cradle areas from influence areas if possible. - Polygons anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius); + Shape anti_preferred_areas = volumes_.getAntiPreferredAreas(layer_idx - 1, actual_radius); bool anti_preferred_exists = volumes_.getFirstAntiPreferredLayerIdx() < layer_idx; if (! anti_preferred_areas.empty()) { @@ -772,25 +772,25 @@ std::optional TreeSupport::increaseSingleArea( } if (current_elem.to_buildplate_) { - Polygons to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); + Shape to_bp_without_anti = to_bp_data.difference(anti_preferred_areas); // If already moving fast there is not much to do. The anti preferred with collision radius will then later be subtracted if it is not subtracted here. if (to_bp_without_anti.area() > EPSILON || (settings.use_anti_preferred_ && ! is_fast)) { to_bp_data = to_bp_without_anti; - Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + Shape to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); to_model_data = to_model_data_without_anti; - Polygons increased_without_anti = increased.difference(anti_preferred_areas); + Shape increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; current_elem.ensure_valid_anti_preferred_ = true; } } else { - Polygons to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); + Shape to_model_data_without_anti = to_model_data.difference(anti_preferred_areas); if (to_model_data_without_anti.area() > EPSILON || (settings.use_anti_preferred_ && ! is_fast)) { to_model_data = to_model_data_without_anti; - Polygons increased_without_anti = increased.difference(anti_preferred_areas); + Shape increased_without_anti = increased.difference(anti_preferred_areas); increased = increased_without_anti; current_elem.ensure_valid_anti_preferred_ = true; } @@ -806,7 +806,7 @@ std::optional TreeSupport::increaseSingleArea( // Has to be also subtracted from increased, as otherwise a merge directly below the anti-preferred area may force a branch inside it. if (anti_preferred_exists && settings.use_anti_preferred_) { - const Polygons anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_); + const Shape& anti_preferred = volumes_.getAntiPreferredAvoidance(radius, layer_idx - 1, settings.type_, ! current_elem.to_buildplate_, settings.use_min_distance_); if (current_elem.to_buildplate_) { to_bp_data = to_bp_data.difference(anti_preferred); @@ -977,8 +977,8 @@ std::optional TreeSupport::increaseSingleArea( //Try to ensure the branch stays away from potential walls if possible. if (config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) { - Polygons new_to_bp_data; - Polygons new_to_model_data; + Shape new_to_bp_data; + Shape new_to_model_data; if (current_elem.to_buildplate_) { @@ -1599,8 +1599,8 @@ void TreeSupport::handleCradleLineValidity( bool immutable = elem->area_ != nullptr; bool to_bp = elem->to_buildplate_; - Polygons relevant_influence; - Polygons full_influence; + Shape relevant_influence; + Shape full_influence; if (! immutable) { relevant_influence = to_bp ? to_bp_areas[*elem] : to_model_areas[*elem]; @@ -1622,7 +1622,7 @@ void TreeSupport::handleCradleLineValidity( cradle_area_aabb.expand(config.getRadius(*elem) + config.xy_distance); if (cradle_area_aabb.hit(relevant_influence_aabb)) { - Polygons cradle_influence = TreeSupportUtils::safeOffsetInc( + Shape cradle_influence = TreeSupportUtils::safeOffsetInc( cradle.getCradleLine()->area_, config.getRadius(*elem) + config.xy_distance, volumes_.getCollision(config.getCollisionRadius(*elem), layer_idx, true), @@ -1631,7 +1631,7 @@ void TreeSupport::handleCradleLineValidity( 1, config.support_line_distance / 2, &config.simplifier); - Polygons next_relevant_influence = relevant_influence.difference(cradle_influence); + Shape next_relevant_influence = relevant_influence.difference(cradle_influence); if (next_relevant_influence.area() > EPSILON) { @@ -1785,10 +1785,10 @@ void TreeSupport::createLayerPathing(std::vector>& } // Place already fully constructed elements in the output. - for (std::pair tup : bypass_merge_areas) + for (std::pair tup : bypass_merge_areas) { const TreeSupportElement elem = tup.first; - Polygons* new_area = new Polygons(TreeSupportUtils::safeUnion(tup.second)); + Shape* new_area = new Shape(TreeSupportUtils::safeUnion(tup.second)); TreeSupportElement* next = new TreeSupportElement(elem, new_area); move_bounds[layer_idx - 1].emplace(next); if (new_area->area() < 1) @@ -2379,14 +2379,14 @@ void TreeSupport::dropNonGraciousAreas( void TreeSupport::prepareSupportAreas( std::vector& support_layer_storage, - std::vector& support_layer_storage_fractional, - 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, + std::vector& support_layer_storage_fractional, + 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>& cradle_data) { @@ -2394,10 +2394,10 @@ void TreeSupport::prepareSupportAreas( 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_support_line_roof_areas(support_layer_storage.size()); // All cradle lines that have to be added as roof + 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. - std::vector missing_cradle_line_xy_distance_areas(support_layer_storage.size()); // All missing (because of cradle z distance) cradle lines offset by xy distance. + std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); // All cradle lines offset by xy distance. + std::vector missing_cradle_line_xy_distance_areas(support_layer_storage.size()); // All missing (because of cradle z distance) cradle lines offset by xy distance. std::mutex critical_cradle_line_xy_distance_areas; std::mutex critical_cradle_support_line_areas; @@ -2416,22 +2416,22 @@ void TreeSupport::prepareSupportAreas( if (cradle_data[layer_idx][cradle_idx]->is_roof_) { std::lock_guard critical_section_cradle(critical_support_roof_storage); - cradle_base_areas[layer_idx - base_idx].add(base); - (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx - base_idx].add(base); + cradle_base_areas[layer_idx - base_idx].push_back(base); + (config.support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx - base_idx].push_back(base); if (base_idx == 0 && config.z_distance_top % config.layer_height != 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) { - (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx + 1].add(base); + (config.support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx + 1].push_back(base); } } else { // Dead code. Currently, Cradles that are not roofs do not have a base area, just a tip. This is just here for the case that this changes std::lock_guard critical_section_cradle(critical_support_layer_storage); - cradle_base_areas[layer_idx - base_idx].add(base); - support_layer_storage[layer_idx - base_idx].add(base); + cradle_base_areas[layer_idx - base_idx].push_back(base); + support_layer_storage[layer_idx - base_idx].push_back(base); if (base_idx == 0 && config.z_distance_top % config.layer_height != 0 && layer_idx + 1 < support_layer_storage_fractional.size()) { - support_layer_storage_fractional[layer_idx + 1].add(base); + support_layer_storage_fractional[layer_idx + 1].push_back(base); } } } @@ -2440,11 +2440,11 @@ void TreeSupport::prepareSupportAreas( { if (! cradle_data[layer_idx][cradle_idx]->lines_[line_idx].empty()) { - Polygons previous_line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().area_; + Shape previous_line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().area_; LayerIndex previous_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().layer_idx_; for (int64_t height_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - 1; height_idx >= 0; height_idx--) { - Polygons line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + Shape line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; @@ -2454,7 +2454,7 @@ void TreeSupport::prepareSupportAreas( { for (LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) { - Polygons line_areas = TreeSupportUtils::safeOffsetInc( + Shape line_areas = TreeSupportUtils::safeOffsetInc( previous_line_area, config.xy_distance, volumes_.getCollision(0, xy_dist_layer_idx), @@ -2464,7 +2464,7 @@ void TreeSupport::prepareSupportAreas( config.min_feature_size, &config.simplifier); std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); - missing_cradle_line_xy_distance_areas[xy_dist_layer_idx].add(line_areas); + missing_cradle_line_xy_distance_areas[xy_dist_layer_idx].push_back(line_areas); } } @@ -2476,7 +2476,7 @@ void TreeSupport::prepareSupportAreas( { cradle_support_line_roof_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } - cradle_support_line_roof_areas[cradle_line_layer_idx].add(line_area); + cradle_support_line_roof_areas[cradle_line_layer_idx].push_back(line_area); } else { @@ -2486,11 +2486,11 @@ void TreeSupport::prepareSupportAreas( { cradle_support_line_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } - cradle_support_line_areas[cradle_line_layer_idx].add(line_area); + cradle_support_line_areas[cradle_line_layer_idx].push_back(line_area); } if (! is_base) { - Polygons line_areas = TreeSupportUtils::safeOffsetInc( + Shape line_areas = TreeSupportUtils::safeOffsetInc( line_area, config.xy_distance, volumes_.getCollision(0, cradle_line_layer_idx), @@ -2500,7 +2500,7 @@ void TreeSupport::prepareSupportAreas( config.min_feature_size, &config.simplifier); std::lock_guard critical_section_cradle(critical_cradle_line_xy_distance_areas); - cradle_line_xy_distance_areas[cradle_line_layer_idx].add(line_areas); + cradle_line_xy_distance_areas[cradle_line_layer_idx].push_back(line_areas); } previous_layer_idx = cradle_line_layer_idx; previous_line_area = line_area; @@ -2515,25 +2515,25 @@ void TreeSupport::prepareSupportAreas( support_layer_storage.size(), [&](const LayerIndex layer_idx) { - Polygons fake_roof; - Polygons fake_roof_lines; + Shape fake_roof; + Shape fake_roof_lines; for (FakeRoofArea& f_roof : fake_roof_areas[layer_idx]) { - fake_roof.add(f_roof.area_); - fake_roof_lines.add( + fake_roof.push_back(f_roof.area_); + fake_roof_lines.push_back( TreeSupportUtils::generateSupportInfillLines(f_roof.area_, config, false, layer_idx, f_roof.line_distance_, storage.support.cross_fill_provider, 0) - .offsetPolyLine(config.support_line_width / 2)); + .offset(config.support_line_width / 2)); } fake_roof_lines = fake_roof_lines.unionPolygons(); fake_roof = fake_roof.unionPolygons(); fake_roof_areas_combined[layer_idx] = fake_roof; - Polygons remove_from_support = cradle_line_xy_distance_areas[layer_idx]; - remove_from_support.add(missing_cradle_line_xy_distance_areas[layer_idx]); - remove_from_support.add(fake_roof_lines); - remove_from_support.add(support_free_areas[layer_idx]); + Shape remove_from_support = cradle_line_xy_distance_areas[layer_idx]; + remove_from_support.push_back(missing_cradle_line_xy_distance_areas[layer_idx]); + remove_from_support.push_back(fake_roof_lines); + remove_from_support.push_back(support_free_areas[layer_idx]); remove_from_support = remove_from_support.unionPolygons(); support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))) @@ -2541,7 +2541,7 @@ void TreeSupport::prepareSupportAreas( .offset(open_close_distance * 2) .offset(-open_close_distance); support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].unionPolygons(); - Polygons original_fractional = support_layer_storage_fractional[layer_idx]; + Shape original_fractional = support_layer_storage_fractional[layer_idx]; support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(support_layer_storage[layer_idx]); // ensure there is at lease one line space for fractional support. Overlap is removed later! support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].offset(config.support_line_width).intersection(original_fractional); @@ -2568,11 +2568,11 @@ void TreeSupport::prepareSupportAreas( { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: { - Polygons all_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - all_roof.add(support_roof_storage[layer_idx]); - all_roof.add(support_roof_storage_fractional[layer_idx]); - all_roof.add(support_roof_extra_wall_storage[layer_idx]); - all_roof.add(support_roof_extra_wall_storage_fractional[layer_idx]); + Shape all_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + all_roof.push_back(support_roof_storage[layer_idx]); + all_roof.push_back(support_roof_storage_fractional[layer_idx]); + all_roof.push_back(support_roof_extra_wall_storage[layer_idx]); + all_roof.push_back(support_roof_extra_wall_storage_fractional[layer_idx]); all_roof = all_roof.unionPolygons(); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(all_roof); support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(all_roof); @@ -2581,11 +2581,11 @@ void TreeSupport::prepareSupportAreas( case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: { - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons support_areas = support_layer_storage[layer_idx].unionPolygons(support_layer_storage_fractional[layer_idx]); - Polygons invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); + Shape existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Shape support_areas = support_layer_storage[layer_idx].unionPolygons(support_layer_storage_fractional[layer_idx]); + Shape invalid_roof = existing_roof.intersection(support_layer_storage[layer_idx]); AABB invalid_roof_aabb = AABB(invalid_roof); - storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportInfillAreas(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); support_roof_storage[layer_idx] = support_roof_storage[layer_idx].difference(support_areas); support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].difference(support_areas); support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].difference(support_areas); @@ -2596,25 +2596,25 @@ void TreeSupport::prepareSupportAreas( break; } } - Polygons cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); - Polygons remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); + Shape cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); + Shape remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); if (! support_free_areas[layer_idx].empty()) { - remove_from_next_roof.add(support_free_areas[layer_idx]); + remove_from_next_roof.push_back(support_free_areas[layer_idx]); } //Remove only already added roof from line areas. Should not be needed, but better safe than sorry. cradle_lines_roof = cradle_lines_roof.difference(remove_from_next_roof); cradle_support_line_areas[layer_idx] = cradle_support_line_areas[layer_idx].unionPolygons().difference(remove_from_next_roof); //Collect remaining parts that non cradle line roof areas may not intersect with. - remove_from_next_roof.add(cradle_line_xy_distance_areas[layer_idx]); + remove_from_next_roof.push_back(cradle_line_xy_distance_areas[layer_idx]); remove_from_next_roof = remove_from_next_roof.unionPolygons(); - Polygons remove_from_next_fractional_roof = remove_from_next_roof; + Shape remove_from_next_fractional_roof = remove_from_next_roof; remove_from_next_roof = remove_from_next_roof.unionPolygons(missing_cradle_line_xy_distance_areas[layer_idx]); - Polygons roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); - Polygons roof = support_roof_storage[layer_idx]; + Shape roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); + Shape roof = support_roof_storage[layer_idx]; if (config.support_roof_wall_count) { roof = roof.difference(remove_from_next_roof); @@ -2629,27 +2629,27 @@ void TreeSupport::prepareSupportAreas( storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, config.support_roof_line_width, config.support_wall_count, false); storage.support.supportLayers[layer_idx].fillRoofParts(roof, config.support_roof_line_width, config.support_roof_wall_count, false); - remove_from_next_fractional_roof.add(roof_extra_wall); - remove_from_next_fractional_roof.add(roof); + remove_from_next_fractional_roof.push_back(roof_extra_wall); + remove_from_next_fractional_roof.push_back(roof); remove_from_next_fractional_roof = remove_from_next_fractional_roof.unionPolygons(); - Polygons fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof); + Shape fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof); storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, config.support_roof_line_width, config.support_wall_count, true); - Polygons fractional_roof = support_roof_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof.unionPolygons(fractional_roof_extra_wall)); + Shape 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); }); } -void TreeSupport::calculateSupportHoles(std::vector& support_layer_storage, - std::vector>& hole_parts, +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) + std::function reversePolygon = [&](Shape& poly) { for (size_t idx = 0; idx < poly.size(); idx++) { @@ -2658,7 +2658,7 @@ void TreeSupport::calculateSupportHoles(std::vector& support_layer_sto }; - std::vector support_holes(support_layer_storage.size(), Polygons()); + std::vector support_holes(support_layer_storage.size(), Shape()); // Extract all holes as polygon objects cura::parallel_for( @@ -2712,8 +2712,8 @@ void TreeSupport::calculateSupportHoles(std::vector& support_layer_sto } const Shape& 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); + Shape outer_walls + = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).createTubeShape(config.support_line_width * config.support_wall_count, 0); for (auto [idx, hole] : hole_parts[layer_idx] | ranges::views::enumerate) @@ -2724,11 +2724,6 @@ void TreeSupport::calculateSupportHoles(std::vector& support_layer_sto { valid_holes[layer_idx].emplace(idx); } - else if (hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).area() > hole.length() * 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 { if(!hole.intersection(PolygonUtils::clipPolygonWithAABB(relevant_forbidden, hole_aabb)).offset(- config.xy_min_distance / 2).empty()) @@ -2752,17 +2747,17 @@ void TreeSupport::calculateSupportHoles(std::vector& support_layer_sto 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& 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::vector>& layer_tree_polygons) { std::mutex critical_support_layer_storage; @@ -2779,24 +2774,24 @@ void TreeSupport::generateSupportSkin( } const coord_t roof_stable_range_after_contact = config.support_roof_line_width * (config.support_roof_wall_count + 0.5); - Polygons support_shell_capable_of_supporting_roof + Shape support_shell_capable_of_supporting_roof = support_layer_storage[layer_idx] .getOutsidePolygons() - .tubeShape( + .createTubeShape( config.support_line_width * (config.support_wall_count + 0.5) + roof_stable_range_after_contact, roof_stable_range_after_contact, ClipperLib::JoinType::jtRound) .unionPolygons() .offset(-config.support_line_width / 4) .offset(config.support_line_width / 4); // Getting rid of small rounding errors. If an area thinner than 1/2 line-width said it needs skin, it is lying. - Polygons needs_supporting; + Shape needs_supporting; if (storage.support.supportLayers.size() > layer_idx + 1) { - Polygons roof_above = storage.support.supportLayers[layer_idx + 1].getTotalAreaFromParts(storage.support.supportLayers[layer_idx + 1].support_roof); + Shape 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_roof_areas_combined[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); - needs_supporting.add(cradle_support_line_areas[layer_idx + 1]); + needs_supporting.push_back(roof_above.difference(support_shell_capable_of_supporting_roof)); + needs_supporting.push_back(fake_roof_areas_combined[layer_idx + 1].difference(support_shell_capable_of_supporting_roof)); + needs_supporting.push_back(cradle_support_line_areas[layer_idx + 1]); for(size_t hole_idx : non_removable_holes[layer_idx + 1]) { @@ -2813,27 +2808,27 @@ void TreeSupport::generateSupportSkin( 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.push_back(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. + needs_supporting.push_back(cradle_base_areas[layer_idx]); // cradle bases should be skin. needs_supporting = needs_supporting.unionPolygons(); - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Shape existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons already_supports; - already_supports.add(existing_roof); // roof - 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]); + Shape already_supports; + already_supports.push_back(existing_roof); // roof + already_supports.push_back(fake_roof_areas_combined[layer_idx]); + already_supports.push_back(support_layer_storage[layer_idx].getOutsidePolygons().createTubeShape(config.support_line_width * config.support_wall_count, 0)); + already_supports.push_back(cradle_support_line_areas[layer_idx]); already_supports = already_supports.unionPolygons().offset(FUDGE_LENGTH).unionPolygons(); - Polygons may_need_skin_area_topmost = needs_supporting.difference(already_supports); + Shape may_need_skin_area_topmost = needs_supporting.difference(already_supports); - for (std::pair data_pair : layer_tree_polygons[layer_idx]) + for (std::pair data_pair : layer_tree_polygons[layer_idx]) { bool has_parent_roof = false; @@ -2851,19 +2846,19 @@ void TreeSupport::generateSupportSkin( if (element_viable_for_skin) { - may_need_skin_area_topmost.add(data_pair.second); + may_need_skin_area_topmost.push_back(data_pair.second); } } may_need_skin_area_topmost = may_need_skin_area_topmost.unionPolygons(); - Polygons may_need_skin_area = may_need_skin_area_topmost; + Shape may_need_skin_area = may_need_skin_area_topmost; for (LayerIndex support_skin_ctr = 0; support_skin_ctr < std::min(LayerIndex(config.support_skin_layers), layer_idx); support_skin_ctr++) { - Polygons next_skin; - Polygons support_on_layer; + Shape next_skin; + Shape support_on_layer; - Polygons remaining_regular_areas; + Shape remaining_regular_areas; { std::lock_guard critical_section_cradle(critical_support_layer_storage); @@ -2875,13 +2870,13 @@ void TreeSupport::generateSupportSkin( may_need_skin_area_topmost = may_need_skin_area_topmost.intersection(support_on_layer); } - for (Polygons part : support_on_layer.splitIntoParts()) + for (Shape part : support_on_layer.splitIntoParts()) { - Polygons part_outline = part.getOutsidePolygons().difference(volumes_.getCollision(0, layer_idx - support_skin_ctr, true)); + Shape part_outline = part.getOutsidePolygons().difference(volumes_.getCollision(0, layer_idx - support_skin_ctr, true)); if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area, AABB(part_outline)).empty()) { // Use line infill to scan which area the line infill has to occupy to reach the outer outline of a branch. - Polygons scan_lines = TreeSupportUtils::generateSupportInfillLines( + OpenLinesSet scan_lines = TreeSupportUtils::generateSupportInfillLines( part_outline, config, false, @@ -2891,7 +2886,7 @@ void TreeSupport::generateSupportSkin( 0, EFillMethod::LINES, true); - Polygons intersecting_lines; + OpenLinesSet intersecting_lines; for (auto line : scan_lines) { bool valid_for_may_need_skin = PolygonUtils::polygonCollidesWithLineSegment(may_need_skin_area, line.front(), line.back()) @@ -2900,21 +2895,21 @@ void TreeSupport::generateSupportSkin( || may_need_skin_area_topmost.inside(line.front()) || may_need_skin_area_topmost.inside(line.back()); if (valid_for_may_need_skin && valid_for_may_need_skin_area_topmost) { - intersecting_lines.addLine(line.front(), line.back()); + intersecting_lines.addSegment(line.front(), line.back()); } } - Polygons partial_skin_area - = intersecting_lines.offsetPolyLine(config.support_skin_line_distance + FUDGE_LENGTH).unionPolygons().intersection(part_outline); + Shape partial_skin_area + = intersecting_lines.offset(config.support_skin_line_distance + FUDGE_LENGTH).unionPolygons().intersection(part_outline); // If a some scan lines had contact with two parts of part_outline, but the second part is outside of may_need_skin_area it could cause a separate skin // area, that then cuts a branch in half that could have been completely normal. This area that does not need to be skin will be filtered out here. { - Polygons filtered_partial_skin_area; + Shape filtered_partial_skin_area; for (auto p_skin : partial_skin_area.splitIntoParts()) { if (! PolygonUtils::clipPolygonWithAABB(may_need_skin_area, AABB(p_skin)).intersection(p_skin).empty()) { - filtered_partial_skin_area.add(p_skin); + filtered_partial_skin_area.push_back(p_skin); } } partial_skin_area = filtered_partial_skin_area; @@ -2922,7 +2917,7 @@ void TreeSupport::generateSupportSkin( double part_area = part.area(); part = part.difference(partial_skin_area); - Polygons remaining_part; + Shape remaining_part; for (auto sub_part : part.splitIntoParts()) { // Prevent small slivers of a branch to generate as support. The heuristic to detect if a part is too small or thin could maybe be improved. @@ -2933,16 +2928,16 @@ void TreeSupport::generateSupportSkin( } else { - remaining_part.add(sub_part); + remaining_part.push_back(sub_part); } } part = remaining_part; - next_skin.add(partial_skin_area.intersection(may_need_skin_area_topmost)); + next_skin.push_back(partial_skin_area.intersection(may_need_skin_area_topmost)); std::lock_guard critical_section_cradle(critical_support_layer_storage); - support_skin_storage[layer_idx - support_skin_ctr].add(partial_skin_area); + support_skin_storage[layer_idx - support_skin_ctr].push_back(partial_skin_area); } - remaining_regular_areas.add(part); + remaining_regular_areas.push_back(part); } may_need_skin_area = next_skin.unionPolygons(); } @@ -2967,7 +2962,7 @@ void TreeSupport::generateSupportSkin( { // 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(); + hole_parts[layer_idx][idx] = Shape(); } else if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(support_skin_storage[layer_idx - 1], hole_aabb)).empty()) { @@ -2978,8 +2973,8 @@ void TreeSupport::generateSupportSkin( }); } -void TreeSupport::removeFloatingHoles(std::vector& support_layer_storage, - std::vector>& hole_parts, +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) @@ -3071,7 +3066,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( { case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: { - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Shape existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(existing_roof); support_skin_storage[layer_idx] = support_skin_storage[layer_idx].difference(existing_roof); support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].difference(existing_roof); @@ -3080,13 +3075,13 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: { - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons support_areas = support_layer_storage[layer_idx]; - support_areas.add(support_skin_storage[layer_idx]); - support_areas.add(support_layer_storage_fractional[layer_idx]); - Polygons invalid_roof = existing_roof.intersection(support_areas.unionPolygons()); + Shape existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Shape support_areas = support_layer_storage[layer_idx]; + support_areas.push_back(support_skin_storage[layer_idx]); + support_areas.push_back(support_layer_storage_fractional[layer_idx]); + Shape invalid_roof = existing_roof.intersection(support_areas.unionPolygons()); AABB invalid_roof_aabb = AABB(invalid_roof); - storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportInfillAreas(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); break; } @@ -3096,7 +3091,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( for (SupportInfillPart& roof_part : storage.support.supportLayers[layer_idx].support_roof) { - interface_lines.add(TreeSupportUtils::generateSupportInfillLines( + interface_lines.push_back(TreeSupportUtils::generateSupportInfillLines( roof_part.outline_, config, true, @@ -3127,9 +3122,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) - .offsetPolyLine(config.support_line_width / 2)); + .offset(config.support_line_width / 2)); - Polygons support_skin_lines; + Shape support_skin_lines; support_skin_lines = support_skin_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( support_skin_storage[layer_idx], config, @@ -3141,9 +3136,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( EFillMethod::LINES) .offset(config.support_line_width / 2)); - Polygons invalid_roof = existing_roof.intersection(support_skin_lines.unionPolygons(tree_lines)); + Shape invalid_roof = existing_roof.intersection(support_skin_lines.unionPolygons(tree_lines)); AABB invalid_roof_aabb = AABB(invalid_roof); - storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportInfillAreas(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. } break; @@ -3195,9 +3190,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( // This only works because fractional support is always just projected upwards regular support or skin. // Also technically violates skin height, but there is no good way to prevent that. Shape fractional_support; - Polygons fractional_skin; - Polygons support_areas = support_layer_storage[layer_idx]; - Polygons skin_areas = support_skin_storage[layer_idx]; + Shape fractional_skin; + Shape support_areas = support_layer_storage[layer_idx]; + Shape skin_areas = support_skin_storage[layer_idx]; if (layer_idx > 0) { @@ -3208,8 +3203,8 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( // todo deduplicate code if (interface_pref == InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE) { - Polygons existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); - Polygons tree_lines; + Shape existing_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof); + Shape tree_lines; tree_lines = tree_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( fractional_support, config, @@ -3218,9 +3213,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( config.support_line_distance, storage.support.cross_fill_provider, config.support_wall_count) - .offsetPolyLine(config.support_line_width / 2)); + .offset(config.support_line_width / 2)); - Polygons support_skin_lines; + Shape support_skin_lines; support_skin_lines = support_skin_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( fractional_skin, config, @@ -3230,10 +3225,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( storage.support.cross_fill_provider, std::max(0, config.support_wall_count - 1), EFillMethod::LINES) - .offsetPolyLine(config.support_line_width / 2)); - Polygons invalid_roof = existing_roof.intersection(tree_lines); + .offset(config.support_line_width / 2)); + Shape invalid_roof = existing_roof.intersection(tree_lines); AABB invalid_roof_aabb = AABB(invalid_roof); - storage.support.supportLayers[layer_idx].excludeAreasFromSupportParts(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); + storage.support.supportLayers[layer_idx].excludeAreasFromSupportInfillAreas(storage.support.supportLayers[layer_idx].support_roof, invalid_roof, invalid_roof_aabb); } // Remove overlap between fractional and regular support that may have been created in generateSupportSkin. @@ -3296,16 +3291,16 @@ void TreeSupport::drawAreas(std::vector>& move_bou std::vector support_layer_storage_fractional(move_bounds.size()); std::vector support_roof_storage_fractional(move_bounds.size()); std::vector support_roof_extra_wall_storage_fractional(move_bounds.size()); - 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 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::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. std::vector> @@ -3373,7 +3368,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou { if (data_pair.first->missing_roof_layers_ > data_pair.first->distance_to_top_ && config.support_roof_wall_count == 0) { - Polygons roof_lines = TreeSupportUtils::generateSupportInfillLines( + OpenLinesSet roof_lines = TreeSupportUtils::generateSupportInfillLines( data_pair.second, config, true, @@ -3381,7 +3376,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou config.support_roof_line_distance, nullptr, config.support_roof_wall_count); - if (roof_lines.polyLineLength() < data_pair.second.polyLineLength()) // arbitrary threshold to check if the interface pattern is propper. + if (roof_lines.length() < data_pair.second.length()) // arbitrary threshold to check if the interface pattern is propper. { std::vector to_disable_roofs; to_disable_roofs.emplace_back(data_pair.first); @@ -3414,7 +3409,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou { if (data_pair.first->roof_with_enforced_walls) { - support_roof_extra_wall_storage_fractional[layer_idx + 1].add(data_pair.second); + support_roof_extra_wall_storage_fractional[layer_idx + 1].push_back(data_pair.second); } else { @@ -3430,7 +3425,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou { if (data_pair.first->roof_with_enforced_walls) { - support_roof_extra_wall_storage[layer_idx].add(data_pair.second); + support_roof_extra_wall_storage[layer_idx].push_back(data_pair.second); } else { @@ -3439,7 +3434,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } else { - support_layer_storage[layer_idx].add(data_pair.second); + support_layer_storage[layer_idx].push_back(data_pair.second); } } if (layer_idx + 1 < support_roof_storage_fractional.size()) diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 4eb637a0ee..704b7fec9c 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -47,33 +47,33 @@ void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mes floating_parts_map_below_[mesh_idx].resize(max_layer + 1); std::mutex critical_sections; - Polygons completely_supported = volumes_.getCollision(0, 0, true); - Polygons layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter + Shape completely_supported = volumes_.getCollision(0, 0, true); + Shape layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) { - Polygons next_completely_supported; + Shape next_completely_supported; // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. - Polygons layer = mesh.layers[layer_idx].getOutlines(); + Shape layer = mesh.layers[layer_idx].getOutlines(); - const std::vector layer_parts = layer.splitIntoParts(); + const std::vector layer_parts = layer.splitIntoParts(); cura::parallel_for( 0, layer_parts.size(), [&](const size_t part_idx) { - const PolygonsPart& part = layer_parts[part_idx]; + const SingleShape& part = layer_parts[part_idx]; AABB part_aabb(part); bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(layer_below, part_aabb).intersection(part).empty(); if (! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) { std::lock_guard critical_section_add(critical_sections); - next_completely_supported.add(part); + next_completely_supported.push_back(part); return; } - Polygons overhang = mesh.overhang_areas[layer_idx].intersection(part); + Shape overhang = mesh.overhang_areas[layer_idx].intersection(part); coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * min_wall_line_width * min_wall_line_width); if (! has_support_below) { @@ -116,7 +116,7 @@ void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mes else { std::lock_guard critical_section_add(critical_sections); - next_completely_supported.add(part); + next_completely_supported.push_back(part); } }); layer_below = layer; @@ -238,7 +238,7 @@ std::vector> SupportCradleGeneration::generateCr continue; } - std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + cradle_config->cradle_z_distance_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Polygons()); + std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + cradle_config->cradle_z_distance_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Shape()); std::vector all_pointy_idx{ pointy_info.index }; Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); @@ -250,17 +250,17 @@ std::vector> SupportCradleGeneration::generateCr accumulated_model[z_distance] = pointy_info.area; cradle_main->centers_.emplace_back(center_prev); } - Polygons shadow; // A combination of all outlines of the model that will be supported with a cradle. + Shape shadow; // A combination of all outlines of the model that will be supported with a cradle. bool aborted = false; bool contacted_other_pointy = false; - std::vector unsupported_model(accumulated_model.size()); + std::vector unsupported_model(accumulated_model.size()); for (size_t cradle_up_layer = 0; cradle_up_layer < accumulated_model.size() - z_distance_top_layers; cradle_up_layer++) { // shadow model up => not cradle where model // then drop cradle down // cut into parts => get close to original pointy that are far enough from each other. std::vector next_pointy_idx; - Polygons model_outline; + Shape model_outline; bool blocked_by_dedupe = false; // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up if (cradle_up_layer > 1) @@ -275,13 +275,13 @@ std::vector> SupportCradleGeneration::generateCr contacted_other_pointy = true; continue; } - unsupported_model[cradle_up_layer].add(next_pointy_data.area); + unsupported_model[cradle_up_layer].push_back(next_pointy_data.area); // Ensure each area is only handles once std::lock_guard critical_section_cradle(critical_dedupe); if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) { dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data.index); - model_outline.add(next_pointy_data.area); + model_outline.push_back(next_pointy_data.area); next_pointy_idx.emplace_back(next_pointy_data.index); } else @@ -303,7 +303,7 @@ std::vector> SupportCradleGeneration::generateCr // Only add if area below does not have it's own cradle. if (prev_pointy_data.height < cradle_config->cradle_layers_min_) { - accumulated_model[cradle_down_layer].add(prev_pointy_data.area); + accumulated_model[cradle_down_layer].push_back(prev_pointy_data.area); next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); } } @@ -318,7 +318,7 @@ std::vector> SupportCradleGeneration::generateCr } else { - model_outline.add(pointy_info.area); + model_outline.push_back(pointy_info.area); } if (model_outline.empty()) @@ -335,7 +335,7 @@ std::vector> SupportCradleGeneration::generateCr // When an area that should have a cradle merges with a buildplate supported area above, it will no longer exist for a cradle. // But if the cradle stops there will be z distance layer between the end of the cradle and said merge. // To reduce the impact an area is estimated where the cradle should be for these areas. - Polygons previous_area = shadow; + Shape previous_area = shadow; for (size_t cradle_up_layer_z_distance = cradle_up_layer; cradle_up_layer_z_distance < std::min(cradle_up_layer + cradle_config->cradle_z_distance_layers_, accumulated_model.size() - z_distance_top_layers); cradle_up_layer_z_distance++) @@ -365,7 +365,7 @@ std::vector> SupportCradleGeneration::generateCr // If aborted remove all model information for the cradle generation except the pointy overhang, as it may be needed to cut a small hole in the large interface // base. todo reimplement that - Polygons cradle_0 = accumulated_model[0]; + Shape cradle_0 = accumulated_model[0]; accumulated_model.clear(); accumulated_model.emplace_back(cradle_0); delete cradle_main; @@ -416,18 +416,18 @@ void SupportCradleGeneration::generateCradleLines(std::vector cradle->config_->cradle_z_distance_layers_ && ! model_shadow.empty()) { - Polygons relevant_forbidden = volumes_.getAvoidance( + Shape relevant_forbidden = volumes_.getAvoidance( 0, layer_idx + idx, (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, support_rests_on_model, true); - Polygons this_part_influence = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2); + Shape this_part_influence = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2); for (size_t layer_offset = 1; layer_offset <= z_distance_bottom_layers && layer_offset <= idx; layer_offset++) { - this_part_influence.add(accumulated_model[idx - layer_offset].offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2)); + this_part_influence.push_back(accumulated_model[idx - layer_offset].offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2)); } for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers && layer_offset + idx < accumulated_model.size(); layer_offset++) @@ -436,7 +436,7 @@ void SupportCradleGeneration::generateCradleLines(std::vectorconfig_->cradle_line_width_ / 2) - ((layer_offset - (z_distance_top_layers == 1 ? 0.5 : 0)) * (current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2) / z_distance_top_layers)); - this_part_influence.add(accumulated_model[idx + layer_offset].offset(required_range_x)); + this_part_influence.push_back(accumulated_model[idx + layer_offset].offset(required_range_x)); } this_part_influence = this_part_influence.unionPolygons(); @@ -459,52 +459,52 @@ void SupportCradleGeneration::generateCradleLines(std::vectorconfig_->cradle_line_count_), 1.9 * std::numbers::pi)); + cradle->config_->cradle_line_count_); // create lines that go from the furthest possible location to the center - Polygons lines_to_center; + OpenLinesSet lines_to_center; for (Point2LL p : max_outer_points) { Point2LL direction = p - center; - lines_to_center.addLine(p, center + normal(direction, support_line_width)); + lines_to_center.addSegment(p, center + normal(direction, support_line_width)); } // Subtract the model shadow up until this layer from the lines. if (idx > 0) { - lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2).unionPolygons().differencePolyLines(lines_to_center, false); + lines_to_center = model_shadow.offset(current_cradle_xy_distance + cradle->config_->cradle_line_width_ / 2).unionPolygons().difference(lines_to_center, false); } // shorten lines to be at most SUPPORT_TREE_CRADLE_WIDTH long, with the location closest to the center not changing - Polygons shortened_lines_to_center; + OpenLinesSet shortened_lines_to_center; for (auto [line_idx, line] : lines_to_center | ranges::views::enumerate) { bool front_closer = vSize2(line.front() - center) < vSize2(line.back() - center); Point2LL closer = front_closer ? line.front() : line.back(); Point2LL further = front_closer ? line.back() : line.front(); - coord_t cradle_line_length = Polygon(line).polylineLength(); + coord_t cradle_line_length = line.length(); if (cradle_line_length < cradle->config_->cradle_length_min_) { continue; } - if (Polygon(line).polylineLength() <= current_cradle_length) + if (line.length() <= current_cradle_length) { - shortened_lines_to_center.add(line); + shortened_lines_to_center.push_back(line); } else { double scale = (double(current_cradle_length) / double(vSize(further - closer))); Point2LL correct_length = closer + (further - closer) * scale; - shortened_lines_to_center.addLine(correct_length, closer); + shortened_lines_to_center.addSegment(correct_length, closer); } } // If a line is drawn, but half of it removed as it would collide with the collision, there may not actually be a print line. The offset should prevent // this. - shortened_lines_to_center = relevant_forbidden.differencePolyLines(shortened_lines_to_center, false); - std::vector ordered_lines_to_center(cradle->config_->cradle_line_count_); + shortened_lines_to_center = relevant_forbidden.difference(shortened_lines_to_center, false); + std::vector ordered_lines_to_center(cradle->config_->cradle_line_count_); // Evaluate which lines are still valid after the avoidance was subtracted for (auto [line_idx, line] : shortened_lines_to_center | ranges::views::enumerate) @@ -529,20 +529,22 @@ void SupportCradleGeneration::generateCradleLines(std::vector= cradle->config_->cradle_z_distance_layers_ + 1) { - const Polygons actually_forbidden = volumes_.getAvoidance( + const Shape& actually_forbidden = volumes_.getAvoidance( minimum_area_to_be_supportable, layer_idx + idx - (cradle->config_->cradle_z_distance_layers_ + 1), (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, support_rests_on_model, true); - too_short |= actually_forbidden.differencePolyLines(shortened_lines_to_center[line_idx].offset(0)).polyLineLength() < support_line_width; + OpenLinesSet current_shortened; + current_shortened.push_back(shortened_lines_to_center[line_idx]); + too_short |= actually_forbidden.difference(current_shortened).length() < support_line_width; } if (! too_short && ! too_long_jump) { if (ordered_lines_to_center[angle_idx].empty()) { - ordered_lines_to_center[angle_idx].add(closer); - ordered_lines_to_center[angle_idx].add(further); + ordered_lines_to_center[angle_idx].push_back(closer); + ordered_lines_to_center[angle_idx].push_back(further); } else { @@ -552,14 +554,14 @@ void SupportCradleGeneration::generateCradleLines(std::vectorconfig_->cradle_z_distance_layers_ + 1 && ! cradle->lines_[angle_idx].empty()) { @@ -598,18 +600,18 @@ void SupportCradleGeneration::generateCradleLines(std::vectorgetCenter(line.layer_idx_); if (vSize2(line_end - center) > vSize2(line.line_.back() - center)) { - Polygons line_extension; - line_extension.addLine(line.line_.back(), line_end); - coord_t line_length_before = line_extension.polyLineLength(); - Polygons actually_forbidden = volumes_.getAvoidance( + OpenLinesSet line_extension; + line_extension.addSegment(line.line_.back(), line_end); + coord_t line_length_before = line_extension.length(); + Shape actually_forbidden = volumes_.getAvoidance( 0, line.layer_idx_, (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, support_rests_on_model, true); - line_extension = actually_forbidden.differencePolyLines(line_extension); + line_extension = actually_forbidden.difference(line_extension); - if (line_extension.polyLineLength() + EPSILON < line_length_before) + if (line_extension.length() + EPSILON < line_length_before) { for (auto line_part : line_extension) { @@ -703,8 +705,8 @@ void SupportCradleGeneration::cleanCradleLineOverlaps() } if (bounding_box_current.hit(AABB(cradle_line_inner->line_))) { - Polygon& outer_line = cradle_line->line_; - Polygon& inner_line = cradle_line_inner->line_; + OpenPolyline& outer_line = cradle_line->line_; + OpenPolyline& inner_line = cradle_line_inner->line_; Point2LL intersect; if (LinearAlg2D::lineLineIntersection(outer_line.front(), outer_line.back(), inner_line.front(), inner_line.back(), intersect) && ! LinearAlg2D::pointIsProjectedBeyondLine(intersect, outer_line.front(), outer_line.back()) @@ -872,8 +874,6 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor for (size_t cradle_height = 0; cradle_height <= cradle.config_->cradle_layers_; cradle_height++) { - Polygons line_tips; - std::vector> all_tips_center; // generate trapezoid line tip with front width of support line width, back cradle_width. @@ -885,7 +885,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor continue; } TreeSupportCradleLine* cradle_line = line_opt.value(); - Polygon line = cradle_line->line_; + OpenPolyline line = cradle_line->line_; coord_t current_cradle_line_width = cradle.config_->cradle_line_width_; @@ -893,7 +893,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor coord_t triangle_length = cradle.config_->cradle_line_count_ <= 2 ? 0 : ((current_cradle_line_width - support_line_width) / 2) * tan(std::numbers::pi / 2 - assumed_half_center_angle); - const coord_t line_length = line.polylineLength(); + const coord_t line_length = line.length(); if (triangle_length >= line_length + cradle.config_->cradle_line_width_) { triangle_length = line_length + cradle.config_->cradle_line_width_; @@ -936,25 +936,25 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor all_tips_center.emplace_back(center_up, center_down); Polygon line_tip; - line_tip.add(back_down); + line_tip.push_back(back_down); if (current_cradle_line_width == cradle.config_->cradle_line_width_) { coord_t distance_end_front = line_length - triangle_length + cradle.config_->cradle_line_width_ - tip_shift; Point2LL line_end_down = back_down + normal(direction, distance_end_front); Point2LL line_end_up = back_up + normal(direction, distance_end_front); - line_tip.add(line_end_down); - line_tip.add(line_end_up); + line_tip.push_back(line_end_down); + line_tip.push_back(line_end_up); } - line_tip.add(back_up); - line_tip.add(center_up); - line_tip.add(center_down); + line_tip.push_back(back_up); + line_tip.push_back(center_up); + line_tip.push_back(center_down); if (line_tip.area() < 0) { line_tip.reverse(); } - cradle_line->area_.add(line_tip); + cradle_line->area_.push_back(line_tip); - Polygons anti_preferred = cradle_line->area_.offset(xy_distance); + Shape anti_preferred = cradle_line->area_.offset(xy_distance); std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); for (size_t z_distance_idx = 0; z_distance_idx < z_distance_top_layers; z_distance_idx++) { @@ -963,35 +963,34 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } } - Polygons shadow = cradle.shadow_[0]; - Polygons cradle_base = shadow; + Shape shadow = cradle.shadow_[0]; + Shape cradle_base = shadow; if (support_roof_layers) { - Polygons cut_line_base; - Polygons first_cradle_areas; + Shape cut_line_base; + Shape first_cradle_areas; if (cradle.config_->large_cradle_base_) { // If a large cradle base is used there needs to be a small hole cut into it to ensure that there will be a line for the pointy overhang to rest // on. This line is just a diagonal though the original pointy overhang area (from min to max). Technically this line is covering more than just // the overhang, but that should not cause any issues. - Point2LL min_p = cradle_base.min(); - Point2LL max_p = cradle_base.max(); - Polygons rest_line; - rest_line.addLine(min_p, max_p); - cut_line_base = rest_line.offsetPolyLine(small_hole_size); + AABB cradle_base_aabb = AABB(cradle_base); + OpenLinesSet rest_line; + rest_line.addSegment(cradle_base_aabb.min_, cradle_base_aabb.max_); + cut_line_base = rest_line.offset(small_hole_size); } { std::lock_guard critical_section_cradle(critical_support_free_areas_and_cradle_areas); for (size_t interface_down = 0; interface_down < layer_idx && interface_down < support_roof_layers; interface_down++) { - support_free_areas_[layer_idx - interface_down].add(cut_line_base); + support_free_areas_[layer_idx - interface_down].push_back(cut_line_base); } } if (cradle.config_->large_cradle_base_) { cradle_base = cradle_base.offset(cradle.config_->cradle_support_base_area_radius_, ClipperLib::jtRound); - Polygons center_removed = cradle_base.difference(cut_line_base); + Shape center_removed = cradle_base.difference(cut_line_base); if (center_removed.area() > 1) { cradle_base = center_removed; @@ -1000,16 +999,16 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor else if (cradle.config_->cradle_base_roof_) { // collect all inner points and connect to center for thin cradle base - Polygons connected_cradle_base; + OpenLinesSet connected_cradle_base; for (size_t line_idx = 0; line_idx < cradle.lines_.size(); line_idx++) { std::optional line_opt = cradle.getCradleLineOfIndex(layer_idx + cradle.config_->cradle_z_distance_layers_ + 1, line_idx); if (line_opt) { - connected_cradle_base.addLine(cradle.getCenter(line_opt.value()->layer_idx_), line_opt.value()->line_.front()); + connected_cradle_base.addSegment(cradle.getCenter(line_opt.value()->layer_idx_), line_opt.value()->line_.front()); } } - cradle_base = connected_cradle_base.offsetPolyLine(cradle.config_->cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); + cradle_base = connected_cradle_base.offset(cradle.config_->cradle_line_width_ / 2 + EPSILON).unionPolygons(cradle_base); } } @@ -1017,13 +1016,13 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor if (cradle.config_->cradle_lines_roof_) { - Polygons forbidden_here = volumes_.getAvoidance( + Shape forbidden_here = volumes_.getAvoidance( 0, layer_idx, (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, support_rests_on_model, ! xy_overrides); - std::vector> roofs; + std::vector> roofs; if(cradle.is_roof_) { @@ -1045,8 +1044,8 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor for (auto roof_area_pair : roofs) { - Polygons roof_area_before = roof_area_pair.first; - Polygons full_overhang_area = TreeSupportUtils::safeOffsetInc( + Shape roof_area_before = roof_area_pair.first; + Shape full_overhang_area = TreeSupportUtils::safeOffsetInc( roof_area_pair.first, roof_outset, forbidden_here, @@ -1057,7 +1056,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor &simplifyer); for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) { - const Polygons forbidden_next = volumes_.getAvoidance( + const Shape forbidden_next = volumes_.getAvoidance( 0, layer_idx - (dtt_roof + 1), (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, @@ -1080,21 +1079,21 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor { if (dtt_roof < cradle.base_below_.size()) { - cradle.base_below_[dtt_roof].add(full_overhang_area); + cradle.base_below_[dtt_roof].push_back(full_overhang_area); } else { cradle.base_below_.emplace_back(full_overhang_area); } } - const Polygons forbidden_before = volumes_.getAvoidance( + const Shape forbidden_before = volumes_.getAvoidance( 0, layer_idx - dtt_roof, (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, support_rests_on_model, ! xy_overrides); - Polygons supported_by_roof_below = TreeSupportUtils::safeOffsetInc( //todo better safeStepOffset values to improve performance + Shape supported_by_roof_below = TreeSupportUtils::safeOffsetInc( //todo better safeStepOffset values to improve performance full_overhang_area, max_roof_movement, forbidden_before, @@ -1103,7 +1102,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor 1, support_line_width, &simplifyer); - Polygons overhang_part = roof_area_before.difference(supported_by_roof_below); + Shape overhang_part = roof_area_before.difference(supported_by_roof_below); if (overhang_part.area() > EPSILON) { OverhangInformation cradle_overhang(overhang_part, false, cradle_data_[mesh_idx][layer_idx][cradle_idx], layer_idx - dtt_roof, roof_area_pair.second); @@ -1133,7 +1132,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor if(!cradle.is_roof_) { - Polygons forbidden_here = volumes_.getAvoidance( + Shape forbidden_here = volumes_.getAvoidance( 0, layer_idx, (only_gracious_ || ! support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, @@ -1207,7 +1206,7 @@ void SupportCradleGeneration::generate(const SliceDataStorage& storage) generateCradleLineAreasAndBase(storage); } -void SupportCradleGeneration::pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx) +void SupportCradleGeneration::pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx) { if(target.size() < cradle_data_[mesh_idx].size()) @@ -1226,7 +1225,7 @@ void SupportCradleGeneration::pushCradleData(std::vector(mesh.settings, "support_tree_enforce_initial_layer_diameter")) , large_cradle_line_tips_(retrieveSetting(mesh.settings, "support_tree_large_cradle_line_tips")) @@ -482,7 +482,7 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m dropOverhangAreas(mesh, dropped_overhangs, true); } - std::vector all_cradle_areas(cradle_data.size()); // All cradle areas. Later offset by min xy distance. + std::vector all_cradle_areas(cradle_data.size()); // All cradle areas. Later offset by min xy distance. for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) @@ -491,12 +491,12 @@ void TreeSupportTipGenerator::calculateRoofAreas(const cura::SliceMeshStorage& m { for (auto [height_idx, line] : lines | ranges::views::enumerate) { - all_cradle_areas[line.layer_idx_].add(line.area_); + all_cradle_areas[line.layer_idx_].push_back(line.area_); } } for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - all_cradle_areas[layer_idx - base_idx].add(base); + all_cradle_areas[layer_idx - base_idx].push_back(base); } } } @@ -807,7 +807,7 @@ void TreeSupportTipGenerator::addLinesAsInfluenceAreas( void TreeSupportTipGenerator::removeUselessAddedPoints(std::vector>& move_bounds, SliceDataStorage& storage, std::vector>& cradle_data) { - std::vector all_cradle_roofs(storage.support.supportLayers.size()); + std::vector all_cradle_roofs(storage.support.supportLayers.size()); for (auto [layer_idx, cradles] : cradle_data | ranges::views::enumerate) { for (auto cradle : cradles) @@ -816,7 +816,7 @@ void TreeSupportTipGenerator::removeUselessAddedPoints(std::vectorbase_below_ | ranges::views::enumerate) { - all_cradle_roofs[layer_idx - base_idx].add(base); + all_cradle_roofs[layer_idx - base_idx].push_back(base); } } } @@ -906,7 +906,7 @@ void TreeSupportTipGenerator::generateTips( std::vector> all_cradles_requiring_support(cradle_data.size()); - std::vector all_cradle_areas(cradle_data.size()); + std::vector all_cradle_areas(cradle_data.size()); for (LayerIndex layer_idx = 0; layer_idx < cradle_data.size(); layer_idx++) { for (size_t cradle_idx = 0; cradle_idx < cradle_data[layer_idx].size(); cradle_idx++) @@ -929,12 +929,12 @@ void TreeSupportTipGenerator::generateTips( all_cradle_areas.resize(line.layer_idx_+1); } - all_cradle_areas[line.layer_idx_].add(line.area_); + all_cradle_areas[line.layer_idx_].push_back(line.area_); } } for (auto [base_idx, base] : cradle_data[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) { - all_cradle_areas[layer_idx - base_idx].add(base); + all_cradle_areas[layer_idx - base_idx].push_back(base); } } } @@ -957,7 +957,7 @@ void TreeSupportTipGenerator::generateTips( = (config_.support_pattern == EFillMethod::TRIANGLES ? 3 : (config_.support_pattern == EFillMethod::GRID ? 2 : 1)) * connect_length; bool force_tip_to_roof = (current_tip_radius * current_tip_radius * std::numbers::pi > minimum_roof_area_ * (1000 * 1000)) && ! use_fake_roof_ && support_roof_layers_; - Polygons relevant_forbidden = volumes_.getAvoidance( + Shape relevant_forbidden = volumes_.getAvoidance( config_.getRadius(0), layer_idx, (only_gracious_ || ! config_.support_rests_on_model) ? AvoidanceType::FAST : AvoidanceType::COLLISION, @@ -1045,9 +1045,9 @@ void TreeSupportTipGenerator::generateTips( // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible. { - Polygons already_supported = support_roof_drawn_[layer_idx]; - already_supported.add(all_cradle_areas[layer_idx]); - already_supported.add(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. + Shape already_supported = support_roof_drawn_[layer_idx]; + already_supported.push_back(all_cradle_areas[layer_idx]); + already_supported.push_back(support_free_areas[layer_idx]); // While point there are not supported, there may be no support anyway. already_supported = already_supported.unionPolygons(); while (extra_total_offset_acc + config_.support_line_width / 8 < extra_outset) //+mesh_config_.support_line_width / 8 to avoid calculating very small (useless) offsets because of rounding errors. @@ -1132,8 +1132,8 @@ void TreeSupportTipGenerator::generateTips( return relevant_forbidden_below.inside(p.first, true); }; - Polygons already_supported = support_roof_drawn_[layer_idx - lag_ctr]; - already_supported.add(support_free_areas[layer_idx - lag_ctr]); // While point there are not supported, there may be no support anyway. + Shape already_supported = support_roof_drawn_[layer_idx - lag_ctr]; + already_supported.push_back(support_free_areas[layer_idx - lag_ctr]); // While point there are not supported, there may be no support anyway. already_supported = already_supported.unionPolygons(); // Remove all points that are for some reason are already supported @@ -1173,12 +1173,12 @@ void TreeSupportTipGenerator::generateTips( for (OverhangInformation overhang_data : overhang_processing) { - Polygons overhang_outset = overhang_data.overhang_; - const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.polygonLength() / connect_length)); + Shape overhang_outset = overhang_data.overhang_; + const size_t min_support_points = std::max(coord_t(2), std::min(coord_t(EPSILON), overhang_outset.length() / connect_length)); std::vector overhang_lines; bool only_lines = true; - Polygons polylines; + OpenLinesSet polylines; // The tip positions are determined here. if (overhang_data.isCradleLine() && overhang_data.cradle_->config_->cradle_line_width_ / 2 < std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_)) @@ -1187,7 +1187,8 @@ void TreeSupportTipGenerator::generateTips( = overhang_data.cradle_->getCradleLineOfIndex(overhang_data.cradle_layer_idx_, overhang_data.cradle_line_idx_); if(cradle_line_opt) { - Polygons line = cradle_line_opt.value()->line_.offset(0); + OpenLinesSet line; + line.push_back(cradle_line_opt.value()->line_); coord_t cradle_line_tip_radius = std::max(current_tip_radius, overhang_data.cradle_->config_->cradle_support_base_area_radius_); polylines = ensureMaximumDistancePolyline(line, cradle_line_tip_radius, 2, false); } @@ -1289,7 +1290,7 @@ void TreeSupportTipGenerator::generateTips( if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) { - Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1]; + Shape all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1]; placed_fake_roof_areas[layer_idx].emplace_back(all_roof_fractional, support_roof_line_distance_, true); } } @@ -1297,8 +1298,8 @@ void TreeSupportTipGenerator::generateTips( { if (config_.z_distance_top % config_.layer_height != 0 && layer_idx > 0) { - Polygons all_roof_below = support_roof_drawn_[layer_idx - 1]; - Polygons all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1].intersection(all_roof_below).difference(support_roof_drawn_[layer_idx]); + Shape all_roof_below = support_roof_drawn_[layer_idx - 1]; + Shape all_roof_fractional = support_roof_drawn_fractional_[layer_idx - 1].intersection(all_roof_below).difference(support_roof_drawn_[layer_idx]); if (config_.support_wall_count > 0 && config_.support_roof_wall_count == 0) // Need to check every area whether it has lines { diff --git a/src/bridge.cpp b/src/bridge.cpp index 48d9c710b3..848ce3852e 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -72,7 +72,7 @@ double bridgeAngle( if (! support_layer->support_roof.empty()) { - Polygons all_roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); + Shape all_roofs = support_layer->getTotalAreaFromParts(support_layer->support_roof); AABB support_roof_bb(all_roofs); if (boundary_box.hit(support_roof_bb)) { diff --git a/src/geometry/LinesSet.cpp b/src/geometry/LinesSet.cpp index ccfd7259a6..d0347a779e 100644 --- a/src/geometry/LinesSet.cpp +++ b/src/geometry/LinesSet.cpp @@ -130,9 +130,9 @@ coord_t LinesSet::length() const } template -Shape LinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset) const +Shape LinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt) const { - return offset(outer_offset).difference(offset(-inner_offset)); + return offset(outer_offset, jt).difference(offset(-inner_offset, jt)); } template @@ -315,7 +315,7 @@ template void OpenLinesSet::removeAt(size_t index); template void OpenLinesSet::splitIntoSegments(OpenLinesSet& result) const; template OpenLinesSet OpenLinesSet::splitIntoSegments() const; template coord_t OpenLinesSet::length() const; -template Shape OpenLinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset) const; +template Shape OpenLinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt) const; template void OpenLinesSet::translate(const Point2LL& delta); template void OpenLinesSet::removeDegenerateVerts(); template void OpenLinesSet::addPaths(ClipperLib::Clipper& clipper, ClipperLib::PolyType PolyTyp) const; @@ -329,7 +329,7 @@ template void ClosedLinesSet::removeAt(size_t index); template void ClosedLinesSet::splitIntoSegments(OpenLinesSet& result) const; template OpenLinesSet ClosedLinesSet::splitIntoSegments() const; template coord_t ClosedLinesSet::length() const; -template Shape ClosedLinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset) const; +template Shape ClosedLinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt) const; template void ClosedLinesSet::translate(const Point2LL& delta); template void ClosedLinesSet::removeDegenerateVerts(); template void ClosedLinesSet::addPaths(ClipperLib::Clipper& clipper, ClipperLib::PolyType PolyTyp) const; @@ -344,7 +344,7 @@ template void LinesSet::removeAt(size_t index); template void LinesSet::splitIntoSegments(OpenLinesSet& result) const; template OpenLinesSet LinesSet::splitIntoSegments() const; template coord_t LinesSet::length() const; -template Shape LinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset) const; +template Shape LinesSet::createTubeShape(const coord_t inner_offset, const coord_t outer_offset, const ClipperLib::JoinType jt) const; template void LinesSet::translate(const Point2LL& delta); template void LinesSet::removeDegenerateVerts(); template void LinesSet::addPaths(ClipperLib::Clipper& clipper, ClipperLib::PolyType PolyTyp) const; diff --git a/src/geometry/Shape.cpp b/src/geometry/Shape.cpp index ed5dffbbf0..a52efa8697 100644 --- a/src/geometry/Shape.cpp +++ b/src/geometry/Shape.cpp @@ -380,6 +380,49 @@ OpenLinesSet Shape::intersection(const LinesSet& polylines, bool resti return result_lines; } +template +OpenLinesSet Shape::difference(const LinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const +{ + + OpenLinesSet split_polylines = polylines.splitIntoSegments(); + + ClipperLib::PolyTree result; + ClipperLib::Clipper clipper(clipper_init); + split_polylines.addPaths(clipper, ClipperLib::ptSubject); + addPaths(clipper, ClipperLib::ptClip); + clipper.Execute(ClipperLib::ctDifference, result); + ClipperLib::Paths result_paths; + ClipperLib::OpenPathsFromPolyTree(result, result_paths); + + OpenLinesSet result_lines(std::move(result_paths)); + + if (restitch) + { + OpenLinesSet result_open_lines; + Shape result_closed_lines; + + const coord_t snap_distance = 10_mu; + OpenPolylineStitcher::stitch(result_lines, result_open_lines, result_closed_lines, max_stitch_distance, snap_distance); + + result_lines = std::move(result_open_lines); + // if open polylines got stitched into closed polylines, split them back up into open polylines again, because the result only admits open polylines + for (ClosedPolyline& closed_line : result_closed_lines) + { + if (! closed_line.empty()) + { + if (closed_line.size() > 2) + { + closed_line.push_back(closed_line.front()); + } + result_lines.emplace_back(std::move(closed_line.getPoints())); + } + } + } + + return result_lines; +} + + Shape Shape::xorPolygons(const Shape& other, ClipperLib::PolyFillType pft) const { if (empty()) @@ -1007,4 +1050,8 @@ template OpenLinesSet Shape::intersection(const OpenLinesSet& polylines, bool re template OpenLinesSet Shape::intersection(const ClosedLinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const; template OpenLinesSet Shape::intersection(const LinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const; +template OpenLinesSet Shape::difference(const OpenLinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const; +template OpenLinesSet Shape::difference(const ClosedLinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const; +template OpenLinesSet Shape::difference(const LinesSet& polylines, bool restitch, const coord_t max_stitch_distance) const; + } // namespace cura diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index c18e6f0cb7..f5ceffb5c1 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -715,7 +715,7 @@ void SliceDataStorage::initializePrimeTower() prime_tower_ = PrimeTower::createPrimeTower(*this); } -void SupportLayer::excludeAreasFromSupportParts(std::vector& parts, const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box) +void SupportLayer::excludeAreasFromSupportInfillAreas(std::vector& parts, const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box) { // record the indexes that need to be removed and do that after std::list to_remove_part_indices; // LIFO for removing diff --git a/src/support.cpp b/src/support.cpp index 93ca0b3de4..349a6ed922 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1721,7 +1721,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh const double minimum_roof_area = mesh.settings.get("minimum_roof_area"); std::vector& support_layers = storage.support.supportLayers; - Polygons roofs_before; + Shape roofs_before; for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ diff --git a/src/utils/AABB.cpp b/src/utils/AABB.cpp index 6707775c3f..3378706eaf 100644 --- a/src/utils/AABB.cpp +++ b/src/utils/AABB.cpp @@ -5,6 +5,7 @@ #include +#include "geometry/Polyline.h" #include "geometry/Polygon.h" #include "geometry/Shape.h" #include "utils/linearAlg2D.h" @@ -39,6 +40,13 @@ AABB::AABB(const Polygon& poly) calculate(poly); } +AABB::AABB(const Polyline& line) + : min_(POINT_MAX, POINT_MAX) + , max_(POINT_MIN, POINT_MIN) +{ + calculate(line); +} + Point2LL AABB::getMiddle() const { return (min_ + max_) / 2; @@ -92,6 +100,16 @@ void AABB::calculate(const Polygon& poly) } } +void AABB::calculate(const Polyline& line) +{ + min_ = Point2LL(POINT_MAX, POINT_MAX); + max_ = Point2LL(POINT_MIN, POINT_MIN); + for (const Point2LL& p : line) + { + include(p); + } +} + bool AABB::contains(const Point2LL& point) const { return point.X >= min_.X && point.X <= max_.X && point.Y >= min_.Y && point.Y <= max_.Y; From ae63e194acd75a67676d8abc9299dae20a0dc2a4 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:42:02 +0200 Subject: [PATCH 094/101] Fix cradle lines stopping because of too long jumps caused by xy distance settings. --- src/TreeSupportCradle.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 704b7fec9c..0d151188d1 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -400,6 +400,8 @@ void SupportCradleGeneration::generateCradleLines(std::vectorconfig_->cradle_length_ / 3; + constexpr bool ignore_xy_dist_for_jumps = true; const coord_t max_cradle_xy_distance = *std::max_element(cradle->config_->cradle_xy_distance_.begin(), cradle->config_->cradle_xy_distance_.end()); std::vector removed_directions(cradle->config_->cradle_line_count_); const auto& accumulated_model = cradle->shadow_; @@ -407,6 +409,7 @@ void SupportCradleGeneration::generateCradleLines(std::vectorgetCenter(layer_idx + idx); const coord_t current_cradle_xy_distance = cradle->config_->cradle_xy_distance_[idx]; + const coord_t previous_cradle_xy_distance = idx > 0 ? cradle->config_->cradle_xy_distance_[idx-1] : current_cradle_xy_distance; const coord_t current_cradle_length = cradle->config_->cradle_length_ + max_cradle_xy_distance - current_cradle_xy_distance; if (cradle->lines_.empty()) @@ -524,9 +527,18 @@ void SupportCradleGeneration::generateCradleLines(std::vectorconfig_->cradle_length_min_; - bool too_long_jump - = ! cradle->lines_[angle_idx].empty() && vSize(cradle->lines_[angle_idx].back().line_.front() - closer) > cradle->config_->cradle_length_ / 3; //todo better non arbitrary limit - // a cradle line should also be removed if there will be no way to support it + bool too_long_jump = false; + Point2LL closest_on_prev_segment; + if(! cradle->lines_[angle_idx].empty()) + { + closest_on_prev_segment = LinearAlg2D::getClosestOnLineSegment(closer, cradle->lines_[angle_idx].back().line_.front(), cradle->lines_[angle_idx].back().line_.back()); + Point2LL closest_on_prev_line = LinearAlg2D::getClosestOnLine(closer, cradle->lines_[angle_idx].back().line_.front(), cradle->lines_[angle_idx].back().line_.back()); + coord_t xy_distance_jump = std::max(coord_t(0), previous_cradle_xy_distance - current_cradle_xy_distance); + //todo if jump too long check if line could be shortened. Do that above! + too_long_jump = vSize(closest_on_prev_segment - closest_on_prev_line) - (ignore_xy_dist_for_jumps? xy_distance_jump : 0) > + max_cradle_jump_length_forward; //todo better non arbitrary limit + } + // a cradle line should also be removed if there will no way to support it if (idx >= cradle->config_->cradle_z_distance_layers_ + 1) { const Shape& actually_forbidden = volumes_.getAvoidance( From ad5155134548e0a688526fc06e60412c261d2a0b Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:38:57 +0200 Subject: [PATCH 095/101] Fix for issue https://github.com/Ultimaker/Cura/issues/19586 in the cradle branch --- src/support.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/support.cpp b/src/support.cpp index 349a6ed922..f08b07345e 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1712,7 +1712,8 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh { return; } - const coord_t z_distance_top = round_up_divide(mesh.settings.get("support_top_distance"), layer_height); // Number of layers between support roof and model. + const coord_t z_distance_top = mesh.settings.get("support_top_distance"); + const coord_t z_distance_top_layers = round_up_divide(z_distance_top, layer_height); // Number of layers between support roof and model. const coord_t roof_line_width = mesh_group_settings.get("support_roof_extruder_nr").settings_.get("support_roof_line_width"); const coord_t roof_outline_offset = mesh_group_settings.get("support_roof_extruder_nr").settings_.get("support_roof_offset"); const coord_t roof_line_distance = mesh.settings.get("support_roof_line_distance"); @@ -1722,21 +1723,21 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh std::vector& support_layers = storage.support.supportLayers; Shape roofs_before; - for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top) - 1; layer_idx >= 0; --layer_idx) + for (LayerIndex layer_idx = static_cast(support_layers.size() - z_distance_top_layers) - 1; layer_idx >= 0; --layer_idx) { const LayerIndex top_layer_idx_above{ - std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top }) + std::min(LayerIndex{ support_layers.size() - 1 }, LayerIndex{ layer_idx + roof_layer_count + z_distance_top_layers }) }; // Maximum layer of the model that generates support roof. Shape mesh_outlines; - for (auto layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top - 1; layer_idx_above -= 1) + for (auto layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top_layers - 1; layer_idx_above -= 1) { mesh_outlines.push_back(mesh.layers[layer_idx_above].getOutlines()); } Shape roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); // If roof is added as fractional, even though non can exist because remaining z distance is 0 it will be regular roof. - support_layers[layer_idx].fillRoofParts(layer_idx > 0 ? roofs.intersection(roofs_before) : roofs, roof_line_width, roof_wall_line_count); - if (layer_idx > 0) + support_layers[layer_idx].fillRoofParts(layer_idx > 0 && z_distance_top % layer_height != 0? roofs.intersection(roofs_before) : roofs, roof_line_width, roof_wall_line_count); + if (layer_idx > 0 && z_distance_top % layer_height != 0) { support_layers[layer_idx].fillRoofParts(roofs.difference(roofs_before), roof_line_width, roof_wall_line_count, true); } @@ -1745,7 +1746,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh } // Remove support in between the support roof and the model. Subtracts the roof polygons from the support polygons on the layers above it. - for (auto [layer_idx, support_layer] : support_layers | ranges::views::enumerate | ranges::views::drop(1) | ranges::views::drop_last(z_distance_top)) + for (auto [layer_idx, support_layer] : support_layers | ranges::views::enumerate | ranges::views::drop(1) | ranges::views::drop_last(z_distance_top_layers)) { if (support_layer.support_roof.empty()) { @@ -1753,7 +1754,7 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh } int lower = static_cast(layer_idx); - int upper = std::min(static_cast(layer_idx + roof_layer_count + z_distance_top + 5), static_cast(global_support_areas_per_layer.size()) - 1); + int upper = std::min(static_cast(layer_idx + roof_layer_count + z_distance_top_layers + 5), static_cast(global_support_areas_per_layer.size()) - 1); for (Shape& global_support : global_support_areas_per_layer | ranges::views::slice(lower, upper)) { global_support = global_support.difference(support_layer.getTotalAreaFromParts(support_layer.support_roof)); From 6e73eb80a2c6c38b62e50c13b178d6a33edafb05 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:49:54 +0200 Subject: [PATCH 096/101] Add ability to manually place cradles, refactor the way support modifier meshes are stored and fix a small bug preventing non drop down support meshes from slicing correctly --- include/TreeModelVolumes.h | 5 - include/TreeSupport.h | 4 +- include/TreeSupportCradle.h | 67 ++++---- include/TreeSupportEnums.h | 9 ++ include/sliceDataStorage.h | 46 +++++- src/TreeModelVolumes.cpp | 12 +- src/TreeSupport.cpp | 30 ++-- src/TreeSupportCradle.cpp | 302 +++++++++++++++++++----------------- src/support.cpp | 89 +++++++++-- 9 files changed, 358 insertions(+), 206 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 8236eaab0a..fc9dd746f9 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -511,11 +511,6 @@ class TreeModelVolumes */ RestPreference support_rest_preference_; - /*! - * \brief How tall the cradle will at most be. - */ - size_t max_cradle_layers = 0; - /*! * \brief Largest DTT a cradle supporting tip may have. */ diff --git a/include/TreeSupport.h b/include/TreeSupport.h index e89d51f66d..619230c1de 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -37,7 +37,6 @@ constexpr auto SUPPORT_TREE_MINIMUM_FAKE_ROOF_AREA = 100.0; constexpr auto SUPPORT_TREE_MINIMUM_FAKE_ROOF_LAYERS = 1; constexpr auto SUPPORT_TREE_MINIMUM_ROOF_AREA_HARD_LIMIT = false; constexpr auto SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL = false; -constexpr auto SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true; constexpr coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = 1000; constexpr auto SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5; constexpr size_t SUPPORT_TREE_PRE_EXPONENTIAL_STEPS = 1; @@ -100,9 +99,10 @@ class TreeSupport * * \param storage[in] Background storage to access meshes. * \param currently_processing_meshes[in] Indexes of all meshes that are processed in this iteration + * \param top_most_cradle_layer_idx[in] Layer_idx of the top-most cradle. * \return Uppermost layer precalculated. -1 if no layer were precalculated as no overhang is present. */ - LayerIndex precalculate(const SliceDataStorage& storage, std::vector currently_processing_meshes); + LayerIndex precalculate(const SliceDataStorage& storage, std::vector currently_processing_meshes, LayerIndex top_most_cradle_layer_idx); /*! diff --git a/include/TreeSupportCradle.h b/include/TreeSupportCradle.h index 4560b77fbd..e1ae8106e6 100644 --- a/include/TreeSupportCradle.h +++ b/include/TreeSupportCradle.h @@ -246,15 +246,16 @@ struct TreeSupportCradle std::vector centers_; std::vector shadow_; std::unordered_map> overhang_; - + CradlePlacementMethod cradle_placement_method_; const std::shared_ptr config_; size_t mesh_idx_; - TreeSupportCradle(LayerIndex layer_idx, Point2LL center, bool roof, std::shared_ptr config, size_t mesh_idx) + TreeSupportCradle(LayerIndex layer_idx, Point2LL center, bool roof, CradlePlacementMethod cradle_placement_method, std::shared_ptr config, size_t mesh_idx) : layer_idx_(layer_idx) , centers_({ center }) , is_roof_(roof) + , cradle_placement_method_(cradle_placement_method) , config_ (config) , mesh_idx_(mesh_idx) { @@ -383,14 +384,19 @@ struct CradlePresenceInformation class SupportCradleGeneration { public: - + /*! + * \brief Add meshes to generate cradles. + * \param storage[in] The storage that the mesh is in. + * \param mesh_idx[in] The idx of the mesh that is currently processed. + */ + void addMeshToCradleCalculation(const SliceDataStorage& storage, size_t mesh_idx); /*! - * \brief Add meshes to generate cradles and generate cradle centers. Not threadsafe! - * \param mesh[in] The mesh that is currently processed. + * \brief Generates mesh-specific cradle data. Calls addMeshToCradleCalculation if mesh was not already added. Not threadsafe! + * \param storage[in] The storage that the mesh is in. * \param mesh_idx[in] The idx of the mesh that is currently processed. */ - void addMeshToCradleCalculation(const SliceMeshStorage& mesh, size_t mesh_idx); + void generateCradleForMesh(const SliceDataStorage& storage, size_t mesh_idx); /*! * \brief Generate all cradle areas and line areas for the previously added meshes. Not threadsafe! Should only called once. @@ -407,54 +413,57 @@ class SupportCradleGeneration */ void pushCradleData(std::vector>& target, std::vector& support_free_areas, size_t mesh_idx); + /*! + * \brief Give the largest layer index that contains the first layer of a cradle. Only considers meshes that were added with addMeshToCradleCalculation. + */ + LayerIndex getTopMostCradleLayer() + { + return top_most_cradle_layer_; + } SupportCradleGeneration(const SliceDataStorage& storage, TreeModelVolumes& volumes_); private: struct UnsupportedAreaInformation { - UnsupportedAreaInformation(const Shape area, size_t index, size_t height, coord_t accumulated_supportable_overhang) + UnsupportedAreaInformation(const Shape area, LayerIndex layer_idx, size_t height, coord_t accumulated_supportable_overhang) : area{ area } - , index{ index } + , layer_idx{ layer_idx } , height{ height } , accumulated_supportable_overhang{ accumulated_supportable_overhang } { } const Shape area; - size_t index; + LayerIndex layer_idx; size_t height; coord_t accumulated_supportable_overhang; - }; + CradlePlacementMethod support_required = CradlePlacementMethod::NONE; + // Contains identifiers of all cradles below + std::vector cradles_below; -/*! - * \brief Provides areas that do not have a connection to the buildplate or a certain height. - * \param mesh_idx[in] The idx of the mesh. - * \param layer_idx The layer said area is on. - * \param idx_of_area The index of the area. Only areas that either rest on this area or this area rests on (depending on above) will be returned - * \param above Should the areas above it, that rest on this area should be returned (if true) or if areas that this area rests on (if false) should be returned. - * \return A vector containing the areas, how many layers of material they have below them and the idx of each area usable to get the next one layer above. - */ -std::vector getUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx, size_t idx_of_area, bool above); + std::vector areas_above; + std::vector areas_below; + }; -/*! + /*! * \brief Provides areas that do not have a connection to the buildplate or any other non support material below it. * \param mesh_idx[in] The idx of the mesh. * \param layer_idx The layer said area is on. * \return A vector containing the areas, how many layers of material they have below them (always 0) and the idx of each area usable to get the next one layer above. */ -std::vector getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx); + std::vector getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx); -/*! + /*! * \brief Calculates which parts of the model to not connect with the buildplate and how many layers of material is below them (height). * Results are stored in a cache. * Only area up to the required maximum height are stored. - * \param mesh[in] The mesh that is currently processed. + * \param storage[in] The storage meshes are in. * \param mesh_idx[in] The idx of the mesh. - */ -void calculateFloatingParts(const SliceMeshStorage& mesh, size_t mesh_idx); + */ + void calculateFloatingParts(const SliceDataStorage& storage, size_t mesh_idx); -/*! + /*! * \brief Generate the center points of all generated cradles. * \param mesh[in] The mesh that is currently processed. * \param mesh_idx[in] The idx of the mesh. @@ -503,9 +512,9 @@ TreeModelVolumes& volumes_; */ const bool only_gracious_ = false; -mutable std::vector>> floating_parts_cache_; -mutable std::vector>>> floating_parts_map_; -mutable std::vector>>> floating_parts_map_below_; +LayerIndex top_most_cradle_layer_ = -1; + +mutable std::vector>> floating_parts_cache_; std::unique_ptr critical_floating_parts_cache_ = std::make_unique(); diff --git a/include/TreeSupportEnums.h b/include/TreeSupportEnums.h index f3c959c8dd..ce80188fdd 100644 --- a/include/TreeSupportEnums.h +++ b/include/TreeSupportEnums.h @@ -28,6 +28,15 @@ enum class AvoidanceType COLLISION }; +enum class CradlePlacementMethod +{ + NONE, + AUTOMATIC_POINTY, + AUTOMATIC_SIDE, + MANUAL_POINTY, + MANUAL_SIDE +}; + } // namespace cura #endif // CURAENGINE_TREESUPPORTENUMS_H diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 1f01cfceb2..cadb2b3bda 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -215,7 +215,6 @@ class SupportLayer std::vector support_roof; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom. Shape support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support Shape support_mesh; //!< Areas from support meshes which should NOT be supported by more support - Shape anti_overhang; //!< Areas where no overhang should be detected. Shape getTotalAreaFromParts(const std::vector& parts, PartsFilter filter = PartsFilter::NoFilter) const { @@ -317,6 +316,50 @@ class SupportLayer }; +class SupportGenerationModifier +{ + bool is_anti_support_; + bool is_cradle_modifier_; + bool is_anti_overhang_; + +public: + SupportGenerationModifier(Settings settings, size_t size) + : settings_(settings) + , areas_(size) + , is_anti_support_(settings.get("anti_support_mesh")) + , is_cradle_modifier_(settings.get("cradle_modifier_mesh")) + , is_anti_overhang_(! is_anti_support_ && ! is_cradle_modifier_ && settings.get("anti_overhang_mesh")) + { + } + + Settings settings_; + std::vector areas_; + + bool isAntiOverhang() const + { + return is_anti_overhang_; + } + + bool isAntiSupport() const + { + return is_anti_support_; + } + + bool isCradleModifier() const + { + return is_cradle_modifier_; + } + + void addArea(const Shape& area, LayerIndex layer_idx) + { + if (areas_.size() <= layer_idx) + { + areas_.resize(layer_idx + 1); + } + areas_[layer_idx].push_back(area); + } +}; + class SupportStorage { public: @@ -330,6 +373,7 @@ class SupportStorage std::vector support_bottom_angles; //!< a list of angle values which is cycled through to determine the infill angle of each layer std::vector supportLayers; + std::vector supportGenerationModifiers; std::shared_ptr cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern SupportStorage(); diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 66e657cc03..f6ed170868 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -80,9 +80,6 @@ TreeModelVolumes::TreeModelVolumes( min_maximum_deviation = std::min(min_maximum_deviation, data_pair.first.get("meshfix_maximum_deviation")); min_maximum_resolution = std::min(min_maximum_resolution, data_pair.first.get("meshfix_maximum_resolution")); min_maximum_area_deviation = std::min(min_maximum_area_deviation, data_pair.first.get("meshfix_maximum_extrusion_area_deviation")); - const coord_t extra_cradle_distance = round_divide(retrieveSetting(data_pair.first, "support_tree_cradle_z_distance"), config.layer_height); - max_cradle_layers - = std::max(coord_t(max_cradle_layers), extra_cradle_distance + retrieveSetting(data_pair.first, "support_tree_cradle_height") / config.layer_height); max_cradle_dtt = std::max(max_cradle_dtt, config.tip_layers); // todo better estimation } @@ -151,10 +148,12 @@ TreeModelVolumes::TreeModelVolumes( { anti_overhang_[layer_idx].push_back(additional_excluded_areas[layer_idx]); } - - if (SUPPORT_TREE_AVOID_SUPPORT_BLOCKER) + for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) { - anti_overhang_[layer_idx].push_back(storage.support.supportLayers[layer_idx].anti_overhang); + if (support_modifier.isAntiSupport() && layer_idx < support_modifier.areas_.size()) + { + anti_overhang_[layer_idx].push_back(support_modifier.areas_[layer_idx]); + } } if (storage.prime_tower_) @@ -182,7 +181,6 @@ void TreeModelVolumes::precalculate(LayerIndex max_layer) { const auto t_start = std::chrono::high_resolution_clock::now(); precalculated_ = true; - max_layer = std::min(max_layer + max_cradle_layers, LayerIndex(layer_outlines_[current_outline_idx_].second.size() - 1)); // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant. // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex like initial layer diameter are only done in once. diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 74b04b5832..6dd88a2e61 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -149,8 +149,18 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) progress_offset, exclude); + const auto t_cradle_init = std::chrono::high_resolution_clock::now(); + + // ### Evaluate cradle placement. Topmost cradle layer is needed tor precalculation + std::vector> cradle_data; + SupportCradleGeneration cradle_gen(storage, volumes_); + for (size_t mesh_idx : processing.second) + { + cradle_gen.addMeshToCradleCalculation(storage, mesh_idx); + } + // ### Precalculate avoidances, collision etc. - const LayerIndex max_required_layer = precalculate(storage, processing.second); + const LayerIndex max_required_layer = precalculate(storage, processing.second, cradle_gen.getTopMostCradleLayer()); if (max_required_layer < 0) { spdlog::info("Support tree mesh group {} does not have any overhang. Skipping tree support generation for this support tree mesh group.", counter + 1); @@ -158,12 +168,9 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) } const auto t_precalc = std::chrono::high_resolution_clock::now(); - std::vector> cradle_data; - - SupportCradleGeneration cradle_gen(storage, volumes_); for (size_t mesh_idx : processing.second) { - cradle_gen.addMeshToCradleCalculation(*storage.meshes[mesh_idx], mesh_idx); + cradle_gen.generateCradleForMesh(storage, mesh_idx); } cradle_gen.generate(storage); const auto t_cradle = std::chrono::high_resolution_clock::now(); @@ -198,9 +205,9 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) drawAreas(move_bounds, storage, cradle_data); const auto t_draw = std::chrono::high_resolution_clock::now(); - const auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); + const auto dur_cradle_init = 0.001 * std::chrono::duration_cast(t_cradle_init - t_start).count(); + const auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_cradle_init).count(); const auto dur_cradle = 0.001 * std::chrono::duration_cast(t_cradle - t_precalc).count(); - const auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_cradle).count(); const auto dur_path = 0.001 * std::chrono::duration_cast(t_path - t_gen).count(); const auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); @@ -208,17 +215,17 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) const auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); spdlog::info( "Total time used creating Tree support for the currently grouped meshes: {} ms. Different subtasks:\n" - "Calculating Avoidance: {} ms Calculating Cradle: {} ms Creating initial influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " + "Calculating Avoidance: {} ms Calculating Cradle: {} ms of which {} ms were initialising Creating initial influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " "support {} ms", dur_total, dur_pre_gen, dur_cradle, + dur_cradle_init, dur_gen, dur_path, dur_place, dur_draw); - for (auto& layer : move_bounds) { for (auto elem : layer) @@ -232,10 +239,11 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) storage.support.generated = true; } -LayerIndex TreeSupport::precalculate(const SliceDataStorage& storage, std::vector currently_processing_meshes) +LayerIndex TreeSupport::precalculate(const SliceDataStorage& storage, std::vector currently_processing_meshes, LayerIndex top_most_cradle_layer_idx) { + // Calculate top most layer that is relevant for support. - LayerIndex max_layer = -1; + LayerIndex max_layer = top_most_cradle_layer_idx; for (size_t mesh_idx : currently_processing_meshes) { const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 0d151188d1..da156b0310 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -15,47 +15,32 @@ SupportCradleGeneration::SupportCradleGeneration(const SliceDataStorage& storage : volumes_(volumes_s) , cradle_data_(storage.meshes.size(), std::vector>(storage.print_layer_count)) , floating_parts_cache_(storage.meshes.size()) - , floating_parts_map_(storage.meshes.size()) - , floating_parts_map_below_(storage.meshes.size()) , support_free_areas_(storage.print_layer_count) { } -void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mesh, size_t mesh_idx) +void SupportCradleGeneration::calculateFloatingParts(const SliceDataStorage& storage, size_t mesh_idx) { - + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const coord_t layer_height = mesh.settings.get("layer_height"); const coord_t min_wall_line_width = mesh.settings.get("min_wall_line_width"); const size_t cradle_layers = retrieveSetting(mesh.settings, "support_tree_cradle_height") / layer_height; const double cradle_area_threshold = 1000 * 1000 * retrieveSetting(mesh.settings, "support_tree_maximum_pointy_area"); - LayerIndex max_layer = 0; - for (LayerIndex layer_idx = 0; layer_idx < mesh.overhang_areas.size(); layer_idx++) - { - if (! mesh.overhang_areas[layer_idx].empty()) - { - max_layer = layer_idx; - } - } - max_layer = std::min(max_layer + cradle_layers, LayerIndex(mesh.overhang_areas.size() - 1)); + LayerIndex max_layer = mesh.layers.size(); LayerIndex start_layer = 1; floating_parts_cache_[mesh_idx].resize(max_layer + 1); - floating_parts_map_[mesh_idx].resize(max_layer + 1); - floating_parts_map_below_[mesh_idx].resize(max_layer + 1); - std::mutex critical_sections; + std::mutex critical_floating_parts_cache; + std::mutex critical_manual_cradle_map; + std::vector> manual_cradle_map(storage.support.supportGenerationModifiers.size()); - Shape completely_supported = volumes_.getCollision(0, 0, true); - Shape layer_below = completely_supported; // technically wrong, but the xy distance error on layer 1 should not matter - for (size_t layer_idx = start_layer; layer_idx < max_layer; layer_idx++) + for (LayerIndex layer_idx = start_layer; layer_idx < max_layer; layer_idx++) { - Shape next_completely_supported; - // As the collision contains z distance and xy distance it cant be used to clearly identify which parts of the model are connected to the buildplate. Shape layer = mesh.layers[layer_idx].getOutlines(); - const std::vector layer_parts = layer.splitIntoParts(); cura::parallel_for( 0, @@ -64,23 +49,45 @@ void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mes { const SingleShape& part = layer_parts[part_idx]; AABB part_aabb(part); - bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(layer_below, part_aabb).intersection(part).empty(); - - if (! completely_supported.intersection(part).empty() || (! has_support_below && part.area() > cradle_area_threshold)) - { - std::lock_guard critical_section_add(critical_sections); - next_completely_supported.push_back(part); - return; - } + // Use all models not only this mesh to determine if it rests on something as otherwise cutting meshes lead to issues + bool has_support_below = ! PolygonUtils::clipPolygonWithAABB(storage.getLayerOutlines(layer_idx - 1, false, false), part_aabb).intersection(part).empty(); Shape overhang = mesh.overhang_areas[layer_idx].intersection(part); coord_t overhang_area = std::max(overhang.area(), std::numbers::pi * min_wall_line_width * min_wall_line_width); - if (! has_support_below) + if (! has_support_below || layer_idx == start_layer) { - std::lock_guard critical_section_add(critical_sections); - floating_parts_cache_[mesh_idx][layer_idx].emplace_back(part, floating_parts_cache_[mesh_idx][layer_idx].size(), 0, overhang_area); - floating_parts_map_[mesh_idx][layer_idx].emplace_back(std::vector()); - floating_parts_map_below_[mesh_idx][layer_idx].emplace_back(std::vector()); + UnsupportedAreaInformation* area_info = new UnsupportedAreaInformation(part, layer_idx, 0, overhang_area); + bool add_manual_cradle = false; + if (! has_support_below) + { + for (auto [idx, support_modifier] : storage.support.supportGenerationModifiers | ranges::views::enumerate) + { + bool possible_manual_cradle = support_modifier.isCradleModifier() && layer_idx < support_modifier.areas_.size() + && ! PolygonUtils::clipPolygonWithAABB(support_modifier.areas_[layer_idx], part_aabb).intersection(part).empty(); + + if (possible_manual_cradle) + { + add_manual_cradle = true; + std::lock_guard critical_section_add(critical_manual_cradle_map); + manual_cradle_map[idx].emplace(area_info); + } + } + } + bool add_automatic_cradle = ! has_support_below && overhang_area < cradle_area_threshold; + if (add_manual_cradle) + { + area_info->support_required = CradlePlacementMethod::MANUAL_POINTY; + ; + top_most_cradle_layer_ = layer_idx + cradle_layers + 1; + } + else if (add_automatic_cradle) + { + area_info->support_required = CradlePlacementMethod::AUTOMATIC_POINTY; + ; + top_most_cradle_layer_ = layer_idx + cradle_layers + 1; + } + std::lock_guard critical_section_add(critical_floating_parts_cache); + floating_parts_cache_[mesh_idx][layer_idx].emplace_back(area_info); return; } @@ -88,88 +95,87 @@ void SupportCradleGeneration::calculateFloatingParts(const SliceMeshStorage& mes coord_t supported_overhang_area = 0; bool add = false; std::vector idx_of_floating_below; + std::vector cradles_below; for (auto [idx, floating] : floating_parts_cache_[mesh_idx][layer_idx - 1] | ranges::views::enumerate) { - if (layer_idx > 1 && floating.height < cradle_layers - 1 && ! floating.area.intersection(part).empty()) + if (layer_idx > 1 && ! floating->area.intersection(part).empty()) { idx_of_floating_below.emplace_back(idx); - supported_overhang_area += floating.accumulated_supportable_overhang; - min_resting_on_layers = std::max(min_resting_on_layers, floating.height); + + supported_overhang_area += floating->accumulated_supportable_overhang; + min_resting_on_layers = std::max(min_resting_on_layers, floating->height); + cradles_below.insert(cradles_below.end(), floating->cradles_below.begin(), floating->cradles_below.end()); + if (floating->support_required != CradlePlacementMethod::NONE) + { + cradles_below.emplace_back(floating); + } add = true; } } - - - if (min_resting_on_layers < cradle_layers && add && overhang_area + supported_overhang_area < cradle_area_threshold) + bool add_manual_cradle = false; + std::vector manual_cradle_causes; + for (auto [idx, support_modifier] : storage.support.supportGenerationModifiers | ranges::views::enumerate) { - std::lock_guard critical_section_add(critical_sections); - for (size_t idx : idx_of_floating_below) + bool possible_manual_cradle = support_modifier.isCradleModifier() && layer_idx < support_modifier.areas_.size(); + LayerIndex previous_cradle_layer_idx = -1; + if (possible_manual_cradle) { - floating_parts_map_[mesh_idx][layer_idx - 1][idx].emplace_back(floating_parts_cache_[mesh_idx][layer_idx].size()); + { + std::lock_guard critical_section_add(critical_manual_cradle_map); + for (UnsupportedAreaInformation* cradle_below : cradles_below) + { + previous_cradle_layer_idx = std::max(previous_cradle_layer_idx, cradle_below->layer_idx); + if (manual_cradle_map[idx].contains(cradle_below)) + { + possible_manual_cradle = false; + break; + } + } + } + } + possible_manual_cradle &= previous_cradle_layer_idx == -1 || previous_cradle_layer_idx + cradle_layers < layer_idx; + Shape clipped_modifier = PolygonUtils::clipPolygonWithAABB(support_modifier.areas_[layer_idx], part_aabb); + if (possible_manual_cradle && ! clipped_modifier.intersection(part).empty()) + { + std::lock_guard critical_section_add(critical_manual_cradle_map); + for (UnsupportedAreaInformation* cradle_below : cradles_below) + { + manual_cradle_map[idx].emplace(cradle_below); + } + add_manual_cradle = true; + manual_cradle_causes.emplace_back(idx); } - - floating_parts_map_[mesh_idx][layer_idx].emplace_back(std::vector()); - floating_parts_map_below_[mesh_idx][layer_idx].emplace_back(idx_of_floating_below); - floating_parts_cache_[mesh_idx][layer_idx] - .emplace_back(part, floating_parts_cache_[mesh_idx][layer_idx].size(), min_resting_on_layers + 1, overhang_area + supported_overhang_area); } - else + if (add) { - std::lock_guard critical_section_add(critical_sections); - next_completely_supported.push_back(part); + std::lock_guard critical_section_add(critical_floating_parts_cache); + UnsupportedAreaInformation* area_info = new UnsupportedAreaInformation(part, layer_idx, min_resting_on_layers + 1, overhang_area + supported_overhang_area); + if (add_manual_cradle) + { + top_most_cradle_layer_ = layer_idx + cradle_layers + 1; + area_info->support_required = CradlePlacementMethod::MANUAL_SIDE; + std::lock_guard critical_section_add_to_manual_cradle_map(critical_manual_cradle_map); + for (size_t manual_cradle_idx : manual_cradle_causes) + { + manual_cradle_map[manual_cradle_idx].emplace(area_info); + } + spdlog::debug("Adding manual side-cradle at layer {}", layer_idx); + } + for (size_t idx : idx_of_floating_below) + { + area_info->areas_below.emplace_back(floating_parts_cache_[mesh_idx][layer_idx - 1][idx]); + area_info->cradles_below = cradles_below; + floating_parts_cache_[mesh_idx][layer_idx - 1][idx]->areas_above.emplace_back(area_info); + } + floating_parts_cache_[mesh_idx][layer_idx].emplace_back(area_info); } }); - layer_below = layer; - completely_supported = next_completely_supported; } } -std::vector SupportCradleGeneration::getUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx, size_t idx_of_area, bool above) +std::vector SupportCradleGeneration::getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx) { - std::vector result; - - if (layer_idx == 0) - { - return result; - } - - bool has_result = false; - - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - has_result = layer_idx < floating_parts_cache_[mesh_idx].size(); - } - - if (has_result) - { - std::lock_guard critical_section(*critical_floating_parts_cache_); - if (! floating_parts_cache_[mesh_idx][layer_idx].empty() && above) - { - for (size_t resting_idx : floating_parts_map_[mesh_idx][layer_idx - 1][idx_of_area]) - { - result.emplace_back(floating_parts_cache_[mesh_idx][layer_idx][resting_idx]); - } - } - else if (! floating_parts_cache_[mesh_idx][layer_idx - 1].empty() && ! above) - { - for (size_t resting_idx : floating_parts_map_below_[mesh_idx][layer_idx][idx_of_area]) - { - result.emplace_back(floating_parts_cache_[mesh_idx][layer_idx - 1][resting_idx]); - } - } - } - else - { - spdlog::error("Requested not calculated unsupported area."); - return result; - } - return result; -} - - -std::vector SupportCradleGeneration::getFullyUnsupportedArea(size_t mesh_idx, LayerIndex layer_idx) -{ - std::vector result; + std::vector result; if (layer_idx == 0) { @@ -187,7 +193,7 @@ std::vector SupportCradleGe std::lock_guard critical_section(*critical_floating_parts_cache_); for (auto [idx, floating_data] : floating_parts_cache_[mesh_idx][layer_idx] | ranges::views::enumerate) { - if (floating_data.height == 0) + if (floating_data->support_required != CradlePlacementMethod::NONE) { result.emplace_back(floating_data); } @@ -217,37 +223,40 @@ std::vector> SupportCradleGeneration::generateCr maximum_move_distance_slow = config.maximum_move_distance_slow; } std::mutex critical_dedupe; - std::vector> dedupe(mesh.overhang_areas.size()); + std::vector> dedupe(mesh.overhang_areas.size()); std::vector> result(mesh.overhang_areas.size()); cura::parallel_for( 1, - mesh.overhang_areas.size() - (z_distance_delta_ + 1), + mesh.layers.size() - (z_distance_delta_ + 1), [&](const LayerIndex layer_idx) { - if (mesh.overhang_areas[layer_idx + z_distance_delta_].empty() || getFullyUnsupportedArea(mesh_idx, layer_idx + z_distance_delta_).empty()) + if (getFullyUnsupportedArea(mesh_idx, layer_idx + z_distance_delta_).empty()) { return; } for (auto pointy_info : getFullyUnsupportedArea(mesh_idx, layer_idx + z_distance_delta_)) { - AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta_]); - if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta_], overhang_aabb).intersection(pointy_info.area).empty()) + if (pointy_info->height == 0) { - // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. - continue; + AABB overhang_aabb(mesh.overhang_areas[layer_idx + z_distance_delta_]); + if (PolygonUtils::clipPolygonWithAABB(mesh.overhang_areas[layer_idx + z_distance_delta_], overhang_aabb).intersection(pointy_info->area).empty()) + { + // It will be assumed that if it touches this mesh's overhang, it will be part of that mesh. + continue; + } } std::vector accumulated_model(std::min(cradle_config->cradle_layers_ + cradle_config->cradle_z_distance_layers_ + 1, mesh.overhang_areas.size() - layer_idx), Shape()); - std::vector all_pointy_idx{ pointy_info.index }; + std::vector all_pointy{ pointy_info }; - Point2LL center_prev = Polygon(pointy_info.area.getOutsidePolygons()[0]).centerOfMass(); + Point2LL center_prev = Polygon(pointy_info->area.getOutsidePolygons()[0]).centerOfMass(); std::vector additional_centers; TreeSupportCradle* cradle_main - = new TreeSupportCradle(layer_idx, center_prev, cradle_config->cradle_base_roof_, cradle_config, mesh_idx); + = new TreeSupportCradle(layer_idx, center_prev, cradle_config->cradle_base_roof_, pointy_info->support_required, cradle_config, mesh_idx); for (size_t z_distance = 0; z_distance < z_distance_top_layers; z_distance++) { - accumulated_model[z_distance] = pointy_info.area; + accumulated_model[z_distance] = pointy_info->area; cradle_main->centers_.emplace_back(center_prev); } Shape shadow; // A combination of all outlines of the model that will be supported with a cradle. @@ -259,66 +268,66 @@ std::vector> SupportCradleGeneration::generateCr // shadow model up => not cradle where model // then drop cradle down // cut into parts => get close to original pointy that are far enough from each other. - std::vector next_pointy_idx; + std::vector next_pointy; Shape model_outline; bool blocked_by_dedupe = false; // The cradle base is below the bottommost unsupported and the first cradle layer is around it, so this will be needed only for the second one and up if (cradle_up_layer > 1) { - for (size_t pointy_idx : all_pointy_idx) + for (UnsupportedAreaInformation* pointy : all_pointy) { - for (auto next_pointy_data : getUnsupportedArea(mesh_idx, layer_idx + cradle_up_layer - 1 + z_distance_delta_, pointy_idx, true)) + for (auto next_pointy_data : pointy->areas_above) { - if (next_pointy_data.height - != (cradle_up_layer - 1) + pointy_info.height) // If the area belongs to another pointy overhang stop and let this other overhang handle it + if (next_pointy_data->height + != (cradle_up_layer - 1) + pointy_info->height) // If the area belongs to another pointy overhang stop and let this other overhang handle it { contacted_other_pointy = true; continue; } - unsupported_model[cradle_up_layer].push_back(next_pointy_data.area); + unsupported_model[cradle_up_layer].push_back(next_pointy_data->area); // Ensure each area is only handles once std::lock_guard critical_section_cradle(critical_dedupe); - if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data.index)) + if (! dedupe[layer_idx + cradle_up_layer].contains(next_pointy_data)) { - dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data.index); - model_outline.push_back(next_pointy_data.area); - next_pointy_idx.emplace_back(next_pointy_data.index); + dedupe[layer_idx + cradle_up_layer].emplace(next_pointy_data); + model_outline.push_back(next_pointy_data->area); + next_pointy.emplace_back(next_pointy_data); } else { blocked_by_dedupe = true; } - std::vector all_pointy_idx_below{ next_pointy_data.index }; - for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_idx_below.empty(); cradle_down_layer--) + std::vector all_pointy_below{ next_pointy_data }; + for (int64_t cradle_down_layer = cradle_up_layer; cradle_down_layer > 0 && ! all_pointy_below.empty(); cradle_down_layer--) { - std::vector next_all_pointy_idx_below; + std::vector next_all_pointy_below; - for (size_t pointy_idx_below : all_pointy_idx_below) + for (UnsupportedAreaInformation* pointy_below : all_pointy_below) { - for (auto prev_pointy_data : getUnsupportedArea(mesh_idx, layer_idx + cradle_down_layer - 1 + z_distance_delta_, pointy_idx_below, false)) + for (UnsupportedAreaInformation* prev_pointy_data : pointy_below->areas_below) { - if (prev_pointy_data.index != pointy_idx || cradle_down_layer != cradle_up_layer) + if (prev_pointy_data != pointy || cradle_down_layer != cradle_up_layer) { // Only add if area below does not have it's own cradle. - if (prev_pointy_data.height < cradle_config->cradle_layers_min_) + if (prev_pointy_data->height < cradle_config->cradle_layers_min_) { - accumulated_model[cradle_down_layer].push_back(prev_pointy_data.area); - next_all_pointy_idx_below.emplace_back(prev_pointy_data.index); + accumulated_model[cradle_down_layer].push_back(prev_pointy_data->area); + next_all_pointy_below.emplace_back(prev_pointy_data); } } } } - all_pointy_idx_below = next_all_pointy_idx_below; + all_pointy_below = next_all_pointy_below; accumulated_model[cradle_down_layer] = accumulated_model[cradle_down_layer].unionPolygons(); } } } - all_pointy_idx = next_pointy_idx; + all_pointy = next_pointy; } else { - model_outline.push_back(pointy_info.area); + model_outline.push_back(pointy_info->area); } if (model_outline.empty()) @@ -975,10 +984,10 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } } - Shape shadow = cradle.shadow_[0]; - Shape cradle_base = shadow; + bool is_side_cradle = cradle.cradle_placement_method_ == CradlePlacementMethod::MANUAL_SIDE || cradle.cradle_placement_method_ == CradlePlacementMethod::AUTOMATIC_SIDE; + Shape cradle_base = is_side_cradle ? Shape() : cradle.shadow_[0]; - if (support_roof_layers) + if (support_roof_layers && ! is_side_cradle) { Shape cut_line_base; Shape first_cradle_areas; @@ -1192,13 +1201,27 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } -void SupportCradleGeneration::addMeshToCradleCalculation(const SliceMeshStorage& mesh, size_t mesh_idx) +void SupportCradleGeneration::addMeshToCradleCalculation(const SliceDataStorage& storage, size_t mesh_idx) { - if(retrieveSetting(mesh.settings, "support_tree_cradle_height") < mesh.settings.get("layer_height")) + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; + if (! retrieveSetting(mesh.settings, "support_tree_cradle_enable")) { return; } - calculateFloatingParts(mesh, mesh_idx); + calculateFloatingParts(storage, mesh_idx); +} + +void SupportCradleGeneration::generateCradleForMesh(const SliceDataStorage& storage, size_t mesh_idx) +{ + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; + if (! retrieveSetting(mesh.settings, "support_tree_cradle_enable")) + { + return; + } + if (floating_parts_cache_[mesh_idx].empty()) + { + addMeshToCradleCalculation(storage, mesh_idx); + } std::vector> cradle_data_mesh = generateCradleCenters(mesh, mesh_idx); generateCradleLines(cradle_data_mesh, mesh); if(cradle_data_[mesh_idx].size() < cradle_data_mesh.size()) @@ -1209,7 +1232,6 @@ void SupportCradleGeneration::addMeshToCradleCalculation(const SliceMeshStorage& { cradle_data_[mesh_idx][layer_idx].insert(cradle_data_[mesh_idx][layer_idx].end(), cradles_on_layer.begin(), cradles_on_layer.end()); } - } void SupportCradleGeneration::generate(const SliceDataStorage& storage) diff --git a/src/support.cpp b/src/support.cpp index f08b07345e..5edb7b4b4b 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -53,8 +53,17 @@ bool AreaSupport::handleSupportModifierMesh(SliceDataStorage& storage, const Set SUPPORT_DROP_DOWN, SUPPORT_VANILLA }; - ModifierType modifier_type - = (mesh_settings.get("anti_overhang_mesh")) ? ANTI_OVERHANG : ((mesh_settings.get("support_mesh_drop_down")) ? SUPPORT_DROP_DOWN : SUPPORT_VANILLA); + ModifierType modifier_type; + if (mesh_settings.get("anti_overhang_mesh")) // todo [TR] refactor to support_modifier_mesh + { + modifier_type = ANTI_OVERHANG; + storage.support.supportGenerationModifiers.emplace_back(mesh_settings, slicer->layers.size()); + } + else + { + modifier_type = mesh_settings.get("support_mesh_drop_down") ? SUPPORT_DROP_DOWN : SUPPORT_VANILLA; + } + for (LayerIndex layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++) { SupportLayer& support_layer = storage.support.supportLayers[layer_nr]; @@ -62,7 +71,7 @@ bool AreaSupport::handleSupportModifierMesh(SliceDataStorage& storage, const Set switch (modifier_type) { case ANTI_OVERHANG: - support_layer.anti_overhang.push_back(slicer_layer.polygons_); + storage.support.supportGenerationModifiers.back().addArea(slicer_layer.polygons_, layer_nr); break; case SUPPORT_DROP_DOWN: support_layer.support_mesh_drop_down.push_back(slicer_layer.polygons_); @@ -623,7 +632,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) for (int layer_nr = 0; layer_nr < max_layer_nr_support_mesh_filled; layer_nr++) { SupportLayer& support_layer = storage.support.supportLayers[layer_nr]; - support_layer.anti_overhang = support_layer.anti_overhang.unionPolygons(); support_layer.support_mesh_drop_down = support_layer.support_mesh_drop_down.unionPolygons(); support_layer.support_mesh = support_layer.support_mesh.unionPolygons(); } @@ -1036,6 +1044,12 @@ void AreaSupport::generateSupportAreasForMesh( // The maximum width of an odd wall = 2 * minimum even wall width. auto half_min_feature_width = min_even_wall_line_width + 10; + bool anti_support_meshes_present = false; + for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) + { + anti_support_meshes_present |= support_modifier.isAntiSupport(); + } + cura::parallel_for( 1, layer_count, @@ -1084,6 +1098,25 @@ void AreaSupport::generateSupportAreasForMesh( } }); + std::vector anti_support_areas(mesh.layers.size()); + if (anti_support_meshes_present) + { + cura::parallel_for( + 1, + layer_count, + [&](const size_t layer_idx) + { + for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) + { + if (support_modifier.isAntiSupport() && layer_idx < support_modifier.areas_.size()) + { + anti_support_areas[layer_idx].push_back(support_modifier.areas_[layer_idx]); + } + } + anti_support_areas[layer_idx] = anti_support_areas[layer_idx].unionPolygons(); + }); + } + std::vector tower_roofs; Shape stair_removal; // polygons to subtract from support because of stair-stepping @@ -1171,6 +1204,11 @@ void AreaSupport::generateSupportAreasForMesh( layer_this = layer_this.unionPolygons(storage.support.supportLayers[layer_idx].support_mesh); } layer_this = AreaSupport::join(storage, *layer_above, layer_this).difference(model_mesh_on_layer); + // Ensure support does not continue below anti support + if (anti_support_meshes_present && ! anti_support_areas[layer_idx].empty()) + { + layer_this = layer_this.difference(anti_support_areas[layer_idx]); + } } // make towers for small support @@ -1237,9 +1275,10 @@ void AreaSupport::generateSupportAreasForMesh( } // do stuff for when support on buildplate only - if (support_type == ESupportType::PLATFORM_ONLY) + if (! is_support_mesh_nondrop_place_holder && (support_type == ESupportType::PLATFORM_ONLY || anti_support_meshes_present)) { Shape touching_buildplate = support_areas[0]; // TODO: not working for conical support! + Shape touching_anti_support; const AngleRadians conical_support_angle = infill_settings.get("support_conical_angle"); coord_t conical_support_offset; if (conical_support_angle > 0) @@ -1258,6 +1297,7 @@ void AreaSupport::generateSupportAreasForMesh( if (conical_support) { // with conical support the next layer is allowed to be larger than the previous touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10); + touching_anti_support = touching_anti_support.offset(-(std::abs(conical_support_offset) + 10), ClipperLib::jtMiter, 10); // + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners // // conical support can make @@ -1274,9 +1314,17 @@ void AreaSupport::generateSupportAreasForMesh( // } - touching_buildplate = layer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease! - - support_areas[layer_idx] = touching_buildplate; + if (support_type == ESupportType::PLATFORM_ONLY) + { + touching_buildplate = layer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease! + support_areas[layer_idx] = touching_buildplate; + } + if (anti_support_meshes_present) + { + touching_anti_support.push_back(anti_support_areas[layer_idx]); + touching_anti_support = touching_anti_support.unionPolygons().difference(storage.getLayerOutlines(layer_idx, false, false)); + support_areas[layer_idx] = support_areas[layer_idx].difference(touching_anti_support); + } } } @@ -1464,11 +1512,20 @@ std::pair AreaSupport::computeBasicAndFullOverhang(const SliceData Shape basic_overhang = outlines.difference(outlines_below); const SupportLayer& support_layer = storage.support.supportLayers[layer_idx]; - if (! support_layer.anti_overhang.empty()) + + Shape anti_overhang; + for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) + { + if (support_modifier.isAntiOverhang() && layer_idx < support_modifier.areas_.size()) + { + anti_overhang.push_back(support_modifier.areas_[layer_idx]); + } + } + if (! anti_overhang.empty()) { // Merge anti overhang into one polygon, otherwise overlapping polygons // will create opposite effect. - Shape merged_polygons = support_layer.anti_overhang.unionPolygons(); + Shape merged_polygons = anti_overhang.unionPolygons(); basic_overhang = basic_overhang.difference(merged_polygons); } @@ -1494,6 +1551,16 @@ void AreaSupport::detectOverhangPoints(const SliceDataStorage& storage, SliceMes const SliceLayer& layer = mesh.layers[layer_idx]; const SliceLayer& layer_below = mesh.layers[layer_idx - 1]; + Shape anti_overhang; + for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) + { + if (support_modifier.isAntiOverhang() && layer_idx < support_modifier.areas_.size()) + { + anti_overhang.push_back(support_modifier.areas_[layer_idx]); + } + } + anti_overhang = anti_overhang.unionPolygons(); + for (const SliceLayerPart& part : layer.parts) { if (part.outline.empty()) @@ -1512,7 +1579,7 @@ void AreaSupport::detectOverhangPoints(const SliceDataStorage& storage, SliceMes continue; } - const Shape overhang = part.outline.difference(storage.support.supportLayers[layer_idx].anti_overhang); + const Shape overhang = part.outline.difference(anti_overhang); if (! overhang.empty()) { scripta::log("support_overhangs", overhang, SectionType::SUPPORT, layer_idx); From 5a7a0683fe6017256068c73eff6dd2da0247d4c3 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 11 Sep 2024 09:03:36 +0200 Subject: [PATCH 097/101] Add initial support for cradles if normal support is used. --- include/TreeModelVolumes.h | 2 + include/support.h | 14 +- src/TreeModelVolumes.cpp | 21 ++- src/TreeSupport.cpp | 1 + src/TreeSupportCradle.cpp | 9 +- src/support.cpp | 254 ++++++++++++++++++++++++++++++++++++- 6 files changed, 285 insertions(+), 16 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index fc9dd746f9..2e7fa8a5f4 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -41,6 +41,7 @@ class TreeModelVolumes coord_t max_move, coord_t max_move_slow, coord_t min_offset_per_step, + const coord_t min_radius, size_t current_mesh_idx, double progress_multiplier, double progress_offset, @@ -588,6 +589,7 @@ class TreeModelVolumes mutable std::unordered_map anti_preferred_cache_collision; std::unique_ptr critical_anti_preferred_caches = std::make_unique(); + std::unique_ptr critical_calculation_ = std::make_unique(); std::unique_ptr critical_progress_ = std::make_unique(); diff --git a/include/support.h b/include/support.h index 0d6eaeffa6..98e0b53d14 100644 --- a/include/support.h +++ b/include/support.h @@ -127,6 +127,15 @@ class AreaSupport */ static void generateOverhangAreasForMesh(SliceDataStorage& storage, SliceMeshStorage& mesh); + /*! + * \brief Generate cradles for pointy overhangs + * + * \param storage[in,out] Data storage containing the mesh object and used to store cradles as support. + * \param mesh_idx[in] The index of the mesh object for which to generate cradles. + * \param cradle_overhang_reserved_areas[out] Areas that need to be supported to support generated cradles. + */ + static void generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_idx, std::vector& cradle_overhang_reserved_areas); + /*! * \brief Generate support polygons over all layers for one object. * @@ -148,6 +157,7 @@ class AreaSupport * the support. * \param bottom_settings The settings base to get the bottom interface of * the support. + * \param cradle_overhang_reserved_areas[in] Overhang from cradle areas, required to support said cradles. * \param mesh_idx The index of the object for which to generate support * areas. * \param layer_count Total number of layers. @@ -157,6 +167,7 @@ class AreaSupport const Settings& infill_settings, const Settings& roof_settings, const Settings& bottom_settings, + const std::vector& cradle_overhang_reserved_areas, const size_t mesh_idx, const size_t layer_count, std::vector& support_areas); @@ -185,9 +196,10 @@ class AreaSupport * \param storage Where to find the previously generated support areas and * where to output the new support roof areas. * \param mesh The mesh to generate support roof for. + * \param global_handled_by_cradle_areas_per_layer[in] Areas that are supported by cradles, to be excluded from roof generation. * \param global_support_areas_per_layer the global support areas on each layer. */ - static void generateSupportRoof(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector& global_support_areas_per_layer); + static void generateSupportRoof(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector& global_support_areas_per_layer, std::vector& global_handled_by_cradle_areas_per_layer); /*! * \brief Generate a single layer of support interface. diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index f6ed170868..63f27bd2aa 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -24,6 +24,7 @@ TreeModelVolumes::TreeModelVolumes( const coord_t max_move, const coord_t max_move_slow, const coord_t min_offset_per_step, + const coord_t min_radius, size_t current_mesh_idx, double progress_multiplier, double progress_offset, @@ -33,6 +34,7 @@ TreeModelVolumes::TreeModelVolumes( max_move_slow_{ std::max(max_move_slow - 2, coord_t(0)) } , // -2 to avoid rounding errors min_offset_per_step_{ min_offset_per_step } + , radius_0_(min_radius) , progress_multiplier_{ progress_multiplier } , progress_offset_{ progress_offset } , machine_border_{ calculateMachineBorderCollision(storage.getMachineBorder()) } @@ -172,7 +174,6 @@ TreeModelVolumes::TreeModelVolumes( } // Cache some handy settings in the object itself. - radius_0_ = config.getRadius(0); support_rest_preference_ = config.support_rest_preference; simplifier_ = Simplify(min_maximum_resolution, min_maximum_deviation, min_maximum_area_deviation); } @@ -495,18 +496,23 @@ const Shape& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_idx if (orig_radius == 0) { + // calculateFake0Avoidances may case a regular avoidance calculation using getAvoidance if used lazy. To prevent issues using a recursive_mutex here. + std::lock_guard critical_section(*critical_calculation_); calculateFake0Avoidances(layer_idx); } else if (type == AvoidanceType::COLLISION) { + std::lock_guard critical_section(*critical_calculation_); calculateCollisionAvoidance(key); } else if (to_model) { + std::lock_guard critical_section(*critical_calculation_); calculateAvoidanceToModel(key); } else { + std::lock_guard critical_section(*critical_calculation_); calculateAvoidance(key); } 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. @@ -1168,7 +1174,7 @@ Shape TreeModelVolumes::safeOffset(const Shape& me, coord_t distance, ClipperLib return ret.unionPolygons(collision); } -void TreeModelVolumes::calculateAvoidance(const std::deque& keys) +void TreeModelVolumes::calculateAvoidance(const std::deque& keys) // todo limit to certain avoidance types for better normal support performance { // For every RadiusLayer pair there are 3 avoidances that have to be calculate, calculated in the same paralell_for loop for better parallelization. const std::vector all_types = { AvoidanceType::SLOW, AvoidanceType::FAST_SAFE, AvoidanceType::FAST }; @@ -1415,7 +1421,16 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) std::lock_guard critical_section(*critical_avoidance_cache_); start_layer = 1 + getMaxCalculatedLayer(0, avoidance_cache_); } - + // Ensure all avoidances are calculated + if (! precalculated_) + { + getAvoidance(1, max_layer, AvoidanceType::SLOW, false, true); + getAvoidance(1, max_layer, AvoidanceType::FAST, false, true); + getAvoidance(1, max_layer, AvoidanceType::FAST_SAFE, false, true); + getAvoidance(1, max_layer, AvoidanceType::SLOW, true, true); + getAvoidance(1, max_layer, AvoidanceType::FAST, true, true); + getAvoidance(1, max_layer, AvoidanceType::FAST_SAFE, true, true); + } const coord_t radius_offset = -radius_0_; cura::parallel_for( start_layer, diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 6dd88a2e61..4467f41c5f 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -144,6 +144,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) config.maximum_move_distance, config.maximum_move_distance_slow, config.support_line_width / 2, + config.getRadius(0), processing.second.front(), progress_multiplier, progress_offset, diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index da156b0310..422d2c9835 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -401,7 +401,7 @@ void SupportCradleGeneration::generateCradleLines(std::vector("support_xy_distance_overhang") : xy_distance; const bool support_moves = mesh.settings.get("support_structure") != ESupportStructure::NORMAL; - const coord_t minimum_area_to_be_supportable = support_moves ? mesh.settings.get("support_tree_tip_diameter") / 2 : mesh.settings.get("support_line_distance"); + const coord_t minimum_area_to_be_supportable = support_moves ? mesh.settings.get("support_tree_tip_diameter") / 2 : 0; cura::parallel_for( 1, cradle_data_mesh.size(), @@ -1161,9 +1161,10 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor ! xy_overrides); coord_t offset_radius = cradle.config_->cradle_support_base_area_radius_; - if (cradle_base.offset(offset_radius, ClipperLib::jtRound).difference(forbidden_here).area() > 1) + Shape cradle_base_offset = cradle_base.offset(offset_radius, ClipperLib::jtRound); + if (cradle_base_offset.difference(forbidden_here).area() > 1) { - OverhangInformation cradle_overhang(cradle_base, false, cradle_data_[mesh_idx][layer_idx][cradle_idx]); + OverhangInformation cradle_overhang(support_moves ? cradle_base : cradle_base_offset, false, cradle_data_[mesh_idx][layer_idx][cradle_idx]); cradle.overhang_[layer_idx].emplace_back(cradle_overhang); } } @@ -1195,8 +1196,6 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor } cradle_data_[mesh_idx][layer_idx] = valid_cradles; }); - - } } diff --git a/src/support.cpp b/src/support.cpp index 5edb7b4b4b..30fa85164f 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -24,6 +24,7 @@ #include "ExtruderTrain.h" #include "SkeletalTrapezoidation.h" #include "Slice.h" +#include "TreeSupportCradle.h" #include "infill.h" #include "infill/SierpinskiFillProvider.h" #include "infill/UniformDensityProvider.h" @@ -115,8 +116,6 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage const Shape& global_support_areas = global_support_areas_per_layer[layer_nr]; if (global_support_areas.size() == 0 || layer_nr < min_layer || layer_nr > max_layer) { - // Initialize support_infill_parts empty - storage.support.supportLayers[layer_nr].support_infill_parts.clear(); continue; } @@ -618,6 +617,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) { std::vector global_support_areas_per_layer; global_support_areas_per_layer.resize(storage.print_layer_count); + std::vector global_handled_by_cradle_areas_per_layer(storage.print_layer_count); int max_layer_nr_support_mesh_filled; for (max_layer_nr_support_mesh_filled = storage.support.supportLayers.size() - 1; max_layer_nr_support_mesh_filled >= 0; max_layer_nr_support_mesh_filled--) @@ -680,11 +680,21 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) } std::vector mesh_support_areas_per_layer; mesh_support_areas_per_layer.resize(storage.print_layer_count, Shape()); - - generateSupportAreasForMesh(storage, *infill_settings, *roof_settings, *bottom_settings, mesh_idx, storage.print_layer_count, mesh_support_areas_per_layer); + std::vector cradle_overhang_reserved_areas(storage.print_layer_count); + generateCradlesForMesh(storage, mesh_idx, cradle_overhang_reserved_areas); + generateSupportAreasForMesh( + storage, + *infill_settings, + *roof_settings, + *bottom_settings, + cradle_overhang_reserved_areas, + mesh_idx, + storage.print_layer_count, + mesh_support_areas_per_layer); for (size_t layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++) { global_support_areas_per_layer[layer_idx].push_back(mesh_support_areas_per_layer[layer_idx]); + global_handled_by_cradle_areas_per_layer[layer_idx].push_back(cradle_overhang_reserved_areas[layer_idx]); } } @@ -703,7 +713,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) if (mesh->settings.get("support_roof_enable")) { - generateSupportRoof(storage, *mesh, global_support_areas_per_layer); + generateSupportRoof(storage, *mesh, global_support_areas_per_layer, global_handled_by_cradle_areas_per_layer); } if (mesh->settings.get("support_bottom_enable")) { @@ -975,6 +985,219 @@ Shape AreaSupport::generateVaryingXYDisallowedArea(const SliceMeshStorage& stora return varying_xy_disallowed_areas; } +void AreaSupport::generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_idx, std::vector& cradle_overhang_reserved_areas) +{ + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; + const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; + const coord_t support_line_width = mesh_group_settings.get("support_infill_extruder_nr").settings_.get("support_line_width"); + const coord_t layer_thickness = mesh_group_settings.get("layer_height"); + const coord_t z_distance_top = mesh.settings.get("support_top_distance"); + const size_t layer_z_distance_top = (z_distance_top / layer_thickness) + 1; + const size_t support_roof_wall_count = mesh.settings.get("support_roof_wall_count"); + const coord_t support_roof_line_width = mesh.settings.get("support_roof_line_width"); + const size_t support_wall_count = mesh.settings.get("support_wall_count"); + const bool fractional_support_present = z_distance_top % layer_thickness != 0; + + TreeModelVolumes volumes = TreeModelVolumes( + storage, + 0, + 0, + support_line_width / 2, + std::min(support_line_width, support_roof_line_width), + mesh_idx, + 0, + 0, + std::vector(storage.support.supportLayers.size())); + // Don't precalculate TreeModelVolumes. Most will not be used so using it lazily generating areas should be faster. + SupportCradleGeneration cradle_gen(storage, volumes); + cradle_gen.addMeshToCradleCalculation(storage, mesh_idx); + cradle_gen.generateCradleForMesh(storage, mesh_idx); + cradle_gen.generate(storage); + std::vector> cradle_data_mesh(storage.support.supportLayers.size()); + std::vector support_free_areas = std::vector(storage.support.supportLayers.size(), Shape()); + cradle_gen.pushCradleData(cradle_data_mesh, support_free_areas, mesh_idx); + + // todo figure out what to do if regular support collides with cradle. make it anti_support? + // todo figure out what to do if two cradles block each other with regular support caused by one of them + + std::vector support_layer_extra_wall_storage(cradle_data_mesh.size()); + std::vector support_layer_storage_fractional(cradle_data_mesh.size()); + std::vector support_roof_extra_wall_storage(cradle_data_mesh.size()); + std::vector support_roof_extra_wall_storage_fractional(cradle_data_mesh.size()); + std::vector support_roof_storage(cradle_data_mesh.size()); + std::vector support_roof_storage_fractional(cradle_data_mesh.size()); + cradle_overhang_reserved_areas.resize(cradle_data_mesh.size()); + std::mutex critical_support_roof_storage; + std::mutex critical_support_layer_storage; + std::mutex critical_cradle_reserved_areas; + + cura::parallel_for( + 0, + cradle_data_mesh.size(), + [&](const LayerIndex layer_idx) + { + for (size_t cradle_idx = 0; cradle_idx < cradle_data_mesh[layer_idx].size(); cradle_idx++) + { + TreeSupportCradle* cradle = cradle_data_mesh[layer_idx][cradle_idx]; + Shape overhang_outer_area; + for (auto overhang_pair : cradle->overhang_) + { + Point2LL center = cradle_data_mesh[layer_idx][cradle_idx]->getCenter(layer_idx); + Polygon overhang_outer_area_part; + bool includes_lines = false; + for (OverhangInformation& overhang : overhang_pair.second) + { + if (overhang.isCradleLine()) + { + std::optional cradle_line_opt = cradle->getCradleLineOfIndex(overhang.cradle_layer_idx_, overhang.cradle_line_idx_); + if (cradle_line_opt) + { + overhang_outer_area_part.emplace_back(cradle_line_opt.value()->line_.back()); + includes_lines = true; + } + } + else + { + overhang_outer_area.push_back(overhang.overhang_); + if (fractional_support_present) + { + support_layer_storage_fractional[overhang_pair.first + 1].push_back(overhang.overhang_); + } + } + } + + overhang_outer_area.push_back(overhang_outer_area_part); + overhang_outer_area.makeConvex(); + if (includes_lines) + { + bool large_base_roof = cradle->config_->large_cradle_base_ && cradle->config_->cradle_lines_roof_; + coord_t offset_distance = cradle->config_->cradle_line_width_ / 2 + (large_base_roof ? cradle->config_->cradle_support_base_area_radius_ : 0); + overhang_outer_area = overhang_outer_area.offset(offset_distance); + } + overhang_outer_area = overhang_outer_area.difference(volumes.getCollision(0, overhang_pair.first, true)); + std::lock_guard critical_section_cradle(critical_cradle_reserved_areas); + cradle_overhang_reserved_areas[overhang_pair.first].push_back(overhang_outer_area); + } + + for (auto [base_idx, base] : cradle_data_mesh[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) + { + if (cradle_data_mesh[layer_idx][cradle_idx]->is_roof_) + { + std::lock_guard critical_section_cradle(critical_support_roof_storage); + (support_roof_wall_count ? support_roof_storage : support_roof_extra_wall_storage)[layer_idx - base_idx].push_back(base); + if (base_idx == 0 && z_distance_top % layer_thickness != 0 && layer_idx + 1 < support_roof_extra_wall_storage_fractional.size()) + { + (support_roof_wall_count ? support_roof_storage_fractional : support_roof_extra_wall_storage_fractional)[layer_idx + 1].push_back(base); + } + } + else + { + // Dead code. Currently, Cradles that are not roofs do not have a base area, just a tip. This is just here for the case that this changes + std::lock_guard critical_section_cradle(critical_support_layer_storage); + support_layer_extra_wall_storage[layer_idx - base_idx].push_back(base); + if (base_idx == 0 && z_distance_top % layer_thickness != 0 && layer_idx + 1 < support_layer_storage_fractional.size()) + { + support_layer_storage_fractional[layer_idx + 1].push_back(base); + } + } + } + + for (size_t line_idx = 0; line_idx < cradle_data_mesh[layer_idx][cradle_idx]->lines_.size(); line_idx++) + { + if (! cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx].empty()) + { + for (int64_t height_idx = cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx].size() - 1; height_idx >= 0; height_idx--) + { + Shape line_area = cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + bool is_roof = cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; + LayerIndex cradle_line_layer_idx = cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; + bool is_base = cradle_data_mesh[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; + + if (is_roof) + { + std::lock_guard critical_section_cradle(critical_support_roof_storage); + support_roof_extra_wall_storage[cradle_line_layer_idx].push_back(line_area); + } + else + { + std::lock_guard critical_section_cradle(critical_support_layer_storage); + support_layer_extra_wall_storage[cradle_line_layer_idx].push_back(line_area); + } + } + } + } + } + }); + + cura::parallel_for( + 0, + cradle_data_mesh.size(), + [&](const LayerIndex layer_idx) + { + // union everything and add + + support_layer_storage_fractional[layer_idx] = support_layer_storage_fractional[layer_idx].unionPolygons(); + support_layer_extra_wall_storage[layer_idx] = support_layer_extra_wall_storage[layer_idx].unionPolygons(); + + support_roof_storage[layer_idx] = support_roof_storage[layer_idx].unionPolygons(); + support_roof_storage_fractional[layer_idx] = support_roof_storage_fractional[layer_idx].unionPolygons(); + + support_roof_extra_wall_storage[layer_idx] = support_roof_extra_wall_storage[layer_idx].unionPolygons(); + support_roof_extra_wall_storage_fractional[layer_idx] = support_roof_extra_wall_storage_fractional[layer_idx].unionPolygons(); + + cradle_overhang_reserved_areas[layer_idx] = cradle_overhang_reserved_areas[layer_idx].unionPolygons(); + + Shape remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); + if (! support_free_areas[layer_idx].empty()) + { + remove_from_next_roof.push_back(support_free_areas[layer_idx]); + } + + remove_from_next_roof = remove_from_next_roof.unionPolygons(); + + Shape remove_from_next_fractional_roof = remove_from_next_roof; + + Shape roof_extra_wall = support_roof_extra_wall_storage[layer_idx].difference(remove_from_next_roof); + Shape roof = support_roof_storage[layer_idx]; + if (support_roof_wall_count) + { + roof = roof.difference(remove_from_next_roof); + roof = roof.unionPolygons(roof_extra_wall); + roof_extra_wall.clear(); + } + else + { + roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); + } + + storage.support.supportLayers[layer_idx].fillRoofParts(roof_extra_wall, support_roof_line_width, std::max(size_t(1), support_roof_wall_count), false); + storage.support.supportLayers[layer_idx].fillRoofParts(roof, support_roof_line_width, support_roof_wall_count, false); + + remove_from_next_fractional_roof.push_back(roof_extra_wall); + remove_from_next_fractional_roof.push_back(roof); + remove_from_next_fractional_roof = remove_from_next_fractional_roof.unionPolygons(); + + Shape fractional_roof_extra_wall = support_roof_extra_wall_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof); + storage.support.supportLayers[layer_idx].fillRoofParts(fractional_roof_extra_wall, support_roof_line_width, std::max(size_t(1), support_roof_wall_count), true); + + Shape 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, support_roof_line_width, support_roof_wall_count, true); + + Shape fractional_base = support_layer_storage_fractional[layer_idx].difference(remove_from_next_fractional_roof); + + storage.support.supportLayers[layer_idx] + .fillInfillParts(support_layer_extra_wall_storage[layer_idx], support_line_width, std::max(size_t(1), support_wall_count), false, true); + storage.support.supportLayers[layer_idx].fillInfillParts(fractional_base, support_line_width, support_wall_count, true, true); + if (! (fractional_base.empty() && support_layer_extra_wall_storage[layer_idx].empty() && fractional_roof.empty() && fractional_roof_extra_wall.empty() && roof.empty() + && roof_extra_wall.empty())) + { + std::lock_guard critical_section_storage(critical_support_layer_storage); + storage.support.layer_nr_max_filled_layer = std::max(layer_idx, LayerIndex(storage.support.layer_nr_max_filled_layer)); + } + }); +} + + /* * Algorithm: * From top layer to bottom layer: @@ -992,6 +1215,7 @@ void AreaSupport::generateSupportAreasForMesh( const Settings& infill_settings, const Settings& roof_settings, const Settings& bottom_settings, + const std::vector& cradle_overhang_reserved_areas, const size_t mesh_idx, const size_t layer_count, std::vector& support_areas) @@ -1042,7 +1266,7 @@ void AreaSupport::generateSupportAreasForMesh( xy_disallowed_per_layer[0] = storage.getLayerOutlines(0, no_support, no_prime_tower).offset(xy_distance); // The maximum width of an odd wall = 2 * minimum even wall width. - auto half_min_feature_width = min_even_wall_line_width + 10; + auto half_min_feature_width = min_even_wall_line_width / 2 + 5; bool anti_support_meshes_present = false; for (const SupportGenerationModifier& support_modifier : storage.support.supportGenerationModifiers) @@ -1162,6 +1386,10 @@ void AreaSupport::generateSupportAreasForMesh( for (size_t layer_idx = layer_count - 1 - layer_z_distance_top; layer_idx != static_cast(-1); layer_idx--) { Shape layer_this = mesh.full_overhang_areas[layer_idx + layer_z_distance_top]; + if (layer_idx < cradle_overhang_reserved_areas.size()) + { + layer_this = layer_this.unionPolygons(cradle_overhang_reserved_areas[layer_idx]); + } if (extension_offset && ! is_support_mesh_place_holder) { @@ -1770,7 +1998,11 @@ void AreaSupport::generateSupportBottom(SliceDataStorage& storage, const SliceMe } } -void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector& global_support_areas_per_layer) +void AreaSupport::generateSupportRoof( + SliceDataStorage& storage, + const SliceMeshStorage& mesh, + std::vector& global_support_areas_per_layer, + std::vector& global_handled_by_cradle_areas_per_layer) { const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); @@ -1800,6 +2032,14 @@ void AreaSupport::generateSupportRoof(SliceDataStorage& storage, const SliceMesh { mesh_outlines.push_back(mesh.layers[layer_idx_above].getOutlines()); } + + // Todo [TR] this is a rough way to prevent not wanted roofs. But there may be a good reason why one would want roof on a part of the cradle-line supporting support. + // Do more investigation into if this is good enough! + for (auto layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top_layers - 1; layer_idx_above -= 1) + { + mesh_outlines = mesh_outlines.difference(global_handled_by_cradle_areas_per_layer[layer_idx_above]); + } + Shape roofs; generateSupportInterfaceLayer(global_support_areas_per_layer[layer_idx], mesh_outlines, roof_line_width, roof_outline_offset, minimum_roof_area, roofs); // If roof is added as fractional, even though non can exist because remaining z distance is 0 it will be regular roof. From 85bca65f445f6f8aff30f1b76bf68bcdbfedc820 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:02:05 +0200 Subject: [PATCH 098/101] Fix cradle generation wrongly being called twice if tree support is used --- src/support.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/support.cpp b/src/support.cpp index 30fa85164f..46e8ba49aa 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -989,6 +989,7 @@ void AreaSupport::generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_ { SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; + const bool is_support_mesh_place_holder = mesh.settings.get("support_mesh"); const coord_t support_line_width = mesh_group_settings.get("support_infill_extruder_nr").settings_.get("support_line_width"); const coord_t layer_thickness = mesh_group_settings.get("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); @@ -998,6 +999,12 @@ void AreaSupport::generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_ const size_t support_wall_count = mesh.settings.get("support_wall_count"); const bool fractional_support_present = z_distance_top % layer_thickness != 0; + const ESupportStructure support_structure = mesh.settings.get("support_structure"); + if ((! mesh.settings.get("support_enable") || support_structure != ESupportStructure::NORMAL) && ! is_support_mesh_place_holder) + { + return; + } + TreeModelVolumes volumes = TreeModelVolumes( storage, 0, From 06c648840c2c3a941777c548533a82d62140a44f Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:53:06 +0200 Subject: [PATCH 099/101] Improve resiliency if TreeModelVolumes is used without precalculating. Also prevent parallel for from being used in a nested way. --- include/TreeModelVolumes.h | 26 +++- include/utils/ThreadPool.h | 22 ++- src/TreeModelVolumes.cpp | 291 +++++++++++++++++++++++++++++++------ src/utils/ThreadPool.cpp | 12 ++ 4 files changed, 305 insertions(+), 46 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 2e7fa8a5f4..aefe8dd202 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -589,7 +589,31 @@ class TreeModelVolumes mutable std::unordered_map anti_preferred_cache_collision; std::unique_ptr critical_anti_preferred_caches = std::make_unique(); - std::unique_ptr critical_calculation_ = std::make_unique(); + enum class CalculationType + { + AVOIDANCE, + AVOIDANCE_0, + AVOIDANCE_TO_MODEL, + AVOIDANCE_COLLISION, + COLLISION, + COLLISION_HOLEFREE, + PLACEABLE, + PLACEABLE_ACCUMULATED, + WALL_RESTRICTION + }; + std::unique_ptr critical_calculation_request_ = std::make_unique(); + std::unordered_map, std::shared_ptr> critical_calculation_map_; + + std::shared_ptr getLockForCalculation(coord_t radius, CalculationType type) + { + std::lock_guard critical_section(*critical_calculation_request_); + std::pair key(radius, type); + if (! critical_calculation_map_.contains(key)) + { + critical_calculation_map_[key] = std::make_shared(); + } + return critical_calculation_map_[key]; + } std::unique_ptr critical_progress_ = std::make_unique(); diff --git a/include/utils/ThreadPool.h b/include/utils/ThreadPool.h index ed8db06c92..456e72f8d7 100644 --- a/include/utils/ThreadPool.h +++ b/include/utils/ThreadPool.h @@ -82,6 +82,8 @@ class ThreadPool } } + bool isInPool(); + private: void worker(); @@ -130,9 +132,27 @@ void parallel_for(T first, T last, F&& loop_body, size_t chunk_size_factor = 1, { return; } - const size_t nitems = dist; + // No need to move the work to another thread if there is only work for one thread. + if (dist <= 1) + { + loop_body(first); + return; + } ThreadPool* const thread_pool = Application::getInstance().thread_pool_; + + // This implementation does not really play nice if called nested, so lets not do that. + if (thread_pool->isInPool()) + { + for (T val = first; val < last; val++) + { + loop_body(val); + } + return; + } + + const size_t nitems = dist; + assert(thread_pool); const size_t nworkers = thread_pool->thread_count() + 1; // One task per std::thread + 1 for main thread diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 63f27bd2aa..442c03192f 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -365,7 +365,7 @@ const Shape& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx RadiusLayerPair key{ radius, layer_idx }; { - std::lock_guard critical_section_support_max_layer_nr(*critical_avoidance_cache_); + std::lock_guard critical_critical_collision_cache_(*critical_collision_cache_); result = getArea(collision_cache_, key); } if (result) @@ -376,7 +376,11 @@ const Shape& TreeModelVolumes::getCollision(coord_t radius, LayerIndex layer_idx { spdlog::warn("Had to calculate collision at radius {} and layer {}, but precalculate was called. Performance may suffer!", key.first, key.second); } - calculateCollision(key); + + { + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::COLLISION)); + calculateCollision(key); + } return getCollision(orig_radius, layer_idx, min_xy_dist); } @@ -406,7 +410,10 @@ const Shape& TreeModelVolumes::getCollisionHolefree(coord_t radius, LayerIndex l { spdlog::warn("Had to calculate collision holefree at radius {} and layer {}, but precalculate was called. Performance may suffer!", key.first, key.second); } - calculateCollisionHolefree(key); + { + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::COLLISION_HOLEFREE)); + calculateCollisionHolefree(key); + } return getCollisionHolefree(orig_radius, layer_idx, min_xy_dist); } @@ -419,7 +426,10 @@ const Shape& TreeModelVolumes::getAccumulatedPlaceable0(LayerIndex layer_idx) return accumulated_placeables_cache_radius_0_[layer_idx]; } } - calculateAccumulatedPlaceable0(layer_idx); + { + std::lock_guard critical_section(*getLockForCalculation(0, CalculationType::PLACEABLE_ACCUMULATED)); + calculateAccumulatedPlaceable0(layer_idx); + } return getAccumulatedPlaceable0(layer_idx); } @@ -496,23 +506,22 @@ const Shape& TreeModelVolumes::getAvoidance(coord_t radius, LayerIndex layer_idx if (orig_radius == 0) { - // calculateFake0Avoidances may case a regular avoidance calculation using getAvoidance if used lazy. To prevent issues using a recursive_mutex here. - std::lock_guard critical_section(*critical_calculation_); + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::AVOIDANCE_0)); calculateFake0Avoidances(layer_idx); } else if (type == AvoidanceType::COLLISION) { - std::lock_guard critical_section(*critical_calculation_); + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::AVOIDANCE_COLLISION)); calculateCollisionAvoidance(key); } else if (to_model) { - std::lock_guard critical_section(*critical_calculation_); + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::AVOIDANCE_TO_MODEL)); calculateAvoidanceToModel(key); } else { - std::lock_guard critical_section(*critical_calculation_); + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::AVOIDANCE)); calculateAvoidance(key); } 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. @@ -524,7 +533,6 @@ const Shape& TreeModelVolumes::getPlaceableAreas(coord_t radius, LayerIndex laye const coord_t orig_radius = radius; radius = ceilRadius(radius); RadiusLayerPair key{ radius, layer_idx }; - { std::lock_guard critical_section(*critical_placeable_areas_cache_); result = getArea(placeable_areas_cache_, key); @@ -539,11 +547,13 @@ const Shape& TreeModelVolumes::getPlaceableAreas(coord_t radius, LayerIndex laye } if (radius != 0) { + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::PLACEABLE)); calculatePlaceables(key); } else { - getCollision(0, layer_idx, true); + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::COLLISION)); + calculateCollision(key); } return getPlaceableAreas(orig_radius, layer_idx); } @@ -578,7 +588,10 @@ const Shape& TreeModelVolumes::getWallRestriction(coord_t radius, LayerIndex lay spdlog::warn("Had to calculate Wall restrictions at radius {} and layer {}, but precalculate was called. Performance may suffer!", key.first, key.second); } - calculateWallRestrictions(key); + { + std::lock_guard critical_section(*getLockForCalculation(radius, CalculationType::WALL_RESTRICTION)); + calculateWallRestrictions(key); + } return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved. } @@ -897,18 +910,26 @@ void TreeModelVolumes::calculateCollision(const std::deque& key const coord_t xy_distance = outline_idx == current_outline_idx_ ? current_min_xy_dist_ : layer_outlines_[outline_idx].first.get("support_xy_distance"); // Technically this causes collision for the normal xy_distance to be larger by current_min_xy_dist_delta for all not currently processing meshes as this delta will // be added at request time. 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. + // 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; + coord_t min_layer_insert; { std::lock_guard critical_section(*critical_collision_cache_); - min_layer_bottom = getMaxCalculatedLayer(radius, collision_cache_) - z_distance_bottom_layers; + min_layer_insert = getMaxCalculatedLayer(radius, collision_cache_) + 1; + min_layer_bottom = min_layer_insert - z_distance_bottom_layers; } if (min_layer_bottom < 0) { min_layer_bottom = 0; } + + if (max_required_layer < min_layer_bottom) + { + continue; + } + for (const auto layer_idx : ranges::views::iota(min_layer_bottom, max_required_layer + 1)) { key.second = layer_idx; @@ -984,10 +1005,16 @@ void TreeModelVolumes::calculateCollision(const std::deque& key data[key] = data[key].unionPolygons(max_anti_overhang_layer >= layer_idx ? anti_overhang_[layer_idx].offset(radius) : Shape()); } - for (const auto layer_idx : ranges::views::iota(static_cast(keys[i].second) + 1UL, max_required_layer + 1UL) | ranges::views::reverse) + for (const auto layer_idx : ranges::views::iota(static_cast(keys[i].second) + 1UL, max_required_layer + 1UL)) { data.erase(RadiusLayerPair(radius, layer_idx)); // all these dont have the correct z_distance_top_layers as they can still have areas above them } + for (const auto layer_idx : ranges::views::iota(min_layer_bottom, min_layer_insert)) + { + // Those were already inserted + data.erase(RadiusLayerPair(radius, layer_idx)); + data_placeable.erase(RadiusLayerPair(radius, layer_idx)); + } for (auto pair : data) { @@ -1016,13 +1043,39 @@ void TreeModelVolumes::calculateCollision(const std::deque& key { std::lock_guard critical_section(*critical_collision_cache_); - collision_cache_.insert(data_outer.begin(), data_outer.end()); + bool added_requested = false; + for (const std::pair& ins_elem : data_outer) + { + added_requested |= ins_elem.first.second == keys[i].second; + if (collision_cache_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated collision for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + collision_cache_[ins_elem.first] = ins_elem.second; + } + } + if (! added_requested && ! collision_cache_.contains(keys[i])) + { + printf("ERROR %d, %d\n\n", keys[i].second); + } } if (radius == 0) { { std::lock_guard critical_section(*critical_placeable_areas_cache_); - placeable_areas_cache_.insert(data_placeable_outer.begin(), data_placeable_outer.end()); + for (const std::pair& ins_elem : data_placeable_outer) + { + if (placeable_areas_cache_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated placeable radius 0 for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + placeable_areas_cache_[ins_elem.first] = ins_elem.second; + } + } } } }); @@ -1030,20 +1083,33 @@ void TreeModelVolumes::calculateCollision(const std::deque& key void TreeModelVolumes::calculateCollisionHolefree(const std::deque& keys) { + std::vector min_layer_per_key(keys.size()); + LayerIndex min_layer = 0; LayerIndex max_layer = 0; for (long long unsigned int i = 0; i < keys.size(); i++) { max_layer = std::max(max_layer, keys[i].second); + LayerIndex start_layer; + { + std::lock_guard critical_section(*critical_collision_cache_holefree_); + start_layer = 1 + getMaxCalculatedLayer(keys[i].first, collision_cache_holefree_); + } + min_layer = std::max(min_layer, start_layer); + min_layer_per_key[i] = start_layer; } cura::parallel_for( - 0, + min_layer, LayerIndex(max_layer + 1), [&](const LayerIndex layer_idx) { std::unordered_map data; - for (RadiusLayerPair key : keys) + for (auto [key_idx, key] : keys | ranges::views::enumerate) { + if (layer_idx < min_layer_per_key[key_idx]) + { + continue; + } // Logically increase the collision by increase_until_radius const coord_t radius = key.first; const coord_t increase_radius_ceil = ceilRadius(increase_until_radius_, false) - ceilRadius(radius, true); @@ -1055,7 +1121,17 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::deque critical_section(*critical_collision_cache_holefree_); - collision_cache_holefree_.insert(data.begin(), data.end()); + for (const std::pair& ins_elem : data) + { + if (collision_cache_holefree_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated collision holefree for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + collision_cache_holefree_[ins_elem.first] = ins_elem.second; + } + } } }); } @@ -1101,7 +1177,17 @@ void TreeModelVolumes::calculateAccumulatedPlaceable0(const LayerIndex max_layer }); { std::lock_guard critical_section(*critical_accumulated_placeables_cache_radius_0_); - accumulated_placeables_cache_radius_0_.insert(data.begin(), data.end()); + for (const std::pair& ins_elem : data) + { + if (accumulated_placeables_cache_radius_0_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated accumulated_placeables_cache_radius_0_ on layer {}", ins_elem.first); + } + else + { + accumulated_placeables_cache_radius_0_[ins_elem.first] = ins_elem.second; + } + } } } @@ -1151,7 +1237,17 @@ void TreeModelVolumes::calculateCollisionAvoidance(const std::deque critical_section(*critical_avoidance_cache_collision_); - avoidance_cache_collision_.insert(data.begin(), data.end()); + for (const std::pair& ins_elem : data) + { + if (avoidance_cache_collision_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated collision avoidance for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + avoidance_cache_collision_[ins_elem.first] = ins_elem.second; + } + } } }); } @@ -1257,7 +1353,23 @@ void TreeModelVolumes::calculateAvoidance(const std::deque& key { std::lock_guard critical_section(*(slow ? critical_avoidance_cache_slow_ : holefree ? critical_avoidance_cache_holefree_ : critical_avoidance_cache_)); - (slow ? avoidance_cache_slow_ : holefree ? avoidance_cache_hole_ : avoidance_cache_).insert(data.begin(), data.end()); + auto* cache = &(slow ? avoidance_cache_slow_ : holefree ? avoidance_cache_hole_ : avoidance_cache_); + for (const std::pair& ins_elem : data) + { + if (ins_elem.first.second == -1) + { + // Layer was not calculated + continue; + } + if (cache->contains(ins_elem.first)) + { + spdlog::warn("Recalculated avoidance for {} on layer {} Slow:{} holefree:{}", ins_elem.first.first, ins_elem.first.second, slow, holefree); + } + else + { + (*cache)[ins_elem.first] = ins_elem.second; + } + } } }); } @@ -1266,6 +1378,7 @@ void TreeModelVolumes::calculatePlaceables(const std::deque& ke { // TODO: This should be a parallel for nowait (non-blocking), but as the parallel-for situation (as in, proper compiler support) continues to change, we're using the 'normal' // one right now. + cura::parallel_for( 0, keys.size(), @@ -1314,7 +1427,22 @@ void TreeModelVolumes::calculatePlaceables(const std::deque& ke { std::lock_guard critical_section(*critical_placeable_areas_cache_); - placeable_areas_cache_.insert(data.begin(), data.end()); + for (const std::pair& ins_elem : data) + { + if (ins_elem.first.second == -1) + { + // Layer was not calculated + continue; + } + if (placeable_areas_cache_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated placeables for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + placeable_areas_cache_[ins_elem.first] = ins_elem.second; + } + } } }); } @@ -1409,7 +1537,23 @@ void TreeModelVolumes::calculateAvoidanceToModel(const std::deque& ins_elem : data) + { + if (ins_elem.first.second == -1) + { + // Layer was not calculated + continue; + } + if (cache->contains(ins_elem.first)) + { + spdlog::warn("Recalculated avoidance to model for {} on layer {} Slow:{} holefree:{}", ins_elem.first.first, ins_elem.first.second, slow, holefree); + } + else + { + (*cache)[ins_elem.first] = ins_elem.second; + } + } } }); } @@ -1421,16 +1565,6 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) std::lock_guard critical_section(*critical_avoidance_cache_); start_layer = 1 + getMaxCalculatedLayer(0, avoidance_cache_); } - // Ensure all avoidances are calculated - if (! precalculated_) - { - getAvoidance(1, max_layer, AvoidanceType::SLOW, false, true); - getAvoidance(1, max_layer, AvoidanceType::FAST, false, true); - getAvoidance(1, max_layer, AvoidanceType::FAST_SAFE, false, true); - getAvoidance(1, max_layer, AvoidanceType::SLOW, true, true); - getAvoidance(1, max_layer, AvoidanceType::FAST, true, true); - getAvoidance(1, max_layer, AvoidanceType::FAST_SAFE, true, true); - } const coord_t radius_offset = -radius_0_; cura::parallel_for( start_layer, @@ -1445,15 +1579,36 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) Shape smaller_avoidance_fast_safe = getAvoidance(1, layer_idx, AvoidanceType::FAST_SAFE, false, true).offset(radius_offset, ClipperLib::jtRound); { std::lock_guard critical_section(*critical_avoidance_cache_slow_); - avoidance_cache_slow_[key] = smaller_avoidance_slow; + if (avoidance_cache_slow_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance slow for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_slow_[key] = smaller_avoidance_slow; + } } { std::lock_guard critical_section(*critical_avoidance_cache_); - avoidance_cache_[key] = smaller_avoidance_fast; + if (avoidance_cache_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_[key] = smaller_avoidance_fast; + } } { std::lock_guard critical_section(*critical_avoidance_cache_holefree_); - avoidance_cache_hole_[key] = smaller_avoidance_fast_safe; + if (avoidance_cache_hole_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance holefree for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_hole_[key] = smaller_avoidance_fast_safe; + } } } @@ -1465,21 +1620,49 @@ void TreeModelVolumes::calculateFake0Avoidances(const LayerIndex max_layer) { std::lock_guard critical_section(*critical_avoidance_cache_to_model_slow_); - avoidance_cache_to_model_slow_[key] = smaller_avoidance_to_model_slow; + if (avoidance_cache_to_model_slow_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance to model slow for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_to_model_slow_[key] = smaller_avoidance_to_model_slow; + } } { std::lock_guard critical_section(*critical_avoidance_cache_to_model_); - avoidance_cache_to_model_[key] = smaller_avoidance_to_model_fast; + if (avoidance_cache_to_model_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance to model for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_to_model_[key] = smaller_avoidance_to_model_fast; + } } { std::lock_guard critical_section(*critical_avoidance_cache_holefree_to_model_); - avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; + if (avoidance_cache_hole_to_model_.contains(key)) + { + spdlog::warn("Recalculated fake 0 avoidance to model holefree for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_hole_to_model_[key] = smaller_avoidance_to_model_fast_safe; + } } if (layer_idx > max_layer_idx_without_blocker_) { Shape smaller_avoidance_collision = getAvoidance(1, layer_idx, AvoidanceType::COLLISION, true, true).offset(radius_offset, ClipperLib::jtRound); std::lock_guard critical_section(*critical_avoidance_cache_collision_); - avoidance_cache_collision_[key] = smaller_avoidance_collision; + if (avoidance_cache_collision_.contains(key)) + { + spdlog::warn("Recalculated fake 0 collision avoidance for {} on layer {}", key.first, key.second); + } + else + { + avoidance_cache_collision_[key] = smaller_avoidance_collision; + } } } }); @@ -1562,12 +1745,32 @@ void TreeModelVolumes::calculateWallRestrictions(const std::deque critical_section(*critical_wall_restrictions_cache_); - wall_restrictions_cache_.insert(data.begin(), data.end()); + for (const std::pair& ins_elem : data) + { + if (wall_restrictions_cache_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated wall restriction for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + wall_restrictions_cache_[ins_elem.first] = ins_elem.second; + } + } } { std::lock_guard critical_section(*critical_wall_restrictions_cache_min_); - wall_restrictions_cache_min_.insert(data_min.begin(), data_min.end()); + for (const std::pair& ins_elem : data) + { + if (wall_restrictions_cache_min_.contains(ins_elem.first)) + { + spdlog::warn("Recalculated wall restriction min for {} on layer {}", ins_elem.first.first, ins_elem.first.second); + } + else + { + wall_restrictions_cache_min_[ins_elem.first] = ins_elem.second; + } + } } }); } diff --git a/src/utils/ThreadPool.cpp b/src/utils/ThreadPool.cpp index 0b13f0d7da..eaf6da72b8 100644 --- a/src/utils/ThreadPool.cpp +++ b/src/utils/ThreadPool.cpp @@ -15,6 +15,18 @@ ThreadPool::ThreadPool(size_t nthreads) } } +bool ThreadPool::isInPool() +{ + for (size_t i = 0; i < threads.size(); i++) + { + if (threads[i].get_id() == std::this_thread::get_id()) + { + return true; + } + } + return false; +} + void ThreadPool::worker() { lock_t lock = get_lock(); From 340e70fc4ba4f89eb3af8dbe77601e949b06638f Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:25:43 +0200 Subject: [PATCH 100/101] Fix support for cradle being placed to far up if the cradle is roof. --- src/TreeSupportCradle.cpp | 2 +- src/support.cpp | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/TreeSupportCradle.cpp b/src/TreeSupportCradle.cpp index 422d2c9835..af214965f9 100644 --- a/src/TreeSupportCradle.cpp +++ b/src/TreeSupportCradle.cpp @@ -1065,7 +1065,6 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor for (auto roof_area_pair : roofs) { - Shape roof_area_before = roof_area_pair.first; Shape full_overhang_area = TreeSupportUtils::safeOffsetInc( roof_area_pair.first, roof_outset, @@ -1075,6 +1074,7 @@ void SupportCradleGeneration::generateCradleLineAreasAndBase(const SliceDataStor 1, support_line_width, &simplifyer); + Shape roof_area_before = full_overhang_area; for (LayerIndex dtt_roof = 0; dtt_roof <= support_roof_layers && layer_idx - dtt_roof >= 1; dtt_roof++) { const Shape forbidden_next = volumes_.getAvoidance( diff --git a/src/support.cpp b/src/support.cpp index 46e8ba49aa..3414fffa04 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -1046,14 +1046,19 @@ void AreaSupport::generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_ for (size_t cradle_idx = 0; cradle_idx < cradle_data_mesh[layer_idx].size(); cradle_idx++) { TreeSupportCradle* cradle = cradle_data_mesh[layer_idx][cradle_idx]; - Shape overhang_outer_area; for (auto overhang_pair : cradle->overhang_) { + if(overhang_pair.second.empty()) + { + continue; + } + Shape overhang_area_regular; Point2LL center = cradle_data_mesh[layer_idx][cradle_idx]->getCenter(layer_idx); Polygon overhang_outer_area_part; bool includes_lines = false; for (OverhangInformation& overhang : overhang_pair.second) { + overhang_area_regular.push_back(overhang.overhang_); if (overhang.isCradleLine()) { std::optional cradle_line_opt = cradle->getCradleLineOfIndex(overhang.cradle_layer_idx_, overhang.cradle_line_idx_); @@ -1065,25 +1070,27 @@ void AreaSupport::generateCradlesForMesh(SliceDataStorage& storage, size_t mesh_ } else { - overhang_outer_area.push_back(overhang.overhang_); if (fractional_support_present) { support_layer_storage_fractional[overhang_pair.first + 1].push_back(overhang.overhang_); } } } - - overhang_outer_area.push_back(overhang_outer_area_part); - overhang_outer_area.makeConvex(); + Shape line_support; + line_support.push_back(overhang_outer_area_part); + line_support.makeConvex(); if (includes_lines) { bool large_base_roof = cradle->config_->large_cradle_base_ && cradle->config_->cradle_lines_roof_; - coord_t offset_distance = cradle->config_->cradle_line_width_ / 2 + (large_base_roof ? cradle->config_->cradle_support_base_area_radius_ : 0); - overhang_outer_area = overhang_outer_area.offset(offset_distance); + coord_t offset_distance = std::max(cradle->config_->cradle_line_width_ / 2, (large_base_roof ? cradle->config_->cradle_support_base_area_radius_ : 0)); + line_support = line_support.offset(offset_distance, ClipperLib::jtRound); } - overhang_outer_area = overhang_outer_area.difference(volumes.getCollision(0, overhang_pair.first, true)); + overhang_area_regular = overhang_area_regular.unionPolygons(line_support); + overhang_area_regular = overhang_area_regular.difference(volumes.getCollision(0, overhang_pair.first, true)); + std::lock_guard critical_section_cradle(critical_cradle_reserved_areas); - cradle_overhang_reserved_areas[overhang_pair.first].push_back(overhang_outer_area); + cradle_overhang_reserved_areas[overhang_pair.first].push_back(overhang_area_regular); + } for (auto [base_idx, base] : cradle_data_mesh[layer_idx][cradle_idx]->base_below_ | ranges::views::enumerate) From d7d1f71f3b8f20d484d7c83ee5efb348970f1b46 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:23:02 +0100 Subject: [PATCH 101/101] Implement functionality to select whether cradle lines should be printed towards the model, or away from it. --- include/PathOrderOptimizer.h | 12 ++++++-- include/SupportInfillPart.h | 11 +++++++- include/TreeSupport.h | 4 +-- include/settings/EnumSettings.h | 1 + src/FffGcodeWriter.cpp | 26 ++++++++++------- src/InsetOrderOptimizer.cpp | 5 +++- src/SupportInfillPart.cpp | 4 +-- src/TreeSupport.cpp | 49 +++++++++++++++++++++++++-------- src/sliceDataStorage.cpp | 2 +- 9 files changed, 83 insertions(+), 31 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index be8e61873b..7e3cfc8701 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -205,7 +205,7 @@ class PathOrderOptimizer // For some Z seam types the start position can be pre-computed. // This is faster since we don't need to re-compute the start position at each step then. - precompute_start &= seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; + precompute_start &= seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; if (precompute_start) { for (auto& path : paths_) @@ -584,7 +584,7 @@ class PathOrderOptimizer } const bool precompute_start - = seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; + = seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED ||seam_config_.type_ == EZSeamType::SHARPEST_CORNER; if (! path->is_closed_ || ! precompute_start) // Find the start location unless we've already precomputed it. { path->start_vertex_ = findStartLocation(*path, start_position); @@ -676,6 +676,14 @@ class PathOrderOptimizer { if (! path.is_closed_) { + if (seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED) + { + if(getDirectDistance(path.converted_->back(), seam_config_.pos_) < getDirectDistance(path.converted_->front(), seam_config_.pos_)) + { + return path.converted_->size() - 1; // Back end is closer. + } + return 0; + } // For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. const coord_t back_distance = (combing_boundary_ == nullptr) ? getDirectDistance(path.converted_->back(), target_pos) : getCombingDistance(path.converted_->back(), target_pos); diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 92143d23ab..753ae648af 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -4,6 +4,7 @@ #ifndef SUPPORT_INFILL_PART_H #define SUPPORT_INFILL_PART_H +#include #include #include "geometry/Polygon.h" @@ -38,8 +39,16 @@ class SupportInfillPart coord_t custom_line_distance_; //!< The distance between support infill lines. 0 means use the default line distance instead. bool use_fractional_config_; //!< Request to use the configuration used to fill a partial layer height here, instead of the normal full layer height configuration. EFillMethod custom_line_pattern_; + std::optional start_near_location; - SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); + SupportInfillPart(const SingleShape& outline, + coord_t support_line_width, + bool use_fractional_config, + int inset_count_to_generate = 0, + coord_t custom_line_distance = 0, + EFillMethod custom_line_pattern = EFillMethod::NONE, + std::optional start_near_location = std::optional() + ); const Shape& getInfillArea() const; }; diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 619230c1de..bd10156a7d 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -332,7 +332,7 @@ class TreeSupport * \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 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 cradle_support_line_areas[out] All cradle lines consisting of regular support. Already added as support. * \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. */ @@ -371,7 +371,7 @@ class TreeSupport * \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 cradle_support_line_areas[in] All cradle lines consisting of regular support. Already 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. diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index 650f6a06dc..a94403331e 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -70,6 +70,7 @@ enum class EZSeamType SHORTEST, USER_SPECIFIED, SHARPEST_CORNER, + INTERNAL_SPECIFIED, /* The 'Skirt/brim' type behaves like shortest, except it doesn't try to do tie-breaking for similar locations to * the last attempt, as that gives a different result when the seams are next to each other instead of on top. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9f74df3369..2cf7ed25ed 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3425,8 +3425,10 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; const LayerIndex layer_nr = gcode_layer.getLayerNr(); - ZSeamConfig z_seam_config - = ZSeamConfig(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + const ZSeamConfig z_seam_config(part.start_near_location.has_value() ? EZSeamType::INTERNAL_SPECIFIED : EZSeamType::SHORTEST, + part.start_near_location.has_value() ? part.start_near_location.value() : gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + false); Shape disallowed_area_for_seams{}; if (infill_extruder.settings_.get("support_z_seam_away_from_model") && (layer_nr >= 0)) { @@ -3580,7 +3582,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool spiralize = false; constexpr Ratio flow_ratio = 1.0_r; constexpr bool always_retract = false; - const std::optional start_near_location = std::optional(); gcode_layer.addPolygonsByOptimizer( support_polygons, @@ -3591,7 +3592,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer flow_ratio, always_retract, alternate_layer_print_direction, - start_near_location); + part.start_near_location); added_something = true; } @@ -3600,7 +3601,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool enable_travel_optimization = false; constexpr coord_t wipe_dist = 0; constexpr Ratio flow_ratio = 1.0; - const std::optional near_start_location = std::optional(); constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; gcode_layer.addLinesByOptimizer( @@ -3610,7 +3610,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer enable_travel_optimization, wipe_dist, flow_ratio, - near_start_location, + part.start_near_location, fan_speed, alternate_layer_print_direction); @@ -3715,6 +3715,10 @@ bool FffGcodeWriter::addSupportRoofsToGCode( constexpr coord_t pocket_size = 0; const coord_t max_resolution = roof_extruder.settings_.get("meshfix_maximum_resolution"); const coord_t max_deviation = roof_extruder.settings_.get("meshfix_maximum_deviation"); + const ZSeamConfig z_seam_config(roof_part.start_near_location.has_value() ? EZSeamType::INTERNAL_SPECIFIED : EZSeamType::SHORTEST, + roof_part.start_near_location.has_value() ? roof_part.start_near_location.value() : gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + false); coord_t support_roof_line_distance = roof_part.custom_line_distance_ == 0 ? roof_extruder.settings_.get("support_roof_line_distance") : roof_part.custom_line_distance_; const coord_t support_roof_line_width = roof_extruder.settings_.get("support_roof_line_width"); @@ -3772,20 +3776,19 @@ bool FffGcodeWriter::addSupportRoofsToGCode( gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support if (gcode_layer.getLayerNr() == 0) { - gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); + gcode_layer.addPolygonsByOptimizer(wall, current_roof_config, z_seam_config); } if (! roof_polygons.empty()) { constexpr bool force_comb_retract = false; gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); + gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config, z_seam_config); } if (! roof_paths.empty()) { const GCodePathConfig& config = current_roof_config; constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); InsetOrderOptimizer wall_orderer( *this, @@ -3808,7 +3811,10 @@ bool FffGcodeWriter::addSupportRoofsToGCode( roof_paths, storage.getModelBoundingBox().flatten().getMiddle());wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + constexpr bool enable_travel_optimization = false; + constexpr coord_t wipe_dist = 0; + constexpr Ratio flow_ratio = 1.0; + gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, wipe_dist, flow_ratio, roof_part.start_near_location); } } return added_support; diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index ad036f62b9..581933e125 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -183,6 +183,9 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed case EZSeamType::USER_SPECIFIED: request_point = z_seam_config_.pos_; break; + case EZSeamType::INTERNAL_SPECIFIED: + request_point = z_seam_config_.pos_; + break; case EZSeamType::SHORTEST: request_point = gcode_layer_.getLastPlannedPositionOrStartingPosition(); break; @@ -195,7 +198,7 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed size_t closest_junction_idx = 0; coord_t closest_distance_sqd = std::numeric_limits::max(); bool should_reclaculate_closest = false; - if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED) + if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED || z_seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED) { // For user-defined seams you usually don't _actually_ want the _closest_ point, per-se, // since you want the seam-line to be continuous in 3D space. diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 02d4d4f04f..9ab33d8ca2 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -8,7 +8,7 @@ using namespace cura; -SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern) +SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern, std::optional start_near_location) : outline_(outline) , outline_boundary_box_(outline) , support_line_width_(support_line_width) @@ -16,7 +16,7 @@ SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support , custom_line_distance_(custom_line_distance) , custom_line_pattern_(custom_line_pattern) , use_fractional_config_(use_fractional_config) - + , start_near_location(start_near_location) { infill_area_per_combine_per_density_.clear(); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 4467f41c5f..560d54bd0c 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2399,11 +2399,14 @@ void TreeSupport::prepareSupportAreas( SliceDataStorage& storage, std::vector>& cradle_data) { + using ShapeWithStart = std::pair; 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); + const bool print_cradle_towards_model = true; //todo make setting - std::vector cradle_support_line_roof_areas(support_layer_storage.size()); // All cradle lines that have to be added as roof + std::vector> cradle_support_line_roof_areas_with_start(support_layer_storage.size()); // All cradle lines that have to be added as roof + std::vector> cradle_support_line_areas_with_start(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. std::vector missing_cradle_line_xy_distance_areas(support_layer_storage.size()); // All missing (because of cradle z distance) cradle lines offset by xy distance. @@ -2453,12 +2456,20 @@ void TreeSupport::prepareSupportAreas( LayerIndex previous_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().layer_idx_; for (int64_t height_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - 1; height_idx >= 0; height_idx--) { - Shape line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + if(cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_.empty()) + { + continue; + } + + SingleShape line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_.splitIntoParts(false).front(); //todo prettier. bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; bool was_line_above = height_idx + 1 < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() && ! cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx + 1].is_base_; + Point2LL front = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].line_.front(); + Point2LL back = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].line_.back(); + if (was_line_above) { for (LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) @@ -2481,11 +2492,11 @@ void TreeSupport::prepareSupportAreas( { std::lock_guard critical_section_cradle(critical_support_roof_storage); - if (cradle_support_line_roof_areas.size() <= layer_idx) + if (cradle_support_line_roof_areas_with_start.size() <= layer_idx) { - cradle_support_line_roof_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + cradle_support_line_roof_areas_with_start.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } - cradle_support_line_roof_areas[cradle_line_layer_idx].push_back(line_area); + cradle_support_line_roof_areas_with_start[cradle_line_layer_idx].emplace_back(line_area, print_cradle_towards_model ? back : front); } else { @@ -2496,6 +2507,12 @@ void TreeSupport::prepareSupportAreas( cradle_support_line_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } cradle_support_line_areas[cradle_line_layer_idx].push_back(line_area); + + if (cradle_support_line_areas_with_start.size() <= layer_idx) + { + cradle_support_line_areas_with_start.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + } + cradle_support_line_areas_with_start[cradle_line_layer_idx].emplace_back(line_area, print_cradle_towards_model ? back : front); } if (! is_base) { @@ -2569,7 +2586,6 @@ void TreeSupport::prepareSupportAreas( cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); - cradle_support_line_roof_areas[layer_idx] = cradle_support_line_roof_areas[layer_idx].unionPolygons(); // If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (! storage.support.supportLayers[layer_idx].support_roof.empty()) { @@ -2605,14 +2621,24 @@ void TreeSupport::prepareSupportAreas( break; } } - Shape cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); Shape remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); if (! support_free_areas[layer_idx].empty()) { remove_from_next_roof.push_back(support_free_areas[layer_idx]); } - //Remove only already added roof from line areas. Should not be needed, but better safe than sorry. - cradle_lines_roof = cradle_lines_roof.difference(remove_from_next_roof); + + //Add cradle lines. These need start hints, so its best done early! + for(ShapeWithStart& line : cradle_support_line_roof_areas_with_start[layer_idx]) + { + storage.support.supportLayers[layer_idx].support_roof.emplace_back(line.first, config.support_roof_line_width, false, std::min(1, config.support_roof_wall_count), 0, EFillMethod::NONE, std::optional(line.second)); + remove_from_next_roof.push_back(line.first); + } + + for(ShapeWithStart& line : cradle_support_line_areas_with_start[layer_idx]) + { + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(line.first, config.support_line_width, false, std::min(1, config.support_wall_count), 0, EFillMethod::NONE, std::optional(line.second)); + } + cradle_support_line_areas[layer_idx] = cradle_support_line_areas[layer_idx].unionPolygons().difference(remove_from_next_roof); //Collect remaining parts that non cradle line roof areas may not intersect with. @@ -2627,11 +2653,10 @@ void TreeSupport::prepareSupportAreas( if (config.support_roof_wall_count) { roof = roof.difference(remove_from_next_roof); - roof = roof.unionPolygons(cradle_lines_roof); } else { - roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + roof_extra_wall = roof_extra_wall.unionPolygons(); roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); } @@ -2959,7 +2984,7 @@ void TreeSupport::generateSupportSkin( [&](const LayerIndex layer_idx) { 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]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); if(layer_idx > 0) { diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index f5ceffb5c1..14e8e3e74b 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -756,7 +756,7 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(std::vector