From 0d3439f457c4e2cffcba825894bbd1ce72eade55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20J=C3=A4ger?= Date: Wed, 3 Jul 2024 23:14:24 +0200 Subject: [PATCH 01/32] Add z-height to infill plugin --- include/plugins/converters.h | 2 +- src/infill.cpp | 3 ++- src/plugins/converters.cpp | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/plugins/converters.h b/include/plugins/converters.h index 9af4e7cbae..bee7a9db41 100644 --- a/include/plugins/converters.h +++ b/include/plugins/converters.h @@ -103,7 +103,7 @@ struct postprocess_response : public details::converter { - value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const; + value_type operator()(const native_value_type& inner_contour, const std::string& pattern, const Settings& settings, const coord_t z) const; }; struct infill_generate_response diff --git a/src/infill.cpp b/src/infill.cpp index c085307622..1b19508a2e 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -321,7 +321,8 @@ void Infill::_generate( auto [toolpaths_, generated_result_polygons_, generated_result_lines_] = slots::instance().generate( inner_contour_, mesh ? mesh->settings.get("infill_pattern") : settings.get("infill_pattern"), - mesh ? mesh->settings : settings); + mesh ? mesh->settings : settings, + z_); toolpaths.insert(toolpaths.end(), toolpaths_.begin(), toolpaths_.end()); result_polygons.add(generated_result_polygons_); result_lines.add(generated_result_lines_); diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index fe6cbe9c0b..fadfabf475 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -167,7 +167,7 @@ postprocess_response::native_value_type } infill_generate_request::value_type - infill_generate_request::operator()(const infill_generate_request::native_value_type& inner_contour, const std::string& pattern, const Settings& settings) const + infill_generate_request::operator()(const infill_generate_request::native_value_type& inner_contour, const std::string& pattern, const Settings& settings, const coord_t z) const { value_type message{}; message.set_pattern(pattern); @@ -177,6 +177,11 @@ infill_generate_request::value_type msg_settings->insert({ key, value }); } + // ------------------------------------------------------------ + // Add current z height to settings message + // ------------------------------------------------------------ + msg_settings->insert({ "z" , std::to_string(z) }); + if (inner_contour.empty()) { return message; From 8d2bd2baae13171609604319d48d339edfac2645 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 20 Aug 2024 09:29:05 +0200 Subject: [PATCH 02/32] Make GCodePath use 3D points so that they can have individual Z offsets CURA-12081 --- CMakeLists.txt | 3 +- include/ExtruderPlan.h | 2 +- include/GCodePathConfig.h | 2 +- include/LayerPlan.h | 11 +++ include/geometry/Point2LL.h | 45 +++------ include/geometry/Point3LL.h | 13 ++- include/gradual_flow/FlowLimitedPath.h | 13 ++- include/gradual_flow/Processor.h | 28 +++++- include/infill/ImageBasedDensityProvider.h | 1 + include/pathPlanning/GCodePath.h | 4 +- include/utils/AABB3D.h | 1 + include/utils/Point3D.h | 2 +- include/utils/VoxelUtils.h | 2 +- src/LayerPlan.cpp | 104 ++++++++++++++------- src/LayerPlanBuffer.cpp | 2 +- src/geometry/Point2LL.cpp | 45 +++++++++ src/{utils => geometry}/Point3LL.cpp | 21 +++++ src/plugins/converters.cpp | 7 +- src/utils/ToolpathVisualizer.cpp | 2 + 19 files changed, 222 insertions(+), 86 deletions(-) create mode 100644 src/geometry/Point2LL.cpp rename src/{utils => geometry}/Point3LL.cpp (76%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2287e8f92d..5858a1ab0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,6 @@ set(engine_SRCS # Except main.cpp. src/utils/ListPolyIt.cpp src/utils/Matrix4x3D.cpp src/utils/MinimumSpanningTree.cpp - src/utils/Point3LL.cpp src/utils/PolygonConnector.cpp src/utils/PolygonsPointIndex.cpp src/utils/PolygonsSegmentIndex.cpp @@ -156,6 +155,8 @@ set(engine_SRCS # Except main.cpp. src/utils/VoxelUtils.cpp src/utils/MixedPolylineStitcher.cpp + src/geometry/Point2LL.cpp + src/geometry/Point3LL.cpp src/geometry/Polygon.cpp src/geometry/Shape.cpp src/geometry/PointsSet.cpp diff --git a/include/ExtruderPlan.h b/include/ExtruderPlan.h index 338dfba115..d82fb34b1a 100644 --- a/include/ExtruderPlan.h +++ b/include/ExtruderPlan.h @@ -190,7 +190,7 @@ class ExtruderPlan /*! * @return distance between p0 and p1 as well as the time spend on the segment */ - std::pair getPointToPointTime(const Point2LL& p0, const Point2LL& p1, const GCodePath& path); + std::pair getPointToPointTime(const Point3LL &p0, const Point3LL &p1, const GCodePath& path); /*! * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. diff --git a/include/GCodePathConfig.h b/include/GCodePathConfig.h index 82a2ea78dd..27672049df 100644 --- a/include/GCodePathConfig.h +++ b/include/GCodePathConfig.h @@ -20,7 +20,7 @@ struct GCodePathConfig { static constexpr double FAN_SPEED_DEFAULT = -1.0; - coord_t z_offset{}; // +#include #include #include -#include "geometry/Point3LL.h" +#include "utils/Coord_t.h" #include "utils/types/generic.h" #ifdef __GNUC__ @@ -30,6 +31,8 @@ Integer points are used to avoid floating point rounding errors, and because Cli namespace cura { +class Point3LL; + /* 64bit Points are used mostly throughout the code, these are the 2D points from ClipperLib */ using Point2LL = ClipperLib::IntPoint; @@ -207,39 +210,17 @@ INLINE const Point2LL& make_point(const Point2LL& p) return p; } -inline Point3LL operator+(const Point3LL& p3, const Point2LL& p2) -{ - return { p3.x_ + p2.X, p3.y_ + p2.Y, p3.z_ }; -} +Point2LL operator+(const Point2LL& p2, const Point3LL& p3); -inline Point3LL& operator+=(Point3LL& p3, const Point2LL& p2) -{ - p3.x_ += p2.X; - p3.y_ += p2.Y; - return p3; -} +Point3LL operator+(const Point3LL& p3, const Point2LL& p2); -inline Point2LL operator+(const Point2LL& p2, const Point3LL& p3) -{ - return { p3.x_ + p2.X, p3.y_ + p2.Y }; -} +Point3LL& operator+=(Point3LL& p3, const Point2LL& p2); -inline Point3LL operator-(const Point3LL& p3, const Point2LL& p2) -{ - return { p3.x_ - p2.X, p3.y_ - p2.Y, p3.z_ }; -} +Point3LL operator-(const Point3LL& p3, const Point2LL& p2); -inline Point3LL& operator-=(Point3LL& p3, const Point2LL& p2) -{ - p3.x_ -= p2.X; - p3.y_ -= p2.Y; - return p3; -} +Point3LL& operator-=(Point3LL& p3, const Point2LL& p2); -inline Point2LL operator-(const Point2LL& p2, const Point3LL& p3) -{ - return { p2.X - p3.x_, p2.Y - p3.y_ }; -} +inline Point2LL operator-(const Point2LL& p2, const Point3LL& p3); } // namespace cura @@ -259,4 +240,4 @@ struct hash }; } // namespace std -#endif // UTILS_INT_POINT_H +#endif // GEOMETRY_POINT2LL_H diff --git a/include/geometry/Point3LL.h b/include/geometry/Point3LL.h index fd220b05ef..1fe449205b 100644 --- a/include/geometry/Point3LL.h +++ b/include/geometry/Point3LL.h @@ -10,6 +10,7 @@ #include //For numeric_limits::min and max. #include // for operations on any arithmetic number type +#include "geometry/Point2LL.h" #include "utils/Coord_t.h" #include "utils/types/generic.h" @@ -20,9 +21,9 @@ namespace cura class Point3LL { public: - coord_t x_{}; - coord_t y_{}; - coord_t z_{}; + coord_t x_{ 0 }; + coord_t y_{ 0 }; + coord_t z_{ 0 }; Point3LL() = default; @@ -35,6 +36,8 @@ class Point3LL Point3LL(Point3LL&& point) = default; Point3LL(const Point3LL& point) = default; + Point3LL(const Point2LL& point); + Point3LL& operator=(const Point3LL& point) = default; Point3LL& operator=(Point3LL&& point) = default; @@ -148,6 +151,10 @@ class Point3LL return x_ * p.x_ + y_ * p.y_ + z_ * p.z_; } + [[nodiscard]] Point2LL toPoint2LL() const; + + [[nodiscard]] Point3LL resized(coord_t length) const; + coord_t& operator[](const size_t index) { assert(index < 3); diff --git a/include/gradual_flow/FlowLimitedPath.h b/include/gradual_flow/FlowLimitedPath.h index 0e3eab859d..7a8219e678 100644 --- a/include/gradual_flow/FlowLimitedPath.h +++ b/include/gradual_flow/FlowLimitedPath.h @@ -30,7 +30,7 @@ enum class FlowState struct FlowLimitedPath { const GCodePath* original_gcode_path_data; - PointsSet points; + PointsSet points{}; double speed{ targetSpeed() }; // um/s double flow_{ extrusionVolumePerMm() * speed }; // um/s double total_length{ totalLength() }; // um @@ -135,11 +135,14 @@ struct FlowLimitedPath double totalLength() const // um { double path_length = 0; - auto last_point = points.front(); - for (const auto& point : points | ranges::views::drop(1)) + if (! points.empty()) { - path_length += std::hypot(point.X - last_point.X, point.Y - last_point.Y); - last_point = point; + auto last_point = points.front(); + for (const auto& point : points | ranges::views::drop(1)) + { + path_length += std::hypot(point.X - last_point.X, point.Y - last_point.Y); + last_point = point; + } } return path_length; } diff --git a/include/gradual_flow/Processor.h b/include/gradual_flow/Processor.h index c9615d6ba2..48998a6d51 100644 --- a/include/gradual_flow/Processor.h +++ b/include/gradual_flow/Processor.h @@ -32,7 +32,19 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ // Process first path for (const GCodePath& path : extruder_plan_paths | ranges::views::take(1)) { - gcode_paths.emplace_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = PointsSet(path.points) }); + PointsSet points; + + points.reserve(path.points.size()); + std::transform( + path.points.begin(), + path.points.end(), + std::back_inserter(points), + [](const Point3LL& point) + { + return point.toPoint2LL(); + }); + + gcode_paths.push_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = points }); } /* Process remaining paths @@ -47,8 +59,18 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ for (const auto& path : extruder_plan_paths | ranges::views::drop(1)) { PointsSet points{ gcode_paths.back().points.back() }; - points.push_back(PointsSet(path.points)); - gcode_paths.emplace_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = points }); + + points.reserve(path.points.size() + 1); + std::transform( + path.points.begin(), + path.points.end(), + std::back_inserter(points), + [](const Point3LL& point) + { + return point.toPoint2LL(); + }); + + gcode_paths.emplace_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = std::move(points) }); } constexpr auto non_zero_flow_view = ranges::views::transform( diff --git a/include/infill/ImageBasedDensityProvider.h b/include/infill/ImageBasedDensityProvider.h index f8b215ecd7..c090ebf729 100644 --- a/include/infill/ImageBasedDensityProvider.h +++ b/include/infill/ImageBasedDensityProvider.h @@ -7,6 +7,7 @@ #include "../utils/AABB.h" #include "DensityProvider.h" +#include "geometry/Point3LL.h" namespace cura { diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 577cd8efcc..9780d1bda8 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -29,7 +29,7 @@ namespace cura */ struct GCodePath { - coord_t z_offset{}; // mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part @@ -46,7 +46,7 @@ struct GCodePath bool perform_z_hop{ false }; //!< Whether to perform a z_hop in this path, which is assumed to be a travel path. bool perform_prime{ false }; //!< Whether this path is preceded by a prime (blob) bool skip_agressive_merge_hint{ false }; //!< Wheter this path needs to skip merging if any travel paths are in between the extrusions. - std::vector points{}; //!< The points constituting this path. + std::vector points{}; //!< The points constituting this path. The Z coordinate is an offset relative to the actual layer height, added to the global z_offset. bool done{ false }; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. double fan_speed{ GCodePathConfig::FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise TimeMaterialEstimates estimates{}; //!< Naive time and material estimates diff --git a/include/utils/AABB3D.h b/include/utils/AABB3D.h index 402135be66..e7d4b9eda5 100644 --- a/include/utils/AABB3D.h +++ b/include/utils/AABB3D.h @@ -5,6 +5,7 @@ #define UTILS_AABB3D_H #include "geometry/Point2LL.h" +#include "geometry/Point3LL.h" #include "utils/AABB.h" namespace cura diff --git a/include/utils/Point3D.h b/include/utils/Point3D.h index 6000d9be76..fe3b7765f8 100644 --- a/include/utils/Point3D.h +++ b/include/utils/Point3D.h @@ -7,7 +7,7 @@ #include #include -#include "geometry/Point2LL.h" +#include "geometry/Point3LL.h" namespace cura diff --git a/include/utils/VoxelUtils.h b/include/utils/VoxelUtils.h index 9eaca6e447..050d03ebb6 100644 --- a/include/utils/VoxelUtils.h +++ b/include/utils/VoxelUtils.h @@ -7,7 +7,7 @@ #include #include -#include "geometry/Point2LL.h" +#include "geometry/Point3LL.h" #include "geometry/Polygon.h" namespace cura diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index ce654d76ec..ee9f632c27 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -448,7 +448,7 @@ GCodePath& LayerPlan::addTravel(const Point2LL& p, const bool force_retract, con } for (Point2LL& comb_point : combPath) { - if (path->points.empty() || vSize2(path->points.back() - comb_point) > maximum_travel_resolution * maximum_travel_resolution) + if (path->points.empty() || (path->points.back() - comb_point).vSize2() > maximum_travel_resolution * maximum_travel_resolution) { path->points.push_back(comb_point); distance += vSize(last_point - comb_point); @@ -1497,6 +1497,23 @@ void LayerPlan::addLinesInGivenOrder( } } +void LayerPlan::writeTravelRelativeZ(GCodeExport& gcode, const Point3LL& position, const Velocity& speed, const coord_t path_z_offset) +{ + gcode.writeTravel(position + Point3LL(0, 0, z_ + path_z_offset), speed); +} + +void LayerPlan::writeExtrusionRelativeZ( + GCodeExport& gcode, + const Point3LL& position, + const Velocity& speed, + const coord_t path_z_offset, + double extrusion_mm3_per_mm, + PrintFeatureType feature, + bool update_extrusion_offset) +{ + gcode.writeExtrusion(position + Point3LL(0, 0, z_ + path_z_offset), speed, extrusion_mm3_per_mm, feature, update_extrusion_offset); +} + void LayerPlan::addLinesMonotonic( const Shape& area, const OpenLinesSet& lines, @@ -1799,15 +1816,15 @@ double ExtruderPlan::getRetractTime(const GCodePath& path) return retraction_config_.distance / (path.retract ? retraction_config_.speed : retraction_config_.primeSpeed); } -std::pair ExtruderPlan::getPointToPointTime(const Point2LL& p0, const Point2LL& p1, const GCodePath& path) +std::pair ExtruderPlan::getPointToPointTime(const Point3LL& p0, const Point3LL& p1, const GCodePath& path) { - const double length = vSizeMM(p0 - p1); + const double length = (p0 - p1).vSizeMM(); return { length, length / (path.config.getSpeed() * path.speed_factor) }; } TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point2LL starting_position) { - Point2LL p0 = starting_position; + Point3LL p0 = starting_position; const double min_path_speed = fan_speed_layer_time_settings_.cool_min_speed; slowest_path_speed_ = std::accumulate( @@ -1859,9 +1876,9 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates(Point2LL starting_ path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time; } } - for (Point2LL& p1 : path.points) + for (Point3LL& p1 : path.points) { - double length = vSizeMM(p0 - p1); + double length = (p0 - p1).vSizeMM(); if (is_extrusion_path) { if (length > 0) @@ -1968,7 +1985,7 @@ void LayerPlan::processFanSpeedAndMinimalLayerTime(Point2LL starting_position) if (! extruder_plan.paths_.empty() && ! extruder_plan.paths_.back().points.empty()) { - starting_position = extruder_plan.paths_.back().points.back(); + starting_position = extruder_plan.paths_.back().points.back().toPoint2LL(); } } } @@ -2119,6 +2136,8 @@ void LayerPlan::writeGCode(GCodeExport& gcode) GCodePath& path = paths[path_idx]; + assert(! path.points.empty()); + if (path.perform_prime) { gcode.writePrimeTrain(extruder.settings_.get("speed_travel")); @@ -2218,6 +2237,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) else { gcode.writeZhopEnd(); + if (z_ > 0 && path.z_offset != 0) { gcode.setZ(z_ + path.z_offset); @@ -2254,10 +2274,11 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeComment(ss.str()); } - if (! path.spiralize && (! path.retract || ! path.perform_z_hop) && (z_ + path.z_offset != gcode.getPositionZ()) && (path_idx > 0 || layer_nr_ > 0)) + if (! path.spiralize && (! path.retract || ! path.perform_z_hop) && (z_ + path.z_offset + path.points.front().z_ != gcode.getPositionZ()) + && (path_idx > 0 || layer_nr_ > 0)) { // First move to desired height to then make a plain horizontal move - gcode.writeTravel(Point3LL(gcode.getPosition().x_, gcode.getPosition().y_, z_ + path.z_offset), speed); + gcode.writeTravel(Point3LL(gcode.getPosition().x_, gcode.getPosition().y_, z_ + path.z_offset + path.points.front().z_), speed); } if (path.config.isTravelPath()) @@ -2274,7 +2295,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } for (size_t point_idx = 0; point_idx + 1 < path.points.size(); point_idx++) { - gcode.writeTravel(path.points[point_idx], speed); + writeTravelRelativeZ(gcode, path.points[point_idx], speed, path.z_offset); } if (path.unretract_before_last_travel_move && final_travel_z_ == z_) { @@ -2283,7 +2304,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } if (! path.points.empty()) { - gcode.writeTravel(path.points.back(), speed); + writeTravelRelativeZ(gcode, path.points.back(), speed, path.z_offset); } continue; } @@ -2302,15 +2323,23 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above... { // normal path to gcode algorithm - Point2LL prev_point = gcode.getPositionXY(); + Point3LL prev_point = gcode.getPosition(); for (unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++) { const auto [_, time] = extruder_plan.getPointToPointTime(prev_point, path.points[point_idx], path); insertTempOnTime(time, path_idx); const double extrude_speed = speed * path.speed_back_pressure_factor; - communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type, update_extrusion_offset); + communication + ->sendLineTo(path.config.type, path.points[point_idx].toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + writeExtrusionRelativeZ( + gcode, + path.points[point_idx], + extrude_speed, + path.z_offset, + path.getExtrusionMM3perMM(), + path.config.type, + update_extrusion_offset); prev_point = path.points[point_idx]; } @@ -2326,7 +2355,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) GCodePath& _path = paths[_path_idx]; for (unsigned int point_idx = 0; point_idx < _path.points.size(); point_idx++) { - Point2LL p1 = _path.points[point_idx]; + Point2LL p1 = _path.points[point_idx].toPoint2LL(); totalLength += vSizeMM(p0 - p1); p0 = p1; } @@ -2340,7 +2369,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) for (unsigned int point_idx = 0; point_idx < spiral_path.points.size(); point_idx++) { - const Point2LL p1 = spiral_path.points[point_idx]; + const Point2LL p1 = spiral_path.points[point_idx].toPoint2LL(); length += vSizeMM(p0 - p1); p0 = p1; gcode.setZ(std::round(z_ + layer_thickness_ * length / totalLength)); @@ -2348,11 +2377,18 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const double extrude_speed = speed * spiral_path.speed_back_pressure_factor; communication->sendLineTo( spiral_path.config.type, - spiral_path.points[point_idx], + spiral_path.points[point_idx].toPoint2LL(), spiral_path.getLineWidthForLayerView(), spiral_path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(spiral_path.points[point_idx], extrude_speed, spiral_path.getExtrusionMM3perMM(), spiral_path.config.type, update_extrusion_offset); + writeExtrusionRelativeZ( + gcode, + spiral_path.points[point_idx], + extrude_speed, + path.z_offset, + spiral_path.getExtrusionMM3perMM(), + spiral_path.config.type, + update_extrusion_offset); } // for layer display only - the loop finished at the seam vertex but as we started from // the location of the previous layer's seam vertex the loop may have a gap if this layer's @@ -2363,8 +2399,12 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // vertex would not be shifted (as it's the last vertex in the sequence). The smoother the model, // the less the vertices are shifted and the less obvious is the ridge. If the layer display // really displayed a spiral rather than slices of a spiral, this would not be required. - communication - ->sendLineTo(spiral_path.config.type, spiral_path.points[0], spiral_path.getLineWidthForLayerView(), spiral_path.config.getLayerThickness(), speed); + communication->sendLineTo( + spiral_path.config.type, + spiral_path.points[0].toPoint2LL(), + spiral_path.getLineWidthForLayerView(), + spiral_path.config.getLayerThickness(), + speed); } path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path } @@ -2485,11 +2525,11 @@ bool LayerPlan::writePathWithCoasting( std::optional acc_dist_idx_gt_coast_dist; // the index of the first point with accumulated_dist more than coasting_dist (= index into accumulated_dist_per_point) // == the point printed BEFORE the start point for coasting - const Point2LL* last = &path.points[path.points.size() - 1]; + const Point3LL* last = &path.points[path.points.size() - 1]; for (unsigned int backward_point_idx = 1; backward_point_idx < path.points.size(); backward_point_idx++) { - const Point2LL& point = path.points[path.points.size() - 1 - backward_point_idx]; - const coord_t distance = vSize(point - *last); + const Point3LL& point = path.points[path.points.size() - 1 - backward_point_idx]; + const coord_t distance = (point - *last).vSize(); accumulated_dist += distance; accumulated_dist_per_point.push_back(accumulated_dist); @@ -2534,15 +2574,15 @@ bool LayerPlan::writePathWithCoasting( const size_t point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist.value(); - Point2LL start; + Point3LL start; { // computation of begin point of coasting const coord_t residual_dist = actual_coasting_dist - accumulated_dist_per_point[acc_dist_idx_gt_coast_dist.value() - 1]; - const Point2LL& a = path.points[point_idx_before_start]; - const Point2LL& b = path.points[point_idx_before_start + 1]; - start = b + normal(a - b, residual_dist); + const Point3LL& a = path.points[point_idx_before_start]; + const Point3LL& b = path.points[point_idx_before_start + 1]; + start = b + (a - b).resized(residual_dist); } - Point2LL prev_pt = gcode.getPositionXY(); + Point3LL prev_pt = gcode.getPositionXY(); { // write normal extrude path: Communication* communication = Application::getInstance().communication_; for (size_t point_idx = 0; point_idx <= point_idx_before_start; point_idx++) @@ -2550,12 +2590,12 @@ bool LayerPlan::writePathWithCoasting( auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); insertTempOnTime(time, path_idx); - communication->sendLineTo(path.config.type, path.points[point_idx], path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); - gcode.writeExtrusion(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM(), path.config.type); + communication->sendLineTo(path.config.type, path.points[point_idx].toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + writeExtrusionRelativeZ(gcode, path.points[point_idx], extrude_speed, path.z_offset, path.getExtrusionMM3perMM(), path.config.type); prev_pt = path.points[point_idx]; } - communication->sendLineTo(path.config.type, start, path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); + communication->sendLineTo(path.config.type, start.toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config.type); } @@ -2567,7 +2607,7 @@ bool LayerPlan::writePathWithCoasting( const Ratio coasting_speed_modifier = extruder.settings_.get("coasting_speed"); const Velocity speed = Velocity(coasting_speed_modifier * path.config.getSpeed()); - gcode.writeTravel(path.points[point_idx], speed); + writeTravelRelativeZ(gcode, path.points[point_idx], speed, path.z_offset); prev_pt = path.points[point_idx]; } diff --git a/src/LayerPlanBuffer.cpp b/src/LayerPlanBuffer.cpp index db64a130a5..426c077055 100644 --- a/src/LayerPlanBuffer.cpp +++ b/src/LayerPlanBuffer.cpp @@ -91,7 +91,7 @@ void LayerPlanBuffer::addConnectingTravelMove(LayerPlan* prev_layer, const Layer Point2LL first_location_new_layer = new_layer_destination_state->first; assert(newest_layer->extruder_plans_.front().paths_[0].points.size() == 1); - assert(newest_layer->extruder_plans_.front().paths_[0].points[0] == first_location_new_layer); + assert(newest_layer->extruder_plans_.front().paths_[0].points[0].toPoint2LL() == first_location_new_layer); // if the last planned position in the previous layer isn't the same as the first location of the new layer, travel to the new location if (! prev_layer->last_planned_position_ || *prev_layer->last_planned_position_ != first_location_new_layer) diff --git a/src/geometry/Point2LL.cpp b/src/geometry/Point2LL.cpp new file mode 100644 index 0000000000..d3071b82af --- /dev/null +++ b/src/geometry/Point2LL.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2024 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "geometry/Point2LL.h" //The headers we're implementing. + +#include "geometry/Point3LL.h" + +namespace cura +{ + +Point2LL operator+(const Point2LL& p2, const Point3LL& p3) +{ + return { p3.x_ + p2.X, p3.y_ + p2.Y }; +} + +Point3LL operator+(const Point3LL& p3, const Point2LL& p2) +{ + return { p3.x_ + p2.X, p3.y_ + p2.Y, p3.z_ }; +} + +Point3LL& operator+=(Point3LL& p3, const Point2LL& p2) +{ + p3.x_ += p2.X; + p3.y_ += p2.Y; + return p3; +} + +Point3LL operator-(const Point3LL& p3, const Point2LL& p2) +{ + return { p3.x_ - p2.X, p3.y_ - p2.Y, p3.z_ }; +} + +Point3LL& operator-=(Point3LL& p3, const Point2LL& p2) +{ + p3.x_ -= p2.X; + p3.y_ -= p2.Y; + return p3; +} + +Point2LL operator-(const Point2LL& p2, const Point3LL& p3) +{ + return { p2.X - p3.x_, p2.Y - p3.y_ }; +} + +} // namespace cura diff --git a/src/utils/Point3LL.cpp b/src/geometry/Point3LL.cpp similarity index 76% rename from src/utils/Point3LL.cpp rename to src/geometry/Point3LL.cpp index bb8e7c6930..3dd6c15ec4 100644 --- a/src/utils/Point3LL.cpp +++ b/src/geometry/Point3LL.cpp @@ -6,6 +6,12 @@ namespace cura { +Point3LL::Point3LL(const Point2LL& point) + : x_(point.X) + , y_(point.Y) +{ +} + Point3LL Point3LL::operator+(const Point3LL& p) const { return Point3LL(x_ + p.x_, y_ + p.y_, z_ + p.z_); @@ -63,4 +69,19 @@ Point3LL& Point3LL::operator/=(const Point3LL& p) return *this; } +Point2LL Point3LL::toPoint2LL() const +{ + return Point2LL(x_, y_); +} + +Point3LL Point3LL::resized(coord_t length) const +{ + const coord_t actual_length = vSize(); + if (actual_length < 1) + { + return { length, 0, 0 }; + } + return ((*this) * length) / actual_length; +} + } // namespace cura diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 964a50007d..f38a16fddb 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -337,8 +337,9 @@ gcode_paths_modify_request::value_type for (const auto& point : path.points) { auto* points = gcode_path->mutable_path()->add_path(); - points->set_x(point.X); - points->set_y(point.Y); + points->set_x(point.x_); + points->set_y(point.y_); + points->set_z(point.z_); } gcode_path->set_space_fill_type(getSpaceFillType(path.space_fill_type)); gcode_path->set_flow(path.flow); @@ -478,7 +479,7 @@ gcode_paths_modify_response::native_value_type | ranges::views::transform( [](const auto& point_msg) { - return Point2LL{ point_msg.x(), point_msg.y() }; + return Point3LL{ point_msg.x(), point_msg.y(), point_msg.z() }; }) | ranges::to_vector; diff --git a/src/utils/ToolpathVisualizer.cpp b/src/utils/ToolpathVisualizer.cpp index 88161c78c7..84675f992f 100644 --- a/src/utils/ToolpathVisualizer.cpp +++ b/src/utils/ToolpathVisualizer.cpp @@ -4,6 +4,8 @@ #include +#include "geometry/Point3LL.h" + namespace cura { From cf3d3f9ed926213e5de41c967b1cb1a40abbd888 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 20 Aug 2024 10:23:14 +0200 Subject: [PATCH 03/32] Remove unused communication methods CURA-12081 Less code, less bugs --- include/communication/ArcusCommunication.h | 36 ++-------------- include/communication/CommandLine.h | 14 ------- include/communication/Communication.h | 28 ------------- src/communication/ArcusCommunication.cpp | 49 ---------------------- src/communication/CommandLine.cpp | 6 --- 5 files changed, 3 insertions(+), 130 deletions(-) diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index 2638fed7e1..48d653121f 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -113,7 +113,7 @@ class ArcusCommunication : public Communication * visualisation of the layer. * * This will be called after all the polygons and lines of this layer are - * sent via sendPolygons, sendPolygon and sendLineTo. This will flush all + * sent via sendLineTo. This will flush all * visualised data for one layer in one go. * \param layer_nr The layer that was completed. * \param z The z-coordinate of the top side of the layer. @@ -142,34 +142,6 @@ class ArcusCommunication : public Communication */ void sendOptimizedLayerData() override; - /* - * \brief Send a polygon to the front-end to display in layer view. - * - * The polygons are not actually flushed until ``sendLayerComplete`` is - * called. - * \param type The type of print feature the polygon represents (infill, - * wall, support, etc). - * \param polygon The shape to visualise. - * \param line_width The width of the lines in this polygon. - * \param line_thickness The thickness (in the Z direction) of the polygon. - * \param velocity The velocity of printing this polygon. - */ - void sendPolygon(const PrintFeatureType& type, const Polygon& polygon, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) override; - - /* - * \brief Send polygons to the front-end to display in layer view. - * - * The polygons may not actually be flushed until ``sendLayerComplete`` is - * called. - * \param type The type of print feature the polygons represent (infill, - * wall, support, etc). - * \param polygons The shapes to visualise. - * \param line_width The width of the lines in these polygons. - * \param line_thickness The thickness (in the Z direction) of the polygons. - * \param velocity The velocity of printing these polygons. - */ - void sendPolygons(const PrintFeatureType& type, const Shape& polygons, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) override; - /* * \brief Send an estimate of how long the print would take and how much * material it would use. @@ -182,15 +154,13 @@ class ArcusCommunication : public Communication void sendProgress(double progress) const override; /* - * \brief Set which extruder is being used for the following calls to - * ``sendPolygon``, ``sendPolygons`` and ``sendLineTo``. + * \brief Set which extruder is being used for the following calls to ``sendLineTo``. * \param extruder The new extruder to send data for. */ void setExtruderForSend(const ExtruderTrain& extruder) override; /* - * \brief Set which layer is being used for the following calls to - * ``sendPolygon``, ``sendPolygons`` and ``sendLineTo``. + * \brief Set which layer is being used for the following calls to ``sendLineTo``. * \param layer_nr The index of the layer to send data for. This is zero- * indexed but may be negative for raft layers. */ diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 55c742871a..5ee65a325b 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -107,20 +107,6 @@ class CommandLine : public Communication */ void sendOptimizedLayerData() override; - /* - * \brief Send a polygon to show it in layer view. - * - * The command line doesn't show any layer view so this is ignored. - */ - void sendPolygon(const PrintFeatureType&, const Polygon&, const coord_t&, const coord_t&, const Velocity&) override; - - /* - * \brief Send a polygon to show it in layer view. - * - * The command line doesn't show any layer view so this is ignored. - */ - void sendPolygons(const PrintFeatureType&, const Shape&, const coord_t&, const coord_t&, const Velocity&) override; - /* * \brief Show an estimate of how long the print would take and how much * material it would use. diff --git a/include/communication/Communication.h b/include/communication/Communication.h index 9bc990c11e..387bc20ccb 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -64,34 +64,6 @@ class Communication */ virtual void sendLayerComplete(const LayerIndex::value_type& layer_nr, const coord_t& z, const coord_t& thickness) = 0; - /* - * \brief Send polygons to the user to visualise. - * - * The polygons may not actually be flushed until ``sendLayerComplete`` is - * called. - * \param type The type of print feature the polygons represent (infill, - * wall, support, etc). - * \param polygons The shapes to visualise. - * \param line_width The width of the lines in these polygons. - * \param line_thickness The thickness (in the Z direction) of the polygons. - * \param velocity The velocity of printing these polygons. - */ - virtual void sendPolygons(const PrintFeatureType& type, const Shape& polygons, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) = 0; - - /* - * \brief Send a polygon to the user to visualise. - * - * The polygons may not actually be flushed until ``sendLayerComplete`` is - * called. - * \param type The type of print feature the polygon represents (infill, - * wall, support, etc). - * \param polygon The shape to visualise. - * \param line_width The width of the lines in this polygon. - * \param line_thickness The thickness (in the Z direction) of the polygon. - * \param velocity The velocity of printing this polygon. - */ - virtual void sendPolygon(const PrintFeatureType& type, const Polygon& polygon, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) = 0; - /* * \brief Send a line to the user to visualise. * diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 0f0fd4e6d3..1a75fe2516 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -227,42 +227,6 @@ class ArcusCommunication::PathCompiler } } - /*! - * \brief Adds closed polygon to the current path. - * \param print_feature_type The type of feature that the polygon is part of - * (infill, wall, etc). - * \param polygon The shape of the polygon. - * \param width The width of the lines of the polygon. - * \param thickness The layer thickness of the polygon. - * \param velocity How fast the polygon is printed. - */ - void sendPolygon(const PrintFeatureType& print_feature_type, const Polygon& polygon, const coord_t& width, const coord_t& thickness, const Velocity& velocity) - { - if (polygon.size() < 2) // Don't send single points or empty polygons. - { - return; - } - - ClipperLib::Path::const_iterator point = polygon.begin(); - handleInitialPoint(*point); - - // Send all coordinates one by one. - while (++point != polygon.end()) - { - if (*point == last_point) - { - continue; // Ignore zero-length segments. - } - addLineSegment(print_feature_type, *point, width, thickness, velocity); - } - - // Make sure the polygon is closed. - if (*polygon.begin() != polygon.back()) - { - addLineSegment(print_feature_type, *polygon.begin(), width, thickness, velocity); - } - } - private: /*! * \brief Convert and add a point to the points buffer. @@ -444,19 +408,6 @@ void ArcusCommunication::sendOptimizedLayerData() data.slice_data.clear(); } -void ArcusCommunication::sendPolygon(const PrintFeatureType& type, const Polygon& polygon, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) -{ - path_compiler->sendPolygon(type, polygon, line_width, line_thickness, velocity); -} - -void ArcusCommunication::sendPolygons(const PrintFeatureType& type, const Shape& polygons, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) -{ - for (const Polygon& polygon : polygons) - { - path_compiler->sendPolygon(type, polygon, line_width, line_thickness, velocity); - } -} - void ArcusCommunication::sendPrintTimeMaterialEstimates() const { spdlog::debug("Sending print time and material estimates."); diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 12e04adc13..269978ff7f 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -71,12 +71,6 @@ void CommandLine::sendLineTo(const PrintFeatureType&, const Point2LL&, const coo void CommandLine::sendOptimizedLayerData() { } -void CommandLine::sendPolygon(const PrintFeatureType&, const Polygon&, const coord_t&, const coord_t&, const Velocity&) -{ -} -void CommandLine::sendPolygons(const PrintFeatureType&, const Shape&, const coord_t&, const coord_t&, const Velocity&) -{ -} void CommandLine::setExtruderForSend(const ExtruderTrain&) { } From 636a6afe4de8a825c540bb3861535fdad7341bc2 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 21 Aug 2024 10:17:20 +0200 Subject: [PATCH 04/32] Send 3D points to the front-end so that we can display Z changes CURA-12081 --- include/LayerPlan.h | 2 ++ include/communication/ArcusCommunication.h | 4 +-- include/communication/CommandLine.h | 4 +-- include/communication/Communication.h | 4 +-- src/LayerPlan.cpp | 33 +++++++++++----------- src/communication/ArcusCommunication.cpp | 32 ++++++++++----------- src/communication/CommandLine.cpp | 4 +-- src/gcodeExport.cpp | 10 +++++-- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 7ebce6d2e7..788aee555d 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -820,6 +820,8 @@ class LayerPlan : public NoCopy const Ratio flow_ratio, const double fan_speed); + void sendLineTo(const GCodePath& path, const Point3LL& position, const double extrude_speed); + void writeTravelRelativeZ(GCodeExport& gcode, const Point3LL& position, const Velocity& speed, const coord_t path_z_offset); void writeExtrusionRelativeZ( diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index 48d653121f..f6a8c81f66 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -86,7 +86,7 @@ class ArcusCommunication : public Communication * This may indicate the starting position (or any other jump in the path). * \param position The current position to start the next line at. */ - void sendCurrentPosition(const Point2LL& position) override; + void sendCurrentPosition(const Point3LL &position) override; /* * \brief Sends a message to indicate that all the slicing is done. @@ -132,7 +132,7 @@ class ArcusCommunication : public Communication * \param line_thickness The thickness (in the Z direction) of the line. * \param velocity The velocity of printing this polygon. */ - void sendLineTo(const PrintFeatureType& type, const Point2LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) override; + void sendLineTo(const PrintFeatureType& type, const Point3LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) override; /* * \brief Send the sliced layer data to the front-end after the optimisation diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 5ee65a325b..ed6b1cbbb2 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -64,7 +64,7 @@ class CommandLine : public Communication * The command line doesn't do anything with the current position so this is * ignored. */ - void sendCurrentPosition(const Point2LL&) override; + void sendCurrentPosition(const Point3LL&) override; /* * \brief Indicate to the command line that we finished slicing. @@ -98,7 +98,7 @@ class CommandLine : public Communication * * The command line doesn't show any layer view so this is ignored. */ - void sendLineTo(const PrintFeatureType&, const Point2LL&, const coord_t&, const coord_t&, const Velocity&) override; + void sendLineTo(const PrintFeatureType&, const Point3LL&, const coord_t&, const coord_t&, const Velocity&) override; /* * \brief Complete a layer to show it in layer view. diff --git a/include/communication/Communication.h b/include/communication/Communication.h index 387bc20ccb..1a70b15746 100644 --- a/include/communication/Communication.h +++ b/include/communication/Communication.h @@ -76,7 +76,7 @@ class Communication * \param line_thickness The thickness (in the Z direction) of the line. * \param velocity The velocity of printing this polygon. */ - virtual void sendLineTo(const PrintFeatureType& type, const Point2LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) = 0; + virtual void sendLineTo(const PrintFeatureType& type, const Point3LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) = 0; /* * \brief Send the current position to visualise. @@ -84,7 +84,7 @@ class Communication * This may indicate the starting position (or any other jump in the path). * \param position The current position to start the next line at. */ - virtual void sendCurrentPosition(const Point2LL& position) = 0; + virtual void sendCurrentPosition(const Point3LL& position) = 0; /* * \brief Set which extruder is being used for the following calls to diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index ee9f632c27..9ef5d1276e 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1497,6 +1497,17 @@ void LayerPlan::addLinesInGivenOrder( } } +void LayerPlan::sendLineTo(const GCodePath& path, const Point3LL& position, const double extrude_speed) +{ + coord_t total_z_offset = path.z_offset + position.z_; + Application::getInstance().communication_->sendLineTo( + path.config.type, + position + Point3LL(0, 0, z_ + total_z_offset), + path.getLineWidthForLayerView(), + path.config.getLayerThickness() + total_z_offset, + extrude_speed); +} + void LayerPlan::writeTravelRelativeZ(GCodeExport& gcode, const Point3LL& position, const Velocity& speed, const coord_t path_z_offset) { gcode.writeTravel(position + Point3LL(0, 0, z_ + path_z_offset), speed); @@ -2330,8 +2341,6 @@ void LayerPlan::writeGCode(GCodeExport& gcode) insertTempOnTime(time, path_idx); const double extrude_speed = speed * path.speed_back_pressure_factor; - communication - ->sendLineTo(path.config.type, path.points[point_idx].toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); writeExtrusionRelativeZ( gcode, path.points[point_idx], @@ -2340,6 +2349,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) path.getExtrusionMM3perMM(), path.config.type, update_extrusion_offset); + sendLineTo(path, path.points[point_idx], extrude_speed); prev_point = path.points[point_idx]; } @@ -2375,12 +2385,6 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.setZ(std::round(z_ + layer_thickness_ * length / totalLength)); const double extrude_speed = speed * spiral_path.speed_back_pressure_factor; - communication->sendLineTo( - spiral_path.config.type, - spiral_path.points[point_idx].toPoint2LL(), - spiral_path.getLineWidthForLayerView(), - spiral_path.config.getLayerThickness(), - extrude_speed); writeExtrusionRelativeZ( gcode, spiral_path.points[point_idx], @@ -2389,6 +2393,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) spiral_path.getExtrusionMM3perMM(), spiral_path.config.type, update_extrusion_offset); + sendLineTo(spiral_path, spiral_path.points[point_idx], extrude_speed); } // for layer display only - the loop finished at the seam vertex but as we started from // the location of the previous layer's seam vertex the loop may have a gap if this layer's @@ -2399,12 +2404,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) // vertex would not be shifted (as it's the last vertex in the sequence). The smoother the model, // the less the vertices are shifted and the less obvious is the ridge. If the layer display // really displayed a spiral rather than slices of a spiral, this would not be required. - communication->sendLineTo( - spiral_path.config.type, - spiral_path.points[0].toPoint2LL(), - spiral_path.getLineWidthForLayerView(), - spiral_path.config.getLayerThickness(), - speed); + sendLineTo(spiral_path, spiral_path.points[0], speed); } path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path } @@ -2584,19 +2584,18 @@ bool LayerPlan::writePathWithCoasting( Point3LL prev_pt = gcode.getPositionXY(); { // write normal extrude path: - Communication* communication = Application::getInstance().communication_; for (size_t point_idx = 0; point_idx <= point_idx_before_start; point_idx++) { auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); insertTempOnTime(time, path_idx); - communication->sendLineTo(path.config.type, path.points[point_idx].toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); writeExtrusionRelativeZ(gcode, path.points[point_idx], extrude_speed, path.z_offset, path.getExtrusionMM3perMM(), path.config.type); + sendLineTo(path, path.points[point_idx], extrude_speed); prev_pt = path.points[point_idx]; } - communication->sendLineTo(path.config.type, start.toPoint2LL(), path.getLineWidthForLayerView(), path.config.getLayerThickness(), extrude_speed); gcode.writeExtrusion(start, extrude_speed, path.getExtrusionMM3perMM(), path.config.type); + sendLineTo(path, start, extrude_speed); } // write coasting path diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 1a75fe2516..e6c14098a5 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -67,7 +67,7 @@ class ArcusCommunication::PathCompiler std::vector points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is //!< the dimensionality of the point. - Point2LL last_point; + Point3LL last_point; PathCompiler(const PathCompiler&) = delete; PathCompiler& operator=(const PathCompiler&) = delete; @@ -80,13 +80,12 @@ class ArcusCommunication::PathCompiler : _cs_private_data(cs_private_data) , _layer_nr(0) , extruder(0) - , data_point_type(cura::proto::PathSegment::Point2D) + , data_point_type(cura::proto::PathSegment::Point3D) , line_types() , line_widths() , line_thicknesses() , line_velocities() , points() - , last_point{ 0, 0 } { } @@ -143,11 +142,11 @@ class ArcusCommunication::PathCompiler * of the path this jump is marked as `PrintFeatureType::NoneType`. * \param from The initial point of a polygon. */ - void handleInitialPoint(const Point2LL& initial_point) + void handleInitialPoint(const Point3LL& initial_point) { if (points.size() == 0) { - addPoint2D(initial_point); + addPoint3D(initial_point); } else if (initial_point != last_point) { @@ -201,7 +200,7 @@ class ArcusCommunication::PathCompiler /*! * \brief Move the current point of this path to \p position. */ - void setCurrentPosition(const Point2LL& position) + void setCurrentPosition(const Point3LL& position) { handleInitialPoint(position); } @@ -217,7 +216,7 @@ class ArcusCommunication::PathCompiler * \param line_thickness The thickness (in the Z direction) of the line. * \param velocity The velocity of printing this polygon. */ - void sendLineTo(const PrintFeatureType& print_feature_type, const Point2LL& to, const coord_t& width, const coord_t& thickness, const Velocity& feedrate) + void sendLineTo(const PrintFeatureType& print_feature_type, const Point3LL& to, const coord_t& width, const coord_t& thickness, const Velocity& feedrate) { assert(! points.empty() && "A point must already be in the buffer for sendLineTo(.) to function properly."); @@ -231,13 +230,14 @@ class ArcusCommunication::PathCompiler /*! * \brief Convert and add a point to the points buffer. * - * Each point is represented as two consecutive floats. All members adding a - * 2D point to the data should use this function. + * Each point is represented as three consecutive floats. All members adding a + * 3D point to the data should use this function. */ - void addPoint2D(const Point2LL& point) + void addPoint3D(const Point3LL& point) { - points.push_back(INT2MM(point.X)); - points.push_back(INT2MM(point.Y)); + points.push_back(INT2MM(point.x_)); + points.push_back(INT2MM(point.y_)); + points.push_back(INT2MM(point.z_)); last_point = point; } @@ -253,9 +253,9 @@ class ArcusCommunication::PathCompiler * \param thickness The layer thickness of the polygon. * \param velocity How fast the polygon is printed. */ - void addLineSegment(const PrintFeatureType& print_feature_type, const Point2LL& point, const coord_t& width, const coord_t& thickness, const Velocity& velocity) + void addLineSegment(const PrintFeatureType& print_feature_type, const Point3LL& point, const coord_t& width, const coord_t& thickness, const Velocity& velocity) { - addPoint2D(point); + addPoint3D(point); line_types.push_back(print_feature_type); line_widths.push_back(INT2MM(width)); line_thicknesses.push_back(INT2MM(thickness)); @@ -345,7 +345,7 @@ bool ArcusCommunication::hasSlice() const && private_data->slice_count < 1; // Only slice once per run of CuraEngine. See documentation of slice_count. } -void ArcusCommunication::sendCurrentPosition(const Point2LL& position) +void ArcusCommunication::sendCurrentPosition(const Point3LL& position) { path_compiler->setCurrentPosition(position); } @@ -379,7 +379,7 @@ void ArcusCommunication::sendLayerComplete(const LayerIndex::value_type& layer_n layer->set_thickness(thickness); } -void ArcusCommunication::sendLineTo(const PrintFeatureType& type, const Point2LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) +void ArcusCommunication::sendLineTo(const PrintFeatureType& type, const Point3LL& to, const coord_t& line_width, const coord_t& line_thickness, const Velocity& velocity) { path_compiler->sendLineTo(type, to, line_width, line_thickness, velocity); } diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 269978ff7f..496ff681e7 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -56,7 +56,7 @@ void CommandLine::beginGCode() void CommandLine::flushGCode() { } -void CommandLine::sendCurrentPosition(const Point2LL&) +void CommandLine::sendCurrentPosition(const Point3LL &) { } void CommandLine::sendFinishedSlicing() const @@ -65,7 +65,7 @@ void CommandLine::sendFinishedSlicing() const void CommandLine::sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) { } -void CommandLine::sendLineTo(const PrintFeatureType&, const Point2LL&, const coord_t&, const coord_t&, const Velocity&) +void CommandLine::sendLineTo(const PrintFeatureType&, const Point3LL &, const coord_t&, const coord_t&, const Velocity&) { } void CommandLine::sendOptimizedLayerData() diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 1de0935cb7..938980e456 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -975,7 +975,7 @@ void GCodeExport::writeTravel(const coord_t x, const coord_t y, const coord_t z, const PrintFeatureType travel_move_type = extruder_attr_[current_extruder_].retraction_e_amount_current_ ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing; const int display_width = extruder_attr_[current_extruder_].retraction_e_amount_current_ ? MM2INT(0.2) : MM2INT(0.1); const double layer_height = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("layer_height"); - Application::getInstance().communication_->sendLineTo(travel_move_type, Point2LL(x, y), display_width, layer_height, speed); + Application::getInstance().communication_->sendLineTo(travel_move_type, Point3LL(x, y, z), display_width, layer_height, speed); *output_stream_ << "G0"; writeFXYZE(speed, x, y, z, current_e_value_, travel_move_type); @@ -1248,10 +1248,12 @@ void GCodeExport::writeZhopStart(const coord_t hop_height, Velocity speed /*= 0* const ExtruderTrain& extruder = Application::getInstance().current_slice_->scene.extruders[current_extruder_]; speed = extruder.settings_.get("speed_z_hop"); } + const coord_t target_z = current_layer_z_ + is_z_hopped_; is_z_hopped_ = hop_height; current_speed_ = speed; - *output_stream_ << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ current_layer_z_ + is_z_hopped_ } << new_line_; - total_bounding_box_.includeZ(current_layer_z_ + is_z_hopped_); + *output_stream_ << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ target_z } << new_line_; + Application::getInstance().communication_->sendLineTo(PrintFeatureType::MoveRetraction, Point3LL(current_position_.x_, current_position_.y_, target_z), 0, 0, speed); + total_bounding_box_.includeZ(target_z); assert(speed > 0.0 && "Z hop speed should be positive."); } } @@ -1269,6 +1271,8 @@ void GCodeExport::writeZhopEnd(Velocity speed /*= 0*/) current_position_.z_ = current_layer_z_; current_speed_ = speed; *output_stream_ << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ current_layer_z_ } << new_line_; + Application::getInstance() + .communication_->sendLineTo(PrintFeatureType::MoveRetraction, Point3LL(current_position_.x_, current_position_.y_, current_layer_z_), 0, 0, speed); assert(speed > 0.0 && "Z hop speed should be positive."); } } From 7bbbbf76516dce0c6820328adce356a4103503f6 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 09:27:38 +0200 Subject: [PATCH 05/32] Create a scarf seam depending on the given settings CURA-12081 --- include/InsetOrderOptimizer.h | 4 +- include/LayerPlan.h | 16 +- include/geometry/Point2LL.h | 2 +- include/geometry/Point3LL.h | 5 + include/gradual_flow/FlowLimitedPath.h | 23 +- include/gradual_flow/Processor.h | 19 +- include/pathPlanning/GCodePath.h | 1 + src/FffGcodeWriter.cpp | 6 +- src/InsetOrderOptimizer.cpp | 10 +- src/LayerPlan.cpp | 325 ++++++++++++++++--------- 10 files changed, 262 insertions(+), 149 deletions(-) diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index 3a28040376..eb76f4d85e 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -57,7 +57,8 @@ class InsetOrderOptimizer const ZSeamConfig& z_seam_config, const std::vector& paths, const Point2LL& model_center_point, - const Shape& disallowed_areas_for_seams = {}); + const Shape& disallowed_areas_for_seams = {}, + const bool scarf_seam = false); /*! * Adds the insets to the given layer plan. @@ -110,6 +111,7 @@ class InsetOrderOptimizer const LayerIndex layer_nr_; const Point2LL model_center_point_; // Center of the model (= all meshes) axis-aligned bounding-box. Shape disallowed_areas_for_seams_; + const bool scarf_seam_; std::vector> inset_polys_; // vector of vectors holding the inset polygons Shape retraction_region_; // After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 788aee555d..aee8a952ee 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -359,14 +359,15 @@ class LayerPlan : public NoCopy * \param fan_speed Fan speed override for this path. */ void addExtrusionMove( - const Point2LL p, + 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 double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, + const bool travel_to_z = true); /*! * Add polygon to the gcode starting at vertex \p startIdx @@ -451,8 +452,8 @@ class LayerPlan : public NoCopy * the first bridge segment. */ void addWallLine( - const Point2LL& p0, - const Point2LL& p1, + const Point3LL& p0, + const Point3LL& p1, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, @@ -461,7 +462,8 @@ class LayerPlan : public NoCopy const Ratio width_factor, double& non_bridge_line_volume, Ratio speed_factor, - double distance_to_bridge_start); + double distance_to_bridge_start, + const bool travel_to_z = true); /*! * Add a wall to the g-code starting at vertex \p start_idx @@ -522,7 +524,9 @@ class LayerPlan : public NoCopy bool always_retract, const bool is_closed, const bool is_reversed, - const bool is_linked_path); + const bool is_linked_path, + coord_t scarf_seam_length = 0, + Ratio scarf_seam_start_ratio = 1.0); /*! * Add an infill wall to the g-code diff --git a/include/geometry/Point2LL.h b/include/geometry/Point2LL.h index ffd213ea33..91b26c6ab6 100644 --- a/include/geometry/Point2LL.h +++ b/include/geometry/Point2LL.h @@ -220,7 +220,7 @@ Point3LL operator-(const Point3LL& p3, const Point2LL& p2); Point3LL& operator-=(Point3LL& p3, const Point2LL& p2); -inline Point2LL operator-(const Point2LL& p2, const Point3LL& p3); +Point2LL operator-(const Point2LL& p2, const Point3LL& p3); } // namespace cura diff --git a/include/geometry/Point3LL.h b/include/geometry/Point3LL.h index 1fe449205b..0e4b62ed7b 100644 --- a/include/geometry/Point3LL.h +++ b/include/geometry/Point3LL.h @@ -133,6 +133,11 @@ class Point3LL return x_ * x_ + y_ * y_ + z_ * z_; } + [[nodiscard]] double vSize2f() + { + return static_cast(x_) * static_cast(x_) + static_cast(y_) * static_cast(y_) + static_cast(z_) * static_cast(z_); + } + [[nodiscard]] coord_t vSize() const { return std::llrint(sqrt(static_cast(vSize2()))); diff --git a/include/gradual_flow/FlowLimitedPath.h b/include/gradual_flow/FlowLimitedPath.h index 7a8219e678..c1f3864c94 100644 --- a/include/gradual_flow/FlowLimitedPath.h +++ b/include/gradual_flow/FlowLimitedPath.h @@ -30,7 +30,7 @@ enum class FlowState struct FlowLimitedPath { const GCodePath* original_gcode_path_data; - PointsSet points{}; + std::vector points{}; double speed{ targetSpeed() }; // um/s double flow_{ extrusionVolumePerMm() * speed }; // um/s double total_length{ totalLength() }; // um @@ -102,7 +102,7 @@ struct FlowLimitedPath for (auto point : points) { const auto identifier = is_first_point ? "M" : "L"; - path_data += fmt::format("{}{} {} ", identifier, point.X * 1e-3, point.Y * 1e-3); + path_data += fmt::format("{}{} {} ", identifier, point.x_ * 1e-3, point.y_ * 1e-3); is_first_point = false; } return path_data; @@ -140,7 +140,7 @@ struct FlowLimitedPath auto last_point = points.front(); for (const auto& point : points | ranges::views::drop(1)) { - path_length += std::hypot(point.X - last_point.X, point.Y - last_point.Y); + path_length += std::hypot(point.x_ - last_point.x_, point.y_ - last_point.y_); last_point = point; } } @@ -182,12 +182,12 @@ struct FlowLimitedPath auto current_partition_duration = 0.0; auto partition_index = direction == utils::Direction::Forward ? 0 : points.size() - 1; auto iteration_direction = direction == utils::Direction::Forward ? 1 : -1; - auto prev_point = points[partition_index]; + Point3LL prev_point = points[partition_index]; while (true) { - const auto next_point = points[partition_index + iteration_direction]; - const auto segment_length = std::hypot(next_point.X - prev_point.X, next_point.Y - prev_point.Y); + const Point3LL next_point = points[partition_index + iteration_direction]; + const auto segment_length = std::hypot(next_point.x_ - prev_point.x_, next_point.y_ - prev_point.y_); const auto segment_duration = segment_length / partition_speed; if (current_partition_duration + segment_duration < partition_duration) @@ -201,9 +201,10 @@ struct FlowLimitedPath const auto duration_left = partition_duration - current_partition_duration; auto segment_ratio = duration_left / segment_duration; assert(segment_ratio >= -1e-6 && segment_ratio <= 1. + 1e-6); - const auto partition_x = prev_point.X + static_cast(static_cast(next_point.X - prev_point.X) * segment_ratio); - const auto partition_y = prev_point.Y + static_cast(static_cast(next_point.Y - prev_point.Y) * segment_ratio); - const auto partition_point = ClipperLib::IntPoint(partition_x, partition_y); + const auto partition_x = prev_point.x_ + static_cast(static_cast(next_point.x_ - prev_point.x_) * segment_ratio); + const auto partition_y = prev_point.y_ + static_cast(static_cast(next_point.y_ - prev_point.y_) * segment_ratio); + const auto partition_z = prev_point.z_ + static_cast(static_cast(next_point.z_ - prev_point.z_) * segment_ratio); + const Point3LL partition_point(partition_x, partition_y, partition_z); /* * partition point @@ -230,7 +231,7 @@ struct FlowLimitedPath const auto partition_point_index = direction == utils::Direction::Forward ? partition_index + 1 : partition_index; // points left of the partition_index - PointsSet left_points; + std::vector left_points; for (unsigned int i = 0; i < partition_point_index; ++i) { left_points.emplace_back(points[i]); @@ -238,7 +239,7 @@ struct FlowLimitedPath left_points.emplace_back(partition_point); // points right of the partition_index - PointsSet right_points; + std::vector right_points; right_points.emplace_back(partition_point); for (unsigned int i = partition_point_index; i < points.size(); ++i) { diff --git a/include/gradual_flow/Processor.h b/include/gradual_flow/Processor.h index 48998a6d51..4954473959 100644 --- a/include/gradual_flow/Processor.h +++ b/include/gradual_flow/Processor.h @@ -32,19 +32,7 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ // Process first path for (const GCodePath& path : extruder_plan_paths | ranges::views::take(1)) { - PointsSet points; - - points.reserve(path.points.size()); - std::transform( - path.points.begin(), - path.points.end(), - std::back_inserter(points), - [](const Point3LL& point) - { - return point.toPoint2LL(); - }); - - gcode_paths.push_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = points }); + gcode_paths.push_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = path.points }); } /* Process remaining paths @@ -58,8 +46,9 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ */ for (const auto& path : extruder_plan_paths | ranges::views::drop(1)) { - PointsSet points{ gcode_paths.back().points.back() }; + std::vector points{ gcode_paths.back().points.back() }; +#warning this is probably not necessary points.reserve(path.points.size() + 1); std::transform( path.points.begin(), @@ -67,7 +56,7 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ std::back_inserter(points), [](const Point3LL& point) { - return point.toPoint2LL(); + return point; }); gcode_paths.emplace_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = std::move(points) }); diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 9780d1bda8..2eaf050d2e 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -50,6 +50,7 @@ struct GCodePath bool done{ false }; //!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one. double fan_speed{ GCodePathConfig::FAN_SPEED_DEFAULT }; //!< fan speed override for this path, value should be within range 0-100 (inclusive) and ignored otherwise TimeMaterialEstimates estimates{}; //!< Naive time and material estimates + bool travel_to_z{ true }; //! Indicates whether we should add a travel move to the Z height of the first point before processing the path /*! * Whether this config is the config of a travel path. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 552f1c9c60..dee067192a 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2735,6 +2735,8 @@ bool FffGcodeWriter::processInsets( mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); + constexpr Shape disallowed_areas_for_seams; + constexpr bool scarf_seam = true; InsetOrderOptimizer wall_orderer( *this, storage, @@ -2754,7 +2756,9 @@ bool FffGcodeWriter::processInsets( mesh.settings.get("wall_x_extruder_nr").extruder_nr_, z_seam_config, part.wall_toolpaths, - mesh.bounding_box.flatten().getMiddle()); + mesh.bounding_box.flatten().getMiddle(), + disallowed_areas_for_seams, + scarf_seam); added_something |= wall_orderer.addToLayer(); } return added_something; diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index ad036f62b9..ca6bc091b8 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -52,7 +52,8 @@ InsetOrderOptimizer::InsetOrderOptimizer( const ZSeamConfig& z_seam_config, const std::vector& paths, const Point2LL& model_center_point, - const Shape& disallowed_areas_for_seams) + const Shape& disallowed_areas_for_seams, + const bool scarf_seam) : gcode_writer_(gcode_writer) , storage_(storage) , gcode_layer_(gcode_layer) @@ -74,6 +75,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( , layer_nr_(gcode_layer.getLayerNr()) , model_center_point_(model_center_point) , disallowed_areas_for_seams_{ disallowed_areas_for_seams } + , scarf_seam_(scarf_seam) { } @@ -146,6 +148,8 @@ bool InsetOrderOptimizer::addToLayer() const GCodePathConfig& bridge_config = is_outer_wall ? inset_0_bridge_config_ : inset_X_bridge_config_; const coord_t wipe_dist = is_outer_wall && ! is_gap_filler ? wall_0_wipe_dist_ : wall_x_wipe_dist_; const bool retract_before = is_outer_wall ? retract_before_outer_wall_ : false; + const coord_t scarf_seam_length = scarf_seam_ && is_outer_wall ? settings_.get("scarf_joint_seam_length") : 0; + const Ratio scarf_seam_start_ratio = scarf_seam_ && is_outer_wall ? settings_.get("scarf_joint_seam_start_height_ratio") : 1.0_r; const bool revert_inset = alternate_walls && (path.vertices_->inset_idx_ % 2 != 0); const bool revert_layer = alternate_walls && (layer_nr_ % 2 != 0); @@ -166,7 +170,9 @@ bool InsetOrderOptimizer::addToLayer() retract_before, path.is_closed_, backwards, - linked_path); + linked_path, + scarf_seam_length, + scarf_seam_start_ratio); added_something = true; } return added_something; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 9ef5d1276e..c275732c96 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -531,23 +531,25 @@ void LayerPlan::planPrime(double prime_blob_wipe_length) } void LayerPlan::addExtrusionMove( - const Point2LL p, + const Point3LL& p, const GCodePathConfig& config, const SpaceFillType space_fill_type, const Ratio& flow, const Ratio width_factor, const bool spiralize, const Ratio speed_factor, - const double fan_speed) + const double fan_speed, + const bool travel_to_z) { GCodePath* path = getLatestPathWithConfig(config, space_fill_type, config.z_offset, flow, width_factor, spiralize, speed_factor); path->points.push_back(p); path->setFanSpeed(fan_speed); + path->travel_to_z = travel_to_z; if (! static_cast(first_extrusion_acc_jerk_)) { first_extrusion_acc_jerk_ = std::make_pair(path->config.getAcceleration(), path->config.getJerk()); } - last_planned_position_ = p; + last_planned_position_ = p.toPoint2LL(); } void LayerPlan::addPolygon( @@ -647,8 +649,8 @@ void LayerPlan::addPolygonsByOptimizer( static constexpr double max_non_bridge_line_volume = MM2INT(100); // limit to accumulated "volume" of non-bridge lines which is proportional to distance x extrusion rate void LayerPlan::addWallLine( - const Point2LL& p0, - const Point2LL& p1, + const Point3LL& p0, + const Point3LL& p1, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, @@ -657,7 +659,8 @@ void LayerPlan::addWallLine( const Ratio width_factor, double& non_bridge_line_volume, Ratio speed_factor, - double distance_to_bridge_start) + double distance_to_bridge_start, + const bool travel_to_z) { const coord_t min_line_len = 5; // we ignore lines less than 5um long const double acceleration_segment_len = MM2INT(1); // accelerate using segments of this length @@ -668,7 +671,7 @@ void LayerPlan::addWallLine( const Ratio bridge_wall_coast = settings.get("bridge_wall_coast"); const Ratio overhang_speed_factor = settings.get("wall_overhang_speed_factor"); - Point2LL cur_point = p0; + Point3LL cur_point = p0; // helper function to add a single non-bridge line @@ -676,14 +679,14 @@ void LayerPlan::addWallLine( // alternatively, if the line follows a bridge line, it may be segmented and the print speed gradually increased to reduce under-extrusion - auto addNonBridgeLine = [&](const Point2LL& line_end) + auto addNonBridgeLine = [&](const Point3LL& line_end) { - coord_t distance_to_line_end = vSize(cur_point - line_end); + coord_t distance_to_line_end = (cur_point - line_end).vSize(); while (distance_to_line_end > min_line_len) { // if we are accelerating after a bridge line, the segment length is less than the whole line length - Point2LL segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) + Point3LL segment_end = (speed_factor == 1 || distance_to_line_end < acceleration_segment_len) ? line_end : cur_point + (line_end - cur_point) * acceleration_segment_len / distance_to_line_end; @@ -708,7 +711,7 @@ void LayerPlan::addWallLine( segment_end = line_end; } - const coord_t len = vSize(cur_point - segment_end); + const coord_t len = (cur_point - segment_end).vSize(); if (coast_dist > 0 && ((distance_to_bridge_start - len) <= coast_dist)) { if ((len - coast_dist) > min_line_len) @@ -721,7 +724,9 @@ void LayerPlan::addWallLine( segment_flow, width_factor, spiralize, - speed_factor); + speed_factor, + GCodePathConfig::FAN_SPEED_DEFAULT, + travel_to_z); } // then coast to start of bridge segment constexpr Ratio no_flow = 0.0_r; // Coasting has no flow rate. @@ -737,7 +742,10 @@ void LayerPlan::addWallLine( segment_flow, width_factor, spiralize, - (overhang_mask_.empty() || (! overhang_mask_.inside(p0, true) && ! overhang_mask_.inside(p1, true))) ? speed_factor : overhang_speed_factor); + (overhang_mask_.empty() || (! overhang_mask_.inside(p0.toPoint2LL(), true) && ! overhang_mask_.inside(p1.toPoint2LL(), true))) ? speed_factor + : overhang_speed_factor, + GCodePathConfig::FAN_SPEED_DEFAULT, + travel_to_z); } distance_to_bridge_start -= len; @@ -752,16 +760,19 @@ void LayerPlan::addWallLine( segment_flow, width_factor, spiralize, - (overhang_mask_.empty() || (! overhang_mask_.inside(p0, true) && ! overhang_mask_.inside(p1, true))) ? speed_factor : overhang_speed_factor); + (overhang_mask_.empty() || (! overhang_mask_.inside(p0.toPoint2LL(), true) && ! overhang_mask_.inside(p1.toPoint2LL(), true))) ? speed_factor + : overhang_speed_factor, + GCodePathConfig::FAN_SPEED_DEFAULT, + travel_to_z); } - non_bridge_line_volume += vSize(cur_point - segment_end) * segment_flow * width_factor * speed_factor * default_config.getSpeed(); + non_bridge_line_volume += (cur_point - segment_end).vSize() * segment_flow * width_factor * speed_factor * default_config.getSpeed(); cur_point = segment_end; speed_factor = 1 - (1 - speed_factor) * acceleration_factor; if (speed_factor >= 0.9) { speed_factor = 1.0; } - distance_to_line_end = vSize(cur_point - line_end); + distance_to_line_end = (cur_point - line_end).vSize(); } }; @@ -773,7 +784,7 @@ void LayerPlan::addWallLine( // what part of the line segment will be printed with what config. return false; } - return PolygonUtils::polygonCollidesWithLineSegment(roofing_mask_, p0, p1) || roofing_mask_.inside(p1, true); + return PolygonUtils::polygonCollidesWithLineSegment(roofing_mask_, p0.toPoint2LL(), p1.toPoint2LL()) || roofing_mask_.inside(p1.toPoint2LL(), true); }(); if (use_roofing_config) @@ -785,7 +796,7 @@ void LayerPlan::addWallLine( // to the first and last point of the intersected line segments alternating between // roofing and default_config's. OpenLinesSet line_polys; - line_polys.addSegment(p0, p1); + line_polys.addSegment(p0.toPoint2LL(), p1.toPoint2LL()); constexpr bool restitch = false; // only a single line doesn't need stitching auto roofing_line_segments = roofing_mask_.intersection(line_polys, restitch); @@ -794,7 +805,7 @@ void LayerPlan::addWallLine( // roofing_line_segments should never be empty since we already checked that the line segment // intersects with the roofing area. But if it is empty then just print the line segment // using the default_config. - addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r); + addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } else { @@ -823,16 +834,25 @@ void LayerPlan::addWallLine( // if the start of the line segment is not at minimum distance from p0 if (vSize2(line_poly.front() - p0) > min_line_len * min_line_len) { - addExtrusionMove(line_poly.front(), default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r); + addExtrusionMove( + line_poly.front(), + default_config, + SpaceFillType::Polygons, + flow, + width_factor, + spiralize, + 1.0_r, + GCodePathConfig::FAN_SPEED_DEFAULT, + travel_to_z); } - addExtrusionMove(line_poly.back(), roofing_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r); + addExtrusionMove(line_poly.back(), roofing_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } // if the last point is not yet at a minimum distance from p1 then add a move to p1 if (vSize2(roofing_line_segments.back().back() - p1) > min_line_len * min_line_len) { - addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r); + addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } } } @@ -846,19 +866,21 @@ void LayerPlan::addWallLine( flow, width_factor, spiralize, - (overhang_mask_.empty() || (! overhang_mask_.inside(p0, true) && ! overhang_mask_.inside(p1, true))) ? 1.0_r : overhang_speed_factor); + (overhang_mask_.empty() || (! overhang_mask_.inside(p0.toPoint2LL(), true) && ! overhang_mask_.inside(p1.toPoint2LL(), true))) ? 1.0_r : overhang_speed_factor, + GCodePathConfig::FAN_SPEED_DEFAULT, + travel_to_z); } else { // bridges may be required - if (PolygonUtils::polygonCollidesWithLineSegment(bridge_wall_mask_, p0, p1)) + if (PolygonUtils::polygonCollidesWithLineSegment(bridge_wall_mask_, p0.toPoint2LL(), p1.toPoint2LL())) { // the line crosses the boundary between supported and non-supported regions so one or more bridges are required // determine which segments of the line are bridges OpenLinesSet line_polys; - line_polys.addSegment(p0, p1); + line_polys.addSegment(p0.toPoint2LL(), p1.toPoint2LL()); constexpr bool restitch = false; // only a single line doesn't need stitching line_polys = bridge_wall_mask_.intersection(line_polys, restitch); @@ -868,10 +890,10 @@ void LayerPlan::addWallLine( { // find the bridge line segment that's nearest to the current point size_t nearest = 0; - double smallest_dist2 = vSize2f(cur_point - line_polys[0][0]); + double smallest_dist2 = (cur_point - line_polys[0][0]).vSize2f(); for (size_t i = 1; i < line_polys.size(); ++i) { - double dist2 = vSize2f(cur_point - line_polys[i][0]); + double dist2 = (cur_point - line_polys[i][0]).vSize2f(); if (dist2 < smallest_dist2) { nearest = i; @@ -881,10 +903,10 @@ void LayerPlan::addWallLine( const OpenPolyline& bridge = line_polys[nearest]; // set b0 to the nearest vertex and b1 the furthest - Point2LL b0 = bridge[0]; - Point2LL b1 = bridge[1]; + Point3LL b0 = bridge[0]; + Point3LL b1 = bridge[1]; - if (vSize2f(cur_point - b1) < vSize2f(cur_point - b0)) + if ((cur_point - b1).vSize2f() < (cur_point - b0).vSize2f()) { // swap vertex order b0 = bridge[1]; @@ -895,7 +917,7 @@ void LayerPlan::addWallLine( addNonBridgeLine(b0); - const double bridge_line_len = vSize(b1 - cur_point); + const double bridge_line_len = (b1 - cur_point).vSize(); if (bridge_line_len >= min_bridge_line_len) { @@ -903,7 +925,7 @@ void LayerPlan::addWallLine( if (bridge_line_len > min_line_len) { - addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow, width_factor); + addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow, width_factor, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); non_bridge_line_volume = 0; cur_point = b1; // after a bridge segment, start slow and accelerate to avoid under-extrusion due to extruder lag @@ -924,7 +946,7 @@ void LayerPlan::addWallLine( // if we haven't yet reached p1, fill the gap with default_config line addNonBridgeLine(p1); } - else if (bridge_wall_mask_.inside(p0, true) && vSize(p0 - p1) >= min_bridge_line_len) + else if (bridge_wall_mask_.inside(p0.toPoint2LL(), true) && (p0 - p1).vSize() >= min_bridge_line_len) { // both p0 and p1 must be above air (the result will be ugly!) addExtrusionMove(p1, bridge_config, SpaceFillType::Polygons, flow, width_factor); @@ -983,11 +1005,13 @@ void LayerPlan::addWall( const GCodePathConfig& roofing_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, - double flow_ratio, + const double flow_ratio, bool always_retract, const bool is_closed, const bool is_reversed, - const bool is_linked_path) + const bool is_linked_path, + coord_t scarf_seam_length, + Ratio scarf_seam_start_ratio) { if (wall.empty()) { @@ -1091,6 +1115,7 @@ void LayerPlan::addWall( }; bool first_line = true; + bool first_scarf = true; const coord_t small_feature_max_length = settings.get("small_feature_max_length"); const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); @@ -1098,93 +1123,170 @@ void LayerPlan::addWall( small_feature_speed_factor = std::max((double)small_feature_speed_factor, (double)(min_speed / default_config.getSpeed())); const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); + const auto scarf_split_distance = settings.get("scarf_split_distance"); + const coord_t scarf_max_z_offset = -(1.0 - scarf_seam_start_ratio) * layer_thickness_; ExtrusionJunction p0 = wall[start_idx]; const int direction = is_reversed ? -1 : 1; const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); - for (size_t point_idx = 1; point_idx < max_index; point_idx++) + + auto addScarfedWall = [&](const bool is_scarf_closure) { - const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; + coord_t scarf_processed_distance = 0; - if (! bridge_wall_mask_.empty()) + for (size_t point_idx = 1; point_idx < max_index; point_idx++) { - computeDistanceToBridgeStart((wall.size() + start_idx + point_idx * direction - 1) % wall.size()); - } - - if (first_line) - { - addTravel(p0.p_, always_retract); - first_line = false; - } - - /* - If the line has variable width, break it up into pieces with the - following constraints: - - Each piece must be smaller than the Maximum Resolution setting. - - The difference between the trapezoidal shape of the line and the - rectangular shape of the line may not exceed the Maximum Extrusion - Area Deviation setting, unless required by the first constraint. - Since breaking up a line segment into N pieces (each with averaged - width) divides the area deviation by N, we can simply check how many - pieces we'd want to get low enough deviation, then check if each piece - is not too short at the end. - */ - const coord_t delta_line_width = p1.w_ - p0.w_; - const Point2LL line_vector = p1.p_ - p0.p_; - const coord_t line_length = vSize(line_vector); - /* - Calculate how much the line would deviate from the trapezoidal shape if printed at average width. - This formula is: - - Half the length times half the delta width, for the rectangular shape of the deviating side. - - Half of that because the ideal line width is trapezoidal, making the deviating part triangular. - - Double of that because the deviation occurs on both sides of the idealised line width. - This results in delta_line_width / 2 * line_length / 2 / 2 * 2 == delta_line_width * line_length / 4. - */ - const coord_t line_area_deviation = std::abs(delta_line_width) * line_length / 4; - const size_t pieces_limit_deviation = round_up_divide(line_area_deviation, max_area_deviation); // How many pieces we'd need to stay beneath the max area deviation. - const size_t pieces_limit_resolution = line_length / max_resolution; // Round down this time, to not exceed the maximum resolution. - const size_t pieces = std::max(size_t(1), std::min(pieces_limit_deviation, pieces_limit_resolution)); // Resolution overrides deviation, if resolution is a constraint. - const coord_t piece_length = round_divide(line_length, pieces); - - for (size_t piece = 0; piece < pieces; ++piece) - { - const double average_progress = (double(piece) + 0.5) / pieces; // How far along this line to sample the line width in the middle of this piece. - // Round the line_width value to overcome floating point rounding issues, otherwise we may end up with slightly different values - // and the generated GCodePath objects will not be merged together, which some subsequent algorithms rely on (e.g. coasting) - const coord_t line_width = std::lrint(static_cast(p0.w_) + average_progress * static_cast(delta_line_width)); - const Point2LL destination = p0.p_ + normal(line_vector, piece_length * (piece + 1)); - if (is_small_feature) + const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; + + if (! bridge_wall_mask_.empty()) { - constexpr bool spiralize = false; - addExtrusionMove( - destination, - default_config, - SpaceFillType::Polygons, - flow_ratio, - line_width * nominal_line_width_multiplier, - spiralize, - small_feature_speed_factor); + computeDistanceToBridgeStart((wall.size() + start_idx + point_idx * direction - 1) % wall.size()); } - else + + if (first_line) { - const Point2LL origin = p0.p_ + normal(line_vector, piece_length * piece); - addWallLine( - origin, - destination, - settings, - default_config, - roofing_config, - bridge_config, - flow_ratio, - line_width * nominal_line_width_multiplier, - non_bridge_line_volume, - speed_factor, - distance_to_bridge_start); + addTravel(p0.p_, always_retract); + first_line = false; } + + /* + If the line has variable width, break it up into pieces with the + following constraints: + - Each piece must be smaller than the Maximum Resolution setting. + - The difference between the trapezoidal shape of the line and the + rectangular shape of the line may not exceed the Maximum Extrusion + Area Deviation setting, unless required by the first constraint. + Since breaking up a line segment into N pieces (each with averaged + width) divides the area deviation by N, we can simply check how many + pieces we'd want to get low enough deviation, then check if each piece + is not too short at the end. + */ + const coord_t delta_line_width = p1.w_ - p0.w_; + const Point2LL line_vector = p1.p_ - p0.p_; + const coord_t line_length = vSize(line_vector); + /* + Calculate how much the line would deviate from the trapezoidal shape if printed at average width. + This formula is: + - Half the length times half the delta width, for the rectangular shape of the deviating side. + - Half of that because the ideal line width is trapezoidal, making the deviating part triangular. + - Double of that because the deviation occurs on both sides of the idealised line width. + This results in delta_line_width / 2 * line_length / 2 / 2 * 2 == delta_line_width * line_length / 4. + */ + const coord_t line_area_deviation = std::abs(delta_line_width) * line_length / 4; + const size_t pieces_limit_deviation = round_up_divide(line_area_deviation, max_area_deviation); // How many pieces we'd need to stay beneath the max area deviation. + const size_t pieces_limit_resolution = line_length / max_resolution; // Round down this time, to not exceed the maximum resolution. + const size_t pieces = std::max(size_t(1), std::min(pieces_limit_deviation, pieces_limit_resolution)); // Resolution overrides deviation, if resolution is a constraint. + const coord_t piece_length = round_divide(line_length, pieces); + + for (size_t piece = 0; piece < pieces; ++piece) + { + const double average_progress = (double(piece) + 0.5) / pieces; // How far along this line to sample the line width in the middle of this piece. + // Round the line_width value to overcome floating point rounding issues, otherwise we may end up with slightly different values + // and the generated GCodePath objects will not be merged together, which some subsequent algorithms rely on (e.g. coasting) + const coord_t line_width = std::lrint(static_cast(p0.w_) + average_progress * static_cast(delta_line_width)); + const Point2LL destination = p0.p_ + normal(line_vector, piece_length * (piece + 1)); + if (is_small_feature && ! is_scarf_closure) + { + constexpr bool spiralize = false; + addExtrusionMove( + destination, + default_config, + SpaceFillType::Polygons, + flow_ratio, + line_width * nominal_line_width_multiplier, + spiralize, + small_feature_speed_factor); + } + else + { + coord_t piece_processed_distance = 0; + + // Cut piece into smaller parts for scarf seam + while (scarf_processed_distance < scarf_seam_length && piece_processed_distance < piece_length) + { + const double scarf_factor_origin = static_cast(scarf_processed_distance) / static_cast(scarf_seam_length); + Point3LL scarf_origin = p0.p_ + normal(line_vector, piece_length * piece + piece_processed_distance); + if (! is_scarf_closure) + { + scarf_origin.z_ = std::lerp(scarf_max_z_offset, 0.0, scarf_factor_origin); + } + + coord_t length_to_process = std::min({ scarf_seam_length - scarf_processed_distance, piece_length - piece_processed_distance, scarf_split_distance }); + const double scarf_factor_destination = static_cast(scarf_processed_distance + length_to_process) / static_cast(scarf_seam_length); + Point3LL scarf_destination = scarf_origin + normal(line_vector, length_to_process); + if (! is_scarf_closure) + { + scarf_destination.z_ = std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination); + } + + const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; + double scarf_segment_flow_ratio; + if (is_scarf_closure) + { + scarf_segment_flow_ratio = std::lerp(1.0, scarf_seam_start_ratio, scarf_factor_average); + } + else + { + scarf_segment_flow_ratio = std::lerp(scarf_seam_start_ratio, 1.0, scarf_factor_average); + } + + if (first_scarf) + { + // Manually add a Z-only travel move to set the nozzle at the height of the first point + addTravel(p0.p_, always_retract, scarf_origin.z_); + first_scarf = false; + } + + constexpr bool travel_to_z = false; + addWallLine( + scarf_origin, + scarf_destination, + settings, + default_config, + roofing_config, + bridge_config, + flow_ratio * scarf_segment_flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start, + travel_to_z); + + piece_processed_distance += length_to_process; + scarf_processed_distance += length_to_process; + } + + if (piece_processed_distance < piece_length && ! is_scarf_closure) + { + const Point2LL origin = p0.p_ + normal(line_vector, piece_length * piece + piece_processed_distance); + addWallLine( + origin, + destination, + settings, + default_config, + roofing_config, + bridge_config, + flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start); + } + } + } + + p0 = p1; } + }; - p0 = p1; + // First pass to add the wall with the scarf beginning + addScarfedWall(false); + + if (scarf_seam_length) + { + // Second pass to add the scarf closure + addScarfedWall(true); } if (wall.size() >= 2) @@ -1499,12 +1601,11 @@ void LayerPlan::addLinesInGivenOrder( void LayerPlan::sendLineTo(const GCodePath& path, const Point3LL& position, const double extrude_speed) { - coord_t total_z_offset = path.z_offset + position.z_; Application::getInstance().communication_->sendLineTo( path.config.type, - position + Point3LL(0, 0, z_ + total_z_offset), + position + Point3LL(0, 0, z_ + path.z_offset), path.getLineWidthForLayerView(), - path.config.getLayerThickness() + total_z_offset, + path.config.getLayerThickness() + path.z_offset + position.z_, extrude_speed); } @@ -2285,7 +2386,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) gcode.writeComment(ss.str()); } - if (! path.spiralize && (! path.retract || ! path.perform_z_hop) && (z_ + path.z_offset + path.points.front().z_ != gcode.getPositionZ()) + if (! path.spiralize && path.travel_to_z && (! path.retract || ! path.perform_z_hop) && (z_ + path.z_offset + path.points.front().z_ != gcode.getPositionZ()) && (path_idx > 0 || layer_nr_ > 0)) { // First move to desired height to then make a plain horizontal move From 85220697c96142e1aa120d610709d38017eb2f80 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 09:55:17 +0200 Subject: [PATCH 06/32] Fix scarf seam for small walls CURA-12081 --- src/LayerPlan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index c275732c96..18d1f5362f 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1125,6 +1125,7 @@ void LayerPlan::addWall( const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); const auto scarf_split_distance = settings.get("scarf_split_distance"); const coord_t scarf_max_z_offset = -(1.0 - scarf_seam_start_ratio) * layer_thickness_; + scarf_seam_length = std::min(scarf_seam_length, wall.length()); ExtrusionJunction p0 = wall[start_idx]; From 026557b66a307da4376c3aabef4916042086c13e Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 22 Aug 2024 07:56:11 +0000 Subject: [PATCH 07/32] Applied clang-format. --- include/ExtruderPlan.h | 2 +- include/communication/ArcusCommunication.h | 2 +- src/communication/CommandLine.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ExtruderPlan.h b/include/ExtruderPlan.h index d82fb34b1a..859f91a51f 100644 --- a/include/ExtruderPlan.h +++ b/include/ExtruderPlan.h @@ -190,7 +190,7 @@ class ExtruderPlan /*! * @return distance between p0 and p1 as well as the time spend on the segment */ - std::pair getPointToPointTime(const Point3LL &p0, const Point3LL &p1, const GCodePath& path); + std::pair getPointToPointTime(const Point3LL& p0, const Point3LL& p1, const GCodePath& path); /*! * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. diff --git a/include/communication/ArcusCommunication.h b/include/communication/ArcusCommunication.h index f6a8c81f66..913c38418f 100644 --- a/include/communication/ArcusCommunication.h +++ b/include/communication/ArcusCommunication.h @@ -86,7 +86,7 @@ class ArcusCommunication : public Communication * This may indicate the starting position (or any other jump in the path). * \param position The current position to start the next line at. */ - void sendCurrentPosition(const Point3LL &position) override; + void sendCurrentPosition(const Point3LL& position) override; /* * \brief Sends a message to indicate that all the slicing is done. diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 496ff681e7..38a8f16734 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -56,7 +56,7 @@ void CommandLine::beginGCode() void CommandLine::flushGCode() { } -void CommandLine::sendCurrentPosition(const Point3LL &) +void CommandLine::sendCurrentPosition(const Point3LL&) { } void CommandLine::sendFinishedSlicing() const @@ -65,7 +65,7 @@ void CommandLine::sendFinishedSlicing() const void CommandLine::sendLayerComplete(const LayerIndex::value_type&, const coord_t&, const coord_t&) { } -void CommandLine::sendLineTo(const PrintFeatureType&, const Point3LL &, const coord_t&, const coord_t&, const Velocity&) +void CommandLine::sendLineTo(const PrintFeatureType&, const Point3LL&, const coord_t&, const coord_t&, const Velocity&) { } void CommandLine::sendOptimizedLayerData() From fb36ac463b1e70e7fced5cd424730a2f4ca6c742 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 12:45:37 +0200 Subject: [PATCH 08/32] Scarf seam calculation optimization CURA-12081 --- src/LayerPlan.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 18d1f5362f..18a64abf7d 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1132,6 +1132,10 @@ void LayerPlan::addWall( const int direction = is_reversed ? -1 : 1; const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); + double scarf_factor_origin = 0.0; + Point3LL scarf_origin = p0.p_; + scarf_origin.z_ = scarf_max_z_offset; + auto addScarfedWall = [&](const bool is_scarf_closure) { coord_t scarf_processed_distance = 0; @@ -1206,13 +1210,6 @@ void LayerPlan::addWall( // Cut piece into smaller parts for scarf seam while (scarf_processed_distance < scarf_seam_length && piece_processed_distance < piece_length) { - const double scarf_factor_origin = static_cast(scarf_processed_distance) / static_cast(scarf_seam_length); - Point3LL scarf_origin = p0.p_ + normal(line_vector, piece_length * piece + piece_processed_distance); - if (! is_scarf_closure) - { - scarf_origin.z_ = std::lerp(scarf_max_z_offset, 0.0, scarf_factor_origin); - } - coord_t length_to_process = std::min({ scarf_seam_length - scarf_processed_distance, piece_length - piece_processed_distance, scarf_split_distance }); const double scarf_factor_destination = static_cast(scarf_processed_distance + length_to_process) / static_cast(scarf_seam_length); Point3LL scarf_destination = scarf_origin + normal(line_vector, length_to_process); @@ -1256,6 +1253,8 @@ void LayerPlan::addWall( piece_processed_distance += length_to_process; scarf_processed_distance += length_to_process; + scarf_origin = scarf_destination; + scarf_factor_origin = scarf_factor_destination; } if (piece_processed_distance < piece_length && ! is_scarf_closure) From 5a184f30f5d38222c7e9a5f4bcd7a4e1355c3ca3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 12:46:00 +0200 Subject: [PATCH 09/32] Gradual flow calculation optimization CURA-12081 --- include/gradual_flow/Processor.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/include/gradual_flow/Processor.h b/include/gradual_flow/Processor.h index 4954473959..c04b0c4734 100644 --- a/include/gradual_flow/Processor.h +++ b/include/gradual_flow/Processor.h @@ -47,17 +47,7 @@ void process(std::vector& extruder_plan_paths, const size_t extruder_ for (const auto& path : extruder_plan_paths | ranges::views::drop(1)) { std::vector points{ gcode_paths.back().points.back() }; - -#warning this is probably not necessary - points.reserve(path.points.size() + 1); - std::transform( - path.points.begin(), - path.points.end(), - std::back_inserter(points), - [](const Point3LL& point) - { - return point; - }); + points.insert(points.end(), path.points.begin(), path.points.end()); gcode_paths.emplace_back(FlowLimitedPath{ .original_gcode_path_data = &path, .points = std::move(points) }); } From 816d3feca28991cb44c47a55e56d2ccf0b2b47b6 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 12:49:11 +0200 Subject: [PATCH 10/32] Fix scarf seam after optimization CURA-12081 --- src/LayerPlan.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 18a64abf7d..764247a7e6 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1132,13 +1132,15 @@ void LayerPlan::addWall( const int direction = is_reversed ? -1 : 1; const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); - double scarf_factor_origin = 0.0; - Point3LL scarf_origin = p0.p_; - scarf_origin.z_ = scarf_max_z_offset; - auto addScarfedWall = [&](const bool is_scarf_closure) { coord_t scarf_processed_distance = 0; + double scarf_factor_origin = 0.0; + Point3LL scarf_origin = p0.p_; + if (! is_scarf_closure) + { + scarf_origin.z_ = scarf_max_z_offset; + } for (size_t point_idx = 1; point_idx < max_index; point_idx++) { From 86eabfb8de6c39ac70039452e990cf1e5b393189 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 13:38:20 +0200 Subject: [PATCH 11/32] Fix comment CURA-12081 --- include/gradual_flow/FlowLimitedPath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gradual_flow/FlowLimitedPath.h b/include/gradual_flow/FlowLimitedPath.h index c1f3864c94..49242f7130 100644 --- a/include/gradual_flow/FlowLimitedPath.h +++ b/include/gradual_flow/FlowLimitedPath.h @@ -327,7 +327,7 @@ struct GCodeState discretized_duration_remaining = 0; // set the current flow to the target end flow. When executing the backward pass we want to - // we start with this flow and gradually increase it to the target flow. However, if the + // start with this flow and gradually increase it to the target flow. However, if the // highest flow we can achieve is lower than this target flow we want to use that flow // instead. current_flow = std::min(current_flow, target_end_flow); From 2ff17e6147535adb657ebd90c7aa4fd090b917ba Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 22 Aug 2024 14:51:34 +0200 Subject: [PATCH 12/32] Retrieve scarf settings more consistently CURA-12081 --- include/LayerPlan.h | 3 +-- src/InsetOrderOptimizer.cpp | 6 ++---- src/LayerPlan.cpp | 7 ++++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index aee8a952ee..f4fe59fd32 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -525,8 +525,7 @@ class LayerPlan : public NoCopy const bool is_closed, const bool is_reversed, const bool is_linked_path, - coord_t scarf_seam_length = 0, - Ratio scarf_seam_start_ratio = 1.0); + const bool scarf_seam = false); /*! * Add an infill wall to the g-code diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index ca6bc091b8..e809405a60 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -148,8 +148,7 @@ bool InsetOrderOptimizer::addToLayer() const GCodePathConfig& bridge_config = is_outer_wall ? inset_0_bridge_config_ : inset_X_bridge_config_; const coord_t wipe_dist = is_outer_wall && ! is_gap_filler ? wall_0_wipe_dist_ : wall_x_wipe_dist_; const bool retract_before = is_outer_wall ? retract_before_outer_wall_ : false; - const coord_t scarf_seam_length = scarf_seam_ && is_outer_wall ? settings_.get("scarf_joint_seam_length") : 0; - const Ratio scarf_seam_start_ratio = scarf_seam_ && is_outer_wall ? settings_.get("scarf_joint_seam_start_height_ratio") : 1.0_r; + const bool scarf_seam = scarf_seam_ && is_outer_wall; const bool revert_inset = alternate_walls && (path.vertices_->inset_idx_ % 2 != 0); const bool revert_layer = alternate_walls && (layer_nr_ % 2 != 0); @@ -171,8 +170,7 @@ bool InsetOrderOptimizer::addToLayer() path.is_closed_, backwards, linked_path, - scarf_seam_length, - scarf_seam_start_ratio); + scarf_seam); added_something = true; } return added_something; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 764247a7e6..932d4cf974 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1010,8 +1010,7 @@ void LayerPlan::addWall( const bool is_closed, const bool is_reversed, const bool is_linked_path, - coord_t scarf_seam_length, - Ratio scarf_seam_start_ratio) + const bool scarf_seam) { if (wall.empty()) { @@ -1022,6 +1021,7 @@ void LayerPlan::addWall( // make sure wall start point is not above air! start_idx = locateFirstSupportedVertex(wall, start_idx); } + const bool actual_scarf_seam = scarf_seam && is_closed; double non_bridge_line_volume = max_non_bridge_line_volume; // assume extruder is fully pressurised before first non-bridge line is output double speed_factor = 1.0; // start first line at normal speed @@ -1123,9 +1123,10 @@ void LayerPlan::addWall( small_feature_speed_factor = std::max((double)small_feature_speed_factor, (double)(min_speed / default_config.getSpeed())); const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); + const coord_t scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); + const Ratio scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; const auto scarf_split_distance = settings.get("scarf_split_distance"); const coord_t scarf_max_z_offset = -(1.0 - scarf_seam_start_ratio) * layer_thickness_; - scarf_seam_length = std::min(scarf_seam_length, wall.length()); ExtrusionJunction p0 = wall[start_idx]; From 9d2f285033fe9969cb048358fd3c83d316554daf Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 07:58:20 +0200 Subject: [PATCH 13/32] Apply acceleration after seam CURA-12080 --- include/InsetOrderOptimizer.h | 4 +- include/LayerPlan.h | 3 +- src/FffGcodeWriter.cpp | 4 +- src/InsetOrderOptimizer.cpp | 8 ++- src/LayerPlan.cpp | 117 +++++++++++++++++++++++----------- 5 files changed, 95 insertions(+), 41 deletions(-) diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index eb76f4d85e..afba5dc452 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -58,7 +58,8 @@ class InsetOrderOptimizer const std::vector& paths, const Point2LL& model_center_point, const Shape& disallowed_areas_for_seams = {}, - const bool scarf_seam = false); + const bool scarf_seam = false, + const bool smooth_speed = false); /*! * Adds the insets to the given layer plan. @@ -112,6 +113,7 @@ class InsetOrderOptimizer const Point2LL model_center_point_; // Center of the model (= all meshes) axis-aligned bounding-box. Shape disallowed_areas_for_seams_; const bool scarf_seam_; + const bool smooth_speed_; std::vector> inset_polys_; // vector of vectors holding the inset polygons Shape retraction_region_; // After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see diff --git a/include/LayerPlan.h b/include/LayerPlan.h index f4fe59fd32..65b0d56b98 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -525,7 +525,8 @@ class LayerPlan : public NoCopy const bool is_closed, const bool is_reversed, const bool is_linked_path, - const bool scarf_seam = false); + const bool scarf_seam = false, + const bool smooth_speed = false); /*! * Add an infill wall to the g-code diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index dee067192a..a7b8980adf 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2737,6 +2737,7 @@ bool FffGcodeWriter::processInsets( mesh.settings.get("wall_line_width_0") * 2); constexpr Shape disallowed_areas_for_seams; constexpr bool scarf_seam = true; + constexpr bool smooth_speed = true; InsetOrderOptimizer wall_orderer( *this, storage, @@ -2758,7 +2759,8 @@ bool FffGcodeWriter::processInsets( part.wall_toolpaths, mesh.bounding_box.flatten().getMiddle(), disallowed_areas_for_seams, - scarf_seam); + scarf_seam, + smooth_speed); added_something |= wall_orderer.addToLayer(); } return added_something; diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index e809405a60..56961e2bde 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -53,7 +53,8 @@ InsetOrderOptimizer::InsetOrderOptimizer( const std::vector& paths, const Point2LL& model_center_point, const Shape& disallowed_areas_for_seams, - const bool scarf_seam) + const bool scarf_seam, + const bool smooth_speed) : gcode_writer_(gcode_writer) , storage_(storage) , gcode_layer_(gcode_layer) @@ -76,6 +77,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( , model_center_point_(model_center_point) , disallowed_areas_for_seams_{ disallowed_areas_for_seams } , scarf_seam_(scarf_seam) + , smooth_speed_(smooth_speed) { } @@ -149,6 +151,7 @@ bool InsetOrderOptimizer::addToLayer() const coord_t wipe_dist = is_outer_wall && ! is_gap_filler ? wall_0_wipe_dist_ : wall_x_wipe_dist_; const bool retract_before = is_outer_wall ? retract_before_outer_wall_ : false; const bool scarf_seam = scarf_seam_ && is_outer_wall; + const bool smooth_speed = smooth_speed_ && is_outer_wall; const bool revert_inset = alternate_walls && (path.vertices_->inset_idx_ % 2 != 0); const bool revert_layer = alternate_walls && (layer_nr_ % 2 != 0); @@ -170,7 +173,8 @@ bool InsetOrderOptimizer::addToLayer() path.is_closed_, backwards, linked_path, - scarf_seam); + scarf_seam, + smooth_speed); added_something = true; } return added_something; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 932d4cf974..560b424e53 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -866,7 +866,7 @@ void LayerPlan::addWallLine( flow, width_factor, spiralize, - (overhang_mask_.empty() || (! overhang_mask_.inside(p0.toPoint2LL(), true) && ! overhang_mask_.inside(p1.toPoint2LL(), true))) ? 1.0_r : overhang_speed_factor, + (overhang_mask_.empty() || (! overhang_mask_.inside(p0.toPoint2LL(), true) && ! overhang_mask_.inside(p1.toPoint2LL(), true))) ? speed_factor : overhang_speed_factor, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } @@ -1010,7 +1010,8 @@ void LayerPlan::addWall( const bool is_closed, const bool is_reversed, const bool is_linked_path, - const bool scarf_seam) + const bool scarf_seam, + const bool smooth_speed) { if (wall.empty()) { @@ -1120,13 +1121,19 @@ void LayerPlan::addWall( const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); const Velocity min_speed = fan_speed_layer_time_settings_per_extruder_[getLastPlannedExtruderTrain()->extruder_nr_].cool_min_speed; - small_feature_speed_factor = std::max((double)small_feature_speed_factor, (double)(min_speed / default_config.getSpeed())); + small_feature_speed_factor = std::max(static_cast(small_feature_speed_factor), static_cast(min_speed / default_config.getSpeed())); const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! - const coord_t max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); - const coord_t scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); - const Ratio scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; + const auto max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); + const auto scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); + const auto scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; const auto scarf_split_distance = settings.get("scarf_split_distance"); - const coord_t scarf_max_z_offset = -(1.0 - scarf_seam_start_ratio) * layer_thickness_; + const coord_t scarf_max_z_offset = static_cast(-(1.0 - scarf_seam_start_ratio) * static_cast(layer_thickness_)); + const Ratio start_speed_ratio = smooth_speed ? settings.get("wall_0_start_speed_ratio") : 1.0_r; + const int acceleration = settings.get("wall_0_acceleration"); // mm/s² + const Velocity top_speed = default_config.getSpeed(); + const double start_speed = top_speed * start_speed_ratio; // mm/s + const coord_t accelerate_split_distance = settings.get("wall_0_speed_split_distance"); // mm + const coord_t accelerate_length = (smooth_speed && start_speed_ratio < 1.0) ? MM2INT((std::pow(top_speed.value, 2) - std::pow(start_speed, 2)) / (2.0 * acceleration)) : 0; ExtrusionJunction p0 = wall[start_idx]; @@ -1135,14 +1142,18 @@ void LayerPlan::addWall( auto addScarfedWall = [&](const bool is_scarf_closure) { - coord_t scarf_processed_distance = 0; + coord_t scarf_remaining_distance = scarf_seam_length; double scarf_factor_origin = 0.0; - Point3LL scarf_origin = p0.p_; + + Point3LL split_origin = p0.p_; if (! is_scarf_closure) { - scarf_origin.z_ = scarf_max_z_offset; + split_origin.z_ = scarf_max_z_offset; } + coord_t accelerate_remaining_distance = accelerate_length; + double accelerate_factor_origin = 0.0; + for (size_t point_idx = 1; point_idx < max_index; point_idx++) { const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; @@ -1208,41 +1219,71 @@ void LayerPlan::addWall( } else { - coord_t piece_processed_distance = 0; + coord_t piece_remaining_distance = piece_length; // Cut piece into smaller parts for scarf seam - while (scarf_processed_distance < scarf_seam_length && piece_processed_distance < piece_length) + while ((scarf_remaining_distance > 0 || accelerate_remaining_distance > 0) && piece_remaining_distance > 0) { - coord_t length_to_process = std::min({ scarf_seam_length - scarf_processed_distance, piece_length - piece_processed_distance, scarf_split_distance }); - const double scarf_factor_destination = static_cast(scarf_processed_distance + length_to_process) / static_cast(scarf_seam_length); - Point3LL scarf_destination = scarf_origin + normal(line_vector, length_to_process); - if (! is_scarf_closure) + std::vector split_distances{ piece_remaining_distance }; + if (scarf_remaining_distance > 0) { - scarf_destination.z_ = std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination); + split_distances.push_back(scarf_remaining_distance); + split_distances.push_back(scarf_split_distance); } - - const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; - double scarf_segment_flow_ratio; - if (is_scarf_closure) + if (! is_scarf_closure && accelerate_remaining_distance > 0) { - scarf_segment_flow_ratio = std::lerp(1.0, scarf_seam_start_ratio, scarf_factor_average); + split_distances.push_back(accelerate_remaining_distance); + split_distances.push_back(accelerate_split_distance); } - else + + coord_t length_to_process = *std::min_element(split_distances.begin(), split_distances.end()); + Point3LL split_destination = split_origin + normal(line_vector, length_to_process); + + double scarf_segment_flow_ratio = 1.0; + double scarf_factor_destination = 1.0; + if (scarf_remaining_distance > 0) { - scarf_segment_flow_ratio = std::lerp(scarf_seam_start_ratio, 1.0, scarf_factor_average); + scarf_factor_destination = 1.0 - (static_cast(scarf_remaining_distance - length_to_process) / static_cast(scarf_seam_length)); + if (! is_scarf_closure) + { + split_destination.z_ = std::llrint(std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination)); + } + + const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; + double scarf_segment_flow_ratio; + if (is_scarf_closure) + { + scarf_segment_flow_ratio = std::lerp(1.0, scarf_seam_start_ratio, scarf_factor_average); + } + else + { + scarf_segment_flow_ratio = std::lerp(scarf_seam_start_ratio, 1.0, scarf_factor_average); + } + + if (first_scarf) + { + // Manually add a Z-only travel move to set the nozzle at the height of the first point + addTravel(p0.p_, always_retract, split_origin.z_); + first_scarf = false; + } } - if (first_scarf) + double acceleration_speed_factor = 1.0; + double acceleration_factor_destination = 1.0; + if (accelerate_remaining_distance > 0) { - // Manually add a Z-only travel move to set the nozzle at the height of the first point - addTravel(p0.p_, always_retract, scarf_origin.z_); - first_scarf = false; + acceleration_factor_destination + = 1.0 - (static_cast(accelerate_remaining_distance - length_to_process) / static_cast(accelerate_length)); + + const double acceleration_factor_average = (accelerate_factor_origin + acceleration_factor_destination) / 2.0; + + acceleration_speed_factor = std::lerp(start_speed_ratio, 1.0, acceleration_factor_average); } constexpr bool travel_to_z = false; addWallLine( - scarf_origin, - scarf_destination, + split_origin, + split_destination, settings, default_config, roofing_config, @@ -1250,19 +1291,23 @@ void LayerPlan::addWall( flow_ratio * scarf_segment_flow_ratio, line_width * nominal_line_width_multiplier, non_bridge_line_volume, - speed_factor, + speed_factor * acceleration_speed_factor, distance_to_bridge_start, travel_to_z); - piece_processed_distance += length_to_process; - scarf_processed_distance += length_to_process; - scarf_origin = scarf_destination; + piece_remaining_distance -= length_to_process; + split_origin = split_destination; + + scarf_remaining_distance -= length_to_process; scarf_factor_origin = scarf_factor_destination; + + accelerate_remaining_distance -= length_to_process; + accelerate_factor_origin = acceleration_factor_destination; } - if (piece_processed_distance < piece_length && ! is_scarf_closure) + if (piece_remaining_distance > 0 && ! is_scarf_closure) { - const Point2LL origin = p0.p_ + normal(line_vector, piece_length * piece + piece_processed_distance); + const Point2LL origin = p0.p_ + normal(line_vector, piece_length * (piece + 1) - piece_remaining_distance); addWallLine( origin, destination, From fd1e268cb4ba9bf1512899acdc09572d2d86e341 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 09:21:03 +0200 Subject: [PATCH 14/32] Refactor wall line splitting CURA-12080 --- include/LayerPlan.h | 38 ++- src/LayerPlan.cpp | 594 ++++++++++++++++++++++++-------------------- 2 files changed, 357 insertions(+), 275 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 65b0d56b98..20b3023e82 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -482,7 +482,7 @@ class LayerPlan : public NoCopy */ void addWall( const Polygon& wall, - int start_idx, + size_t start_idx, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, @@ -514,7 +514,7 @@ class LayerPlan : public NoCopy */ void addWall( const ExtrusionLine& wall, - int start_idx, + size_t start_idx, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, @@ -679,7 +679,7 @@ class LayerPlan : public NoCopy * \return The index of the first supported vertex - if no vertices are supported, start_idx is returned */ template - unsigned locateFirstSupportedVertex(const T& wall, const unsigned start_idx) const + size_t locateFirstSupportedVertex(const T& wall, const size_t start_idx) const { if (bridge_wall_mask_.empty() && seam_overhang_mask_.empty()) { @@ -688,7 +688,7 @@ class LayerPlan : public NoCopy const auto air_below = bridge_wall_mask_.unionPolygons(seam_overhang_mask_); - unsigned curr_idx = start_idx; + size_t curr_idx = start_idx; while (true) { @@ -836,6 +836,36 @@ class LayerPlan : public NoCopy double extrusion_mm3_per_mm, PrintFeatureType feature, bool update_extrusion_offset = false); + + void addWallSplitted( + const ExtrusionLine& wall, + size_t start_idx, + const int direction, + const size_t max_index, + const Settings& settings, + const GCodePathConfig& default_config, + const GCodePathConfig& roofing_config, + const GCodePathConfig& bridge_config, + const double flow_ratio, + const Ratio nominal_line_width_multiplier, + double& non_bridge_line_volume, + const coord_t min_bridge_line_len, + const bool always_retract, + const bool is_small_feature, + Ratio small_feature_speed_factor, + const coord_t max_area_deviation, + const auto max_resolution, + const auto scarf_seam_length, + const auto scarf_seam_start_ratio, + const auto scarf_split_distance, + const coord_t scarf_max_z_offset, + const Ratio start_speed_ratio, + const coord_t accelerate_split_distance, + const coord_t accelerate_length, + const bool is_scarf_closure); + + // helper function to calculate 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; }; } // namespace cura diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 560b424e53..82f5361157 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -962,7 +962,7 @@ void LayerPlan::addWallLine( void LayerPlan::addWall( const Polygon& wall, - int start_idx, + size_t start_idx, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, @@ -997,356 +997,408 @@ void LayerPlan::addWall( addWall(ewall, start_idx, settings, default_config, roofing_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract, is_closed, is_reversed, is_linked_path); } -void LayerPlan::addWall( +void LayerPlan::addWallSplitted( const ExtrusionLine& wall, - int start_idx, + size_t start_idx, + const int direction, + const size_t max_index, const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, const GCodePathConfig& bridge_config, - coord_t wall_0_wipe_dist, const double flow_ratio, - bool always_retract, - const bool is_closed, - const bool is_reversed, - const bool is_linked_path, - const bool scarf_seam, - const bool smooth_speed) + const Ratio nominal_line_width_multiplier, + double& non_bridge_line_volume, + const coord_t min_bridge_line_len, + const bool always_retract, + const bool is_small_feature, + Ratio small_feature_speed_factor, + const coord_t max_area_deviation, + const auto max_resolution, + const auto scarf_seam_length, + const auto scarf_seam_start_ratio, + const auto scarf_split_distance, + const coord_t scarf_max_z_offset, + const Ratio start_speed_ratio, + const coord_t accelerate_split_distance, + const coord_t accelerate_length, + const bool is_scarf_closure) { - if (wall.empty()) - { - return; - } - if (is_closed) - { - // make sure wall start point is not above air! - start_idx = locateFirstSupportedVertex(wall, start_idx); - } - const bool actual_scarf_seam = scarf_seam && is_closed; + constexpr double speed_factor = 1.0; - double non_bridge_line_volume = max_non_bridge_line_volume; // assume extruder is fully pressurised before first non-bridge line is output - double speed_factor = 1.0; // start first line at normal speed coord_t distance_to_bridge_start = 0; // will be updated before each line is processed + ExtrusionJunction p0 = wall[start_idx]; + bool first_line = ! is_scarf_closure; + bool first_split = ! is_scarf_closure; + Point3LL split_origin = p0.p_; + if (! is_scarf_closure) + { + split_origin.z_ = scarf_max_z_offset; + } - const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); - - const Ratio nominal_line_width_multiplier{ - 1.0 / Ratio{ static_cast(default_config.getLineWidth()) } - }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + coord_t scarf_remaining_distance = scarf_seam_length; + double scarf_factor_origin = 0.0; - // helper function to calculate the distance from the start of the current wall line to the first bridge segment + coord_t accelerate_remaining_distance = accelerate_length; + double accelerate_factor_origin = 0.0; - auto computeDistanceToBridgeStart = [&](unsigned current_index) + for (size_t point_idx = 1; point_idx < max_index; point_idx++) { - distance_to_bridge_start = 0; + const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; if (! bridge_wall_mask_.empty()) { - // there is air below the part so iterate through the lines that have not yet been output accumulating the total distance to the first bridge segment - for (unsigned point_idx = current_index; point_idx < wall.size(); ++point_idx) + distance_to_bridge_start = computeDistanceToBridgeStart(wall, (wall.size() + start_idx + point_idx * direction - 1) % wall.size(), min_bridge_line_len); + } + + if (first_line) + { + addTravel(p0.p_, always_retract); + first_line = false; + } + + /* + If the line has variable width, break it up into pieces with the + following constraints: + - Each piece must be smaller than the Maximum Resolution setting. + - The difference between the trapezoidal shape of the line and the + rectangular shape of the line may not exceed the Maximum Extrusion + Area Deviation setting, unless required by the first constraint. + Since breaking up a line segment into N pieces (each with averaged + width) divides the area deviation by N, we can simply check how many + pieces we'd want to get low enough deviation, then check if each piece + is not too short at the end. + */ + const coord_t delta_line_width = p1.w_ - p0.w_; + const Point2LL line_vector = p1.p_ - p0.p_; + const coord_t line_length = vSize(line_vector); + /* + Calculate how much the line would deviate from the trapezoidal shape if printed at average width. + This formula is: + - Half the length times half the delta width, for the rectangular shape of the deviating side. + - Half of that because the ideal line width is trapezoidal, making the deviating part triangular. + - Double of that because the deviation occurs on both sides of the idealised line width. + This results in delta_line_width / 2 * line_length / 2 / 2 * 2 == delta_line_width * line_length / 4. + */ + const coord_t line_area_deviation = std::abs(delta_line_width) * line_length / 4; + const size_t pieces_limit_deviation = round_up_divide(line_area_deviation, max_area_deviation); // How many pieces we'd need to stay beneath the max area deviation. + const size_t pieces_limit_resolution = line_length / max_resolution; // Round down this time, to not exceed the maximum resolution. + const size_t pieces = std::max(size_t(1), std::min(pieces_limit_deviation, pieces_limit_resolution)); // Resolution overrides deviation, if resolution is a constraint. + const coord_t piece_length = round_divide(line_length, pieces); + + for (size_t piece = 0; piece < pieces; ++piece) + { + const double average_progress = (double(piece) + 0.5) / pieces; // How far along this line to sample the line width in the middle of this piece. + // Round the line_width value to overcome floating point rounding issues, otherwise we may end up with slightly different values + // and the generated GCodePath objects will not be merged together, which some subsequent algorithms rely on (e.g. coasting) + const coord_t line_width = std::lrint(static_cast(p0.w_) + average_progress * static_cast(delta_line_width)); + const Point2LL destination = p0.p_ + normal(line_vector, piece_length * (piece + 1)); + if (is_small_feature && ! is_scarf_closure) + { + constexpr bool spiralize = false; + addExtrusionMove( + destination, + default_config, + SpaceFillType::Polygons, + flow_ratio, + line_width * nominal_line_width_multiplier, + spiralize, + small_feature_speed_factor); + } + else { - const ExtrusionJunction& p0 = wall[point_idx]; - const ExtrusionJunction& p1 = wall[(point_idx + 1) % wall.size()]; + coord_t piece_remaining_distance = piece_length; - if (PolygonUtils::polygonCollidesWithLineSegment(bridge_wall_mask_, p0.p_, p1.p_)) + // Cut piece into smaller parts for scarf seam + while ((scarf_remaining_distance > 0 || accelerate_remaining_distance > 0) && piece_remaining_distance > 0) { - // the line crosses the boundary between supported and non-supported regions so it will contain one or more bridge segments - - // determine which segments of the line are bridges + std::vector split_distances{ piece_remaining_distance }; + if (scarf_remaining_distance > 0) + { + split_distances.push_back(scarf_remaining_distance); + split_distances.push_back(scarf_split_distance); + } + if (! is_scarf_closure && accelerate_remaining_distance > 0) + { + split_distances.push_back(accelerate_remaining_distance); + split_distances.push_back(accelerate_split_distance); + } - OpenLinesSet line_polys; - line_polys.addSegment(p0.p_, p1.p_); - constexpr bool restitch = false; // only a single line doesn't need stitching - line_polys = bridge_wall_mask_.intersection(line_polys, restitch); + coord_t length_to_process = *std::min_element(split_distances.begin(), split_distances.end()); + Point3LL split_destination = split_origin + normal(line_vector, length_to_process); - while (line_polys.size() > 0) + double scarf_segment_flow_ratio = 1.0; + double scarf_factor_destination = 1.0; + if (scarf_remaining_distance > 0) { - // find the bridge line segment that's nearest to p0 - size_t nearest = 0; - double smallest_dist2 = vSize2f(p0.p_ - line_polys[0][0]); - for (unsigned i = 1; i < line_polys.size(); ++i) + scarf_factor_destination = 1.0 - (static_cast(scarf_remaining_distance - length_to_process) / static_cast(scarf_seam_length)); + if (! is_scarf_closure) { - double dist2 = vSize2f(p0.p_ - line_polys[i][0]); - if (dist2 < smallest_dist2) - { - nearest = i; - smallest_dist2 = dist2; - } + split_destination.z_ = std::llrint(std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination)); } - const OpenPolyline& bridge = line_polys[nearest]; - // set b0 to the nearest vertex and b1 the furthest - Point2LL b0 = bridge[0]; - Point2LL b1 = bridge[1]; - - if (vSize2f(p0.p_ - b1) < vSize2f(p0.p_ - b0)) + const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; + if (is_scarf_closure) { - // swap vertex order - b0 = bridge[1]; - b1 = bridge[0]; + scarf_segment_flow_ratio = std::lerp(1.0, scarf_seam_start_ratio, scarf_factor_average); + } + else + { + scarf_segment_flow_ratio = std::lerp(scarf_seam_start_ratio, 1.0, scarf_factor_average); } - distance_to_bridge_start += vSize(b0 - p0.p_); - - const double bridge_line_len = vSize(b1 - b0); - - if (bridge_line_len >= min_bridge_line_len) + if (first_split) { - // job done, we have found the first bridge line - return; + // Manually add a Z-only travel move to set the nozzle at the height of the first point + addTravel(p0.p_, always_retract, split_origin.z_); + first_split = false; } + } + + double accelerate_speed_factor = 1.0; + double accelerate_factor_destination = 1.0; + if (accelerate_remaining_distance > 0) + { + accelerate_factor_destination = 1.0 - (static_cast(accelerate_remaining_distance - length_to_process) / static_cast(accelerate_length)); - distance_to_bridge_start += bridge_line_len; + const double accelerate_factor_average = (accelerate_factor_origin + accelerate_factor_destination) / 2.0; - // finished with this segment - line_polys.removeAt(nearest); + accelerate_speed_factor = std::lerp(start_speed_ratio, 1.0, accelerate_factor_average); } + + constexpr bool travel_to_z = false; + addWallLine( + split_origin, + split_destination, + settings, + default_config, + roofing_config, + bridge_config, + flow_ratio * scarf_segment_flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + accelerate_speed_factor, + distance_to_bridge_start, + travel_to_z); + + piece_remaining_distance -= length_to_process; + split_origin = split_destination; + + scarf_remaining_distance -= length_to_process; + scarf_factor_origin = scarf_factor_destination; + + accelerate_remaining_distance -= length_to_process; + accelerate_factor_origin = accelerate_factor_destination; } - else if (! bridge_wall_mask_.inside(p0.p_, true)) + + if (piece_remaining_distance > 0 && ! is_scarf_closure) { - // none of the line is over air - distance_to_bridge_start += vSize(p1.p_ - p0.p_); + const Point2LL origin = p0.p_ + normal(line_vector, piece_length * (piece + 1) - piece_remaining_distance); + addWallLine( + origin, + destination, + settings, + default_config, + roofing_config, + bridge_config, + flow_ratio, + line_width * nominal_line_width_multiplier, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start); } } - - // we have got all the way to the end of the wall without finding a bridge segment so disable coasting by setting distance_to_bridge_start back to 0 - - distance_to_bridge_start = 0; } - }; - bool first_line = true; - bool first_scarf = true; - const coord_t small_feature_max_length = settings.get("small_feature_max_length"); - const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); - Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); - const Velocity min_speed = fan_speed_layer_time_settings_per_extruder_[getLastPlannedExtruderTrain()->extruder_nr_].cool_min_speed; - small_feature_speed_factor = std::max(static_cast(small_feature_speed_factor), static_cast(min_speed / default_config.getSpeed())); - const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! - const auto max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); - const auto scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); - const auto scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; - const auto scarf_split_distance = settings.get("scarf_split_distance"); - const coord_t scarf_max_z_offset = static_cast(-(1.0 - scarf_seam_start_ratio) * static_cast(layer_thickness_)); - const Ratio start_speed_ratio = smooth_speed ? settings.get("wall_0_start_speed_ratio") : 1.0_r; - const int acceleration = settings.get("wall_0_acceleration"); // mm/s² - const Velocity top_speed = default_config.getSpeed(); - const double start_speed = top_speed * start_speed_ratio; // mm/s - const coord_t accelerate_split_distance = settings.get("wall_0_speed_split_distance"); // mm - const coord_t accelerate_length = (smooth_speed && start_speed_ratio < 1.0) ? MM2INT((std::pow(top_speed.value, 2) - std::pow(start_speed, 2)) / (2.0 * acceleration)) : 0; - - ExtrusionJunction p0 = wall[start_idx]; + p0 = p1; + } +} - const int direction = is_reversed ? -1 : 1; - const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); +coord_t LayerPlan::computeDistanceToBridgeStart(const ExtrusionLine& wall, const size_t current_index, const coord_t min_bridge_line_len) const +{ + coord_t distance_to_bridge_start = 0; - auto addScarfedWall = [&](const bool is_scarf_closure) + if (! bridge_wall_mask_.empty()) { - coord_t scarf_remaining_distance = scarf_seam_length; - double scarf_factor_origin = 0.0; - - Point3LL split_origin = p0.p_; - if (! is_scarf_closure) - { - split_origin.z_ = scarf_max_z_offset; - } - - coord_t accelerate_remaining_distance = accelerate_length; - double accelerate_factor_origin = 0.0; - - for (size_t point_idx = 1; point_idx < max_index; point_idx++) + // there is air below the part so iterate through the lines that have not yet been output accumulating the total distance to the first bridge segment + for (unsigned point_idx = current_index; point_idx < wall.size(); ++point_idx) { - const ExtrusionJunction& p1 = wall[(wall.size() + start_idx + point_idx * direction) % wall.size()]; + const ExtrusionJunction& p0 = wall[point_idx]; + const ExtrusionJunction& p1 = wall[(point_idx + 1) % wall.size()]; - if (! bridge_wall_mask_.empty()) + if (PolygonUtils::polygonCollidesWithLineSegment(bridge_wall_mask_, p0.p_, p1.p_)) { - computeDistanceToBridgeStart((wall.size() + start_idx + point_idx * direction - 1) % wall.size()); - } + // the line crosses the boundary between supported and non-supported regions so it will contain one or more bridge segments - if (first_line) - { - addTravel(p0.p_, always_retract); - first_line = false; - } + // determine which segments of the line are bridges - /* - If the line has variable width, break it up into pieces with the - following constraints: - - Each piece must be smaller than the Maximum Resolution setting. - - The difference between the trapezoidal shape of the line and the - rectangular shape of the line may not exceed the Maximum Extrusion - Area Deviation setting, unless required by the first constraint. - Since breaking up a line segment into N pieces (each with averaged - width) divides the area deviation by N, we can simply check how many - pieces we'd want to get low enough deviation, then check if each piece - is not too short at the end. - */ - const coord_t delta_line_width = p1.w_ - p0.w_; - const Point2LL line_vector = p1.p_ - p0.p_; - const coord_t line_length = vSize(line_vector); - /* - Calculate how much the line would deviate from the trapezoidal shape if printed at average width. - This formula is: - - Half the length times half the delta width, for the rectangular shape of the deviating side. - - Half of that because the ideal line width is trapezoidal, making the deviating part triangular. - - Double of that because the deviation occurs on both sides of the idealised line width. - This results in delta_line_width / 2 * line_length / 2 / 2 * 2 == delta_line_width * line_length / 4. - */ - const coord_t line_area_deviation = std::abs(delta_line_width) * line_length / 4; - const size_t pieces_limit_deviation = round_up_divide(line_area_deviation, max_area_deviation); // How many pieces we'd need to stay beneath the max area deviation. - const size_t pieces_limit_resolution = line_length / max_resolution; // Round down this time, to not exceed the maximum resolution. - const size_t pieces = std::max(size_t(1), std::min(pieces_limit_deviation, pieces_limit_resolution)); // Resolution overrides deviation, if resolution is a constraint. - const coord_t piece_length = round_divide(line_length, pieces); - - for (size_t piece = 0; piece < pieces; ++piece) - { - const double average_progress = (double(piece) + 0.5) / pieces; // How far along this line to sample the line width in the middle of this piece. - // Round the line_width value to overcome floating point rounding issues, otherwise we may end up with slightly different values - // and the generated GCodePath objects will not be merged together, which some subsequent algorithms rely on (e.g. coasting) - const coord_t line_width = std::lrint(static_cast(p0.w_) + average_progress * static_cast(delta_line_width)); - const Point2LL destination = p0.p_ + normal(line_vector, piece_length * (piece + 1)); - if (is_small_feature && ! is_scarf_closure) - { - constexpr bool spiralize = false; - addExtrusionMove( - destination, - default_config, - SpaceFillType::Polygons, - flow_ratio, - line_width * nominal_line_width_multiplier, - spiralize, - small_feature_speed_factor); - } - else - { - coord_t piece_remaining_distance = piece_length; + OpenLinesSet line_polys; + line_polys.addSegment(p0.p_, p1.p_); + constexpr bool restitch = false; // only a single line doesn't need stitching + line_polys = bridge_wall_mask_.intersection(line_polys, restitch); - // Cut piece into smaller parts for scarf seam - while ((scarf_remaining_distance > 0 || accelerate_remaining_distance > 0) && piece_remaining_distance > 0) + while (line_polys.size() > 0) + { + // find the bridge line segment that's nearest to p0 + size_t nearest = 0; + double smallest_dist2 = vSize2f(p0.p_ - line_polys[0][0]); + for (unsigned i = 1; i < line_polys.size(); ++i) { - std::vector split_distances{ piece_remaining_distance }; - if (scarf_remaining_distance > 0) - { - split_distances.push_back(scarf_remaining_distance); - split_distances.push_back(scarf_split_distance); - } - if (! is_scarf_closure && accelerate_remaining_distance > 0) + double dist2 = vSize2f(p0.p_ - line_polys[i][0]); + if (dist2 < smallest_dist2) { - split_distances.push_back(accelerate_remaining_distance); - split_distances.push_back(accelerate_split_distance); + nearest = i; + smallest_dist2 = dist2; } + } + const OpenPolyline& bridge = line_polys[nearest]; - coord_t length_to_process = *std::min_element(split_distances.begin(), split_distances.end()); - Point3LL split_destination = split_origin + normal(line_vector, length_to_process); + // set b0 to the nearest vertex and b1 the furthest + Point2LL b0 = bridge[0]; + Point2LL b1 = bridge[1]; - double scarf_segment_flow_ratio = 1.0; - double scarf_factor_destination = 1.0; - if (scarf_remaining_distance > 0) - { - scarf_factor_destination = 1.0 - (static_cast(scarf_remaining_distance - length_to_process) / static_cast(scarf_seam_length)); - if (! is_scarf_closure) - { - split_destination.z_ = std::llrint(std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination)); - } + if (vSize2f(p0.p_ - b1) < vSize2f(p0.p_ - b0)) + { + // swap vertex order + b0 = bridge[1]; + b1 = bridge[0]; + } - const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; - double scarf_segment_flow_ratio; - if (is_scarf_closure) - { - scarf_segment_flow_ratio = std::lerp(1.0, scarf_seam_start_ratio, scarf_factor_average); - } - else - { - scarf_segment_flow_ratio = std::lerp(scarf_seam_start_ratio, 1.0, scarf_factor_average); - } + distance_to_bridge_start += vSize(b0 - p0.p_); - if (first_scarf) - { - // Manually add a Z-only travel move to set the nozzle at the height of the first point - addTravel(p0.p_, always_retract, split_origin.z_); - first_scarf = false; - } - } + const double bridge_line_len = vSize(b1 - b0); - double acceleration_speed_factor = 1.0; - double acceleration_factor_destination = 1.0; - if (accelerate_remaining_distance > 0) - { - acceleration_factor_destination - = 1.0 - (static_cast(accelerate_remaining_distance - length_to_process) / static_cast(accelerate_length)); + if (bridge_line_len >= min_bridge_line_len) + { + // job done, we have found the first bridge line + return distance_to_bridge_start; + } - const double acceleration_factor_average = (accelerate_factor_origin + acceleration_factor_destination) / 2.0; + distance_to_bridge_start += bridge_line_len; - acceleration_speed_factor = std::lerp(start_speed_ratio, 1.0, acceleration_factor_average); - } + // finished with this segment + line_polys.removeAt(nearest); + } + } + else if (! bridge_wall_mask_.inside(p0.p_, true)) + { + // none of the line is over air + distance_to_bridge_start += vSize(p1.p_ - p0.p_); + } + } - constexpr bool travel_to_z = false; - addWallLine( - split_origin, - split_destination, - settings, - default_config, - roofing_config, - bridge_config, - flow_ratio * scarf_segment_flow_ratio, - line_width * nominal_line_width_multiplier, - non_bridge_line_volume, - speed_factor * acceleration_speed_factor, - distance_to_bridge_start, - travel_to_z); + // we have got all the way to the end of the wall without finding a bridge segment so disable coasting by setting distance_to_bridge_start back to 0 - piece_remaining_distance -= length_to_process; - split_origin = split_destination; + distance_to_bridge_start = 0; + } - scarf_remaining_distance -= length_to_process; - scarf_factor_origin = scarf_factor_destination; + return distance_to_bridge_start; +} - accelerate_remaining_distance -= length_to_process; - accelerate_factor_origin = acceleration_factor_destination; - } +void LayerPlan::addWall( + const ExtrusionLine& wall, + size_t start_idx, + const Settings& settings, + const GCodePathConfig& default_config, + const GCodePathConfig& roofing_config, + const GCodePathConfig& bridge_config, + coord_t wall_0_wipe_dist, + const double flow_ratio, + bool always_retract, + const bool is_closed, + const bool is_reversed, + const bool is_linked_path, + const bool scarf_seam, + const bool smooth_speed) +{ + if (wall.empty()) + { + return; + } + if (is_closed) + { + // make sure wall start point is not above air! + start_idx = locateFirstSupportedVertex(wall, start_idx); + } + const bool actual_scarf_seam = scarf_seam && is_closed; - if (piece_remaining_distance > 0 && ! is_scarf_closure) - { - const Point2LL origin = p0.p_ + normal(line_vector, piece_length * (piece + 1) - piece_remaining_distance); - addWallLine( - origin, - destination, - settings, - default_config, - roofing_config, - bridge_config, - flow_ratio, - line_width * nominal_line_width_multiplier, - non_bridge_line_volume, - speed_factor, - distance_to_bridge_start); - } - } - } + double non_bridge_line_volume = max_non_bridge_line_volume; // assume extruder is fully pressurised before first non-bridge line is output - p0 = p1; - } + const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); + + const Ratio nominal_line_width_multiplier{ + 1.0 / Ratio{ static_cast(default_config.getLineWidth()) } + }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + + const coord_t small_feature_max_length = settings.get("small_feature_max_length"); + const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); + const Velocity min_speed = fan_speed_layer_time_settings_per_extruder_[getLastPlannedExtruderTrain()->extruder_nr_].cool_min_speed; + Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); + small_feature_speed_factor = std::max(static_cast(small_feature_speed_factor), static_cast(min_speed / default_config.getSpeed())); + const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! + const auto max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); + const auto scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); + const auto scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; + const auto scarf_split_distance = settings.get("scarf_split_distance"); + const coord_t scarf_max_z_offset = static_cast(-(1.0 - scarf_seam_start_ratio) * static_cast(layer_thickness_)); + const Ratio start_speed_ratio = smooth_speed ? settings.get("wall_0_start_speed_ratio") : 1.0_r; + const int acceleration = settings.get("wall_0_acceleration"); // mm/s² + const Velocity top_speed = default_config.getSpeed(); + const double start_speed = top_speed * start_speed_ratio; // mm/s + const coord_t accelerate_split_distance = settings.get("wall_0_speed_split_distance"); // mm + const coord_t accelerate_length = (smooth_speed && start_speed_ratio < 1.0) ? MM2INT((square(top_speed.value) - square(start_speed)) / (2.0 * acceleration)) : 0; + const int direction = is_reversed ? -1 : 1; + const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); + + auto addWallSplittedPass = [&](bool is_scarf_closure) + { + addWallSplitted( + wall, + start_idx, + direction, + max_index, + settings, + default_config, + roofing_config, + bridge_config, + flow_ratio, + nominal_line_width_multiplier, + non_bridge_line_volume, + min_bridge_line_len, + always_retract, + is_small_feature, + small_feature_speed_factor, + max_area_deviation, + max_resolution, + scarf_seam_length, + scarf_seam_start_ratio, + scarf_split_distance, + scarf_max_z_offset, + start_speed_ratio, + accelerate_split_distance, + accelerate_length, + is_scarf_closure); }; - // First pass to add the wall with the scarf beginning - addScarfedWall(false); + // First pass to add the wall with the scarf beginning and acceleration + addWallSplittedPass(false); if (scarf_seam_length) { // Second pass to add the scarf closure - addScarfedWall(true); + addWallSplittedPass(true); } if (wall.size() >= 2) { if (! bridge_wall_mask_.empty()) { - computeDistanceToBridgeStart((start_idx + wall.size() - 1) % wall.size()); + computeDistanceToBridgeStart(wall, (start_idx + wall.size() - 1) % wall.size(), min_bridge_line_len); } if (wall_0_wipe_dist > 0 && ! is_linked_path) { // apply outer wall wipe - p0 = wall[start_idx]; + ExtrusionJunction p0 = wall[start_idx]; int distance_traversed = 0; for (unsigned int point_idx = 1;; point_idx++) { From cf3ece99dd31265aea85dfa51c9380d65c9850cc Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 12:02:45 +0200 Subject: [PATCH 15/32] Apply deceleration when ending an outer wall print CURA-12080 --- include/LayerPlan.h | 5 +- src/LayerPlan.cpp | 133 +++++++++++++++++++++++++------------------- 2 files changed, 80 insertions(+), 58 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 20b3023e82..2782f485be 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -839,6 +839,7 @@ class LayerPlan : public NoCopy void addWallSplitted( const ExtrusionLine& wall, + const coord_t wall_length, size_t start_idx, const int direction, const size_t max_index, @@ -859,9 +860,11 @@ class LayerPlan : public NoCopy const auto scarf_seam_start_ratio, const auto scarf_split_distance, const coord_t scarf_max_z_offset, + const coord_t speed_split_distance, const Ratio start_speed_ratio, - const coord_t accelerate_split_distance, const coord_t accelerate_length, + const Ratio end_speed_ratio, + const coord_t decelerate_length, const bool is_scarf_closure); // helper function to calculate the distance from the start of the current wall line to the first bridge segment diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 82f5361157..302152f13f 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -999,6 +999,7 @@ void LayerPlan::addWall( void LayerPlan::addWallSplitted( const ExtrusionLine& wall, + const coord_t wall_length, size_t start_idx, const int direction, const size_t max_index, @@ -1019,13 +1020,13 @@ void LayerPlan::addWallSplitted( const auto scarf_seam_start_ratio, const auto scarf_split_distance, const coord_t scarf_max_z_offset, + const coord_t speed_split_distance, const Ratio start_speed_ratio, - const coord_t accelerate_split_distance, const coord_t accelerate_length, + const Ratio end_speed_ratio, + const coord_t decelerate_length, const bool is_scarf_closure) { - constexpr double speed_factor = 1.0; - coord_t distance_to_bridge_start = 0; // will be updated before each line is processed ExtrusionJunction p0 = wall[start_idx]; bool first_line = ! is_scarf_closure; @@ -1036,11 +1037,11 @@ void LayerPlan::addWallSplitted( split_origin.z_ = scarf_max_z_offset; } - coord_t scarf_remaining_distance = scarf_seam_length; + coord_t wall_processed_distance = 0; double scarf_factor_origin = 0.0; - - coord_t accelerate_remaining_distance = accelerate_length; double accelerate_factor_origin = 0.0; + double decelerate_factor_origin = 0.0; + const coord_t start_decelerating_position = wall_length - decelerate_length; for (size_t point_idx = 1; point_idx < max_index; point_idx++) { @@ -1109,29 +1110,48 @@ void LayerPlan::addWallSplitted( { coord_t piece_remaining_distance = piece_length; - // Cut piece into smaller parts for scarf seam - while ((scarf_remaining_distance > 0 || accelerate_remaining_distance > 0) && piece_remaining_distance > 0) + // Cut piece into smaller parts for scarf seam and acceleration/deceleration + while ((! is_scarf_closure && piece_remaining_distance > 0) || (is_scarf_closure && wall_processed_distance < scarf_seam_length)) { - std::vector split_distances{ piece_remaining_distance }; - if (scarf_remaining_distance > 0) + std::vector split_positions{ wall_processed_distance + piece_remaining_distance }; + + const bool process_scarf = wall_processed_distance < scarf_seam_length; + if (process_scarf) + { + split_positions.push_back(scarf_seam_length); + split_positions.push_back(wall_processed_distance + scarf_split_distance); + } + + const bool process_acceleration = ! is_scarf_closure && wall_processed_distance < accelerate_length; + if (process_acceleration) { - split_distances.push_back(scarf_remaining_distance); - split_distances.push_back(scarf_split_distance); + split_positions.push_back(accelerate_length); + split_positions.push_back(wall_processed_distance + speed_split_distance); } - if (! is_scarf_closure && accelerate_remaining_distance > 0) + + bool deceleration_started = false; + if (! is_scarf_closure && decelerate_length > 0) { - split_distances.push_back(accelerate_remaining_distance); - split_distances.push_back(accelerate_split_distance); + deceleration_started = wall_processed_distance >= start_decelerating_position; + if (deceleration_started) + { + split_positions.push_back(wall_processed_distance + speed_split_distance); + } + else + { + split_positions.push_back(start_decelerating_position); + } } - coord_t length_to_process = *std::min_element(split_distances.begin(), split_distances.end()); + const coord_t destination_position = *std::min_element(split_positions.begin(), split_positions.end()); + const coord_t length_to_process = destination_position - wall_processed_distance; Point3LL split_destination = split_origin + normal(line_vector, length_to_process); double scarf_segment_flow_ratio = 1.0; double scarf_factor_destination = 1.0; - if (scarf_remaining_distance > 0) + if (process_scarf) { - scarf_factor_destination = 1.0 - (static_cast(scarf_remaining_distance - length_to_process) / static_cast(scarf_seam_length)); + scarf_factor_destination = static_cast(destination_position) / static_cast(scarf_seam_length); if (! is_scarf_closure) { split_destination.z_ = std::llrint(std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination)); @@ -1155,17 +1175,24 @@ void LayerPlan::addWallSplitted( } } - double accelerate_speed_factor = 1.0; + Ratio accelerate_speed_factor = 1.0_r; double accelerate_factor_destination = 1.0; - if (accelerate_remaining_distance > 0) + if (process_acceleration) { - accelerate_factor_destination = 1.0 - (static_cast(accelerate_remaining_distance - length_to_process) / static_cast(accelerate_length)); - + accelerate_factor_destination = static_cast(destination_position) / static_cast(accelerate_length); const double accelerate_factor_average = (accelerate_factor_origin + accelerate_factor_destination) / 2.0; - accelerate_speed_factor = std::lerp(start_speed_ratio, 1.0, accelerate_factor_average); } + Ratio decelerate_speed_factor = is_scarf_closure ? end_speed_ratio : 1.0_r; + double decelerate_factor_destination = 0.0; + if (deceleration_started) + { + decelerate_factor_destination = 1.0 - (static_cast(wall_length - destination_position) / static_cast(decelerate_length)); + const double decelerate_factor_average = (decelerate_factor_origin + decelerate_factor_destination) / 2.0; + decelerate_speed_factor = std::lerp(1.0, end_speed_ratio, decelerate_factor_average); + } + constexpr bool travel_to_z = false; addWallLine( split_origin, @@ -1177,35 +1204,16 @@ void LayerPlan::addWallSplitted( flow_ratio * scarf_segment_flow_ratio, line_width * nominal_line_width_multiplier, non_bridge_line_volume, - accelerate_speed_factor, + accelerate_speed_factor * decelerate_speed_factor, distance_to_bridge_start, travel_to_z); + wall_processed_distance = destination_position; piece_remaining_distance -= length_to_process; split_origin = split_destination; - - scarf_remaining_distance -= length_to_process; scarf_factor_origin = scarf_factor_destination; - - accelerate_remaining_distance -= length_to_process; accelerate_factor_origin = accelerate_factor_destination; - } - - if (piece_remaining_distance > 0 && ! is_scarf_closure) - { - const Point2LL origin = p0.p_ + normal(line_vector, piece_length * (piece + 1) - piece_remaining_distance); - addWallLine( - origin, - destination, - settings, - default_config, - roofing_config, - bridge_config, - flow_ratio, - line_width * nominal_line_width_multiplier, - non_bridge_line_volume, - speed_factor, - distance_to_bridge_start); + decelerate_factor_origin = decelerate_factor_destination; } } } @@ -1221,7 +1229,7 @@ coord_t LayerPlan::computeDistanceToBridgeStart(const ExtrusionLine& wall, const if (! bridge_wall_mask_.empty()) { // there is air below the part so iterate through the lines that have not yet been output accumulating the total distance to the first bridge segment - for (unsigned point_idx = current_index; point_idx < wall.size(); ++point_idx) + for (size_t point_idx = current_index; point_idx < wall.size(); ++point_idx) { const ExtrusionJunction& p0 = wall[point_idx]; const ExtrusionJunction& p1 = wall[(point_idx + 1) % wall.size()]; @@ -1330,6 +1338,7 @@ void LayerPlan::addWall( 1.0 / Ratio{ static_cast(default_config.getLineWidth()) } }; // we multiply the flow with the actual wanted line width (for that junction), and then multiply with this + const coord_t wall_length = wall.length(); const coord_t small_feature_max_length = settings.get("small_feature_max_length"); const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); const Velocity min_speed = fan_speed_layer_time_settings_per_extruder_[getLastPlannedExtruderTrain()->extruder_nr_].cool_min_speed; @@ -1337,23 +1346,31 @@ void LayerPlan::addWall( small_feature_speed_factor = std::max(static_cast(small_feature_speed_factor), static_cast(min_speed / default_config.getSpeed())); const coord_t max_area_deviation = std::max(settings.get("meshfix_maximum_extrusion_area_deviation"), 1); // Square micrometres! const auto max_resolution = std::max(settings.get("meshfix_maximum_resolution"), coord_t(1)); - const auto scarf_seam_length = std::min(wall.length(), actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); + const int direction = is_reversed ? -1 : 1; + const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); + + const auto scarf_seam_length = std::min(wall_length, actual_scarf_seam ? settings.get("scarf_joint_seam_length") : 0); const auto scarf_seam_start_ratio = actual_scarf_seam ? settings.get("scarf_joint_seam_start_height_ratio") : 1.0_r; const auto scarf_split_distance = settings.get("scarf_split_distance"); const coord_t scarf_max_z_offset = static_cast(-(1.0 - scarf_seam_start_ratio) * static_cast(layer_thickness_)); + + const Velocity top_speed = default_config.getSpeed(); + const coord_t speed_split_distance = settings.get("wall_0_speed_split_distance"); // mm const Ratio start_speed_ratio = smooth_speed ? settings.get("wall_0_start_speed_ratio") : 1.0_r; const int acceleration = settings.get("wall_0_acceleration"); // mm/s² - const Velocity top_speed = default_config.getSpeed(); - const double start_speed = top_speed * start_speed_ratio; // mm/s - const coord_t accelerate_split_distance = settings.get("wall_0_speed_split_distance"); // mm - const coord_t accelerate_length = (smooth_speed && start_speed_ratio < 1.0) ? MM2INT((square(top_speed.value) - square(start_speed)) / (2.0 * acceleration)) : 0; - const int direction = is_reversed ? -1 : 1; - const size_t max_index = is_closed ? wall.size() + 1 : wall.size(); + const Velocity start_speed = top_speed * start_speed_ratio; // mm/s + const coord_t accelerate_length = (smooth_speed && start_speed_ratio < 1.0) ? MM2INT((square(top_speed) - square(start_speed)) / (2.0 * acceleration)) : 0; // µm + + const Ratio end_speed_ratio = smooth_speed ? settings.get("wall_0_end_speed_ratio") : 1.0_r; + const int deceleration = settings.get("wall_0_deceleration"); // mm/s² + const Velocity end_speed = top_speed * end_speed_ratio; // mm/s + const coord_t decelerate_length = (smooth_speed && end_speed_ratio < 1.0) ? MM2INT((square(top_speed) - square(end_speed)) / (2.0 * deceleration)) : 0; // µm auto addWallSplittedPass = [&](bool is_scarf_closure) { addWallSplitted( wall, + wall_length, start_idx, direction, max_index, @@ -1374,16 +1391,18 @@ void LayerPlan::addWall( scarf_seam_start_ratio, scarf_split_distance, scarf_max_z_offset, + speed_split_distance, start_speed_ratio, - accelerate_split_distance, accelerate_length, + end_speed_ratio, + decelerate_length, is_scarf_closure); }; // First pass to add the wall with the scarf beginning and acceleration addWallSplittedPass(false); - if (scarf_seam_length) + if (scarf_seam_length > 0) { // Second pass to add the scarf closure addWallSplittedPass(true); @@ -1399,7 +1418,7 @@ void LayerPlan::addWall( if (wall_0_wipe_dist > 0 && ! is_linked_path) { // apply outer wall wipe ExtrusionJunction p0 = wall[start_idx]; - int distance_traversed = 0; + coord_t distance_traversed = 0; for (unsigned int point_idx = 1;; point_idx++) { if (point_idx > wall.size() && distance_traversed == 0) // Wall has a total circumference of 0. This loop would never end. @@ -1407,7 +1426,7 @@ void LayerPlan::addWall( break; // No wipe if the wall has no circumference. } ExtrusionJunction p1 = wall[(start_idx + point_idx) % wall.size()]; - int p0p1_dist = vSize(p1 - p0); + coord_t p0p1_dist = vSize(p1 - p0); if (distance_traversed + p0p1_dist >= wall_0_wipe_dist) { Point2LL vector = p1.p_ - p0.p_; From 6fc32c1466ebc3dbe1ce251de107c66d9f51aa6e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 12:49:49 +0200 Subject: [PATCH 16/32] Add code documentation CURA-12080 --- include/LayerPlan.h | 66 +++++++++++++++++++++++++++++++++++++++++++-- src/LayerPlan.cpp | 48 ++++++++++++++++++--------------- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 2782f485be..00e6126660 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -824,10 +824,34 @@ class LayerPlan : public NoCopy const Ratio flow_ratio, const double fan_speed); + /*! + * @brief Send a GCodePath line to the communication object, applying proper Z offsets + * @param path The path to be sent + * @param position The start position (which is not included in the path points) + * @param extrude_speed The actual used extrusion speed + */ void sendLineTo(const GCodePath& path, const Point3LL& position, const double extrude_speed); + /*! + * @brief Write a travel move and properly apply the various Z offsets + * @param gcode The actual GCode exporter + * @param position The position to move to. The Z coordinate is an offset to the current layer position + * @param speed The actual used speed + * @param path_z_offset The global path Z offset to be applied + * @note This function is to be used when dealing with 3D coordinates. If you have 2D coordinates, just call gcode.writeTravel() + */ void writeTravelRelativeZ(GCodeExport& gcode, const Point3LL& position, const Velocity& speed, const coord_t path_z_offset); + /*! + * \brief Write an extrusion move and properly apply the various Z offsets + * \param gcode The actual GCode exporter + * \param position The position to move to. The Z coordinate is an offset to the current layer position + * \param speed The actual used speed + * \param path_z_offset The global path Z offset to be applied + * \param extrusion_mm3_per_mm The desired flow rate + * \param feature The current feature being printed + * \param update_extrusion_offset whether to update the extrusion offset to match the current flow rate + */ void writeExtrusionRelativeZ( GCodeExport& gcode, const Point3LL& position, @@ -837,6 +861,38 @@ class LayerPlan : public NoCopy PrintFeatureType feature, bool update_extrusion_offset = false); + /*! + * \brief Add a wall to the gcode with optimized order + * \param wall The full wall to be added + * \param wall_length The pre-calculated full wall length + * \param start_idx The index of the point where to start printing the wall + * \param direction The direction along which to print the wall, which should be 1 or -1 + * \param max_index The last index to be used when iterating over the wall segments + * \param settings The settings which should apply to this wall added to the layer plan + * \param default_config The config with which to print the wall lines that are not spanning a bridge or are exposed to air + * \param roofing_config The config with which to print the wall lines that are exposed to air + * \param bridge_config The config with which to print the wall lines that are spanning a bridge + * \param flow_ratio The ratio with which to multiply the extrusion amount + * \param line_width_ratio The line width ratio to be applied + * \param non_bridge_line_volume A pseudo-volume that is derived from the print speed and flow of the non-bridge lines that have preceded this lin + * \param min_bridge_line_len The minimum line width to allow an extrusion move to be processed as a bridge move + * \param always_retract Whether to force a retraction when moving to the start of the polygon (used for outer walls) + * \param is_small_feature Indicates whether the wall is so small that it should be processed differently + * \param small_feature_speed_factor The speed factor to be applied to small feature walls + * \param max_area_deviation The maximum allowed area deviation to split a segment into pieces + * \param max_resolution The maximum resolution to split a segment into pieces + * \param scarf_seam_length The length of the scarf joint seam, which may be 0 if there is none + * \param scarf_seam_start_ratio The ratio of the line thickness to start the scarf seam with + * \param scarf_split_distance The maximum length of a segment to apply the scarf seam gradient, longer segments will be splitted + * \param scarf_max_z_offset The maximum Z offset te be applied at the lowest position of the scarf seam + * \param speed_split_distance The maximum length of a segment to apply the acceleration/deceleration gradient, longer segments will be splitted + * \param start_speed_ratio The ratio of the top speed to be applied when starting the segment, then accelerate gradually to full speed + * \param accelerate_length The pre-calculated length of the acceleration phase + * \param end_speed_ratio The ratio of the top speed to be applied when finishing a segment + * \param decelerate_length The pre-calculated length of the deceleration phase + * \param is_scarf_closure Indicates whether this function is called to make the scarf closure (overlap over the first scarf pass) or + * the normal first pass of the wall + */ void addWallSplitted( const ExtrusionLine& wall, const coord_t wall_length, @@ -848,7 +904,7 @@ class LayerPlan : public NoCopy const GCodePathConfig& roofing_config, const GCodePathConfig& bridge_config, const double flow_ratio, - const Ratio nominal_line_width_multiplier, + const Ratio line_width_ratio, double& non_bridge_line_volume, const coord_t min_bridge_line_len, const bool always_retract, @@ -867,7 +923,13 @@ class LayerPlan : public NoCopy const coord_t decelerate_length, const bool is_scarf_closure); - // helper function to calculate the distance from the start of the current wall line to the first bridge segment + /*! + * \brief Helper function to calculate the distance from the start of the current wall line to the first bridge segment + * \param wall The currently processed wall + * \param current_index The index of the currently processed point + * \param min_bridge_line_len The minimum line width to allow an extrusion move to be processed as a bridge move + * \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; }; diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 302152f13f..646cb3f0fa 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1008,7 +1008,7 @@ void LayerPlan::addWallSplitted( const GCodePathConfig& roofing_config, const GCodePathConfig& bridge_config, const double flow_ratio, - const Ratio nominal_line_width_multiplier, + const Ratio line_width_ratio, double& non_bridge_line_volume, const coord_t min_bridge_line_len, const bool always_retract, @@ -1037,11 +1037,11 @@ void LayerPlan::addWallSplitted( split_origin.z_ = scarf_max_z_offset; } - coord_t wall_processed_distance = 0; - double scarf_factor_origin = 0.0; - double accelerate_factor_origin = 0.0; - double decelerate_factor_origin = 0.0; - const coord_t start_decelerating_position = wall_length - decelerate_length; + coord_t wall_processed_distance = 0; // This will grow while we travel along the wall, to the total wall length + double scarf_factor_origin = 0.0; // Interpolation factor at the current point for the scarf + double accelerate_factor_origin = 0.0; // Interpolation factor at the current point for the acceleration + double decelerate_factor_origin = 0.0; // Interpolation factor at the current point for the deceleration + const coord_t start_decelerate_position = wall_length - decelerate_length; for (size_t point_idx = 1; point_idx < max_index; point_idx++) { @@ -1097,14 +1097,7 @@ void LayerPlan::addWallSplitted( if (is_small_feature && ! is_scarf_closure) { constexpr bool spiralize = false; - addExtrusionMove( - destination, - default_config, - SpaceFillType::Polygons, - flow_ratio, - line_width * nominal_line_width_multiplier, - spiralize, - small_feature_speed_factor); + addExtrusionMove(destination, default_config, SpaceFillType::Polygons, flow_ratio, line_width * line_width_ratio, spiralize, small_feature_speed_factor); } else { @@ -1113,6 +1106,8 @@ void LayerPlan::addWallSplitted( // Cut piece into smaller parts for scarf seam and acceleration/deceleration while ((! is_scarf_closure && piece_remaining_distance > 0) || (is_scarf_closure && wall_processed_distance < scarf_seam_length)) { + // Make a list of all the possible incoming positions where we would eventually want to stop next + // The positions are expressed in distance from wall start along the wall segments std::vector split_positions{ wall_processed_distance + piece_remaining_distance }; const bool process_scarf = wall_processed_distance < scarf_seam_length; @@ -1132,31 +1127,37 @@ void LayerPlan::addWallSplitted( bool deceleration_started = false; if (! is_scarf_closure && decelerate_length > 0) { - deceleration_started = wall_processed_distance >= start_decelerating_position; + deceleration_started = wall_processed_distance >= start_decelerate_position; if (deceleration_started) { split_positions.push_back(wall_processed_distance + speed_split_distance); } else { - split_positions.push_back(start_decelerating_position); + split_positions.push_back(start_decelerate_position); } } + // Now take the closest position candidate and make a sub-segment to it const coord_t destination_position = *std::min_element(split_positions.begin(), split_positions.end()); const coord_t length_to_process = destination_position - wall_processed_distance; Point3LL split_destination = split_origin + normal(line_vector, length_to_process); double scarf_segment_flow_ratio = 1.0; - double scarf_factor_destination = 1.0; + double scarf_factor_destination = 1.0; // Out of range, scarf is done => 1.0 if (process_scarf) { + // Calculate scarf interpolation factor on the destination point scarf_factor_destination = static_cast(destination_position) / static_cast(scarf_seam_length); + + // Interpolate Z offset according to interpolation factor if (! is_scarf_closure) { split_destination.z_ = std::llrint(std::lerp(scarf_max_z_offset, 0.0, scarf_factor_destination)); } + // Interpolate flow according to interpolation factor average, because it can't be different + // at start and end positions const double scarf_factor_average = (scarf_factor_origin + scarf_factor_destination) / 2.0; if (is_scarf_closure) { @@ -1176,23 +1177,28 @@ void LayerPlan::addWallSplitted( } Ratio accelerate_speed_factor = 1.0_r; - double accelerate_factor_destination = 1.0; + double accelerate_factor_destination = 1.0; // Out of range, acceleration is done => 1.0 if (process_acceleration) { + // Interpolate speed according to interpolation factor average, because it can't be different + // at start and end positions accelerate_factor_destination = static_cast(destination_position) / static_cast(accelerate_length); const double accelerate_factor_average = (accelerate_factor_origin + accelerate_factor_destination) / 2.0; accelerate_speed_factor = std::lerp(start_speed_ratio, 1.0, accelerate_factor_average); } Ratio decelerate_speed_factor = is_scarf_closure ? end_speed_ratio : 1.0_r; - double decelerate_factor_destination = 0.0; + double decelerate_factor_destination = 0.0; // Out of range, deceleration is not started => 0.0 if (deceleration_started) { + // Interpolate speed according to interpolation factor average, because it can't be different + // at start and end positions decelerate_factor_destination = 1.0 - (static_cast(wall_length - destination_position) / static_cast(decelerate_length)); const double decelerate_factor_average = (decelerate_factor_origin + decelerate_factor_destination) / 2.0; decelerate_speed_factor = std::lerp(1.0, end_speed_ratio, decelerate_factor_average); } + // now add the (sub-)segment constexpr bool travel_to_z = false; addWallLine( split_origin, @@ -1202,7 +1208,7 @@ void LayerPlan::addWallSplitted( roofing_config, bridge_config, flow_ratio * scarf_segment_flow_ratio, - line_width * nominal_line_width_multiplier, + line_width * line_width_ratio, non_bridge_line_volume, accelerate_speed_factor * decelerate_speed_factor, distance_to_bridge_start, @@ -1340,7 +1346,7 @@ void LayerPlan::addWall( const coord_t wall_length = wall.length(); const coord_t small_feature_max_length = settings.get("small_feature_max_length"); - const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall.shorterThan(small_feature_max_length); + const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || wall.inset_idx_ == 0) && wall_length < small_feature_max_length; const Velocity min_speed = fan_speed_layer_time_settings_per_extruder_[getLastPlannedExtruderTrain()->extruder_nr_].cool_min_speed; Ratio small_feature_speed_factor = settings.get((layer_nr_ == 0) ? "small_feature_speed_factor_0" : "small_feature_speed_factor"); small_feature_speed_factor = std::max(static_cast(small_feature_speed_factor), static_cast(min_speed / default_config.getSpeed())); From e184deb6a98e4958439d46fcdaff93d804538a9f Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 13:59:36 +0200 Subject: [PATCH 17/32] Fix edge cases with small models CURA-12081 --- src/LayerPlan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 646cb3f0fa..eb08b2b234 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1032,7 +1032,7 @@ void LayerPlan::addWallSplitted( bool first_line = ! is_scarf_closure; bool first_split = ! is_scarf_closure; Point3LL split_origin = p0.p_; - if (! is_scarf_closure) + if (! is_scarf_closure && scarf_seam_length > 0) { split_origin.z_ = scarf_max_z_offset; } @@ -1104,7 +1104,7 @@ void LayerPlan::addWallSplitted( coord_t piece_remaining_distance = piece_length; // Cut piece into smaller parts for scarf seam and acceleration/deceleration - while ((! is_scarf_closure && piece_remaining_distance > 0) || (is_scarf_closure && wall_processed_distance < scarf_seam_length)) + while (piece_remaining_distance > 0 && (! is_scarf_closure || wall_processed_distance < scarf_seam_length)) { // Make a list of all the possible incoming positions where we would eventually want to stop next // The positions are expressed in distance from wall start along the wall segments From 19fa63cc9dec0154103e4a2d34323d8e6a015c31 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 14:47:19 +0200 Subject: [PATCH 18/32] Fix GCode display issue CURA-12081 --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index eb08b2b234..029fb038c5 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2238,7 +2238,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) { Communication* communication = Application::getInstance().communication_; communication->setLayerForSend(layer_nr_); - communication->sendCurrentPosition(gcode.getPositionXY()); + communication->sendCurrentPosition(gcode.getPosition()); gcode.setLayerNr(layer_nr_); gcode.writeLayerComment(layer_nr_); From 3428bf807d76bc2f6305ae4856a4f27456ec515d Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 15:28:36 +0200 Subject: [PATCH 19/32] Fix MacOS build CURA-12081 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 8ccc943895..ee853ca551 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3126,7 +3126,7 @@ bool FffGcodeWriter::processInsets( mesh.getZSeamHint(), mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); - constexpr Shape disallowed_areas_for_seams; + const Shape disallowed_areas_for_seams; constexpr bool scarf_seam = true; constexpr bool smooth_speed = true; InsetOrderOptimizer wall_orderer( From 008ff2ae18bed9b3b0dbb1a86c6fcf84120613b6 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Aug 2024 16:51:59 +0200 Subject: [PATCH 20/32] Fix MacOS build CURA-12081 --- include/geometry/Point2LL.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geometry/Point2LL.h b/include/geometry/Point2LL.h index 91b26c6ab6..424ba212d2 100644 --- a/include/geometry/Point2LL.h +++ b/include/geometry/Point2LL.h @@ -10,8 +10,8 @@ Integer points are used to avoid floating point rounding errors, and because Cli */ #define INLINE static inline +#include #include -#include #include #include From 7056d2ca336fbdb5f08da9b314167693f7cbe12c Mon Sep 17 00:00:00 2001 From: HellAholic Date: Wed, 28 Aug 2024 13:16:09 +0000 Subject: [PATCH 21/32] Set conan package version 5.8.1 --- conandata.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conandata.yml b/conandata.yml index c3ba739591..103c698ff9 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,4 +1,4 @@ -version: "5.8.0" +version: "5.8.1" requirements: - "scripta/0.1.0@ultimaker/testing" requirements_arcus: @@ -6,4 +6,4 @@ requirements_arcus: requirements_plugins: - "curaengine_grpc_definitions/0.2.1" requirements_cura_resources: - - "cura_resources/5.8.0" + - "cura_resources/5.8.1" From 969c312b22ce9f62cad8831035c810a4ef787da4 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 24 Sep 2024 20:16:56 +0200 Subject: [PATCH 22/32] Remove dead code and fix tests. Most of the tests where going wrong because the z-height introduced for the scarf seam introduced a refactor that used a variable before it had the correct assignment. part of CURA-12081 --- src/FffPolygonGenerator.cpp | 9 --------- src/gcodeExport.cpp | 2 +- tests/FffGcodeWriterTest.cpp | 4 ++-- tests/GCodeExportTest.cpp | 2 +- tests/arcus/MockCommunication.h | 6 ++---- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 0a0351e460..68db1cf1c1 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -213,15 +213,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe slicerList.push_back(slicer); - /* - for(SlicerLayer& layer : slicer->layers) - { - //Reporting the outline here slows down the engine quite a bit, so only do so when debugging. - sendPolygons("outline", layer_nr, layer.z, layer.polygonList); - sendPolygons("openoutline", layer_nr, layer.openPolygonList); - } - */ - Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size()); } diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 938980e456..e94c988b78 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1248,8 +1248,8 @@ void GCodeExport::writeZhopStart(const coord_t hop_height, Velocity speed /*= 0* const ExtruderTrain& extruder = Application::getInstance().current_slice_->scene.extruders[current_extruder_]; speed = extruder.settings_.get("speed_z_hop"); } - const coord_t target_z = current_layer_z_ + is_z_hopped_; is_z_hopped_ = hop_height; + const coord_t target_z = current_layer_z_ + is_z_hopped_; current_speed_ = speed; *output_stream_ << "G1 F" << PrecisionedDouble{ 1, speed * 60 } << " Z" << MMtoStream{ target_z } << new_line_; Application::getInstance().communication_->sendLineTo(PrintFeatureType::MoveRetraction, Point3LL(current_position_.x_, current_position_.y_, target_z), 0, 0, speed); diff --git a/tests/FffGcodeWriterTest.cpp b/tests/FffGcodeWriterTest.cpp index a3c52d6509..452dd4a749 100644 --- a/tests/FffGcodeWriterTest.cpp +++ b/tests/FffGcodeWriterTest.cpp @@ -155,12 +155,12 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) Point2LL last; for (const auto& path:gcode_layer.extruder_plans_[0].paths_) { for (const auto& point: path.points) { - Point2LL closest_here = LinearAlg2D::getClosestOnLineSegment(p, point, last); + Point2LL closest_here = LinearAlg2D::getClosestOnLineSegment(p, point.toPoint2LL(), last); int64_t dist = vSize2(p - closest_here); if (dist Date: Tue, 24 Sep 2024 23:06:34 +0200 Subject: [PATCH 23/32] The 'spiralize' variable was missing. In the previous spike, the 'travel_to_z' argument was added (at the end), causing the need for previously omitted (default) parameters to be added as well. In this case, 'spiralize' was omitted, causing the FAN_SPEED_DEFAULT (which is an enum set to -1) to be interpreted as a speed_factor instead. part of CURA-12081 --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 35b18b5ed9..a3e507a5f6 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -925,7 +925,7 @@ void LayerPlan::addWallLine( if (bridge_line_len > min_line_len) { - addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow, width_factor, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); + addExtrusionMove(b1, bridge_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); non_bridge_line_volume = 0; cur_point = b1; // after a bridge segment, start slow and accelerate to avoid under-extrusion due to extruder lag From 348929ed06312660e6c136113b48987da423914f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Sep 2024 15:23:02 +0200 Subject: [PATCH 24/32] Rename 'Splitted' to 'Split' part of CURA-12081 --- include/LayerPlan.h | 2 +- src/LayerPlan.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 111d827193..40866afb37 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -894,7 +894,7 @@ class LayerPlan : public NoCopy * \param is_scarf_closure Indicates whether this function is called to make the scarf closure (overlap over the first scarf pass) or * the normal first pass of the wall */ - void addWallSplitted( + void addSplitWall( const ExtrusionLine& wall, const coord_t wall_length, size_t start_idx, diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index a3e507a5f6..b4ad5d1097 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -997,7 +997,7 @@ void LayerPlan::addWall( addWall(ewall, start_idx, settings, default_config, roofing_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract, is_closed, is_reversed, is_linked_path); } -void LayerPlan::addWallSplitted( +void LayerPlan::addSplitWall( const ExtrusionLine& wall, const coord_t wall_length, size_t start_idx, @@ -1372,9 +1372,9 @@ void LayerPlan::addWall( const Velocity end_speed = top_speed * end_speed_ratio; // mm/s const coord_t decelerate_length = (smooth_speed && end_speed_ratio < 1.0) ? MM2INT((square(top_speed) - square(end_speed)) / (2.0 * deceleration)) : 0; // µm - auto addWallSplittedPass = [&](bool is_scarf_closure) + auto addSplitWallPass = [&](bool is_scarf_closure) { - addWallSplitted( + addSplitWall( wall, wall_length, start_idx, @@ -1406,12 +1406,12 @@ void LayerPlan::addWall( }; // First pass to add the wall with the scarf beginning and acceleration - addWallSplittedPass(false); + addSplitWallPass(false); if (scarf_seam_length > 0) { // Second pass to add the scarf closure - addWallSplittedPass(true); + addSplitWallPass(true); } if (wall.size() >= 2) @@ -2233,7 +2233,6 @@ void LayerPlan::processFanSpeedAndMinimalLayerTime(Point2LL starting_position) last_extruder_plan.processFanSpeedForMinimalLayerTime(maximum_cool_min_layer_time, other_extr_plan_time); } - void LayerPlan::writeGCode(GCodeExport& gcode) { auto communication = Application::getInstance().communication_; From e7ab2d1538bb22857e1f59b0ec5862a8946900f9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Sep 2024 15:28:04 +0200 Subject: [PATCH 25/32] Fix coasting for 'split-up' paths. The coasting functionality previously relied (partially) on paths being their own 'complete' extrusions, in between wich where (presumably, mostly) travels. So it only coasted on the last path before a travel, and didn't take into account paths that where to short to complete the full coast (it didn't continue on to the next). This commit fixes that partially -- the volume already coasted isn't taken into account yet, but otherwise it should work as before, but then with the split paths the scarf and accellleration start/end introduced. part of CURA-12081 --- include/LayerPlan.h | 13 ++++++++++- src/LayerPlan.cpp | 55 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 40866afb37..4550a80b9f 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -57,6 +57,14 @@ class LayerPlan : public NoCopy #endif public: + // 'AdjustCoasting'; because split-up paths from the same extruder (with no travel moves between them) should count as the same path w.r.t. coasting. + enum class AdjustCoasting + { + AsNormal, + CoastEntirePath, + ContinueCoasting + }; + const PathConfigStorage configs_storage_; //!< The line configs for this layer for each feature type const coord_t z_; coord_t final_travel_z_; @@ -740,6 +748,8 @@ class LayerPlan : public NoCopy * \param path_idx The index into LayerPlan::paths for the next path to be * written to GCode. * \param layer_thickness The height of the current layer. + * \param insertTempOnTime A function that inserts temperature changes at a given time. + * \param coasting_adjust Paths can be split up, so we need to know when to continue coasting from last, or even coast the entire path. * \return Whether any GCode has been written for the path. */ bool writePathWithCoasting( @@ -747,7 +757,8 @@ class LayerPlan : public NoCopy const size_t extruder_plan_idx, const size_t path_idx, const coord_t layer_thickness, - const std::function insertTempOnTime); + const std::function insertTempOnTime, + const AdjustCoasting coasting_adjust); /*! * Applying speed corrections for minimal layer times and determine the fanSpeed. diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index b4ad5d1097..69c4021ee0 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -32,6 +32,8 @@ #include "utils/polygonUtils.h" #include "utils/section_type.h" +#include "range/v3/view/chunk_by.hpp" + namespace cura { @@ -2365,6 +2367,45 @@ void LayerPlan::writeGCode(GCodeExport& gcode) extruder_plan.handleInserts(path_idx, gcode, cumulative_path_time); }; + const double coasting_volume = extruder.settings_.get("coasting_volume"); + std::vector coasting_adjust_per_path(paths.size(), AdjustCoasting::AsNormal); + if (coasting_volume > 0) + { + // Chunk paths by travel paths, and find out which paths are a 'continuation' w.r.t. coasting (and which need to be 'coasted away' entirely). + // Note that this doesn't perform the coasting itself, it just calculates the 'adjust coasting' vector needed by the 'writePathWithCoasting' func. + // All of this is nescesary since we split up paths because of scarf and accelleration-adjustments (start/end), so we need to have adjacency info. + for (const auto& reversed_chunk : + paths | + ranges::views::enumerate | + ranges::views::reverse | + ranges::views::chunk_by([](const auto& path_a, const auto& path_b) { return (! std::get<1>(path_a).isTravelPath()) || std::get<1>(path_b).isTravelPath(); })) + { + double coasting_left = coasting_volume; + for (const auto& [path_idx, path] : reversed_chunk) + { + if (path.isTravelPath()) + { + break; + } + + coord_t accumulated_length = 0; + for (size_t i_pt = 1; i_pt < path.points.size(); i_pt++) + { + accumulated_length += (path.points[i_pt - 1] - path.points[i_pt]).vSize(); + } + const double path_volume = INT2MM2(INT2MM(accumulated_length * path.config.getLineWidth()) * layer_thickness_); + + coasting_adjust_per_path[path_idx] = path_volume < coasting_left ? AdjustCoasting::CoastEntirePath : AdjustCoasting::ContinueCoasting; + + coasting_left -= path_volume; + if (coasting_left <= 0) + { + break; + } + } + } + } + for (int64_t path_idx = 0; path_idx < paths.size(); path_idx++) { extruder_plan.handleInserts(path_idx, gcode); @@ -2555,7 +2596,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) bool coasting = extruder.settings_.get("coasting_enable"); if (coasting) { - coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layer_thickness_, insertTempOnTime); + coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layer_thickness_, insertTempOnTime, coasting_adjust_per_path[path_idx]); } if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above... { // normal path to gcode algorithm @@ -2713,7 +2754,8 @@ bool LayerPlan::writePathWithCoasting( const size_t extruder_plan_idx, const size_t path_idx, const coord_t layer_thickness, - const std::function insertTempOnTime) + const std::function insertTempOnTime, + const AdjustCoasting coasting_adjust) { ExtruderPlan& extruder_plan = extruder_plans_[extruder_plan_idx]; const ExtruderTrain& extruder = Application::getInstance().current_slice_->scene.extruders[extruder_plan.extruder_nr_]; @@ -2724,7 +2766,7 @@ bool LayerPlan::writePathWithCoasting( } const std::vector& paths = extruder_plan.paths_; const GCodePath& path = paths[path_idx]; - if (path_idx + 1 >= paths.size() || (path.isTravelPath() || ! paths[path_idx + 1].config.isTravelPath()) || path.points.size() < 2) + if (path_idx + 1 >= paths.size() || (path.isTravelPath() || !(paths[path_idx + 1].config.isTravelPath() || coasting_adjust == AdjustCoasting::ContinueCoasting)) || path.points.size() < 2) { return false; } @@ -2733,8 +2775,10 @@ bool LayerPlan::writePathWithCoasting( const double extrude_speed = path.config.getSpeed() * path.speed_factor * path.speed_back_pressure_factor; - const coord_t coasting_dist - = MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues + coord_t coasting_dist + = coasting_adjust == AdjustCoasting::CoastEntirePath + ? std::numeric_limits::max() / 2 + : MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues const double coasting_min_volume = extruder.settings_.get("coasting_min_volume"); const coord_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues @@ -2771,6 +2815,7 @@ bool LayerPlan::writePathWithCoasting( last = &point; } + coasting_dist = std::min(coasting_dist, accumulated_dist); // if the path is shorter than coasting_dist, we should coast the whole path if (accumulated_dist < coasting_min_dist_considered) { From 5608352b2dae51a23fa27b138e5ccfaf8fc94e20 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Sep 2024 16:07:48 +0200 Subject: [PATCH 26/32] Coast less if the next ('split') path(s) are already 'coasted away'. If, due to the compensation for the paths being split (because of scarf-seams or introduced start/end acceleration-changes), the next path(s) in the 'chunk' are already going to be comletely gone due to coasting, coast less than the 'full' volume for the remainder. part of CURA-12081 --- include/LayerPlan.h | 2 +- src/LayerPlan.cpp | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 4550a80b9f..a2ab689f3b 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -758,7 +758,7 @@ class LayerPlan : public NoCopy const size_t path_idx, const coord_t layer_thickness, const std::function insertTempOnTime, - const AdjustCoasting coasting_adjust); + const std::pair coasting_adjust); /*! * Applying speed corrections for minimal layer times and determine the fanSpeed. diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 69c4021ee0..b07bf03fd2 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -2367,18 +2367,25 @@ void LayerPlan::writeGCode(GCodeExport& gcode) extruder_plan.handleInserts(path_idx, gcode, cumulative_path_time); }; - const double coasting_volume = extruder.settings_.get("coasting_volume"); - std::vector coasting_adjust_per_path(paths.size(), AdjustCoasting::AsNormal); - if (coasting_volume > 0) + std::vector> coasting_adjust_per_path; + if (extruder.settings_.get("coasting_enable")) { // Chunk paths by travel paths, and find out which paths are a 'continuation' w.r.t. coasting (and which need to be 'coasted away' entirely). // Note that this doesn't perform the coasting itself, it just calculates the 'adjust coasting' vector needed by the 'writePathWithCoasting' func. // All of this is nescesary since we split up paths because of scarf and accelleration-adjustments (start/end), so we need to have adjacency info. + + const double coasting_volume = extruder.settings_.get("coasting_volume"); + coasting_adjust_per_path.assign(paths.size(), { AdjustCoasting::AsNormal, coasting_volume }); + for (const auto& reversed_chunk : paths | ranges::views::enumerate | ranges::views::reverse | - ranges::views::chunk_by([](const auto& path_a, const auto& path_b) { return (! std::get<1>(path_a).isTravelPath()) || std::get<1>(path_b).isTravelPath(); })) + ranges::views::chunk_by( + [](const auto& path_a, const auto& path_b) + { + return (! std::get<1>(path_a).isTravelPath()) || std::get<1>(path_b).isTravelPath(); + })) { double coasting_left = coasting_volume; for (const auto& [path_idx, path] : reversed_chunk) @@ -2395,7 +2402,14 @@ void LayerPlan::writeGCode(GCodeExport& gcode) } const double path_volume = INT2MM2(INT2MM(accumulated_length * path.config.getLineWidth()) * layer_thickness_); - coasting_adjust_per_path[path_idx] = path_volume < coasting_left ? AdjustCoasting::CoastEntirePath : AdjustCoasting::ContinueCoasting; + if (path_volume < coasting_left) + { + coasting_adjust_per_path[path_idx].first = AdjustCoasting::CoastEntirePath; + } + else + { + coasting_adjust_per_path[path_idx] = { AdjustCoasting::ContinueCoasting, coasting_left }; + } coasting_left -= path_volume; if (coasting_left <= 0) @@ -2755,18 +2769,18 @@ bool LayerPlan::writePathWithCoasting( const size_t path_idx, const coord_t layer_thickness, const std::function insertTempOnTime, - const AdjustCoasting coasting_adjust) + const std::pair coasting_adjust) { ExtruderPlan& extruder_plan = extruder_plans_[extruder_plan_idx]; const ExtruderTrain& extruder = Application::getInstance().current_slice_->scene.extruders[extruder_plan.extruder_nr_]; - const double coasting_volume = extruder.settings_.get("coasting_volume"); + const double coasting_volume = std::min(extruder.settings_.get("coasting_volume"), coasting_adjust.second); if (coasting_volume <= 0) { return false; } const std::vector& paths = extruder_plan.paths_; const GCodePath& path = paths[path_idx]; - if (path_idx + 1 >= paths.size() || (path.isTravelPath() || !(paths[path_idx + 1].config.isTravelPath() || coasting_adjust == AdjustCoasting::ContinueCoasting)) || path.points.size() < 2) + if (path_idx + 1 >= paths.size() || (path.isTravelPath() || !(paths[path_idx + 1].config.isTravelPath() || coasting_adjust.first == AdjustCoasting::ContinueCoasting)) || path.points.size() < 2) { return false; } @@ -2776,7 +2790,7 @@ bool LayerPlan::writePathWithCoasting( const double extrude_speed = path.config.getSpeed() * path.speed_factor * path.speed_back_pressure_factor; coord_t coasting_dist - = coasting_adjust == AdjustCoasting::CoastEntirePath + = coasting_adjust.first == AdjustCoasting::CoastEntirePath ? std::numeric_limits::max() / 2 : MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues const double coasting_min_volume = extruder.settings_.get("coasting_min_volume"); From 5e365b066bdfd0f14f6cf6b2151486d3bb676e87 Mon Sep 17 00:00:00 2001 From: rburema Date: Wed, 25 Sep 2024 14:08:42 +0000 Subject: [PATCH 27/32] Applied clang-format. --- src/LayerPlan.cpp | 28 ++++++++++++---------------- src/plugins/converters.cpp | 5 +++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index b07bf03fd2..4f09f02738 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -24,6 +24,7 @@ #include "pathPlanning/CombPaths.h" #include "plugins/slots.h" #include "raft.h" // getTotalExtraLayers +#include "range/v3/view/chunk_by.hpp" #include "settings/types/Ratio.h" #include "sliceDataStorage.h" #include "utils/Simplify.h" @@ -32,8 +33,6 @@ #include "utils/polygonUtils.h" #include "utils/section_type.h" -#include "range/v3/view/chunk_by.hpp" - namespace cura { @@ -2377,15 +2376,12 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const double coasting_volume = extruder.settings_.get("coasting_volume"); coasting_adjust_per_path.assign(paths.size(), { AdjustCoasting::AsNormal, coasting_volume }); - for (const auto& reversed_chunk : - paths | - ranges::views::enumerate | - ranges::views::reverse | - ranges::views::chunk_by( - [](const auto& path_a, const auto& path_b) - { - return (! std::get<1>(path_a).isTravelPath()) || std::get<1>(path_b).isTravelPath(); - })) + for (const auto& reversed_chunk : paths | ranges::views::enumerate | ranges::views::reverse + | ranges::views::chunk_by( + [](const auto&path_a, const auto&path_b) + { + return (! std::get<1>(path_a).isTravelPath()) || std::get<1>(path_b).isTravelPath(); + })) { double coasting_left = coasting_volume; for (const auto& [path_idx, path] : reversed_chunk) @@ -2780,7 +2776,8 @@ bool LayerPlan::writePathWithCoasting( } const std::vector& paths = extruder_plan.paths_; const GCodePath& path = paths[path_idx]; - if (path_idx + 1 >= paths.size() || (path.isTravelPath() || !(paths[path_idx + 1].config.isTravelPath() || coasting_adjust.first == AdjustCoasting::ContinueCoasting)) || path.points.size() < 2) + if (path_idx + 1 >= paths.size() || (path.isTravelPath() || ! (paths[path_idx + 1].config.isTravelPath() || coasting_adjust.first == AdjustCoasting::ContinueCoasting)) + || path.points.size() < 2) { return false; } @@ -2789,10 +2786,9 @@ bool LayerPlan::writePathWithCoasting( const double extrude_speed = path.config.getSpeed() * path.speed_factor * path.speed_back_pressure_factor; - coord_t coasting_dist - = coasting_adjust.first == AdjustCoasting::CoastEntirePath - ? std::numeric_limits::max() / 2 - : MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues + coord_t coasting_dist = coasting_adjust.first == AdjustCoasting::CoastEntirePath + ? std::numeric_limits::max() / 2 + : MM2INT(MM2_2INT(coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues const double coasting_min_volume = extruder.settings_.get("coasting_min_volume"); const coord_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layer_thickness) / path.config.getLineWidth(); // closing brackets of MM2INT at weird places for precision issues diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index f1bd4dad79..f9dfcfed90 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -168,7 +168,8 @@ postprocess_response::native_value_type } infill_generate_request::value_type - infill_generate_request::operator()(const infill_generate_request::native_value_type& inner_contour, const std::string& pattern, const Settings& settings, const coord_t z) const + infill_generate_request::operator()(const infill_generate_request::native_value_type& inner_contour, const std::string& pattern, const Settings& settings, const coord_t z) + const { value_type message{}; message.set_pattern(pattern); @@ -181,7 +182,7 @@ infill_generate_request::value_type // ------------------------------------------------------------ // Add current z height to settings message // ------------------------------------------------------------ - msg_settings->insert({ "z" , std::to_string(z) }); + msg_settings->insert({ "z", std::to_string(z) }); if (inner_contour.empty()) { From 8c46f31847b10d70f07a5e8b1c6e224479fc687b Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 25 Sep 2024 16:32:29 +0200 Subject: [PATCH 28/32] Disable scarf for initial layer (protects the buildplate). part of CURA-12081 --- src/LayerPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 4f09f02738..b28cc5034c 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1394,7 +1394,7 @@ void LayerPlan::addWall( small_feature_speed_factor, max_area_deviation, max_resolution, - scarf_seam_length, + layer_nr_ > 0 ? scarf_seam_length : 0, scarf_seam_start_ratio, scarf_split_distance, scarf_max_z_offset, From 5024ded188cf2a10e476ae111e41d8459fae97da Mon Sep 17 00:00:00 2001 From: Remco Burema <41987080+rburema@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:54:38 +0200 Subject: [PATCH 29/32] Review: Better whitespace in comment. Co-authored-by: Jaime van Kessel --- include/LayerPlan.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index a2ab689f3b..697a20dfc1 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -902,8 +902,7 @@ class LayerPlan : public NoCopy * \param accelerate_length The pre-calculated length of the acceleration phase * \param end_speed_ratio The ratio of the top speed to be applied when finishing a segment * \param decelerate_length The pre-calculated length of the deceleration phase - * \param is_scarf_closure Indicates whether this function is called to make the scarf closure (overlap over the first scarf pass) or - * the normal first pass of the wall + * \param is_scarf_closure Indicates whether this function is called to make the scarf closure (overlap over the first scarf pass) or the normal first pass of the wall */ void addSplitWall( const ExtrusionLine& wall, From 23c45a061fc10c68345a5283e59bf4ace3aeeeea Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 1 Oct 2024 13:01:12 +0200 Subject: [PATCH 30/32] Make comment more clear for the new add-split-wall function. done as part of CURA-12077 --- include/LayerPlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 697a20dfc1..0b5f632489 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -874,7 +874,7 @@ class LayerPlan : public NoCopy bool update_extrusion_offset = false); /*! - * \brief Add a wall to the gcode with optimized order + * \brief Add a wall to the gcode with optimized order, but split into pieces in order to facilitate the scarf seam and/or speed gradient. * \param wall The full wall to be added * \param wall_length The pre-calculated full wall length * \param start_idx The index of the point where to start printing the wall From 54609c88b2ff06061fe2c07d7e6ab1e95288926d Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 3 Oct 2024 15:45:22 +0200 Subject: [PATCH 31/32] Use specific grpc definitions --- conandata.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conandata.yml b/conandata.yml index 909c921748..62a79abdae 100644 --- a/conandata.yml +++ b/conandata.yml @@ -4,6 +4,6 @@ requirements: requirements_arcus: - "arcus/5.4.1" requirements_plugins: - - "curaengine_grpc_definitions/0.2.1" + - "curaengine_grpc_definitions/0.2.1+910f59@ultimaker/cura_12081" requirements_cura_resources: - "cura_resources/(latest)@ultimaker/testing" From 5cfb461e201c0862268dcc91fba76fb48c15b500 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Fri, 4 Oct 2024 12:25:05 +0200 Subject: [PATCH 32/32] Revert "Use specific grpc definitions" This reverts commit 54609c88b2ff06061fe2c07d7e6ab1e95288926d. --- conandata.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conandata.yml b/conandata.yml index 62a79abdae..909c921748 100644 --- a/conandata.yml +++ b/conandata.yml @@ -4,6 +4,6 @@ requirements: requirements_arcus: - "arcus/5.4.1" requirements_plugins: - - "curaengine_grpc_definitions/0.2.1+910f59@ultimaker/cura_12081" + - "curaengine_grpc_definitions/0.2.1" requirements_cura_resources: - "cura_resources/(latest)@ultimaker/testing"