diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index 7ff5300090..f23aae04bb 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -55,7 +55,8 @@ class InsetOrderOptimizer const size_t wall_0_extruder_nr, const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, - const std::vector& paths); + const std::vector& paths, + const Polygons& disallowed_areas_for_seams = {}); /*! * Adds the insets to the given layer plan. @@ -106,6 +107,7 @@ class InsetOrderOptimizer const ZSeamConfig& z_seam_config_; const std::vector& paths_; const LayerIndex layer_nr_; + Polygons disallowed_areas_for_seams_; std::vector> inset_polys_; // vector of vectors holding the inset polygons Polygons retraction_region_; // After printing an outer wall, move into this region so that retractions do not leave visible blobs. Calculated lazily if needed (see diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index df1cc07c7e..1a8c174cc7 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -61,6 +61,8 @@ class PathOrderOptimizer { public: using OrderablePath = PathOrdering; + /* Areas defined here are not allowed to have the start the prints */ + Polygons disallowed_area_for_seams; /*! * After optimizing, this contains the paths that need to be printed in the * correct order. @@ -110,7 +112,8 @@ class PathOrderOptimizer const Polygons* combing_boundary = nullptr, const bool reverse_direction = false, const std::unordered_multimap& order_requirements = no_order_requirements_, - const bool group_outer_walls = false) + const bool group_outer_walls = false, + const Polygons& disallowed_areas_for_seams = {}) : start_point_(start_point) , seam_config_(seam_config) , combing_boundary_((combing_boundary != nullptr && ! combing_boundary->empty()) ? combing_boundary : nullptr) @@ -118,6 +121,8 @@ class PathOrderOptimizer , reverse_direction_(reverse_direction) , _group_outer_walls(group_outer_walls) , order_requirements_(&order_requirements) + , disallowed_area_for_seams{ disallowed_areas_for_seams } + { } @@ -604,6 +609,52 @@ class PathOrderOptimizer return best_candidate; } + /** + * @brief Analyze the positions in a path and determine the next optimal position based on a proximity criterion. + * + * This function iteratively examines positions along the given path, checking if the position is close to 3D model. + * Each position is specified by an index, starting with `best_pos`. If the position is close to the model according to + * `isVertexCloseToPolygonPath` function, the function recursively calls itself with the next position. This process is + * repeated until all positions have been checked or `number_of_paths_analysed` becomes equal to `path_size`. + * If `number_of_paths_analysed` becomes equal to `path_size`, it logs a warning and returns the current best position. + * + * @param best_pos The index of the initial position for analysis in the path. + * @param path An OrderablePath instance containing the path to be examined. + * @param number_of_paths_analysed Optionally, the initial index of paths analysed. Defaults to 0. + * @return The index of the next optimal position in the path sequence. May be the same as the input `best_pos`, + * or may be incremented to a different location based on the proximity criterion. + * + * @note This function uses recursion to evaluate each position in the path. + * @note The process stops prematurely if no start path is found for the support z seam distance. + * This typically happens when the distance of the support seam from the model is bigger than all the support wall points. + */ + + size_t pathIfZseamIsInDisallowedArea(size_t best_pos, const OrderablePath& path, size_t number_of_paths_analysed) + { + size_t path_size = path.converted_->size(); + if (path_size > number_of_paths_analysed) + { + if (! disallowed_area_for_seams.empty()) + { + Point2LL current_candidate = (path.converted_)->at(best_pos); + if (disallowed_area_for_seams.inside(current_candidate, true)) + { + size_t next_best_position = (path_size > best_pos + 1) ? best_pos + 1 : 0; + number_of_paths_analysed += 1; + best_pos = pathIfZseamIsInDisallowedArea(next_best_position, path, number_of_paths_analysed); + } + } + } + else + { + spdlog::warn("No start path found for support z seam distance"); + // We can also calculate the best point to start at this point. + // This usually happens when the distance of support seam from model is bigger than the whole support wall points. + } + return best_pos; + } + + /*! * Find the vertex which will be the starting point of printing a polygon or * polyline. @@ -674,6 +725,7 @@ class PathOrderOptimizer // angles > 0 are convex (right turning) double corner_shift; + if (seam_config_.type_ == EZSeamType::SHORTEST) { // the more a corner satisfies our criteria, the closer it appears to be @@ -740,6 +792,10 @@ class PathOrderOptimizer } } + if (! disallowed_area_for_seams.empty()) + { + best_i = pathIfZseamIsInDisallowedArea(best_i, path, 0); + } return best_i; } diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index e055d99a9c..bbfdaf47eb 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -35,6 +35,7 @@ #include "utils/linearAlg2D.h" #include "utils/math.h" #include "utils/orderOptimizer.h" +#include "utils/polygonUtils.h" namespace cura { @@ -3456,7 +3457,26 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer const GCodePathConfig& config = configs[0]; constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; - const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + ZSeamConfig z_seam_config + = ZSeamConfig(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + Polygons disallowed_area_for_seams{}; + if (infill_extruder.settings_.get("support_z_seam_away_from_model")) + { + for (std::shared_ptr mesh_ptr : storage.meshes) + { + auto& mesh = *mesh_ptr; + for (auto& part : mesh.layers[gcode_layer.getLayerNr()].parts) + { + disallowed_area_for_seams.add(part.print_outline); + } + } + if (! disallowed_area_for_seams.empty()) + { + coord_t min_distance = infill_extruder.settings_.get("support_z_seam_min_distance"); + disallowed_area_for_seams = disallowed_area_for_seams.offset(min_distance, ClipperLib::jtRound); + } + } + InsetOrderOptimizer wall_orderer( *this, storage, @@ -3475,7 +3495,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer extruder_nr, extruder_nr, z_seam_config, - wall_toolpaths); + wall_toolpaths, + disallowed_area_for_seams); added_something |= wall_orderer.addToLayer(); } diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 82d4199d01..03ad5434cb 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -50,7 +50,8 @@ InsetOrderOptimizer::InsetOrderOptimizer( const size_t wall_0_extruder_nr, const size_t wall_x_extruder_nr, const ZSeamConfig& z_seam_config, - const std::vector& paths) + const std::vector& paths, + const Polygons& disallowed_areas_for_seams) : gcode_writer_(gcode_writer) , storage_(storage) , gcode_layer_(gcode_layer) @@ -70,6 +71,7 @@ InsetOrderOptimizer::InsetOrderOptimizer( , z_seam_config_(z_seam_config) , paths_(paths) , layer_nr_(gcode_layer.getLayerNr()) + , disallowed_areas_for_seams_{ disallowed_areas_for_seams } { } @@ -98,8 +100,15 @@ bool InsetOrderOptimizer::addToLayer() const auto group_outer_walls = settings_.get("group_outer_walls"); // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. - PathOrderOptimizer - order_optimizer(gcode_layer_.getLastPlannedPositionOrStartingPosition(), z_seam_config_, detect_loops, combing_boundary, reverse, order, group_outer_walls); + PathOrderOptimizer order_optimizer( + gcode_layer_.getLastPlannedPositionOrStartingPosition(), + z_seam_config_, + detect_loops, + combing_boundary, + reverse, + order, + group_outer_walls, + disallowed_areas_for_seams_); for (const auto& line : walls_to_be_added) {