Skip to content

Commit 84be5f5

Browse files
committed
Merge remote-tracking branch 'origin/main' into CURA-11978_retract-and-unretract-in-a-travel
2 parents 270b1b5 + 2dec76d commit 84be5f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1321
-121
lines changed

doc/gradual_overhang_speed.svg

Lines changed: 857 additions & 0 deletions
Loading

include/FanSpeedLayerTime.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
//Copyright (c) 2020 Ultimaker B.V.
2-
//CuraEngine is released under the terms of the AGPLv3 or higher.
1+
// Copyright (c) 2020 Ultimaker B.V.
2+
// CuraEngine is released under the terms of the AGPLv3 or higher.
33

44
#ifndef FAN_SPEED_LAYER_TIME_H
55
#define FAN_SPEED_LAYER_TIME_H
66

7+
#include <utils/Coord_t.h>
8+
79
#include "settings/types/Duration.h"
810
#include "settings/types/LayerIndex.h"
911
#include "settings/types/Velocity.h"
1012

11-
namespace cura
13+
namespace cura
1214
{
1315

1416
/*!
@@ -19,7 +21,7 @@ namespace cura
1921
* store these settings over and over again for each part, even though the
2022
* settings may be different for each part on a layer.
2123
*/
22-
struct FanSpeedLayerTimeSettings
24+
struct FanSpeedLayerTimeSettings
2325
{
2426
public:
2527
/*!
@@ -28,6 +30,16 @@ struct FanSpeedLayerTimeSettings
2830
*/
2931
Duration cool_min_layer_time;
3032

33+
/*!
34+
* Similar to Minimum layer time, but to be applied for layers that contain overhanging extrusion.
35+
*/
36+
Duration cool_min_layer_time_overhang;
37+
38+
/*!
39+
* The specific minimum layer time for overhanging will be applied only if there is at least one overhanging segment longer that this threshold
40+
*/
41+
coord_t cool_min_layer_time_overhang_min_segment_length;
42+
3143
/*!
3244
* "Regular/Maximum Fan Speed Threshold". If the layers take longer to print
3345
* than this, they'll use the regular fan speed. If they take shorter, we'll

include/LayerPlan.h

Lines changed: 26 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_;
@@ -129,8 +135,13 @@ class LayerPlan : public NoCopy
129135
Comb* comb_;
130136
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.
131137
Shape bridge_wall_mask_; //!< The regions of a layer part that are not supported, used for bridging
132-
Shape overhang_mask_; //!< The regions of a layer part where the walls overhang
138+
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
139+
//!< overhanging. For a visual explanation of the result, see doc/gradual_overhang_speed.svg
133140
Shape seam_overhang_mask_; //!< The regions of a layer part where the walls overhang, specifically as defined for the seam
141+
bool currently_overhanging_{ false }; //!< Indicates whether the last extrusion move was overhanging
142+
coord_t current_overhang_length_{ 0 }; //!< When doing consecutive overhanging moves, this is the current accumulated overhanging length
143+
coord_t max_overhang_length_{ 0 }; //!< From all consecutive overhanging moves in the layer, this is the longest one
144+
134145
Shape roofing_mask_; //!< The regions of a layer part where the walls are exposed to the air
135146

136147
bool min_layer_time_used = false; //!< Wether or not the minimum layer time (cool_min_layer_time) was actually used in this layerplan.
@@ -309,11 +320,11 @@ class LayerPlan : public NoCopy
309320
void setBridgeWallMask(const Shape& polys);
310321

311322
/*!
312-
* Set overhang_mask.
323+
* Set overhang_masks.
313324
*
314-
* \param polys The overhung areas of the part currently being processed that will require modified print settings
325+
* \param masks The overhung areas of the part currently being processed that will require modified print settings
315326
*/
316-
void setOverhangMask(const Shape& polys);
327+
void setOverhangMasks(const std::vector<OverhangMask>& masks);
317328

318329
/*!
319330
* Set seam_overhang_mask.
@@ -407,6 +418,17 @@ class LayerPlan : public NoCopy
407418
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
408419
const bool travel_to_z = true);
409420

421+
void addExtrusionMoveWithGradualOverhang(
422+
const Point3LL& p,
423+
const GCodePathConfig& config,
424+
const SpaceFillType space_fill_type,
425+
const Ratio& flow = 1.0_r,
426+
const Ratio width_factor = 1.0_r,
427+
const bool spiralize = false,
428+
const Ratio speed_factor = 1.0_r,
429+
const double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT,
430+
const bool travel_to_z = true);
431+
410432
/*!
411433
* Add polygon to the gcode starting at vertex \p startIdx
412434
* \param polygon The polygon
@@ -1021,13 +1043,6 @@ class LayerPlan : public NoCopy
10211043
*/
10221044
coord_t computeDistanceToBridgeStart(const ExtrusionLine& wall, const size_t current_index, const coord_t min_bridge_line_len) const;
10231045

1024-
/*!
1025-
* \brief Calculates whether the given segment is to be treated as overhanging
1026-
* \param p0 The start point of the segment
1027-
* \param p1 The end point of the segment
1028-
*/
1029-
bool segmentIsOnOverhang(const Point3LL& p0, const Point3LL& p1) const;
1030-
10311046
/*!
10321047
* Compute the Z-hop and travel duration for the given travel path
10331048
* @param gcode The gcode exporter, which we need to get the current nozzle position

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: 80 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

@@ -318,6 +319,8 @@ void FffGcodeWriter::setConfigFanSpeedLayerTime()
318319
fan_speed_layer_time_settings_per_extruder.emplace_back();
319320
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings = fan_speed_layer_time_settings_per_extruder.back();
320321
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");
321324
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = train.settings_.get<Duration>("cool_min_layer_time_fan_speed_max");
322325
fan_speed_layer_time_settings.cool_fan_speed_0 = train.settings_.get<Ratio>("cool_fan_speed_0") * 100.0;
323326
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(
30843087
gcode_layer.setBridgeWallMask(Shape());
30853088
}
30863089

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
30883094
{
3089-
if (overhang_angle >= 90)
3090-
{
3091-
return Shape(); // keep empty to disable overhang detection
3092-
}
30933095
// the overhang mask is set to the area of the current part's outline minus the region that is considered to be supported
30943096
// the supported region is made up of those areas that really are supported by either model or support on the layer below
30953097
// expanded to take into account the overhang angle, the greater the overhang angle, the larger the supported area is
30963098
// 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();
30993106
};
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+
}
31023172

31033173
const auto roofing_mask_fn = [&]() -> Shape
31043174
{
@@ -3129,7 +3199,7 @@ bool FffGcodeWriter::processInsets(
31293199
// clear to disable use of bridging settings
31303200
gcode_layer.setBridgeWallMask(Shape());
31313201
// clear to disable overhang detection
3132-
gcode_layer.setOverhangMask(Shape());
3202+
gcode_layer.setOverhangMasks({});
31333203
// clear to disable overhang detection
31343204
gcode_layer.setSeamOverhangMask(Shape());
31353205
// clear to disable use of roofing settings

0 commit comments

Comments
 (0)