diff --git a/CMakeLists.txt b/CMakeLists.txt index daf6022fbb..47581ceca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ set(engine_SRCS # Except main.cpp. src/Mold.cpp src/multiVolumes.cpp src/path_ordering.cpp + src/PathAdapter.cpp src/Preheat.cpp src/PrimeTower/PrimeTower.cpp src/PrimeTower/PrimeTowerNormal.cpp diff --git a/include/ExtruderPlan.h b/include/ExtruderPlan.h index b5c579496c..cfd2070975 100644 --- a/include/ExtruderPlan.h +++ b/include/ExtruderPlan.h @@ -191,7 +191,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) const; /*! * Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates. diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 0be204faf2..8652e3d584 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -38,6 +38,9 @@ class Comb; class SliceDataStorage; class LayerPlanBuffer; +template +class PathAdapter; + /*! * The LayerPlan class stores multiple moves that are planned. * @@ -57,20 +60,27 @@ 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_; bool mode_skip_agressive_merge_; //!< Whether to give every new path the 'skip_agressive_merge_hint' property (see GCodePath); default is false. private: + // Indicates how coasting should be processed on the given path. + enum class ApplyCoasting + { + NoCoasting, // Do not apply coasting on this path, extrude it normally + CoastEntirePath, // Fully coast this path, i.e. replace it by travel moves + PartialCoasting // Extrude the first part of the path and coast the end + }; + + struct PathCoasting + { + ApplyCoasting apply_coasting{ ApplyCoasting::NoCoasting }; + size_t coasting_start_index{ 0 }; + Point3LL coasting_start_pos; + }; + const SliceDataStorage& storage_; //!< The polygon data obtained from FffPolygonProcessor const LayerIndex layer_nr_; //!< The layer number of this layer plan const bool is_initial_layer_; //!< Whether this is the first layer (which might be raft) @@ -393,16 +403,21 @@ class LayerPlan : public NoCopy * \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon * \param flow_ratio The ratio with which to multiply the extrusion amount * \param always_retract Whether to force a retraction when moving to the start of the polygon (used for outer walls) + * \param scarf_seam Indicates whether we may use a scarf seam for the path + * \param smooth_speed Indicates whether we may use a speed gradient for the path */ void addPolygon( const Polygon& polygon, int startIdx, const bool reverse, + const Settings& settings, const GCodePathConfig& config, coord_t wall_0_wipe_dist = 0, bool spiralize = false, const Ratio& flow_ratio = 1.0_r, - bool always_retract = false); + bool always_retract = false, + bool scarf_seam = false, + bool smooth_speed = false); /*! * Add polygons to the gcode with optimized order. @@ -431,17 +446,22 @@ class LayerPlan : public NoCopy * \param reverse_order Adds polygons in reverse order. * \param start_near_location Start optimising the path near this location. * If unset, this causes it to start near the last planned location. + * \param scarf_seam Indicates whether we may use a scarf seam for the path + * \param smooth_speed Indicates whether we may use a speed gradient for the path */ void addPolygonsByOptimizer( const Shape& polygons, const GCodePathConfig& config, + const Settings& settings, const ZSeamConfig& z_seam_config = ZSeamConfig(), coord_t wall_0_wipe_dist = 0, bool spiralize = false, const Ratio flow_ratio = 1.0_r, bool always_retract = false, bool reverse_order = false, - const std::optional start_near_location = std::optional()); + const std::optional start_near_location = std::optional(), + bool scarf_seam = false, + bool smooth_acceleration = false); /*! * Add a single line that is part of a wall to the gcode. @@ -525,6 +545,8 @@ class LayerPlan : public NoCopy * polyline). * \param is_reversed Whether to print this wall in reverse direction. * \param is_linked_path Whether the path is a continuation off the previous path + * \param scarf_seam Indicates whether we may use a scarf seam for the path + * \param smooth_speed Indicates whether we may use a speed gradient for the path */ void addWall( const ExtrusionLine& wall, @@ -699,28 +721,6 @@ class LayerPlan : public NoCopy */ bool makeRetractSwitchRetract(unsigned int extruder_plan_idx, unsigned int path_idx); - /*! - * Writes a path to GCode and performs coasting, or returns false if it did nothing. - * - * Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines. - * - * \param gcode The gcode to write the planned paths to. - * \param extruder_plan_idx The index of the current extruder plan. - * \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( - GCodeExport& gcode, - const size_t extruder_plan_idx, - const size_t path_idx, - const coord_t layer_thickness, - const std::function insertTempOnTime, - const std::pair coasting_adjust); - /*! * Applying speed corrections for minimal layer times and determine the fanSpeed. * @@ -834,20 +834,34 @@ class LayerPlan : public NoCopy PrintFeatureType feature, bool update_extrusion_offset = false); + /*! + * \brief Alias for a function definition that adds an extrusion segment + * \param start The start position of the segment + * \param end The end position of the segment + * \param speed_factor The speed factor to be applied when extruding this specific segment (relative to nominal speed for the entire path) + * \param flow_ratio The flow ratio to be applied when extruding this specific segment (relative to nominal flow for the entire path) + * \param line_width_ratio The line width ratio to be applied when extruding this specific segment (relative to nominal line width for the entire path) + * \param distance_to_bridge_start The calculate distance to the next bridge start, which may be irrelevant in some cases + */ + using AddExtrusionSegmentFunction = std::function; + /*! * \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. + * \tparam PathType The type of path to be processed, either ExtrusionLine or some subclass of Polyline * \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 nominal_line_width The nominal line width for the wall * \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 @@ -864,26 +878,26 @@ class LayerPlan : public NoCopy * \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 compute_distance_to_bridge_start Whether we should compute the distance to start of bridge. This is + * possible only if PathType is ExtrusionLine and will be ignored otherwise. + * \param func_add_segment The function to be called to actually add an extrusion segment with the given parameters */ + template void addSplitWall( - const ExtrusionLine& wall, + const PathAdapter& wall, const coord_t wall_length, - size_t start_idx, - const int direction, + const size_t start_idx, const size_t max_index, - const Settings& settings, + const int direction, const GCodePathConfig& default_config, - const GCodePathConfig& roofing_config, - const GCodePathConfig& bridge_config, - const double flow_ratio, - const Ratio line_width_ratio, - 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 double flow_ratio, + const coord_t nominal_line_width, + const coord_t min_bridge_line_len, const auto scarf_seam_length, const auto scarf_seam_start_ratio, const auto scarf_split_distance, @@ -893,7 +907,71 @@ class LayerPlan : public NoCopy const coord_t accelerate_length, const Ratio end_speed_ratio, const coord_t decelerate_length, - const bool is_scarf_closure); + const bool is_scarf_closure, + const bool compute_distance_to_bridge_start, + const AddExtrusionSegmentFunction& func_add_segment); + + /*! + * \brief Add a wall to the gcode with optimized order, possibly adding a scarf seam / speed gradient according to settings + * \tparam PathType The type of path to be processed, either ExtrusionLine or some subclass of Polyline + * \param wall The full wall to be added + * \param start_idx The index of the point where to start printing the wall + * \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 flow_ratio The ratio with which to multiply the extrusion amount + * \param always_retract Whether to force a retraction when moving to the start of the polygon (used for outer walls) + * \param is_closed Indicates whether the path is closed (or open) + * \param is_reversed Indicates if the path is to be processed backwards + * \param is_candidate_small_feature Indicates whether the path should be tested for being treated as a smell feature + * \param scarf_seam Indicates whether we may use a scarf seam for the path + * \param smooth_speed Indicates whether we may use a speed gradient for the path + * \param func_add_segment The function to be called to actually add an extrusion segment with the given parameters + */ + template + void addWallWithScarfSeam( + const PathAdapter& wall, + size_t start_idx, + const Settings& settings, + const GCodePathConfig& default_config, + const double flow_ratio, + bool always_retract, + const bool is_closed, + const bool is_reversed, + const bool is_candidate_small_feature, + const bool scarf_seam, + const bool smooth_speed, + const AddExtrusionSegmentFunction& func_add_segment); + + /*! + * Pre-calculates the coasting to be applied on the paths + * + * \param extruder_settings The current extruder settings + * \param paths The current set of paths to be written to GCode + * \param current_position The last position set in the gcode writer + * \return The list of coasting settings to be applied on the paths. It will always have the same size as paths. + */ + std::vector calculatePathsCoasting(const Settings& extruder_settings, const std::vector& paths, const Point3LL& current_position) const; + + /*! + * Writes a path to GCode and performs coasting, or returns false if it did nothing. + * + * Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines. + * + * \param gcode The gcode to write the planned paths to. + * \param extruder_plan_idx The index of the current extruder plan. + * \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 path_coasting The actual coasting to be applied to the path. + * \return Whether any GCode has been written for the path. + */ + bool writePathWithCoasting( + GCodeExport& gcode, + const size_t extruder_plan_idx, + const size_t path_idx, + const std::function insertTempOnTime, + const PathCoasting& path_coasting); /*! * \brief Helper function to calculate the distance from the start of the current wall line to the first bridge segment diff --git a/include/PathAdapter.h b/include/PathAdapter.h new file mode 100644 index 0000000000..21a71ee1da --- /dev/null +++ b/include/PathAdapter.h @@ -0,0 +1,63 @@ +// Copyright (c) 2024 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef PATH_ADAPTER_H +#define PATH_ADAPTER_H + +#include + +#include "geometry/Point2LL.h" +#include "utils/Coord_t.h" + +namespace cura +{ + +/* Adapter class to allow extrusion-related functions to work with either an ExtrusionLine or a Polygon */ +template +class PathAdapter +{ +public: + /*! + * \brief Base constructor + * \param path The actual stored path + * \param fixed_line_width The fixed line width in case the stored path doesn't handle information about a variable + * line width. Can be omitted otherwise. + */ + PathAdapter(const PathType& path, coord_t fixed_line_width = 0) + : path_(path) + , fixed_line_width_(fixed_line_width) + { + } + + bool empty() const + { + return path_.empty(); + } + + size_t size() const + { + return path_.size(); + } + + coord_t length() const + { + return path_.length(); + } + + const Point2LL& pointAt(size_t index) const; + + coord_t lineWidthAt(size_t index) const; + + const PathType& getPath() const + { + return path_; + } + +private: + const PathType& path_; + const coord_t fixed_line_width_; +}; + +} // namespace cura + +#endif // PATH_ADAPTER_H diff --git a/include/geometry/Point3LL.h b/include/geometry/Point3LL.h index 0e4b62ed7b..604e93af3c 100644 --- a/include/geometry/Point3LL.h +++ b/include/geometry/Point3LL.h @@ -192,6 +192,11 @@ inline Point3LL operator*(const T i, const Point3LL& rhs) return rhs * i; } +inline Point3LL lerp(const Point3LL& a, const Point3LL& b, const double t) +{ + return Point3LL(cura::lerp(a.x_, b.x_, t), cura::lerp(a.y_, b.y_, t), cura::lerp(a.z_, b.z_, t)); +} + } // namespace cura diff --git a/include/utils/Coord_t.h b/include/utils/Coord_t.h index bd8f3e088b..ac2e4741af 100644 --- a/include/utils/Coord_t.h +++ b/include/utils/Coord_t.h @@ -6,8 +6,11 @@ // Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition. +#include #include +#include "utils/types/generic.h" + namespace cura { @@ -16,7 +19,7 @@ using coord_t = ClipperLib::cInt; static inline coord_t operator"" _mu(unsigned long long i) { return static_cast(i); -}; +} #define INT2MM(n) (static_cast(n) / 1000.0) #define INT2MM2(n) (static_cast(n) / 1000000.0) @@ -27,6 +30,12 @@ static inline coord_t operator"" _mu(unsigned long long i) #define INT2MICRON(n) ((n) / 1) #define MICRON2INT(n) ((n)*1) +template +[[nodiscard]] inline coord_t lerp(coord_t a, coord_t b, FactorType t) +{ + return std::llrint(std::lerp(static_cast(a), static_cast(b), t)); +} + } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 2417723014..ec35040e6b 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -735,6 +735,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) gcode_layer.addPolygonsByOptimizer( raft_polygons, gcode_layer.configs_storage_.raft_base_config, + mesh_group_settings, ZSeamConfig(), wipe_dist, spiralize, @@ -895,6 +896,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) gcode_layer.addPolygonsByOptimizer( raft_polygons, gcode_layer.configs_storage_.raft_interface_config, + mesh_group_settings, ZSeamConfig(), wipe_dist, spiralize, @@ -1073,6 +1075,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) gcode_layer.addPolygonsByOptimizer( raft_polygons, gcode_layer.configs_storage_.raft_surface_config, + mesh_group_settings, ZSeamConfig(), wipe_dist, spiralize, @@ -1477,13 +1480,14 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, LayerPlan& gcode_layer) const { LayerIndex layer_nr = std::max(LayerIndex{ 0 }, gcode_layer.getLayerNr()); - if (layer_nr == 0 && Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("adhesion_type") == EPlatformAdhesion::BRIM) + const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; + if (layer_nr == 0 && mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::BRIM) { return; // ooze shield already generated by brim } if (storage.ooze_shield.size() > 0 && layer_nr < storage.ooze_shield.size()) { - gcode_layer.addPolygonsByOptimizer(storage.ooze_shield[layer_nr], gcode_layer.configs_storage_.skirt_brim_config_per_extruder[0]); + gcode_layer.addPolygonsByOptimizer(storage.ooze_shield[layer_nr], gcode_layer.configs_storage_.skirt_brim_config_per_extruder[0], mesh_group_settings); } } @@ -1516,7 +1520,7 @@ void FffGcodeWriter::processDraftShield(const SliceDataStorage& storage, LayerPl } } - gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, gcode_layer.configs_storage_.skirt_brim_config_per_extruder[0]); + gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, gcode_layer.configs_storage_.skirt_brim_config_per_extruder[0], mesh_group_settings); } void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& storage) @@ -1723,7 +1727,26 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceMeshStorage& mesh.settings.get("z_seam_corner"), mesh.settings.get("wall_line_width_0") * 2); const bool spiralize = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("magic_spiralize"); - gcode_layer.addPolygonsByOptimizer(polygons, mesh_config.inset0_config, z_seam_config, mesh.settings.get("wall_0_wipe_dist"), spiralize); + constexpr Ratio flow_ratio = 1.0; + constexpr bool always_retract = false; + constexpr bool reverse_order = false; + const std::optional start_near_location = std::nullopt; + constexpr bool scarf_seam = true; + constexpr bool smooth_speed = true; + + gcode_layer.addPolygonsByOptimizer( + polygons, + mesh_config.inset0_config, + mesh.settings, + z_seam_config, + mesh.settings.get("wall_0_wipe_dist"), + spiralize, + flow_ratio, + always_retract, + reverse_order, + start_near_location, + scarf_seam, + smooth_speed); addMeshOpenPolyLinesToGCode(mesh, mesh_config, gcode_layer); } @@ -1966,7 +1989,7 @@ bool FffGcodeWriter::processMultiLayerInfill( { constexpr bool force_comb_retract = false; gcode_layer.addTravel(infill_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[combine_idx]); + gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[combine_idx], mesh.settings); } if (! infill_lines.empty()) @@ -2723,7 +2746,7 @@ bool FffGcodeWriter::processSingleLayerInfill( constexpr bool force_comb_retract = false; // start the infill polygons at the nearest vertex to the current location gcode_layer.addTravel(PolygonUtils::findNearestVert(gcode_layer.getLastPlannedPositionOrStartingPosition(), infill_polygons).p(), force_comb_retract); - gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[0], ZSeamConfig(), 0, false, 1.0_r, false, false, near_start_location); + gcode_layer.addPolygonsByOptimizer(infill_polygons, mesh_config.infill_config[0], mesh.settings, ZSeamConfig(), 0, false, 1.0_r, false, false, near_start_location); } const bool enable_travel_optimization = mesh.settings.get("infill_enable_travel_optimization"); if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC @@ -2965,7 +2988,7 @@ bool FffGcodeWriter::processInsets( gcode_layer.addTravel(spiral_inset[spiral_start_vertex]); } int wall_0_wipe_dist(0); - gcode_layer.addPolygonsByOptimizer(part.spiral_wall, mesh_config.inset0_config, ZSeamConfig(), wall_0_wipe_dist); + gcode_layer.addPolygonsByOptimizer(part.spiral_wall, mesh_config.inset0_config, mesh.settings, ZSeamConfig(), wall_0_wipe_dist); } } // for non-spiralized layers, determine the shape of the unsupported areas below this part @@ -3596,7 +3619,7 @@ void FffGcodeWriter::processSkinPrintFeature( { constexpr bool force_comb_retract = false; gcode_layer.addTravel(skin_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(skin_polygons, config); + gcode_layer.addPolygonsByOptimizer(skin_polygons, config, mesh.settings); } if (monotonic) @@ -4001,6 +4024,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer gcode_layer.addPolygonsByOptimizer( support_polygons, configs[combine_idx], + mesh_group_settings, z_seam_config, wall_0_wipe_dist, spiralize, @@ -4167,13 +4191,13 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, con gcode_layer.setIsInside(false); // going to print stuff outside print object, i.e. support if (gcode_layer.getLayerNr() == 0) { - gcode_layer.addPolygonsByOptimizer(wall, current_roof_config); + gcode_layer.addPolygonsByOptimizer(wall, current_roof_config, roof_extruder.settings_); } if (! roof_polygons.empty()) { constexpr bool force_comb_retract = false; gcode_layer.addTravel(roof_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config); + gcode_layer.addPolygonsByOptimizer(roof_polygons, current_roof_config, roof_extruder.settings_); } if (! roof_paths.empty()) { @@ -4287,7 +4311,7 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L { constexpr bool force_comb_retract = false; gcode_layer.addTravel(bottom_polygons[0][0], force_comb_retract); - gcode_layer.addPolygonsByOptimizer(bottom_polygons, gcode_layer.configs_storage_.support_bottom_config); + gcode_layer.addPolygonsByOptimizer(bottom_polygons, gcode_layer.configs_storage_.support_bottom_config, bottom_extruder.settings_); } if (! bottom_paths.empty()) { diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index c1198d7f20..7856d9f393 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -14,6 +14,7 @@ #include "Application.h" //To communicate layer view data. #include "ExtruderTrain.h" +#include "PathAdapter.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. #include "Slice.h" #include "WipeScriptConfig.h" @@ -557,29 +558,54 @@ void LayerPlan::addPolygon( const Polygon& polygon, int start_idx, const bool backwards, + const Settings& settings, const GCodePathConfig& config, coord_t wall_0_wipe_dist, bool spiralize, const Ratio& flow_ratio, - bool always_retract) + bool always_retract, + bool scarf_seam, + bool smooth_speed) { - constexpr Ratio width_ratio = 1.0_r; // Not printed with variable line width. + constexpr bool is_closed = true; + constexpr bool is_candidate_small_feature = false; + Point2LL p0 = polygon[start_idx]; addTravel(p0, always_retract, config.z_offset); - const int direction = backwards ? -1 : 1; - for (size_t point_idx = 1; point_idx < polygon.size(); point_idx++) - { - Point2LL p1 = polygon[(start_idx + point_idx * direction + polygon.size()) % polygon.size()]; - addExtrusionMove(p1, config, SpaceFillType::Polygons, flow_ratio, width_ratio, spiralize); - p0 = p1; - } + + addWallWithScarfSeam( + PathAdapter(polygon, config.getLineWidth()), + start_idx, + settings, + config, + flow_ratio, + always_retract, + is_closed, + backwards, + is_candidate_small_feature, + scarf_seam, + smooth_speed, + [this, &config, &spiralize]( + const Point3LL& /*start*/, + const Point3LL& end, + const Ratio& speed_factor, + const Ratio& actual_flow_ratio, + const Ratio& line_width_ratio, + const coord_t /*distance_to_bridge_start*/) + { + constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; + constexpr bool travel_to_z = false; + + addExtrusionMove(end, config, SpaceFillType::Polygons, actual_flow_ratio, line_width_ratio, spiralize, speed_factor, fan_speed, travel_to_z); + }); + + if (polygon.size() > 2) { - addExtrusionMove(polygon[start_idx], config, SpaceFillType::Polygons, flow_ratio, width_ratio, spiralize); - if (wall_0_wipe_dist > 0) { // apply outer wall wipe p0 = polygon[start_idx]; + const int direction = backwards ? -1 : 1; int distance_traversed = 0; for (size_t point_idx = 1;; point_idx++) { @@ -611,13 +637,16 @@ void LayerPlan::addPolygon( void LayerPlan::addPolygonsByOptimizer( const Shape& polygons, const GCodePathConfig& config, + const Settings& settings, const ZSeamConfig& z_seam_config, coord_t wall_0_wipe_dist, bool spiralize, const Ratio flow_ratio, bool always_retract, bool reverse_order, - const std::optional start_near_location) + const std::optional start_near_location, + bool scarf_seam, + bool smooth_speed) { if (polygons.empty()) { @@ -630,20 +659,33 @@ void LayerPlan::addPolygonsByOptimizer( } orderOptimizer.optimize(); - if (! reverse_order) + const auto add_polygons + = [this, &config, &settings, &wall_0_wipe_dist, &spiralize, &flow_ratio, &always_retract, &scarf_seam, &smooth_speed](const auto& iterator_begin, const auto& iterator_end) { - for (const PathOrdering& path : orderOptimizer.paths_) + for (auto iterator = iterator_begin; iterator != iterator_end; ++iterator) { - addPolygon(*path.vertices_, path.start_vertex_, path.backwards_, config, wall_0_wipe_dist, spiralize, flow_ratio, always_retract); + addPolygon( + *iterator->vertices_, + iterator->start_vertex_, + iterator->backwards_, + settings, + config, + wall_0_wipe_dist, + spiralize, + flow_ratio, + always_retract, + scarf_seam, + smooth_speed); } + }; + + if (! reverse_order) + { + add_polygons(orderOptimizer.paths_.begin(), orderOptimizer.paths_.end()); } else { - for (int index = orderOptimizer.paths_.size() - 1; index >= 0; --index) - { - const PathOrdering& path = orderOptimizer.paths_[index]; - addPolygon(*path.vertices_, path.start_vertex_, path.backwards_, config, wall_0_wipe_dist, spiralize, flow_ratio, always_retract); - } + add_polygons(orderOptimizer.paths_.rbegin(), orderOptimizer.paths_.rend()); } } @@ -865,7 +907,7 @@ void LayerPlan::addWallLine( flow, width_factor, spiralize, - segmentIsOnOverhang(p0, p1) ? overhang_speed_factor : 1.0_r, + segmentIsOnOverhang(p0, p1) ? overhang_speed_factor : speed_factor, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } @@ -996,25 +1038,22 @@ 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); } +template void LayerPlan::addSplitWall( - const ExtrusionLine& wall, + const PathAdapter& wall, const coord_t wall_length, - size_t start_idx, - const int direction, + const size_t start_idx, const size_t max_index, - const Settings& settings, + const int direction, const GCodePathConfig& default_config, - const GCodePathConfig& roofing_config, - const GCodePathConfig& bridge_config, - const double flow_ratio, - const Ratio line_width_ratio, - 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 double flow_ratio, + const coord_t nominal_line_width, + const coord_t min_bridge_line_len, const auto scarf_seam_length, const auto scarf_seam_start_ratio, const auto scarf_split_distance, @@ -1024,13 +1063,16 @@ void LayerPlan::addSplitWall( const coord_t accelerate_length, const Ratio end_speed_ratio, const coord_t decelerate_length, - const bool is_scarf_closure) + const bool is_scarf_closure, + const bool compute_distance_to_bridge_start, + const AddExtrusionSegmentFunction& func_add_segment) { coord_t distance_to_bridge_start = 0; // will be updated before each line is processed - ExtrusionJunction p0 = wall[start_idx]; + Point2LL p0 = wall.pointAt(start_idx); + coord_t w0 = wall.lineWidthAt(start_idx); bool first_line = ! is_scarf_closure; bool first_split = ! is_scarf_closure; - Point3LL split_origin = p0.p_; + Point3LL split_origin = p0; if (! is_scarf_closure && scarf_seam_length > 0) { split_origin.z_ = scarf_max_z_offset; @@ -1044,16 +1086,24 @@ void LayerPlan::addSplitWall( 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()]; + const size_t actual_point_index = (wall.size() + start_idx + point_idx * direction) % wall.size(); + const Point2LL& p1 = wall.pointAt(actual_point_index); + const coord_t w1 = wall.lineWidthAt(actual_point_index); - if (! bridge_wall_mask_.empty()) + if constexpr (std::is_same_v) { - distance_to_bridge_start = computeDistanceToBridgeStart(wall, (wall.size() + start_idx + point_idx * direction - 1) % wall.size(), min_bridge_line_len); + // The bridging functionality has not been designed to work with anything else than ExtrusionLine objects, + // and there is no need to do it otherwise yet. So the compute_distance_to_bridge_start argument will + // just be ignored if using an other PathType (e.g. Polygon) + if (compute_distance_to_bridge_start && ! bridge_wall_mask_.empty()) + { + distance_to_bridge_start = computeDistanceToBridgeStart(wall.getPath(), (wall.size() + start_idx + point_idx * direction - 1) % wall.size(), min_bridge_line_len); + } } if (first_line) { - addTravel(p0.p_, always_retract); + addTravel(p0, always_retract); first_line = false; } @@ -1069,8 +1119,8 @@ void LayerPlan::addSplitWall( 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 delta_line_width = w1 - w0; + const Point2LL line_vector = p1 - p0; const coord_t line_length = vSize(line_vector); /* Calculate how much the line would deviate from the trapezoidal shape if printed at average width. @@ -1091,12 +1141,13 @@ void LayerPlan::addSplitWall( 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)); + const coord_t line_width = std::lrint(static_cast(w0) + average_progress * static_cast(delta_line_width)); + const Ratio line_width_ratio = static_cast(line_width) / nominal_line_width; + const Point2LL destination = p0 + 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 * line_width_ratio, spiralize, small_feature_speed_factor); + addExtrusionMove(destination, default_config, SpaceFillType::Polygons, flow_ratio, line_width_ratio, spiralize, small_feature_speed_factor); } else { @@ -1170,7 +1221,7 @@ void LayerPlan::addSplitWall( if (first_split) { // 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_); + addTravel(p0, always_retract, split_origin.z_); first_split = false; } } @@ -1198,20 +1249,13 @@ void LayerPlan::addSplitWall( } // now add the (sub-)segment - constexpr bool travel_to_z = false; - addWallLine( + func_add_segment( split_origin, split_destination, - settings, - default_config, - roofing_config, - bridge_config, - flow_ratio * scarf_segment_flow_ratio, - line_width * line_width_ratio, - non_bridge_line_volume, accelerate_speed_factor * decelerate_speed_factor, - distance_to_bridge_start, - travel_to_z); + flow_ratio * scarf_segment_flow_ratio, + line_width_ratio, + distance_to_bridge_start); wall_processed_distance = destination_position; piece_remaining_distance -= length_to_process; @@ -1227,6 +1271,109 @@ void LayerPlan::addSplitWall( } } +std::vector + LayerPlan::calculatePathsCoasting(const Settings& extruder_settings, const std::vector& paths, const Point3LL& current_position) const +{ + std::vector path_coastings; + path_coastings.resize(paths.size()); + + 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 coasting values which will be applied by the 'writePathWithCoasting' func. + // All of this is necessary since we split up paths because of scarf and acceleration-adjustments (start/end), so we need to have adjacency info. + + const double coasting_volume = extruder_settings.get("coasting_volume"); + const double coasting_min_volume = extruder_settings.get("coasting_min_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(); + })) + { + double accumulated_volume = 0.0; + bool chunk_coasting_point_reached = false; + bool chunk_min_volume_reached = false; + + for (const auto& [path_idx, path] : reversed_chunk) + { + if (path.isTravelPath()) + { + continue; + } + + PathCoasting& path_coasting = path_coastings[path_idx]; + const double path_extrusion_per_length = path.config.extrusion_mm3_per_mm * path.flow; + + for (const auto& [point_idx, point] : path.points | ranges::views::enumerate | ranges::views::reverse) + { + Point3LL previous_point; + if (point_idx > 0) + { + previous_point = path.points[point_idx - 1]; + } + else if (path_idx > 0) + { + previous_point = paths[path_idx - 1].points.back(); + } + else + { + previous_point = current_position; + } + + const double segment_length = INT2MM((point - previous_point).vSize()); + const double segment_volume = segment_length * path_extrusion_per_length; + + accumulated_volume += segment_volume; + + if (! chunk_coasting_point_reached && path_coasting.apply_coasting == ApplyCoasting::NoCoasting) + { + if (accumulated_volume >= coasting_volume) + { + const double start_pos_factor = (accumulated_volume - coasting_volume) / segment_volume; + Point3LL coasting_start_pos = cura::lerp(previous_point, point, start_pos_factor); + path_coasting = { ApplyCoasting::PartialCoasting, point_idx, coasting_start_pos }; + chunk_coasting_point_reached = true; + } + else if (point_idx == 0) // End of path reached (reverse iteration), coasting not fulfilled + { + path_coasting.apply_coasting = ApplyCoasting::CoastEntirePath; + } + } + + if (accumulated_volume >= coasting_min_volume) + { + chunk_min_volume_reached = true; + } + + if (chunk_min_volume_reached && chunk_coasting_point_reached) + { + break; + } + } + + if (chunk_min_volume_reached && chunk_coasting_point_reached) + { + break; + } + } + + if (! chunk_min_volume_reached) + { + // It turns out this chunk doesn't fit the minimum requirements for coasting, so skip it + for (const auto& [path_idx, path] : reversed_chunk) + { + path_coastings[path_idx].apply_coasting = ApplyCoasting::NoCoasting; + } + } + } + } + + return path_coastings; +} + 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; @@ -1308,39 +1455,35 @@ coord_t LayerPlan::computeDistanceToBridgeStart(const ExtrusionLine& wall, const return distance_to_bridge_start; } -void LayerPlan::addWall( - const ExtrusionLine& wall, +template +void LayerPlan::addWallWithScarfSeam( + const PathAdapter& 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 is_candidate_small_feature, const bool scarf_seam, - const bool smooth_speed) + const bool smooth_speed, + const AddExtrusionSegmentFunction& func_add_segment) { if (wall.empty()) { return; } - 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 + const bool actual_scarf_seam = scarf_seam && is_closed; 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 nominal_line_width = default_config.getLineWidth(); 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_length < small_feature_max_length; + const bool is_small_feature = (small_feature_max_length > 0) && (layer_nr_ == 0 || is_candidate_small_feature) && 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())); @@ -1368,25 +1511,23 @@ void LayerPlan::addWall( auto addSplitWallPass = [&](bool is_scarf_closure) { + constexpr bool compute_distance_to_bridge_start = true; + addSplitWall( - wall, + PathAdapter(wall), wall_length, start_idx, - direction, max_index, - settings, + direction, 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, + flow_ratio, + nominal_line_width, + min_bridge_line_len, layer_nr_ > 0 ? scarf_seam_length : 0, scarf_seam_start_ratio, scarf_split_distance, @@ -1396,7 +1537,9 @@ void LayerPlan::addWall( accelerate_length, end_speed_ratio, decelerate_length, - is_scarf_closure); + is_scarf_closure, + compute_distance_to_bridge_start, + func_add_segment); }; // First pass to add the wall with the scarf beginning and acceleration @@ -1407,6 +1550,67 @@ void LayerPlan::addWall( // Second pass to add the scarf closure addSplitWallPass(true); } +} + +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; + } + + double non_bridge_line_volume = max_non_bridge_line_volume; // assume extruder is fully pressurised before first non-bridge line is output + const coord_t min_bridge_line_len = settings.get("bridge_wall_min_length"); + + addWallWithScarfSeam( + PathAdapter(wall), + start_idx, + settings, + default_config, + flow_ratio, + always_retract, + is_closed, + is_reversed, + wall.inset_idx_ == 0, + scarf_seam, + smooth_speed, + [&](const Point3LL& start, + const Point3LL& end, + const Ratio& speed_factor, + const Ratio& actual_flow_ratio, + const Ratio& line_width_ratio, + const coord_t distance_to_bridge_start) + { + constexpr bool travel_to_z = false; + + addWallLine( + start, + end, + settings, + default_config, + roofing_config, + bridge_config, + actual_flow_ratio, + line_width_ratio, + non_bridge_line_volume, + speed_factor, + distance_to_bridge_start, + travel_to_z); + }); if (wall.size() >= 2) { @@ -2054,7 +2258,7 @@ double ExtruderPlan::getRetractTime(const GCodePath& path) return retraction_config_.distance / (path.retract ? retraction_config_.speed : retraction_config_.primeSpeed); } -std::pair ExtruderPlan::getPointToPointTime(const Point3LL& p0, const Point3LL& p1, const GCodePath& path) +std::pair ExtruderPlan::getPointToPointTime(const Point3LL& p0, const Point3LL& p1, const GCodePath& path) const { const double length = (p0 - p1).vSizeMM(); return { length, length / (path.config.getSpeed() * path.speed_factor) }; @@ -2369,57 +2573,9 @@ void LayerPlan::writeGCode(GCodeExport& gcode) extruder_plan.handleInserts(path_idx, gcode, cumulative_path_time); }; - 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(); - })) - { - 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_); - - 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 }; - } + const std::vector coasting_per_path = calculatePathsCoasting(extruder.settings_, paths, gcode.getPosition()); - coasting_left -= path_volume; - if (coasting_left <= 0) - { - break; - } - } - } - } - - for (int64_t path_idx = 0; path_idx < paths.size(); path_idx++) + for (size_t path_idx = 0; path_idx < paths.size(); path_idx++) { extruder_plan.handleInserts(path_idx, gcode); cumulative_path_time = 0.; // reset to 0 for current path. @@ -2612,7 +2768,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_adjust_per_path[path_idx]); + coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, insertTempOnTime, coasting_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 @@ -2769,133 +2925,59 @@ bool LayerPlan::writePathWithCoasting( GCodeExport& gcode, const size_t extruder_plan_idx, const size_t path_idx, - const coord_t layer_thickness, const std::function insertTempOnTime, - const std::pair coasting_adjust) + const PathCoasting& path_coasting) { - 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 = std::min(extruder.settings_.get("coasting_volume"), coasting_adjust.second); - if (coasting_volume <= 0) + if (path_coasting.apply_coasting == ApplyCoasting::NoCoasting) { return false; } + + size_t coasting_start_index; + Point3LL previous_position = gcode.getPosition(); + const ExtruderPlan& extruder_plan = extruder_plans_[extruder_plan_idx]; 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) - { - return false; - } - - coord_t coasting_min_dist_considered = MM2INT(0.1); // hardcoded setting for when to not perform coasting - - 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 - 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 - // /\ the minimal distance when coasting will coast the full coasting volume instead of linearly less with linearly smaller paths - - std::vector accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...) - accumulated_dist_per_point.push_back(0); - - coord_t accumulated_dist = 0; - - bool length_is_less_than_min_dist = true; - - 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 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 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); - - if (! acc_dist_idx_gt_coast_dist.has_value() && accumulated_dist >= coasting_dist) - { - acc_dist_idx_gt_coast_dist = backward_point_idx; // the newly added point - } - - if (accumulated_dist >= coasting_min_dist) - { - length_is_less_than_min_dist = false; - break; - } - - last = &point; - } - coasting_dist = std::min(coasting_dist, accumulated_dist); // if the path is shorter than coasting_dist, we should coast the whole path + const ExtruderTrain& extruder = Application::getInstance().current_slice_->scene.extruders[extruder_plan.extruder_nr_]; - if (accumulated_dist < coasting_min_dist_considered) + if (path_coasting.apply_coasting == ApplyCoasting::CoastEntirePath) { - return false; + coasting_start_index = 0; } - coord_t actual_coasting_dist = coasting_dist; - if (length_is_less_than_min_dist) + else { - // in this case accumulated_dist is the length of the whole path - actual_coasting_dist = accumulated_dist * coasting_dist / coasting_min_dist; - if (actual_coasting_dist == 0) // Downscaling due to Minimum Coasting Distance reduces coasting to less than 1 micron. - { - return false; // Skip coasting at all then. - } - for (acc_dist_idx_gt_coast_dist = 1; acc_dist_idx_gt_coast_dist.value() < accumulated_dist_per_point.size(); acc_dist_idx_gt_coast_dist.value()++) - { // search for the correct coast_dist_idx - if (accumulated_dist_per_point[acc_dist_idx_gt_coast_dist.value()] >= actual_coasting_dist) - { - break; - } - } - } + const double extrude_speed = path.config.getSpeed() * path.speed_factor * path.speed_back_pressure_factor; + coasting_start_index = path_coasting.coasting_start_index; - assert( - acc_dist_idx_gt_coast_dist.has_value() && acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size()); // something has gone wrong; coasting_min_dist < coasting_dist ? - - const size_t point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist.value(); - - 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 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); - } - - Point3LL prev_pt = gcode.getPositionXY(); - { // write normal extrude path: - for (size_t point_idx = 0; point_idx <= point_idx_before_start; point_idx++) + // write normal extrude path, followed by split extrusion to coasting starting point + for (size_t point_idx = 0; point_idx < coasting_start_index; point_idx++) { - auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); + auto [_, time] = extruder_plan.getPointToPointTime(previous_position, path.points[point_idx], path); insertTempOnTime(time, path_idx); 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]; + previous_position = path.points[point_idx]; } + writeExtrusionRelativeZ(gcode, start, extrude_speed, path.z_offset, path.getExtrusionMM3perMM(), path.config.type); - sendLineTo(path, start, extrude_speed); + sendLineTo(path, path_coasting.coasting_start_pos, extrude_speed); } // write coasting path - for (size_t point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++) + for (size_t point_idx = coasting_start_index; point_idx < path.points.size(); point_idx++) { - auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); + auto [_, time] = extruder_plan.getPointToPointTime(previous_position, path.points[point_idx], path); insertTempOnTime(time, path_idx); const Ratio coasting_speed_modifier = extruder.settings_.get("coasting_speed"); const Velocity speed = Velocity(coasting_speed_modifier * path.config.getSpeed()); writeTravelRelativeZ(gcode, path.points[point_idx], speed, path.z_offset); - prev_pt = path.points[point_idx]; + previous_position = path.points[point_idx]; } + return true; } diff --git a/src/PathAdapter.cpp b/src/PathAdapter.cpp new file mode 100644 index 0000000000..09a5437493 --- /dev/null +++ b/src/PathAdapter.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2024 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "PathAdapter.h" + +#include "utils/ExtrusionLine.h" + +namespace cura +{ + +template<> +const Point2LL& PathAdapter::pointAt(size_t index) const +{ + return path_.junctions_.at(index).p_; +} + +template<> +coord_t PathAdapter::lineWidthAt(size_t index) const +{ + return path_.junctions_.at(index).w_; +} + +template<> +const Point2LL& PathAdapter::pointAt(size_t index) const +{ + return path_.at(index); +} + +template<> +coord_t PathAdapter::lineWidthAt(size_t /*index*/) const +{ + return fixed_line_width_; +} + +} // namespace cura diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index 15a6b03cf8..beb6154d3e 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -124,7 +124,7 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage { constexpr bool force_comb_retract = false; layer.addTravel(ironing_polygons[0][0], force_comb_retract); - layer.addPolygonsByOptimizer(ironing_polygons, line_config, ZSeamConfig()); + layer.addPolygonsByOptimizer(ironing_polygons, line_config, mesh.settings, ZSeamConfig()); added = true; } if (! ironing_lines.empty()) diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index f9749b0d6e..da5d9661ef 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -417,7 +417,7 @@ const Point3LL& GCodeExport::getPosition() const } Point2LL GCodeExport::getPositionXY() const { - return Point2LL(current_position_.x_, current_position_.y_); + return current_position_.toPoint2LL(); } int GCodeExport::getPositionZ() const