Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CURA-11966 reduce speed on overhang #2186

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
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
Loading