|
12 | 12 | #include <optional> |
13 | 13 | #include <unordered_set> |
14 | 14 |
|
| 15 | +#include <range/v3/view/chunk_by.hpp> |
15 | 16 | #include <range/v3/view/concat.hpp> |
16 | 17 | #include <spdlog/spdlog.h> |
17 | 18 |
|
@@ -318,6 +319,8 @@ void FffGcodeWriter::setConfigFanSpeedLayerTime() |
318 | 319 | fan_speed_layer_time_settings_per_extruder.emplace_back(); |
319 | 320 | FanSpeedLayerTimeSettings& fan_speed_layer_time_settings = fan_speed_layer_time_settings_per_extruder.back(); |
320 | 321 | fan_speed_layer_time_settings.cool_min_layer_time = train.settings_.get<Duration>("cool_min_layer_time"); |
| 322 | + fan_speed_layer_time_settings.cool_min_layer_time_overhang = train.settings_.get<Duration>("cool_min_layer_time_overhang"); |
| 323 | + fan_speed_layer_time_settings.cool_min_layer_time_overhang_min_segment_length = train.settings_.get<coord_t>("cool_min_layer_time_overhang_min_segment_length"); |
321 | 324 | fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = train.settings_.get<Duration>("cool_min_layer_time_fan_speed_max"); |
322 | 325 | fan_speed_layer_time_settings.cool_fan_speed_0 = train.settings_.get<Ratio>("cool_fan_speed_0") * 100.0; |
323 | 326 | fan_speed_layer_time_settings.cool_fan_speed_min = train.settings_.get<Ratio>("cool_fan_speed_min") * 100.0; |
@@ -3084,21 +3087,88 @@ bool FffGcodeWriter::processInsets( |
3084 | 3087 | gcode_layer.setBridgeWallMask(Shape()); |
3085 | 3088 | } |
3086 | 3089 |
|
3087 | | - const auto get_overhang_region = [&](const AngleDegrees overhang_angle) -> Shape |
| 3090 | + const Shape fully_supported_region = outlines_below.offset(-half_outer_wall_width); |
| 3091 | + const Shape part_print_region = part.outline.offset(-half_outer_wall_width); |
| 3092 | + |
| 3093 | + const auto get_supported_region = [&fully_supported_region, &layer_height](const AngleDegrees& overhang_angle) -> Shape |
3088 | 3094 | { |
3089 | | - if (overhang_angle >= 90) |
3090 | | - { |
3091 | | - return Shape(); // keep empty to disable overhang detection |
3092 | | - } |
3093 | 3095 | // the overhang mask is set to the area of the current part's outline minus the region that is considered to be supported |
3094 | 3096 | // the supported region is made up of those areas that really are supported by either model or support on the layer below |
3095 | 3097 | // expanded to take into account the overhang angle, the greater the overhang angle, the larger the supported area is |
3096 | 3098 | // considered to be |
3097 | | - const coord_t overhang_width = layer_height * std::tan(overhang_angle / (180 / std::numbers::pi)); |
3098 | | - return part.outline.offset(-half_outer_wall_width).difference(outlines_below.offset(10 + overhang_width - half_outer_wall_width)).offset(10); |
| 3099 | + if (overhang_angle < 90.0) |
| 3100 | + { |
| 3101 | + const coord_t overhang_width = layer_height * std::tan(AngleRadians(overhang_angle)); |
| 3102 | + return fully_supported_region.offset(overhang_width + 10); |
| 3103 | + } |
| 3104 | + |
| 3105 | + return Shape(); |
3099 | 3106 | }; |
3100 | | - gcode_layer.setOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("wall_overhang_angle"))); |
3101 | | - gcode_layer.setSeamOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("seam_overhang_angle"))); |
| 3107 | + |
| 3108 | + // Build supported regions for all the overhang speeds. For a visual explanation of the result, see doc/gradual_overhang_speed.svg |
| 3109 | + std::vector<LayerPlan::OverhangMask> overhang_masks; |
| 3110 | + const auto overhang_speed_factors = mesh.settings.get<std::vector<Ratio>>("wall_overhang_speed_factors"); |
| 3111 | + const size_t overhang_angles_count = overhang_speed_factors.size(); |
| 3112 | + const auto wall_overhang_angle = mesh.settings.get<AngleDegrees>("wall_overhang_angle"); |
| 3113 | + if (overhang_angles_count > 0 && wall_overhang_angle < 90.0) |
| 3114 | + { |
| 3115 | + struct SpeedRegion |
| 3116 | + { |
| 3117 | + AngleDegrees overhang_angle; |
| 3118 | + Ratio speed_factor; |
| 3119 | + bool chunk = true; |
| 3120 | + }; |
| 3121 | + |
| 3122 | + // Create raw speed regions |
| 3123 | + const AngleDegrees overhang_step = (90.0 - wall_overhang_angle) / static_cast<double>(overhang_angles_count); |
| 3124 | + std::vector<SpeedRegion> speed_regions; |
| 3125 | + speed_regions.reserve(overhang_angles_count + 2); |
| 3126 | + |
| 3127 | + constexpr bool dont_chunk_first = false; // Never merge internal region in order to detect actual overhanging |
| 3128 | + speed_regions.push_back(SpeedRegion{ wall_overhang_angle, 1.0_r, dont_chunk_first }); // Initial internal region, always 100% speed factor |
| 3129 | + |
| 3130 | + for (size_t angle_index = 1; angle_index <= overhang_angles_count; ++angle_index) |
| 3131 | + { |
| 3132 | + const AngleDegrees actual_wall_overhang_angle = wall_overhang_angle + static_cast<double>(angle_index) * overhang_step; |
| 3133 | + const Ratio speed_factor = overhang_speed_factors[angle_index - 1]; |
| 3134 | + |
| 3135 | + speed_regions.push_back(SpeedRegion{ actual_wall_overhang_angle, speed_factor }); |
| 3136 | + } |
| 3137 | + |
| 3138 | + speed_regions.push_back(SpeedRegion{ 90.0, overhang_speed_factors.back() }); // Final "everything else" speed region |
| 3139 | + |
| 3140 | + // Now merge regions that have similar speed factors (saves calculations and avoid generating micro-segments) |
| 3141 | + auto merged_regions = speed_regions |
| 3142 | + | ranges::views::chunk_by( |
| 3143 | + [](const auto& region_a, const auto& region_b) |
| 3144 | + { |
| 3145 | + return region_a.chunk && region_b.chunk && region_a.speed_factor == region_b.speed_factor; |
| 3146 | + }); |
| 3147 | + |
| 3148 | + // If finally necessary, add actual calculated speed regions |
| 3149 | + if (ranges::distance(merged_regions) > 1) |
| 3150 | + { |
| 3151 | + for (const auto& regions : merged_regions) |
| 3152 | + { |
| 3153 | + const SpeedRegion& last_region = *ranges::prev(regions.end()); |
| 3154 | + overhang_masks.push_back(LayerPlan::OverhangMask{ get_supported_region(last_region.overhang_angle), last_region.speed_factor }); |
| 3155 | + } |
| 3156 | + } |
| 3157 | + } |
| 3158 | + gcode_layer.setOverhangMasks(overhang_masks); |
| 3159 | + |
| 3160 | + // the seam overhang mask is set to the area of the current part's outline minus the region that is considered to be supported, |
| 3161 | + // which will then be empty if everything is considered supported i.r.t. the angle |
| 3162 | + const AngleDegrees seam_overhang_angle = mesh.settings.get<AngleDegrees>("seam_overhang_angle"); |
| 3163 | + if (seam_overhang_angle < 90.0) |
| 3164 | + { |
| 3165 | + const Shape supported_region_seam = get_supported_region(seam_overhang_angle); |
| 3166 | + gcode_layer.setSeamOverhangMask(part_print_region.difference(supported_region_seam).offset(10)); |
| 3167 | + } |
| 3168 | + else |
| 3169 | + { |
| 3170 | + gcode_layer.setSeamOverhangMask(Shape()); |
| 3171 | + } |
3102 | 3172 |
|
3103 | 3173 | const auto roofing_mask_fn = [&]() -> Shape |
3104 | 3174 | { |
@@ -3129,7 +3199,7 @@ bool FffGcodeWriter::processInsets( |
3129 | 3199 | // clear to disable use of bridging settings |
3130 | 3200 | gcode_layer.setBridgeWallMask(Shape()); |
3131 | 3201 | // clear to disable overhang detection |
3132 | | - gcode_layer.setOverhangMask(Shape()); |
| 3202 | + gcode_layer.setOverhangMasks({}); |
3133 | 3203 | // clear to disable overhang detection |
3134 | 3204 | gcode_layer.setSeamOverhangMask(Shape()); |
3135 | 3205 | // clear to disable use of roofing settings |
|
0 commit comments