Skip to content

Commit 4c2c378

Browse files
authored
CURA-11966 reduce speed on overhang (#2186)
2 parents 35a8c58 + 0dc57c4 commit 4c2c378

File tree

7 files changed

+1138
-44
lines changed

7 files changed

+1138
-44
lines changed

doc/gradual_overhang_speed.svg

Lines changed: 857 additions & 0 deletions
Loading

include/LayerPlan.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ class LayerPlan : public NoCopy
6060
#endif
6161

6262
public:
63+
struct OverhangMask
64+
{
65+
Shape supported_region;
66+
Ratio speed_ratio;
67+
};
68+
6369
const PathConfigStorage configs_storage_; //!< The line configs for this layer for each feature type
6470
const coord_t z_;
6571
coord_t final_travel_z_;
@@ -115,7 +121,8 @@ class LayerPlan : public NoCopy
115121
Comb* comb_;
116122
coord_t comb_move_inside_distance_; //!< Whenever using the minimum boundary for combing it tries to move the coordinates inside by this distance after calculating the combing.
117123
Shape bridge_wall_mask_; //!< The regions of a layer part that are not supported, used for bridging
118-
Shape overhang_mask_; //!< The regions of a layer part where the walls overhang
124+
std::vector<OverhangMask> overhang_masks_; //!< The regions of a layer part where the walls overhang, calculated for multiple overhang angles. The latter is the most
125+
//!< overhanging. For a visual explanation of the result, see doc/gradual_overhang_speed.svg
119126
Shape seam_overhang_mask_; //!< The regions of a layer part where the walls overhang, specifically as defined for the seam
120127
Shape roofing_mask_; //!< The regions of a layer part where the walls are exposed to the air
121128

@@ -295,11 +302,11 @@ class LayerPlan : public NoCopy
295302
void setBridgeWallMask(const Shape& polys);
296303

297304
/*!
298-
* Set overhang_mask.
305+
* Set overhang_masks.
299306
*
300-
* \param polys The overhung areas of the part currently being processed that will require modified print settings
307+
* \param masks The overhung areas of the part currently being processed that will require modified print settings
301308
*/
302-
void setOverhangMask(const Shape& polys);
309+
void setOverhangMasks(const std::vector<OverhangMask>& masks);
303310

304311
/*!
305312
* Set seam_overhang_mask.
@@ -393,6 +400,17 @@ class LayerPlan : public NoCopy
393400
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
394401
const bool travel_to_z = true);
395402

403+
void addExtrusionMoveWithGradualOverhang(
404+
const Point3LL& p,
405+
const GCodePathConfig& config,
406+
const SpaceFillType space_fill_type,
407+
const Ratio& flow = 1.0_r,
408+
const Ratio width_factor = 1.0_r,
409+
const bool spiralize = false,
410+
const Ratio speed_factor = 1.0_r,
411+
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
412+
const bool travel_to_z = true);
413+
396414
/*!
397415
* Add polygon to the gcode starting at vertex \p startIdx
398416
* \param polygon The polygon
@@ -1000,13 +1018,6 @@ class LayerPlan : public NoCopy
10001018
* \return The distance from the start of the current wall line to the first bridge segment
10011019
*/
10021020
coord_t computeDistanceToBridgeStart(const ExtrusionLine& wall, const size_t current_index, const coord_t min_bridge_line_len) const;
1003-
1004-
/*!
1005-
* \brief Calculates whether the given segment is to be treated as overhanging
1006-
* \param p0 The start point of the segment
1007-
* \param p1 The end point of the segment
1008-
*/
1009-
bool segmentIsOnOverhang(const Point3LL& p0, const Point3LL& p1) const;
10101021
};
10111022

10121023
} // namespace cura

include/geometry/Shape.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,14 @@ class Shape : public LinesSet<Polygon>
256256
*/
257257
void simplify(ClipperLib::PolyFillType fill_type = ClipperLib::pftEvenOdd);
258258

