diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 223ee8443d..6cac19a9ab 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -31,7 +31,7 @@ struct SupportInfillPart std::optional base_inside_contour_; //!< If an inside base is generated, contains the inner contour AABB outline_boundary_box_; //!< The boundary box for the infill area coord_t support_line_width_; //!< The support line width - int inset_count_to_generate_; //!< The number of insets need to be generated from the outline. This is not the actual insets that will be generated. + coord_t inset_width_to_generate_; //!< The width of insets need to be generated from the outline. This is not the actual insets that will be generated. std::vector> infill_area_per_combine_per_density_; //!< a list of separated sub-areas which requires different infill densities and combined thicknesses // for infill_areas[x][n], x means the density level and n means the thickness 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. @@ -39,7 +39,9 @@ struct 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. - 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); + SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, coord_t inset_width_to_generate = 0, coord_t custom_line_distance = 0); + + const Shape& getInfillArea() const; }; } // namespace cura diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 9211a05276..79860b60d1 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -1,9 +1,11 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2026 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef TREESUPPORT_H #define TREESUPPORT_H +#include + #include "TreeModelVolumes.h" #include "TreeSupportBaseCircle.h" #include "TreeSupportElement.h" @@ -15,6 +17,7 @@ #include "settings/EnumSettings.h" #include "sliceDataStorage.h" #include "utils/Coord_t.h" +#include "utils/SparsePointGridInclusive.h" namespace cura { @@ -291,6 +294,7 @@ class TreeSupport void filterFloatingLines(std::vector& support_layer_storage); + using CenterGrid = std::vector>>>; /*! * \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage * @@ -302,6 +306,7 @@ class TreeSupport std::vector& support_layer_storage, std::vector& support_roof_storage, std::vector& support_layer_storage_fractional, + const CenterGrid& center_locator_per_layer, SliceDataStorage& storage); /*! diff --git a/include/TreeSupportSettings.h b/include/TreeSupportSettings.h index 1c4cab4fc6..263e057eae 100644 --- a/include/TreeSupportSettings.h +++ b/include/TreeSupportSettings.h @@ -68,7 +68,8 @@ struct TreeSupportSettings , support_roof_line_width(mesh_group_settings.get("support_roof_line_width")) , support_line_distance(mesh_group_settings.get("support_line_distance")) , support_bottom_offset(mesh_group_settings.get("support_bottom_offset")) - , support_wall_count(mesh_group_settings.get("support_wall_count")) + , support_wall_thickness(mesh_group_settings.get("support_wall_thickness")) + , support_enlarged_wall_thickness(mesh_group_settings.get("support_enlarged_wall_thickness")) , support_roof_wall_count(mesh_group_settings.get("support_roof_wall_count")) , zig_zaggify_support(mesh_group_settings.get("zig_zaggify_support")) , maximum_deviation(mesh_group_settings.get("meshfix_maximum_deviation")) @@ -82,6 +83,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_wall_thickness_bottom_layers(settings.get("support_wall_thickness_bottom_layers")) + , support_wall_thickness_top_layers(settings.get("support_wall_thickness_top_layers")) + , support_wall_thickness_layer_smooth(settings.get("support_wall_thickness_layer_smooth")) , simplifier(Simplify(mesh_group_settings)) { layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); @@ -313,9 +317,14 @@ struct TreeSupportSettings coord_t support_bottom_offset; /*! - * \brief Amount of walls the support area will have. + * \brief Nominal width of walls the support area will have (see also below). */ - int support_wall_count; + coord_t support_wall_thickness; + + /*! + * \brief Width of walls the support areas that need to be sturdier will have. + */ + coord_t support_enlarged_wall_thickness; /*! * \brief Amount of walls the support roof area will have. @@ -383,6 +392,21 @@ struct TreeSupportSettings */ bool fill_outline_gaps; + /*! + * \brief The number of lines from the build-plate at which the nominal line-width switches to the enlarged line-width. (But see also support_line_width_layer_smooth.) + */ + coord_t support_wall_thickness_bottom_layers; + + /*! + * \brief The number of lines from the top of the support at which the nominal line-width switches to the enlarged line-width. (But see also support_line_width_layer_smooth.) + */ + coord_t support_wall_thickness_top_layers; + + /*! + * \brief The number of lines that can be used to smooth out the transition from the nominal line-width to the enlarged line-width. + */ + coord_t support_wall_thickness_layer_smooth; + /*! * \brief Simplifier to simplify polygons. */ @@ -402,8 +426,8 @@ struct TreeSupportSettings && support_line_width == other.support_line_width && support_overrides == other.support_overrides && support_line_distance == other.support_line_distance && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless. - support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern - && roof_pattern == other.roof_pattern + support_bottom_offset == other.support_bottom_offset && support_wall_thickness == other.support_wall_thickness + && support_enlarged_wall_thickness == other.support_enlarged_wall_thickness && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // 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 @@ -412,7 +436,8 @@ 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 && + && fill_outline_gaps == other.fill_outline_gaps && support_wall_thickness_bottom_layers == other.support_wall_thickness_bottom_layers + && support_wall_thickness_top_layers == other.support_wall_thickness_top_layers && support_wall_thickness_layer_smooth == other.support_wall_thickness_layer_smooth && // 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 diff --git a/include/TreeSupportUtils.h b/include/TreeSupportUtils.h index 3dc4b7327c..ba50a20e71 100644 --- a/include/TreeSupportUtils.h +++ b/include/TreeSupportUtils.h @@ -120,7 +120,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 = include_walls ? (! roof ? config.support_wall_thickness : config.support_roof_wall_count) : 0; constexpr coord_t narrow_area_width = 0; const Point2LL infill_origin; constexpr bool skip_stitching = false; diff --git a/include/infill.h b/include/infill.h index 406f56fb01..8936855814 100644 --- a/include/infill.h +++ b/include/infill.h @@ -48,7 +48,7 @@ class Infill coord_t shift_{}; //!< shift of the scanlines in the direction perpendicular to the fill_angle coord_t max_resolution_{}; //!< Min feature size of the output coord_t max_deviation_{}; //!< Max deviation fro the original poly when enforcing max_resolution - size_t wall_line_count_{}; //!< Number of walls to generate at the boundary of the infill region, spaced \ref infill_line_width apart + coord_t wall_thickness_{}; //!< Thickness of walls to generate at the boundary of the infill region coord_t small_area_width_{}; //!< Maximum width of a small infill region to be filled with walls Point2LL infill_origin_{}; //!< origin of the infill pattern bool skip_line_stitching_{ false }; //!< Whether to bypass the line stitching normally performed for polyline type infills @@ -116,7 +116,7 @@ class Infill coord_t shift, coord_t max_resolution, coord_t max_deviation, - size_t wall_line_count, + std::variant walls, coord_t small_area_width, Point2LL infill_origin, bool skip_line_stitching) noexcept @@ -133,7 +133,7 @@ class Infill , shift_{ shift } , max_resolution_{ max_resolution } , max_deviation_{ max_deviation } - , wall_line_count_{ wall_line_count } + , wall_thickness_{ std::holds_alternative(walls) ? std::get(walls) : static_cast(std::get(walls) * infill_line_width) } , small_area_width_{ small_area_width } , infill_origin_{ infill_origin } , skip_line_stitching_{ skip_line_stitching } @@ -154,7 +154,7 @@ class Infill coord_t shift, coord_t max_resolution, coord_t max_deviation, - size_t wall_line_count, + std::variant walls, coord_t small_area_width, Point2LL infill_origin, bool skip_line_stitching, @@ -177,7 +177,7 @@ class Infill , shift_{ shift } , max_resolution_{ max_resolution } , max_deviation_{ max_deviation } - , wall_line_count_{ wall_line_count } + , wall_thickness_{ std::holds_alternative(walls) ? std::get(walls) : static_cast(std::get(walls) * infill_line_width) } , small_area_width_{ small_area_width } , infill_origin_{ infill_origin } , skip_line_stitching_{ skip_line_stitching } @@ -229,7 +229,7 @@ class Infill * * \param toolpaths [out] The generated toolpaths. Binned by inset_idx. * \param outer_contour [in] the outer contour - * \param wall_line_count [in] The number of walls that needs to be generated + * \param wall_thickness [in] The thickness of walls that needs to be generated * \param line_width [in] The optimum wall line width of the walls * \param settings [in] A settings storage to use for generating variable-width walls. * \param generator The generator to use for outer "walls" @@ -238,7 +238,7 @@ class Infill static Shape generateWallToolPaths( std::vector& toolpaths, const Shape& outer_contour, - const size_t wall_line_count, + const coord_t wall_thickness, const coord_t line_width, const Settings& settings, int layer_idx, diff --git a/include/infill/LightningGenerator.h b/include/infill/LightningGenerator.h index 3f8ffdd7dd..0887e6a4c6 100644 --- a/include/infill/LightningGenerator.h +++ b/include/infill/LightningGenerator.h @@ -72,7 +72,7 @@ class LightningGenerator // "Just like Nicola used to make!" * Generate the lightning infill for the given areas with the given settings * @param layer_thickness The actual layer thickness * @param line_width The extrusion line width - * @param wall_line_count The number of external walls in the infill areas + * @param wall_thickness The thickness of external walls in the infill areas * @param line_distance The base "line distance", which represents the density of the infill * @param overhang_angle The angle at which we consider overhanging areas * @param prune_angle The angle of the generated lines overhang @@ -82,7 +82,7 @@ class LightningGenerator // "Just like Nicola used to make!" void generate( const coord_t layer_thickness, const coord_t line_width, - const coord_t wall_line_count, + const coord_t wall_thickness, const coord_t line_distance, const AngleRadians& overhang_angle, const AngleRadians& prune_angle, @@ -97,12 +97,12 @@ class LightningGenerator // "Just like Nicola used to make!" * only when support is generated. For this pattern, we also need to * generate overhang areas for the inside of the model. */ - void generateInitialInternalOverhangs(const coord_t infill_line_width, const coord_t infill_wall_line_count, const std::vector& areas_per_layer); + void generateInitialInternalOverhangs(const coord_t infill_wall_thickness, const std::vector& areas_per_layer); /*! * Calculate the tree structure of all layers. */ - void generateTrees(const coord_t infill_line_width, const coord_t infill_wall_line_count, const std::vector& areas_per_layer); + void generateTrees(const coord_t infill_wall_thickness, const std::vector& areas_per_layer); /*! * How far each piece of infill can support skin in the layer above. diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index f37c0abc46..d815b7b044 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -236,7 +236,7 @@ class SupportLayer * \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 wall_thickness Wall-line thickness 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. @@ -244,14 +244,14 @@ class SupportLayer void fillInfillParts( const Shape& area, const coord_t support_line_width, - const coord_t wall_line_count, + const coord_t wall_thickness, const bool use_fractional_config = false, const bool unionAll = false, const coord_t custom_line_distance = 0) { 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); + support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_thickness, custom_line_distance); } } @@ -263,7 +263,7 @@ class SupportLayer * \param infill_layer_height The layer height of the support-fill. * \param meshes The model meshes to be supported, needed here to handle fractional support layer height. * \param support_line_width Line width of the support extrusions. - * \param wall_line_count Wall-line count around the fill. + * \param wall_thickness Wall-line thickness around the fill. * \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. @@ -274,7 +274,7 @@ class SupportLayer const coord_t infill_layer_height, const std::vector>& meshes, const coord_t support_line_width, - const coord_t wall_line_count, + const coord_t wall_thickness, const coord_t grow_layer_above = 0, const bool unionAll = false, const coord_t custom_line_distance = 0); diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 3c46770762..d6232a4e27 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3629,7 +3629,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer infill_density_multiplier = infill_extruder.settings_.get("support_infill_density_multiplier_initial_layer"); } - const size_t wall_line_count = infill_extruder.settings_.get("support_wall_count"); + const size_t wall_thickness = infill_extruder.settings_.get("support_wall_thickness"); const coord_t max_resolution = infill_extruder.settings_.get("meshfix_maximum_resolution"); const coord_t max_deviation = infill_extruder.settings_.get("meshfix_maximum_deviation"); coord_t default_support_line_width = infill_extruder.settings_.get("support_line_width"); @@ -3679,7 +3679,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const auto& configs = part.use_fractional_config_ ? gcode_layer.configs_storage_.support_fractional_infill_config : gcode_layer.configs_storage_.support_infill_config; // always process the wall overlap if walls are generated - const int current_support_infill_overlap = (part.inset_count_to_generate_ > 0) ? default_support_infill_overlap : 0; + const int current_support_infill_overlap = (part.inset_width_to_generate_ > 0) ? default_support_infill_overlap : 0; // The support infill walls were generated separately, first. Always add them, regardless of how many densities we have. std::vector wall_toolpaths = part.wall_toolpaths_; @@ -3787,7 +3787,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer area, support_line_width, support_line_distance_here, - current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_line_count * support_line_width), + current_support_infill_overlap - (density_idx == max_density_idx ? 0 : wall_thickness), infill_multiplier, support_infill_angle, gcode_layer.z_ + configs[combine_idx].z_offset, @@ -3890,7 +3890,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer // If we're printing with a support wall, that support wall generates gap filling as well. // If not, the pattern may still generate gap filling (if it's connected infill or zigzag). We still want to print those. - if (wall_line_count == 0 || ! wall_toolpaths_here.empty()) + if (wall_thickness == 0 || ! wall_toolpaths_here.empty()) { const GCodePathConfig& config = configs[0]; constexpr coord_t wipe_dist = 0; diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index dd6edf2d03..8b805ed422 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -8,11 +8,16 @@ 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) +SupportInfillPart::SupportInfillPart( + const SingleShape& outline, + coord_t support_line_width, + bool use_fractional_config, + coord_t inset_width_to_generate, + coord_t custom_line_distance) : outline_(outline) , outline_boundary_box_(outline) , support_line_width_(support_line_width) - , inset_count_to_generate_(inset_count_to_generate) + , inset_width_to_generate_(inset_width_to_generate) , custom_line_distance_(custom_line_distance) , use_fractional_config_(use_fractional_config) { diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 53e7df8aa5..b1885454d1 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2026 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeSupport.h" @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -1702,7 +1703,7 @@ void TreeSupport::generateBranchAreas( } coord_t max_speed_sqd = 0; - std::function generateArea = [&](coord_t offset) + const std::function generateArea = [&](coord_t offset) { Shape poly; @@ -1974,7 +1975,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) { 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 closing_dist = config.support_wall_thickness; 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); @@ -2172,6 +2173,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( std::vector& support_layer_storage, std::vector& support_roof_storage, std::vector& support_layer_storage_fractional, + const CenterGrid& center_locator_per_layer, SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; @@ -2285,9 +2287,30 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( { constexpr bool convert_every_part = true; // Convert every part into a SingleShape 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); + const auto* center_locator = center_locator_per_layer[layer_idx].get(); + for (const SingleShape& support_part : support_layer_storage[layer_idx].splitIntoParts()) + { + // For each point L (actually) inside the connected support part, retrieve the wall-width and area that the branch representing point L would have at that point. + SparsePointGridInclusive::Elem nearest; + const AABB aabb(support_part); + const coord_t search_radius = EPSILON + std::max(aabb.width(), aabb.height()) / 2; + const auto located_data = center_locator->getNearby(aabb.getMiddle(), search_radius); + + // Take the weighted average of the wall-widths and areas of all points inside the support part, where the area is weighted by the area of the branch at that point. + const std::pair total_area_width = std::accumulate( + located_data.begin(), + located_data.end(), + std::make_pair(0.f, 0.f), + [&support_part](const auto& res, const auto& elem) + { + return support_part.inside(elem.point) ? std::make_pair((elem.val.first * elem.val.second) + res.first, elem.val.second + res.second) : res; + }); + const auto weighted_average = static_cast(total_area_width.first / std::max(1.f, total_area_width.second)); + // Clamp wall-thickness to configured values, and draw the support part with the calculated wall-thickness. + const auto wall_thickness = std::clamp(weighted_average, config.support_wall_thickness, config.support_enlarged_wall_thickness); + storage.support.supportLayers[layer_idx].fillInfillParts(support_part, config.support_line_width, wall_thickness, false, convert_every_part); + } // 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. @@ -2302,7 +2325,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( fractional_support = support_layer_storage_fractional[layer_idx]; } - 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_thickness, true, convert_every_part); for (FakeRoofArea& fake_roof : fake_roof_areas[layer_idx]) @@ -2363,7 +2386,6 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } - // Reorder the processed data by layers again. The map also could be a vector>: std::vector> layer_tree_polygons(move_bounds.size()); const auto t_start = std::chrono::high_resolution_clock::now(); @@ -2391,6 +2413,32 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } + CenterGrid center_locator_per_layer(move_bounds.size()); + cura::parallel_for( + 0, + center_locator_per_layer.size(), + [&](const size_t layer_idx) + { + center_locator_per_layer[layer_idx] = std::make_unique>>(config.branch_radius * 2 + EPSILON); + auto* center_locator = center_locator_per_layer[layer_idx].get(); + + for (const auto& elem : move_bounds[layer_idx]) + { + const float lerp_mul = static_cast(std::min( + config.support_wall_thickness_layer_smooth, + std::max( + config.support_wall_thickness_bottom_layers - static_cast(layer_idx), + config.support_wall_thickness_top_layers - static_cast(elem->distance_to_top_)))) + / std::max(coord_t{ 1 }, config.support_wall_thickness_layer_smooth); + const coord_t nominal_wall_width + = std::lerp(config.support_enlarged_wall_thickness, std::max(coord_t{ EPSILON }, config.support_wall_thickness), std::clamp(lerp_mul, 0.f, 1.f)); + + + const auto center = (elem->result_on_layer_.X >= 0 && elem->result_on_layer_.Y >= 0) ? elem->result_on_layer_ : elem->target_position_; + center_locator->insert(center, { nominal_wall_width, elem->area_->area() }); + } + }); + // ensure all branch areas added as roof actually cause a roofline to generate. Else disable turning the branch to roof going down cura::parallel_for( 0, @@ -2462,7 +2510,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou filterFloatingLines(support_layer_storage); const auto t_filter = std::chrono::high_resolution_clock::now(); - finalizeInterfaceAndSupportAreas(support_layer_storage, support_roof_storage, support_layer_storage_fractional, storage); + finalizeInterfaceAndSupportAreas(support_layer_storage, support_roof_storage, support_layer_storage_fractional, center_locator_per_layer, 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 02ebdbd056..6f60eec8e6 100644 --- a/src/TreeSupportTipGenerator.cpp +++ b/src/TreeSupportTipGenerator.cpp @@ -1148,7 +1148,7 @@ void TreeSupportTipGenerator::generateTips( // 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_ && config_.support_wall_thickness >= EPSILON && config_.support_roof_wall_count == 0) { for (auto roof_area : support_roof_drawn_[layer_idx].unionPolygons(roof_tips_drawn_[layer_idx]).splitIntoParts()) { diff --git a/src/infill.cpp b/src/infill.cpp index 217d147673..8608112aac 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -61,7 +61,7 @@ namespace cura Shape Infill::generateWallToolPaths( std::vector& toolpaths, const Shape& outer_contour, - const size_t wall_line_count, + const coord_t wall_thickness, const coord_t line_width, const Settings& settings, int layer_idx, @@ -69,11 +69,13 @@ Shape Infill::generateWallToolPaths( WallToolPathGenerator generator) { Shape inner_contour; - if (wall_line_count > 0) + if (wall_thickness > 0) { constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. constexpr coord_t wall_x_inset = 0; // Don't apply any inner wall inset for these. That's just for the outer wall. - WallToolPaths wall_toolpaths(outer_contour, line_width, wall_line_count, wall_0_inset, wall_x_inset, settings, layer_idx, section_type, generator); + const size_t wall_line_count = std::llrint(static_cast(wall_thickness) / line_width); + const coord_t actual_line_width = wall_thickness / std::max(size_t{ 1 }, wall_line_count); + WallToolPaths wall_toolpaths(outer_contour, actual_line_width, wall_line_count, wall_0_inset, wall_x_inset, settings, layer_idx, section_type, generator); wall_toolpaths.pushToolPaths(toolpaths); inner_contour = wall_toolpaths.getInnerContour(); } @@ -103,7 +105,7 @@ void Infill::generate( return; } - inner_contour_ = generateWallToolPaths(toolpaths, outer_contour_, wall_line_count_, infill_line_width_, settings, layer_idx, section_type, wall_generator); + inner_contour_ = generateWallToolPaths(toolpaths, outer_contour_, wall_thickness_, infill_line_width_, settings, layer_idx, section_type, wall_generator); scripta::log("infill_inner_contour_0", inner_contour_, section_type, layer_idx); inner_contour_ = inner_contour_.offset(infill_overlap_); diff --git a/src/infill/LightningGenerator.cpp b/src/infill/LightningGenerator.cpp index 60a809abb7..71b12ae90d 100644 --- a/src/infill/LightningGenerator.cpp +++ b/src/infill/LightningGenerator.cpp @@ -68,7 +68,7 @@ LightningGenerator::LightningGenerator(const SupportStorage& support) const Settings& extruder_settings = support_extruder.settings_; const auto layer_thickness = extruder_settings.get("support_infill_sparse_thickness"); const auto infill_line_width = extruder_settings.get("support_line_width"); - const auto infill_wall_line_count = static_cast(settings.get("support_wall_count")); + const auto infill_wall_thickness = settings.get("support_wall_thickness"); const auto line_distance = extruder_settings.get("support_line_distance"); const auto overhang_angle = extruder_settings.get("support_lightning_overhang_angle"); const auto prune_angle = extruder_settings.get("support_lightning_prune_angle"); @@ -87,13 +87,13 @@ LightningGenerator::LightningGenerator(const SupportStorage& support) areas_per_layer.push_back(supper_area_here); } - generate(layer_thickness, infill_line_width, infill_wall_line_count, line_distance, overhang_angle, prune_angle, straightening_angle, areas_per_layer); + generate(layer_thickness, infill_line_width, infill_wall_thickness, line_distance, overhang_angle, prune_angle, straightening_angle, areas_per_layer); } -void LightningGenerator::generateInitialInternalOverhangs(const coord_t infill_line_width, const coord_t infill_wall_line_count, const std::vector& shape_per_layer) +void LightningGenerator::generateInitialInternalOverhangs(const coord_t infill_wall_thickness, const std::vector& shape_per_layer) { overhang_per_layer.resize(shape_per_layer.size()); - const coord_t infill_wall_offset = -infill_wall_line_count * infill_line_width; + const coord_t infill_wall_offset = -infill_wall_thickness; Shape infill_area_above; // Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. @@ -118,7 +118,7 @@ const LightningLayer& LightningGenerator::getTreesForLayer(const size_t& layer_i void LightningGenerator::generate( const coord_t layer_thickness, const coord_t line_width, - const coord_t wall_line_count, + const coord_t wall_thickness, const coord_t line_distance, const AngleRadians& overhang_angle, const AngleRadians& prune_angle, @@ -130,14 +130,14 @@ void LightningGenerator::generate( prune_length = layer_thickness * std::tan(prune_angle); straightening_max_distance = layer_thickness * std::tan(straightening_angle); - generateInitialInternalOverhangs(line_width, wall_line_count, shape_per_layer); - generateTrees(line_width, wall_line_count, shape_per_layer); + generateInitialInternalOverhangs(wall_thickness, shape_per_layer); + generateTrees(wall_thickness, shape_per_layer); } -void LightningGenerator::generateTrees(const coord_t infill_line_width, const coord_t infill_wall_line_count, const std::vector& shape_per_layer) +void LightningGenerator::generateTrees(const coord_t infill_wall_thickness, const std::vector& shape_per_layer) { lightning_layers.resize(shape_per_layer.size()); - const coord_t infill_wall_offset = -infill_wall_line_count * infill_line_width; + const coord_t infill_wall_offset = -infill_wall_thickness; std::vector infill_outlines; infill_outlines.insert(infill_outlines.end(), shape_per_layer.size(), Shape()); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 7dcb30b584..e2f57a3cfe 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -800,7 +800,8 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(const Shape& exclude_polyg for (size_t support_island_idx = 1; support_island_idx < smaller_support_islands.size(); ++support_island_idx) { const SingleShape& 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_); + support_infill_parts + .emplace_back(smaller_island, support_infill_part.support_line_width_, support_infill_part.use_fractional_config_, support_infill_part.inset_width_to_generate_); } } @@ -827,7 +828,7 @@ void SupportLayer::fillInfillParts( const coord_t infill_layer_height, const std::vector>& meshes, const coord_t support_line_width, - const coord_t wall_line_count, + const coord_t wall_thickness, const coord_t grow_layer_above /*has default 0*/, const bool unionAll /*has default false*/, const coord_t custom_line_distance /*has default 0*/) @@ -853,7 +854,7 @@ void SupportLayer::fillInfillParts( { for (const SingleShape& island_outline : support_areas.splitIntoParts(unionAll)) { - support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_line_count, custom_line_distance); + support_infill_parts.emplace_back(island_outline, support_line_width, use_fractional_config, wall_thickness, custom_line_distance); } use_fractional_config = false; } diff --git a/src/support.cpp b/src/support.cpp index 95e6573598..282d78926c 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -120,15 +120,15 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage const coord_t support_line_width = infill_extruder.settings_.get("support_line_width"); // The wall line count is used for calculating insets, and we generate support infill patterns within the insets - const size_t wall_line_count = infill_extruder.settings_.get("support_wall_count"); + const auto wall_thickness = infill_extruder.settings_.get("support_wall_thickness"); // Generate separate support islands for (LayerIndex layer_nr = 0; layer_nr < total_layer_count - 1; ++layer_nr) { - unsigned int wall_line_count_this_layer = wall_line_count; + unsigned int wall_thickness_this_layer = wall_thickness; if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { // The first layer will be printed with a grid pattern - wall_line_count_this_layer++; + wall_thickness_this_layer += support_line_width; } const Shape& global_support_areas = global_support_areas_per_layer[layer_nr]; @@ -148,7 +148,7 @@ void AreaSupport::splitGlobalSupportAreasIntoSupportInfillParts(SliceDataStorage // tower will remove themselves from the support, so the outlines of the parts can be changed. const coord_t layer_height = infill_extruder.settings_.get("layer_height"); storage.support.supportLayers[layer_nr] - .fillInfillParts(layer_nr, global_support_areas_per_layer, layer_height, storage.meshes, support_line_width_here, wall_line_count_this_layer); + .fillInfillParts(layer_nr, global_support_areas_per_layer, layer_height, storage.meshes, support_line_width_here, wall_thickness_this_layer); } } @@ -297,7 +297,7 @@ void AreaSupport::generateSupportBase(SliceDataStorage& storage) const coord_t base_inside_extra_width = LinearAlg2D::getSlopedWidth(base_inside_width, base_inside_height, base_inside_curve_magnitude, layer_z); for (SupportInfillPart* part_to_process : parts_to_process) { - const Shape support_inside_area = part_to_process->outline_.offset(-part_to_process->inset_count_to_generate_ * part_to_process->support_line_width_); + const Shape support_inside_area = part_to_process->outline_.offset(-part_to_process->inset_width_to_generate_); PolygonUtils::InsetOutset base_insets = PolygonUtils::generateInsetOutset(support_inside_area, -base_inside_extra_width, support_line_width); part_to_process->base_inside_contour_ = support_inside_area.offset(base_insets.final_contour_offset); for (Shape& base_inset : base_insets.walls) @@ -403,7 +403,7 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) const Shape wall_inside_area = Infill::generateWallToolPaths( support_infill_part.wall_toolpaths_, original_area, - support_infill_part.inset_count_to_generate_, + support_infill_part.inset_width_to_generate_, wall_width, infill_extruder.settings_, layer_nr, @@ -609,7 +609,7 @@ void AreaSupport::cleanup(SliceDataStorage& storage) { SupportInfillPart& part = layer.support_infill_parts[part_idx]; bool can_be_removed = true; - if (part.inset_count_to_generate_ > 0) + if (part.inset_width_to_generate_ > 0) { can_be_removed = false; } diff --git a/src/utils/OBJ.cpp b/src/utils/OBJ.cpp index 9858625d25..336b0c6f31 100644 --- a/src/utils/OBJ.cpp +++ b/src/utils/OBJ.cpp @@ -96,8 +96,8 @@ OBJ::~OBJ() out_material << "newmtl " << materialName(color) << "\n"; SVG::RgbColor color_rgb = SVG::toRgb(color); - out_material << "Kd " << color_rgb.r << color_rgb.g << color_rgb.b << "\n" - << "\n"; + out_material << "Kd " << static_cast(color_rgb.r) / 256.f << " " << static_cast(color_rgb.g) / 256.f << " " << static_cast(color_rgb.b) / 256.f << "\n" + << "\n"; // Extra newline between materials. } }