Skip to content

Commit

Permalink
CURA-11966 reduce speed on overhang (#2186)
Browse files Browse the repository at this point in the history
  • Loading branch information
HellAholic authored Feb 3, 2025
2 parents 35a8c58 + 0dc57c4 commit 4c2c378
Show file tree
Hide file tree
Showing 7 changed files with 1,138 additions and 44 deletions.
857 changes: 857 additions & 0 deletions doc/gradual_overhang_speed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 22 additions & 11 deletions include/LayerPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ class LayerPlan : public NoCopy
#endif

public:
struct OverhangMask
{
Shape supported_region;
Ratio speed_ratio;
};

const PathConfigStorage configs_storage_; //!< The line configs for this layer for each feature type
const coord_t z_;
coord_t final_travel_z_;
Expand Down Expand Up @@ -115,7 +121,8 @@ class LayerPlan : public NoCopy
Comb* comb_;
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.
Shape bridge_wall_mask_; //!< The regions of a layer part that are not supported, used for bridging
Shape overhang_mask_; //!< The regions of a layer part where the walls overhang
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
//!< overhanging. For a visual explanation of the result, see doc/gradual_overhang_speed.svg
Shape seam_overhang_mask_; //!< The regions of a layer part where the walls overhang, specifically as defined for the seam
Shape roofing_mask_; //!< The regions of a layer part where the walls are exposed to the air

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

/*!
* Set overhang_mask.
* Set overhang_masks.
*
* \param polys The overhung areas of the part currently being processed that will require modified print settings
* \param masks The overhung areas of the part currently being processed that will require modified print settings
*/
void setOverhangMask(const Shape& polys);
void setOverhangMasks(const std::vector<OverhangMask>& masks);

/*!
* Set seam_overhang_mask.
Expand Down Expand Up @@ -393,6 +400,17 @@ class LayerPlan : public NoCopy
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
const bool travel_to_z = true);

void addExtrusionMoveWithGradualOverhang(
const Point3LL& p,
const GCodePathConfig& config,
const SpaceFillType space_fill_type,
const Ratio& flow = 1.0_r,
const Ratio width_factor = 1.0_r,
const bool spiralize = false,
const Ratio speed_factor = 1.0_r,
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
const bool travel_to_z = true);

/*!
* Add polygon to the gcode starting at vertex \p startIdx
* \param polygon The polygon
Expand Down Expand Up @@ -1000,13 +1018,6 @@ class LayerPlan : public NoCopy
* \return The distance from the start of the current wall line to the first bridge segment
*/
coord_t computeDistanceToBridgeStart(const ExtrusionLine& wall, const size_t current_index, const coord_t min_bridge_line_len) const;

/*!
* \brief Calculates whether the given segment is to be treated as overhanging
* \param p0 The start point of the segment
* \param p1 The end point of the segment
*/
bool segmentIsOnOverhang(const Point3LL& p0, const Point3LL& p1) const;
};

} // namespace cura
Expand Down
8 changes: 8 additions & 0 deletions include/geometry/Shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ class Shape : public LinesSet<Polygon>
*/
void simplify(ClipperLib::PolyFillType fill_type = ClipperLib::pftEvenOdd);

/*!
* Calculates the intersections between the given segment and all the segments of the shape
* @param start The start position of the segment
* @param end The end position of the segment
* @return The parameters of the intersections on the segment (intersection = start + t * (end - start)), unsorted
*/
std::vector<float> intersectionsWithSegment(const Point2LL& start, const Point2LL& end) const;