259+
/*!
260+
* Calculates the intersections between the given segment and all the segments of the shape
261+
* @param start The start position of the segment
262+
* @param end The end position of the segment
263+
* @return The parameters of the intersections on the segment (intersection = start + t * (end - start)), unsorted
264+
*/
265+
std::vector<float> intersectionsWithSegment(const Point2LL& start, const Point2LL& end) const;
266+
259267
#ifdef BUILD_TESTS
260268
/*!
261269
* @brief Import the polygon from a WKT string

src/FffGcodeWriter.cpp

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <optional>
1313
#include <unordered_set>
1414

15+
#include <range/v3/view/chunk_by.hpp>
1516
#include <range/v3/view/concat.hpp>
1617
#include <spdlog/spdlog.h>
1718

@@ -3082,21 +3083,86 @@ bool FffGcodeWriter::processInsets(
30823083
gcode_layer.setBridgeWallMask(Shape());
30833084
}
30843085

3085-
const auto get_overhang_region = [&](const AngleDegrees overhang_angle) -> Shape
3086+
const Shape fully_supported_region = outlines_below.offset(-half_outer_wall_width);
3087+
const Shape part_print_region = part.outline.offset(-half_outer_wall_width);
3088+
3089+
const auto get_supported_region = [&fully_supported_region, &layer_height](const AngleDegrees& overhang_angle) -> Shape
30863090
{
3087-
if (overhang_angle >= 90)
3088-
{
3089-
return Shape(); // keep empty to disable overhang detection
3090-
}
30913091
// the overhang mask is set to the area of the current part's outline minus the region that is considered to be supported
30923092
// the supported region is made up of those areas that really are supported by either model or support on the layer below
30933093
// expanded to take into account the overhang angle, the greater the overhang angle, the larger the supported area is
30943094
// considered to be
3095-
const coord_t overhang_width = layer_height * std::tan(overhang_angle / (180 / std::numbers::pi));
3096-
return part.outline.offset(-half_outer_wall_width).difference(outlines_below.offset(10 + overhang_width - half_outer_wall_width)).offset(10);
3095+
if (overhang_angle < 90.0)
3096+
{
3097+
const coord_t overhang_width = layer_height * std::tan(AngleRadians(overhang_angle));
3098+
return fully_supported_region.offset(overhang_width + 10);
3099+
}
3100+
3101+
return Shape();
30973102
};
3098-
gcode_layer.setOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("wall_overhang_angle")));
3099-
gcode_layer.setSeamOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("seam_overhang_angle")));
3103+
3104+
// Build supported regions for all the overhang speeds. For a visual explanation of the result, see doc/gradual_overhang_speed.svg
3105+
std::vector<LayerPlan::OverhangMask> overhang_masks;
3106+
const auto overhang_speed_factors = mesh.settings.get<std::vector<Ratio>>("wall_overhang_speed_factors");
3107+
const size_t overhang_angles_count = overhang_speed_factors.size();
3108+
const auto wall_overhang_angle = mesh.settings.get<AngleDegrees>("wall_overhang_angle");
3109+
if (overhang_angles_count > 0 && wall_overhang_angle < 90.0)
3110+
{
3111+
struct SpeedRegion
3112+
{
3113+
AngleDegrees overhang_angle;
3114+
Ratio speed_factor;
3115+
};
3116+
3117+
// Create raw speed regions
3118+
const AngleDegrees overhang_step = (90.0 - wall_overhang_angle) / static_cast<double>(overhang_angles_count);
3119+
std::vector<SpeedRegion> speed_regions;
3120+
speed_regions.reserve(overhang_angles_count + 1);
3121+
3122+
speed_regions.push_back(SpeedRegion{ wall_overhang_angle, 1.0_r }); // Initial internal region, always 100% speed factor
3123+
3124+
for (size_t angle_index = 1; angle_index < overhang_angles_count; ++angle_index)
3125+
{
3126+
const AngleDegrees actual_wall_overhang_angle = wall_overhang_angle + static_cast<double>(angle_index) * overhang_step;
3127+
const Ratio speed_factor = overhang_speed_factors[angle_index - 1];
3128+
3129+
speed_regions.push_back(SpeedRegion{ actual_wall_overhang_angle, speed_factor });
3130+
}
3131+
3132+
speed_regions.push_back(SpeedRegion{ 90.0, overhang_speed_factors.back() }); // Final "everything else" speed region
3133+
3134+
// Now merge regions that have similar speed factors (saves calculations and avoid generating micro-segments)
3135+
auto merged_regions = speed_regions
3136+
| ranges::views::chunk_by(
3137+
[](const auto& region_a, const auto& region_b)
3138+
{
3139+
return region_a.speed_factor == region_b.speed_factor;
3140+
});
3141+
3142+
// If finally necessary, add actual calculated speed regions
3143+
if (ranges::distance(merged_regions) > 1)
3144+
{
3145+
for (const auto& regions : merged_regions)
3146+
{
3147+
const SpeedRegion& last_region = *ranges::prev(regions.end());
3148+
overhang_masks.push_back(LayerPlan::OverhangMask{ get_supported_region(last_region.overhang_angle), last_region.speed_factor });
3149+
}
3150+
}
3151+
}
3152+
gcode_layer.setOverhangMasks(overhang_masks);
3153+
3154+
// the seam overhang mask is set to the area of the current part's outline minus the region that is considered to be supported,
3155+
// which will then be empty if everything is considered supported i.r.t. the angle
3156+
const AngleDegrees seam_overhang_angle = mesh.settings.get<AngleDegrees>("seam_overhang_angle");
3157+
if (seam_overhang_angle < 90.0)
3158+
{
3159+
const Shape supported_region_seam = get_supported_region(seam_overhang_angle);
3160+
gcode_layer.setSeamOverhangMask(part_print_region.difference(supported_region_seam).offset(10));
3161+
}
3162+
else
3163+
{
3164+
gcode_layer.setSeamOverhangMask(Shape());
3165+
}
31003166

31013167
const auto roofing_mask_fn = [&]() -> Shape
31023168
{
@@ -3127,7 +3193,7 @@ bool FffGcodeWriter::processInsets(
31273193
// clear to disable use of bridging settings
31283194
gcode_layer.setBridgeWallMask(Shape());
31293195
// clear to disable overhang detection
3130-
gcode_layer.setOverhangMask(Shape());
3196+
gcode_layer.setOverhangMasks({});
31313197
// clear to disable overhang detection
31323198
gcode_layer.setSeamOverhangMask(Shape());
31333199
// clear to disable use of roofing settings

0 commit comments

Comments
 (0)