From d7d1f71f3b8f20d484d7c83ee5efb348970f1b46 Mon Sep 17 00:00:00 2001 From: Thomas Rahm <67757218+ThomasRahm@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:23:02 +0100 Subject: [PATCH] Implement functionality to select whether cradle lines should be printed towards the model, or away from it. --- include/PathOrderOptimizer.h | 12 ++++++-- include/SupportInfillPart.h | 11 +++++++- include/TreeSupport.h | 4 +-- include/settings/EnumSettings.h | 1 + src/FffGcodeWriter.cpp | 26 ++++++++++------- src/InsetOrderOptimizer.cpp | 5 +++- src/SupportInfillPart.cpp | 4 +-- src/TreeSupport.cpp | 49 +++++++++++++++++++++++++-------- src/sliceDataStorage.cpp | 2 +- 9 files changed, 83 insertions(+), 31 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index be8e61873b..7e3cfc8701 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -205,7 +205,7 @@ class PathOrderOptimizer // For some Z seam types the start position can be pre-computed. // This is faster since we don't need to re-compute the start position at each step then. - precompute_start &= seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; + precompute_start &= seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; if (precompute_start) { for (auto& path : paths_) @@ -584,7 +584,7 @@ class PathOrderOptimizer } const bool precompute_start - = seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::SHARPEST_CORNER; + = seam_config_.type_ == EZSeamType::RANDOM || seam_config_.type_ == EZSeamType::USER_SPECIFIED || seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED ||seam_config_.type_ == EZSeamType::SHARPEST_CORNER; if (! path->is_closed_ || ! precompute_start) // Find the start location unless we've already precomputed it. { path->start_vertex_ = findStartLocation(*path, start_position); @@ -676,6 +676,14 @@ class PathOrderOptimizer { if (! path.is_closed_) { + if (seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED) + { + if(getDirectDistance(path.converted_->back(), seam_config_.pos_) < getDirectDistance(path.converted_->front(), seam_config_.pos_)) + { + return path.converted_->size() - 1; // Back end is closer. + } + return 0; + } // For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. const coord_t back_distance = (combing_boundary_ == nullptr) ? getDirectDistance(path.converted_->back(), target_pos) : getCombingDistance(path.converted_->back(), target_pos); diff --git a/include/SupportInfillPart.h b/include/SupportInfillPart.h index 92143d23ab..753ae648af 100644 --- a/include/SupportInfillPart.h +++ b/include/SupportInfillPart.h @@ -4,6 +4,7 @@ #ifndef SUPPORT_INFILL_PART_H #define SUPPORT_INFILL_PART_H +#include #include #include "geometry/Polygon.h" @@ -38,8 +39,16 @@ class SupportInfillPart coord_t custom_line_distance_; //!< The distance between support infill lines. 0 means use the default line distance instead. bool use_fractional_config_; //!< Request to use the configuration used to fill a partial layer height here, instead of the normal full layer height configuration. EFillMethod custom_line_pattern_; + std::optional start_near_location; - SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate = 0, coord_t custom_line_distance = 0, EFillMethod custom_line_pattern = EFillMethod::NONE ); + SupportInfillPart(const SingleShape& outline, + coord_t support_line_width, + bool use_fractional_config, + int inset_count_to_generate = 0, + coord_t custom_line_distance = 0, + EFillMethod custom_line_pattern = EFillMethod::NONE, + std::optional start_near_location = std::optional() + ); const Shape& getInfillArea() const; }; diff --git a/include/TreeSupport.h b/include/TreeSupport.h index 619230c1de..bd10156a7d 100644 --- a/include/TreeSupport.h +++ b/include/TreeSupport.h @@ -332,7 +332,7 @@ class TreeSupport * \param support_roof_extra_wall_storage_fractional[in] Areas of roof that were projected one layer up, but roofs need to have a wall to print correctly. * \param fake_roof_areas_combined[out] All areas that contain the fake roofs. * \param cradle_base_areas[out] Copy of all cradle base areas. Already added to correct storage. - * \param cradle_support_line_areas[out] All cradle lines consisting of regular support. Will still have to be added as support (Done in generateSupportSkin) + * \param cradle_support_line_areas[out] All cradle lines consisting of regular support. Already added as support. * \param storage[in,out] The storage where the support should be stored. * \param cradle_data[in] All currently existing cradles, with its corresponding cradle lines. */ @@ -371,7 +371,7 @@ class TreeSupport * \param support_skin_storage[out] Areas where high density support should be generated. * \param fake_roof_areas_combined[in] All areas that contain the fake roofs. * \param cradle_base_areas[in] Copy of all cradle base areas. Already added to correct storage. - * \param cradle_support_line_areas[in] All cradle lines consisting of regular support. Will be to be added as support. + * \param cradle_support_line_areas[in] All cradle lines consisting of regular support. Already added as support. * \param hole_parts[in] Parts of holes, ordered by layer. * \param valid_holes[in] Indices of holes that rest on outer wall, by layer. * \param non_removable_holes[in] Indices of holes that can not be removed, by layer. diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index 650f6a06dc..a94403331e 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -70,6 +70,7 @@ enum class EZSeamType SHORTEST, USER_SPECIFIED, SHARPEST_CORNER, + INTERNAL_SPECIFIED, /* The 'Skirt/brim' type behaves like shortest, except it doesn't try to do tie-breaking for similar locations to * the last attempt, as that gives a different result when the seams are next to each other instead of on top. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 9f74df3369..2cf7ed25ed 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3425,8 +3425,10 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool retract_before_outer_wall = false; constexpr coord_t wipe_dist = 0; const LayerIndex layer_nr = gcode_layer.getLayerNr(); - ZSeamConfig z_seam_config - = ZSeamConfig(EZSeamType::SHORTEST, gcode_layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, false); + const ZSeamConfig z_seam_config(part.start_near_location.has_value() ? EZSeamType::INTERNAL_SPECIFIED : EZSeamType::SHORTEST, + part.start_near_location.has_value() ? part.start_near_location.value() : gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + false); Shape disallowed_area_for_seams{}; if (infill_extruder.settings_.get("support_z_seam_away_from_model") && (layer_nr >= 0)) { @@ -3580,7 +3582,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool spiralize = false; constexpr Ratio flow_ratio = 1.0_r; constexpr bool always_retract = false; - const std::optional start_near_location = std::optional(); gcode_layer.addPolygonsByOptimizer( support_polygons, @@ -3591,7 +3592,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer flow_ratio, always_retract, alternate_layer_print_direction, - start_near_location); + part.start_near_location); added_something = true; } @@ -3600,7 +3601,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer constexpr bool enable_travel_optimization = false; constexpr coord_t wipe_dist = 0; constexpr Ratio flow_ratio = 1.0; - const std::optional near_start_location = std::optional(); constexpr double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT; gcode_layer.addLinesByOptimizer( @@ -3610,7 +3610,7 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer enable_travel_optimization, wipe_dist, flow_ratio, - near_start_location, + part.start_near_location, fan_speed, alternate_layer_print_direction); @@ -3715,6 +3715,10 @@ bool FffGcodeWriter::addSupportRoofsToGCode( constexpr coord_t pocket_size = 0; const coord_t max_resolution = roof_extruder.settings_.get("meshfix_maximum_resolution"); const coord_t max_deviation = roof_extruder.settings_.get("meshfix_maximum_deviation"); + const ZSeamConfig z_seam_config(roof_part.start_near_location.has_value() ? EZSeamType::INTERNAL_SPECIFIED : EZSeamType::SHORTEST, + roof_part.start_near_location.has_value() ? roof_part.start_near_location.value() : gcode_layer.getLastPlannedPositionOrStartingPosition(), + EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE, + false); coord_t support_roof_line_distance = roof_part.custom_line_distance_ == 0 ? roof_extruder.settings_.get("support_roof_line_distance") : roof_part.custom_line_distance_; const coord_t support_roof_line_width = roof_extruder.settings_.get("support_roof_line_width"); @@ -3772,20 +3776,19 @@ bool FffGcodeWriter::addSupportRoofsToGCode( 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, z_seam_config); } 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, z_seam_config); } if (! roof_paths.empty()) { const GCodePathConfig& config = current_roof_config; 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); InsetOrderOptimizer wall_orderer( *this, @@ -3808,7 +3811,10 @@ bool FffGcodeWriter::addSupportRoofsToGCode( roof_paths, storage.getModelBoundingBox().flatten().getMiddle());wall_orderer.addToLayer(); } - gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines); + constexpr bool enable_travel_optimization = false; + constexpr coord_t wipe_dist = 0; + constexpr Ratio flow_ratio = 1.0; + gcode_layer.addLinesByOptimizer(roof_lines, current_roof_config, (pattern == EFillMethod::ZIG_ZAG) ? SpaceFillType::PolyLines : SpaceFillType::Lines, enable_travel_optimization, wipe_dist, flow_ratio, roof_part.start_near_location); } } return added_support; diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index ad036f62b9..581933e125 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -183,6 +183,9 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed case EZSeamType::USER_SPECIFIED: request_point = z_seam_config_.pos_; break; + case EZSeamType::INTERNAL_SPECIFIED: + request_point = z_seam_config_.pos_; + break; case EZSeamType::SHORTEST: request_point = gcode_layer_.getLastPlannedPositionOrStartingPosition(); break; @@ -195,7 +198,7 @@ std::optional InsetOrderOptimizer::insertSeamPoint(ExtrusionLine& closed size_t closest_junction_idx = 0; coord_t closest_distance_sqd = std::numeric_limits::max(); bool should_reclaculate_closest = false; - if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED) + if (z_seam_config_.type_ == EZSeamType::USER_SPECIFIED || z_seam_config_.type_ == EZSeamType::INTERNAL_SPECIFIED) { // For user-defined seams you usually don't _actually_ want the _closest_ point, per-se, // since you want the seam-line to be continuous in 3D space. diff --git a/src/SupportInfillPart.cpp b/src/SupportInfillPart.cpp index 02d4d4f04f..9ab33d8ca2 100644 --- a/src/SupportInfillPart.cpp +++ b/src/SupportInfillPart.cpp @@ -8,7 +8,7 @@ using namespace cura; -SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern) +SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support_line_width, bool use_fractional_config, int inset_count_to_generate, coord_t custom_line_distance, EFillMethod custom_line_pattern, std::optional start_near_location) : outline_(outline) , outline_boundary_box_(outline) , support_line_width_(support_line_width) @@ -16,7 +16,7 @@ SupportInfillPart::SupportInfillPart(const SingleShape& outline, coord_t support , custom_line_distance_(custom_line_distance) , custom_line_pattern_(custom_line_pattern) , use_fractional_config_(use_fractional_config) - + , start_near_location(start_near_location) { infill_area_per_combine_per_density_.clear(); diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 4467f41c5f..560d54bd0c 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2399,11 +2399,14 @@ void TreeSupport::prepareSupportAreas( SliceDataStorage& storage, std::vector>& cradle_data) { + using ShapeWithStart = std::pair; const auto t_start = std::chrono::high_resolution_clock::now(); const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5; // based on calculation in WallToolPath const double small_area_length = INT2MM(static_cast(config.support_line_width) / 2); + const bool print_cradle_towards_model = true; //todo make setting - std::vector cradle_support_line_roof_areas(support_layer_storage.size()); // All cradle lines that have to be added as roof + std::vector> cradle_support_line_roof_areas_with_start(support_layer_storage.size()); // All cradle lines that have to be added as roof + std::vector> cradle_support_line_areas_with_start(support_layer_storage.size()); // All cradle lines that have to be added as roof std::vector cradle_line_xy_distance_areas(support_layer_storage.size()); // All cradle lines offset by xy distance. std::vector missing_cradle_line_xy_distance_areas(support_layer_storage.size()); // All missing (because of cradle z distance) cradle lines offset by xy distance. @@ -2453,12 +2456,20 @@ void TreeSupport::prepareSupportAreas( LayerIndex previous_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].back().layer_idx_; for (int64_t height_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - 1; height_idx >= 0; height_idx--) { - Shape line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_; + if(cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_.empty()) + { + continue; + } + + SingleShape line_area = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].area_.splitIntoParts(false).front(); //todo prettier. bool is_roof = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_roof_; LayerIndex cradle_line_layer_idx = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].layer_idx_; bool is_base = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].is_base_; bool was_line_above = height_idx + 1 < cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() && ! cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx + 1].is_base_; + Point2LL front = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].line_.front(); + Point2LL back = cradle_data[layer_idx][cradle_idx]->lines_[line_idx][height_idx].line_.back(); + if (was_line_above) { for (LayerIndex xy_dist_layer_idx = previous_layer_idx - 1; xy_dist_layer_idx > cradle_line_layer_idx; xy_dist_layer_idx--) @@ -2481,11 +2492,11 @@ void TreeSupport::prepareSupportAreas( { std::lock_guard critical_section_cradle(critical_support_roof_storage); - if (cradle_support_line_roof_areas.size() <= layer_idx) + if (cradle_support_line_roof_areas_with_start.size() <= layer_idx) { - cradle_support_line_roof_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + cradle_support_line_roof_areas_with_start.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } - cradle_support_line_roof_areas[cradle_line_layer_idx].push_back(line_area); + cradle_support_line_roof_areas_with_start[cradle_line_layer_idx].emplace_back(line_area, print_cradle_towards_model ? back : front); } else { @@ -2496,6 +2507,12 @@ void TreeSupport::prepareSupportAreas( cradle_support_line_areas.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); } cradle_support_line_areas[cradle_line_layer_idx].push_back(line_area); + + if (cradle_support_line_areas_with_start.size() <= layer_idx) + { + cradle_support_line_areas_with_start.resize(layer_idx + 1 + cradle_data[layer_idx][cradle_idx]->lines_[line_idx].size() - height_idx); + } + cradle_support_line_areas_with_start[cradle_line_layer_idx].emplace_back(line_area, print_cradle_towards_model ? back : front); } if (! is_base) { @@ -2569,7 +2586,6 @@ void TreeSupport::prepareSupportAreas( cradle_line_xy_distance_areas[layer_idx] = cradle_line_xy_distance_areas[layer_idx].unionPolygons(); cradle_base_areas[layer_idx] = cradle_base_areas[layer_idx].unionPolygons(); - cradle_support_line_roof_areas[layer_idx] = cradle_support_line_roof_areas[layer_idx].unionPolygons(); // If areas are overwriting others in can will influence where support skin will be generated. So the differences have to be calculated here. if (! storage.support.supportLayers[layer_idx].support_roof.empty()) { @@ -2605,14 +2621,24 @@ void TreeSupport::prepareSupportAreas( break; } } - Shape cradle_lines_roof = cradle_support_line_roof_areas[layer_idx].unionPolygons(); Shape remove_from_next_roof = storage.support.supportLayers[layer_idx].getTotalAreaFromParts(storage.support.supportLayers[layer_idx].support_roof).unionPolygons(); if (! support_free_areas[layer_idx].empty()) { remove_from_next_roof.push_back(support_free_areas[layer_idx]); } - //Remove only already added roof from line areas. Should not be needed, but better safe than sorry. - cradle_lines_roof = cradle_lines_roof.difference(remove_from_next_roof); + + //Add cradle lines. These need start hints, so its best done early! + for(ShapeWithStart& line : cradle_support_line_roof_areas_with_start[layer_idx]) + { + storage.support.supportLayers[layer_idx].support_roof.emplace_back(line.first, config.support_roof_line_width, false, std::min(1, config.support_roof_wall_count), 0, EFillMethod::NONE, std::optional(line.second)); + remove_from_next_roof.push_back(line.first); + } + + for(ShapeWithStart& line : cradle_support_line_areas_with_start[layer_idx]) + { + storage.support.supportLayers[layer_idx].support_infill_parts.emplace_back(line.first, config.support_line_width, false, std::min(1, config.support_wall_count), 0, EFillMethod::NONE, std::optional(line.second)); + } + cradle_support_line_areas[layer_idx] = cradle_support_line_areas[layer_idx].unionPolygons().difference(remove_from_next_roof); //Collect remaining parts that non cradle line roof areas may not intersect with. @@ -2627,11 +2653,10 @@ void TreeSupport::prepareSupportAreas( if (config.support_roof_wall_count) { roof = roof.difference(remove_from_next_roof); - roof = roof.unionPolygons(cradle_lines_roof); } else { - roof_extra_wall = roof_extra_wall.unionPolygons(cradle_lines_roof); + roof_extra_wall = roof_extra_wall.unionPolygons(); roof = roof.difference(remove_from_next_roof.unionPolygons(roof_extra_wall)); } @@ -2959,7 +2984,7 @@ void TreeSupport::generateSupportSkin( [&](const LayerIndex layer_idx) { support_skin_storage[layer_idx] = support_skin_storage[layer_idx].unionPolygons(); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons(cradle_support_line_areas[layer_idx]).difference(support_skin_storage[layer_idx]); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].unionPolygons().difference(support_skin_storage[layer_idx]); if(layer_idx > 0) { diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index f5ceffb5c1..14e8e3e74b 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -756,7 +756,7 @@ void SupportLayer::excludeAreasFromSupportInfillAreas(std::vector