#ifdef BUILD_TESTS
/*!
* @brief Import the polygon from a WKT string
Expand Down
86 changes: 76 additions & 10 deletions src/FffGcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <optional>
#include <unordered_set>

#include <range/v3/view/chunk_by.hpp>
#include <range/v3/view/concat.hpp>
#include <spdlog/spdlog.h>

Expand Down Expand Up @@ -3082,21 +3083,86 @@ bool FffGcodeWriter::processInsets(
gcode_layer.setBridgeWallMask(Shape());
}

const auto get_overhang_region = [&](const AngleDegrees overhang_angle) -> Shape
const Shape fully_supported_region = outlines_below.offset(-half_outer_wall_width);
const Shape part_print_region = part.outline.offset(-half_outer_wall_width);

const auto get_supported_region = [&fully_supported_region, &layer_height](const AngleDegrees& overhang_angle) -> Shape
{
if (overhang_angle >= 90)
{
return Shape(); // keep empty to disable overhang detection
}
// the overhang mask is set to the area of the current part's outline minus the region that is considered to be supported
// the supported region is made up of those areas that really are supported by either model or support on the layer below
// expanded to take into account the overhang angle, the greater the overhang angle, the larger the supported area is
// considered to be
const coord_t overhang_width = layer_height * std::tan(overhang_angle / (180 / std::numbers::pi));
return part.outline.offset(-half_outer_wall_width).difference(outlines_below.offset(10 + overhang_width - half_outer_wall_width)).offset(10);
if (overhang_angle < 90.0)
{
const coord_t overhang_width = layer_height * std::tan(AngleRadians(overhang_angle));
return fully_supported_region.offset(overhang_width + 10);
}

return Shape();
};
gcode_layer.setOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("wall_overhang_angle")));
gcode_layer.setSeamOverhangMask(get_overhang_region(mesh.settings.get<AngleDegrees>("seam_overhang_angle")));

// Build supported regions for all the overhang speeds. For a visual explanation of the result, see doc/gradual_overhang_speed.svg
std::vector<LayerPlan::OverhangMask> overhang_masks;
const auto overhang_speed_factors = mesh.settings.get<std::vector<Ratio>>("wall_overhang_speed_factors");
const size_t overhang_angles_count = overhang_speed_factors.size();
const auto wall_overhang_angle = mesh.settings.get<AngleDegrees>("wall_overhang_angle");
if (overhang_angles_count > 0 && wall_overhang_angle < 90.0)
{
struct SpeedRegion
{
AngleDegrees overhang_angle;
Ratio speed_factor;
};

// Create raw speed regions
const AngleDegrees overhang_step = (90.0 - wall_overhang_angle) / static_cast<double>(overhang_angles_count);
std::vector<SpeedRegion> speed_regions;
speed_regions.reserve(overhang_angles_count + 1);

speed_regions.push_back(SpeedRegion{ wall_overhang_angle, 1.0_r }); // Initial internal region, always 100% speed factor

for (size_t angle_index = 1; angle_index < overhang_angles_count; ++angle_index)
{
const AngleDegrees actual_wall_overhang_angle = wall_overhang_angle + static_cast<double>(angle_index) * overhang_step;
const Ratio speed_factor = overhang_speed_factors[angle_index - 1];

speed_regions.push_back(SpeedRegion{ actual_wall_overhang_angle, speed_factor });
}

speed_regions.push_back(SpeedRegion{ 90.0, overhang_speed_factors.back() }); // Final "everything else" speed region

// Now merge regions that have similar speed factors (saves calculations and avoid generating micro-segments)
auto merged_regions = speed_regions
| ranges::views::chunk_by(
[](const auto& region_a, const auto& region_b)
{
return region_a.speed_factor == region_b.speed_factor;
});

// If finally necessary, add actual calculated speed regions
if (ranges::distance(merged_regions) > 1)
{
for (const auto& regions : merged_regions)
{
const SpeedRegion& last_region = *ranges::prev(regions.end());
overhang_masks.push_back(LayerPlan::OverhangMask{ get_supported_region(last_region.overhang_angle), last_region.speed_factor });
}
}
}
gcode_layer.setOverhangMasks(overhang_masks);

// the seam overhang mask is set to the area of the current part's outline minus the region that is considered to be supported,
// which will then be empty if everything is considered supported i.r.t. the angle
const AngleDegrees seam_overhang_angle = mesh.settings.get<AngleDegrees>("seam_overhang_angle");
if (seam_overhang_angle < 90.0)
{
const Shape supported_region_seam = get_supported_region(seam_overhang_angle);
gcode_layer.setSeamOverhangMask(part_print_region.difference(supported_region_seam).offset(10));
}
else
{
gcode_layer.setSeamOverhangMask(Shape());
}

const auto roofing_mask_fn = [&]() -> Shape
{
Expand Down Expand Up @@ -3127,7 +3193,7 @@ bool FffGcodeWriter::processInsets(
// clear to disable use of bridging settings
gcode_layer.setBridgeWallMask(Shape());
// clear to disable overhang detection
gcode_layer.setOverhangMask(Shape());
gcode_layer.setOverhangMasks({});
// clear to disable overhang detection
gcode_layer.setSeamOverhangMask(Shape());
// clear to disable use of roofing settings
Expand Down
Loading

0 comments on commit 4c2c378

Please sign in to comment.