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/LayerPlan.h b/include/LayerPlan.h index d373fff7ae..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. * @@ -400,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. @@ -438,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. @@ -532,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, @@ -819,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 @@ -849,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, @@ -878,7 +907,40 @@ 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 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/gcodeExport.h b/include/gcodeExport.h index 7b211f8516..0dd81541b8 100644 --- a/include/gcodeExport.h +++ b/include/gcodeExport.h @@ -409,6 +409,11 @@ class GCodeExport : public NoCopy */ bool needPrimeBlob() const; + /* + * Function is used to write the content of output_stream to the gcode file + */ + void flushOutputStream(); + private: /*! * Coordinates are build plate coordinates, which might be offsetted when extruder offsets are encoded in the gcode. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fcba3b052b..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()) { @@ -4437,6 +4461,7 @@ void FffGcodeWriter::finalize() } gcode.writeComment("End of Gcode"); + gcode.flushOutputStream(); /* the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB. gcode.writeCode("M25 ;Stop reading from this point on."); diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 56d96c0e2a..abd8540644 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; @@ -1411,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())); @@ -1471,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, @@ -1499,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 @@ -1510,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) { @@ -2719,14 +2820,14 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const Point2LL p1 = spiral_path.points[point_idx].toPoint2LL(); length += vSizeMM(p0 - p1); p0 = p1; - gcode.setZ(std::round(z_ + layer_thickness_ * length / totalLength)); + const coord_t z_offset = std::round(layer_thickness_ * length / totalLength); const double extrude_speed = speed * spiral_path.speed_back_pressure_factor; writeExtrusionRelativeZ( gcode, spiral_path.points[point_idx], extrude_speed, - path.z_offset, + path.z_offset + z_offset, spiral_path.getExtrusionMM3perMM(), spiral_path.config.type, update_extrusion_offset); 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/TreeSupport.cpp b/src/TreeSupport.cpp index 8268bc2e9c..77e7acdd19 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2037,10 +2037,11 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) { AABB hole_aabb = AABB(hole); hole_aabb.expand(EPSILON); - if(hole.size() > 1) + if (hole.size() > 1) { // The hole contains other branches! It can not be fully removed. - // This may not fully handle this case as there could be a situation where such a hole becomes invalid, but for now this is the best solution not requiring larger changes. + // This may not fully handle this case as there could be a situation where such a hole becomes invalid, but for now this is the best solution not requiring + // larger changes. holes_resting_outside[layer_idx].emplace(idx); } else if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls, hole_aabb)).empty()) @@ -2057,8 +2058,7 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_storage) for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) { // TODO should technically be outline: Check if this is fine either way as it would save an offset - if (hole_aabb.hit(AABB(hole2)) - && ! hole.intersection(PolygonUtils::clipPolygonWithAABB(hole2, hole_aabb)).empty()) + if (hole_aabb.hit(AABB(hole2)) && ! hole.intersection(PolygonUtils::clipPolygonWithAABB(hole2, hole_aabb)).empty()) { hole_rest_map[layer_idx][idx].emplace_back(idx2); } diff --git a/src/gcodeExport.cpp b/src/gcodeExport.cpp index 32eee9733f..da5d9661ef 100644 --- a/src/gcodeExport.cpp +++ b/src/gcodeExport.cpp @@ -1726,6 +1726,11 @@ void GCodeExport::finalize(const char* endCode) for (int n = 1; n < MAX_EXTRUDERS; n++) if (getTotalFilamentUsed(n) > 0) spdlog::info("Filament {}: {}", n + 1, int(getTotalFilamentUsed(n))); + flushOutputStream(); +} + +void GCodeExport::flushOutputStream() +{ output_stream_->flush(); } diff --git a/src/skin.cpp b/src/skin.cpp index 351290be93..894692c815 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -529,8 +529,7 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) part.infill_area_per_combine_per_density.emplace_back(); std::vector& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back(); const Shape more_dense_infill = infill_area.difference(less_dense_infill); - infill_area_per_combine_current_density.push_back( - simplifier.polygon(more_dense_infill.difference(sum_more_dense).offset(-infill_wall_width).offset(infill_wall_width))); + infill_area_per_combine_current_density.push_back(simplifier.polygon(more_dense_infill.difference(sum_more_dense))); if (is_connected) { sum_more_dense = sum_more_dense.unionPolygons(more_dense_infill); @@ -538,7 +537,7 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) } part.infill_area_per_combine_per_density.emplace_back(); std::vector& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back(); - infill_area_per_combine_current_density.push_back(simplifier.polygon(infill_area.difference(sum_more_dense).offset(-infill_wall_width).offset(infill_wall_width))); + infill_area_per_combine_current_density.push_back(simplifier.polygon(infill_area.difference(sum_more_dense))); part.infill_area_own = std::nullopt; // clear infill_area_own, it's not needed any more. assert(! part.infill_area_per_combine_per_density.empty() && "infill_area_per_combine_per_density is now initialized"); }