diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 4c4061c37b..251712c8fc 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -295,9 +295,11 @@ class FffGcodeWriter : public NoCopy * * \param[in] storage where the slice data is stored. * \param current_extruder The current extruder with which we last printed + * \param global_extruders_used The extruders that are at some point used for the print job * \return The order of extruders for a layer beginning with \p current_extruder */ - std::vector getUsedExtrudersOnLayer(const SliceDataStorage& storage, const size_t start_extruder, const LayerIndex& layer_nr) const; + std::vector + getUsedExtrudersOnLayer(const SliceDataStorage& storage, const size_t start_extruder, const LayerIndex& layer_nr, const std::vector& global_extruders_used) const; /*! * Calculate in which order to plan the meshes of a specific extruder diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 61a0beb901..b11522643d 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -28,8 +28,8 @@ class LayerPlan; class PrimeTower { private: - using MovesByExtruder = std::vector; - using MovesByLayer = std::vector; + using MovesByExtruder = std::map; + using MovesByLayer = std::map>; size_t extruder_count_; //!< Number of extruders @@ -79,10 +79,12 @@ class PrimeTower */ PrimeTower(); + void initializeExtruders(const std::vector& used_extruders); + /*! * Check whether we actually use the prime tower. */ - void checkUsed(const SliceDataStorage& storage); + void checkUsed(); /*! * Generate the prime tower area to be used on each layer diff --git a/include/infill/NoZigZagConnectorProcessor.h b/include/infill/NoZigZagConnectorProcessor.h index 002fb0fb7b..6e59dcdaa5 100644 --- a/include/infill/NoZigZagConnectorProcessor.h +++ b/include/infill/NoZigZagConnectorProcessor.h @@ -29,7 +29,7 @@ class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor } void registerVertex(const Point2LL& vertex); - void registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index); + void registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index, coord_t min_distance_to_scanline); void registerPolyFinished(); }; diff --git a/include/infill/ZigzagConnectorProcessor.h b/include/infill/ZigzagConnectorProcessor.h index 513ad9d8f1..5bc629b685 100644 --- a/include/infill/ZigzagConnectorProcessor.h +++ b/include/infill/ZigzagConnectorProcessor.h @@ -137,7 +137,7 @@ class ZigzagConnectorProcessor * \param intersection The intersection * \param scanline_index Index of the current scanline */ - virtual void registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index); + virtual void registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index, coord_t min_distance_to_scanline); /*! * Handle the end of a polygon and prepare for the next. @@ -166,17 +166,6 @@ class ZigzagConnectorProcessor */ bool shouldAddCurrentConnector(int start_scanline_idx, int end_scanline_idx) const; - /*! - * Checks whether two points are separated at least by "threshold" microns. - * If they are far away from each other enough, the line represented by the two points - * will be added; In case they are close, the second point will be set to be the same - * as the first and this line won't be added. - * - * \param first_point The first of the points - * \param second_point The second of the points - */ - void checkAndAddZagConnectorLine(Point2LL* first_point, Point2LL* second_point); - /*! * Adds a Zag connector represented by the given points. The last line of the connector will not be * added if the given connector is an end piece and "connected_endpieces" is not enabled. @@ -186,6 +175,8 @@ class ZigzagConnectorProcessor */ void addZagConnector(std::vector& points, bool is_endpiece); + bool handleConnectorTooCloseToSegment(const coord_t scanline_x, const coord_t min_distance_to_scanline); + protected: const PointMatrix& rotation_matrix_; //!< The rotation matrix used to enforce the infill angle Polygons& result_; //!< The result of the computation diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index e9e4bed92d..c9a858d8e4 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1552,12 +1552,13 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor } size_t extruder_count = Application::getInstance().current_slice_->scene.extruders.size(); + const std::vector extruders_used = storage.getExtrudersUsed(); const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; PrimeTowerMethod prime_tower_mode = mesh_group_settings.get("prime_tower_mode"); for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr < static_cast(storage.print_layer_count); layer_nr++) { std::vector>& extruder_order_per_layer_here = (layer_nr < 0) ? extruder_order_per_layer_negative_layers : extruder_order_per_layer; - std::vector extruder_order = getUsedExtrudersOnLayer(storage, last_extruder, layer_nr); + std::vector extruder_order = getUsedExtrudersOnLayer(storage, last_extruder, layer_nr, extruders_used); extruder_order_per_layer_here.push_back(extruder_order); if (! extruder_order.empty()) @@ -1582,10 +1583,14 @@ void FffGcodeWriter::calculatePrimeLayerPerExtruder(const SliceDataStorage& stor } } -std::vector FffGcodeWriter::getUsedExtrudersOnLayer(const SliceDataStorage& storage, const size_t start_extruder, const LayerIndex& layer_nr) const +std::vector FffGcodeWriter::getUsedExtrudersOnLayer( + const SliceDataStorage& storage, + const size_t start_extruder, + const LayerIndex& layer_nr, + const std::vector& global_extruders_used) const { const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; - size_t extruder_count = Application::getInstance().current_slice_->scene.extruders.size(); + size_t extruder_count = global_extruders_used.size(); assert(static_cast(extruder_count) > 0); std::vector ret; std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); @@ -1610,7 +1615,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer(const SliceData ordered_extruders.push_back(start_extruder); for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - if (extruder_nr != start_extruder) + if (extruder_nr != start_extruder && global_extruders_used[extruder_nr]) { ordered_extruders.push_back(extruder_nr); } diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index b0d5ff5ff2..24e23b547a 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -404,6 +404,8 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper); + storage.primeTower.initializeExtruders(storage.getExtrudersUsed()); + AreaSupport::generateOverhangAreas(storage); AreaSupport::generateSupportAreas(storage); TreeSupport tree_support_generator(storage); diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 57dc4d9853..f9ebcb4f4c 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -52,35 +52,37 @@ PrimeTower::PrimeTower() && scene.current_mesh_group->settings.get("prime_tower_size") > 10; would_have_actual_tower_ = enabled_; // Assume so for now. +} - extruder_count_ = scene.extruders.size(); - extruder_order_.resize(extruder_count_); - for (unsigned int extruder_nr = 0; extruder_nr < extruder_count_; extruder_nr++) +void PrimeTower::initializeExtruders(const std::vector& used_extruders) +{ + // Add used extruders in default order, then sort. + for (unsigned int extruder_nr = 0; extruder_nr < used_extruders.size(); extruder_nr++) { - extruder_order_[extruder_nr] = extruder_nr; // Start with default order, then sort. + if (used_extruders[extruder_nr]) + { + extruder_order_.push_back(extruder_nr); + } } + + extruder_count_ = extruder_order_.size(); + // Sort from high adhesion to low adhesion. - const Scene* scene_pointer = &scene; // Communicate to lambda via pointer to prevent copy. + const Scene& scene = Application::getInstance().current_slice_->scene; std::stable_sort( extruder_order_.begin(), extruder_order_.end(), - [scene_pointer](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool + [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool { - const Ratio adhesion_a = scene_pointer->extruders[extruder_nr_a].settings_.get("material_adhesion_tendency"); - const Ratio adhesion_b = scene_pointer->extruders[extruder_nr_b].settings_.get("material_adhesion_tendency"); + const Ratio adhesion_a = scene.extruders[extruder_nr_a].settings_.get("material_adhesion_tendency"); + const Ratio adhesion_b = scene.extruders[extruder_nr_b].settings_.get("material_adhesion_tendency"); return adhesion_a < adhesion_b; }); } -void PrimeTower::checkUsed(const SliceDataStorage& storage) +void PrimeTower::checkUsed() { - std::vector extruder_is_used = storage.getExtrudersUsed(); - size_t used_extruder_count = 0; - for (bool is_used : extruder_is_used) - { - used_extruder_count += is_used; - } - if (used_extruder_count <= 1) + if (extruder_count_ <= 1) { enabled_ = false; } @@ -108,7 +110,7 @@ void PrimeTower::generateGroundpoly() void PrimeTower::generatePaths(const SliceDataStorage& storage) { - checkUsed(storage); + checkUsed(); const int raft_total_extra_layers = Raft::getTotalExtraLayers(); would_have_actual_tower_ = storage.max_print_height_second_to_last_extruder @@ -138,9 +140,13 @@ void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_inse const coord_t base_height = std::max(scene.settings.get("prime_tower_base_height"), has_raft ? layer_height : 0); const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); - prime_moves_.resize(extruder_count_); - base_extra_moves_.resize(extruder_count_); - inset_extra_moves_.resize(extruder_count_); + for (size_t extruder_nr : extruder_order_) + { + // By default, add empty moves for every extruder + prime_moves_[extruder_nr]; + base_extra_moves_[extruder_nr]; + inset_extra_moves_[extruder_nr]; + } coord_t cumulative_inset = 0; // Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder. for (size_t extruder_nr : extruder_order_) @@ -226,8 +232,6 @@ void PrimeTower::generatePaths_sparseInfill(const std::vector& cumulati if (method == PrimeTowerMethod::INTERLEAVED || method == PrimeTowerMethod::NORMAL) { - const size_t nb_extruders = scene.extruders.size(); - // Pre-compute radiuses of each extruder ring std::vector rings_radii; const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); @@ -242,9 +246,9 @@ void PrimeTower::generatePaths_sparseInfill(const std::vector& cumulati // Generate all possible extruders combinations, e.g. if there are 4 extruders, we have combinations // 0 / 0-1 / 0-1-2 / 0-1-2-3 / 1 / 1-2 / 1-2-3 / 2 / 2-3 / 3 // A combination is represented by a bitmask - for (size_t first_extruder_idx = 0; first_extruder_idx < nb_extruders; ++first_extruder_idx) + for (size_t first_extruder_idx = 0; first_extruder_idx < extruder_count_; ++first_extruder_idx) { - size_t nb_extruders_sparse = method == PrimeTowerMethod::NORMAL ? first_extruder_idx + 1 : nb_extruders; + size_t nb_extruders_sparse = method == PrimeTowerMethod::NORMAL ? first_extruder_idx + 1 : extruder_count_; for (size_t last_extruder_idx = first_extruder_idx; last_extruder_idx < nb_extruders_sparse; ++last_extruder_idx) { @@ -444,7 +448,7 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext { // Actual prime pattern const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[extruder_nr]; - const Polygons& pattern = prime_moves_[extruder_nr]; + const Polygons& pattern = prime_moves_.at(extruder_nr); gcode_layer.addPolygonsByOptimizer(pattern, config); } } @@ -454,7 +458,7 @@ bool PrimeTower::addToGcode_base(LayerPlan& gcode_layer, const size_t extruder_n const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + raft_total_extra_layers; - const std::vector& pattern_extra_brim = base_extra_moves_[extruder_nr]; + const std::vector& pattern_extra_brim = base_extra_moves_.at(extruder_nr); if (absolute_layer_number < pattern_extra_brim.size()) { // Extra rings for stronger base @@ -477,7 +481,7 @@ bool PrimeTower::addToGcode_inset(LayerPlan& gcode_layer, const size_t extruder_ if (absolute_layer_number == 0) // Extra-adhesion on very first layer only { - const Polygons& pattern_extra_inset = inset_extra_moves_[extruder_nr]; + const Polygons& pattern_extra_inset = inset_extra_moves_.at(extruder_nr); if (! pattern_extra_inset.empty()) { const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[extruder_nr]; diff --git a/src/infill.cpp b/src/infill.cpp index c085307622..7164da9741 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -746,12 +746,12 @@ void Infill::generateLinearBasedInfill( for (int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction) { - int x = scanline_idx * line_distance + shift; - int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); + const int x = scanline_idx * line_distance + shift; + const int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!"); cut_list[scanline_idx - scanline_min_idx].push_back(y); Point2LL scanline_linesegment_intersection(x, y); - zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx); + zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx, line_distance / 4); crossings_per_scanline[scanline_idx - min_scanline_index].emplace_back(scanline_linesegment_intersection, poly_idx, point_idx); } zigzag_connector_processor.registerVertex(p1); diff --git a/src/infill/NoZigZagConnectorProcessor.cpp b/src/infill/NoZigZagConnectorProcessor.cpp index 1ffa6532f4..25cb0fa48e 100644 --- a/src/infill/NoZigZagConnectorProcessor.cpp +++ b/src/infill/NoZigZagConnectorProcessor.cpp @@ -14,7 +14,7 @@ void NoZigZagConnectorProcessor::registerVertex(const Point2LL&) // No need to add anything. } -void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point2LL&, int) +void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point2LL&, int, coord_t) { // No need to add anything. } diff --git a/src/infill/ZigzagConnectorProcessor.cpp b/src/infill/ZigzagConnectorProcessor.cpp index 7f75b608ff..4e28dee774 100644 --- a/src/infill/ZigzagConnectorProcessor.cpp +++ b/src/infill/ZigzagConnectorProcessor.cpp @@ -83,8 +83,26 @@ bool ZigzagConnectorProcessor::shouldAddCurrentConnector(int start_scanline_idx, return should_add; } +bool ZigzagConnectorProcessor::handleConnectorTooCloseToSegment(const coord_t scanline_x, const coord_t min_distance_to_scanline) +{ + if (current_connector_.empty()) + { + return false; + } + else + { + return std::find_if( + current_connector_.begin(), + current_connector_.end(), + [scanline_x, min_distance_to_scanline](const Point2LL& point) + { + return std::abs(point.X - scanline_x) >= min_distance_to_scanline; + }) + == current_connector_.end(); + } +} -void ZigzagConnectorProcessor::registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index) +void ZigzagConnectorProcessor::registerScanlineSegmentIntersection(const Point2LL& intersection, int scanline_index, coord_t min_distance_to_scanline) { if (is_first_connector_) { @@ -97,7 +115,7 @@ void ZigzagConnectorProcessor::registerScanlineSegmentIntersection(const Point2L else { // add the current connector if needed - if (shouldAddCurrentConnector(last_connector_index_, scanline_index)) + if (shouldAddCurrentConnector(last_connector_index_, scanline_index) && ! handleConnectorTooCloseToSegment(intersection.X, min_distance_to_scanline)) { const bool is_this_endpiece = scanline_index == last_connector_index_; current_connector_.push_back(intersection); diff --git a/src/skin.cpp b/src/skin.cpp index 842a6e576f..32469b2766 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -14,6 +14,7 @@ #include "settings/types/Angle.h" //For the infill support angle. #include "settings/types/Ratio.h" #include "sliceDataStorage.h" +#include "utils/Simplify.h" #include "utils/math.h" #include "utils/polygonUtils.h" @@ -465,9 +466,12 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) const LayerIndex mesh_min_layer = mesh.settings.get("initial_bottom_layers"); const LayerIndex mesh_max_layer = mesh.layers.size() - 1 - mesh.settings.get("top_layers"); + const Simplify simplifier(mesh.settings.get("infill_extruder_nr").settings_); + const auto infill_wall_count = mesh.settings.get("infill_wall_line_count"); const auto infill_wall_width = mesh.settings.get("infill_line_width"); const auto infill_overlap = mesh.settings.get("infill_overlap_mm"); + const auto is_connected = mesh.settings.get("zig_zaggify_infill") || mesh.settings.get("infill_pattern") == EFillMethod::ZIG_ZAG; for (LayerIndex layer_idx = 0; layer_idx < static_cast(mesh.layers.size()); layer_idx++) { // loop also over layers which don't contain infill cause of bottom_ and top_layer to initialize their infill_area_per_combine_per_density SliceLayer& layer = mesh.layers[layer_idx]; @@ -494,6 +498,7 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh) continue; } Polygons less_dense_infill = infill_area; // one step less dense with each infill_step + Polygons sum_more_dense; // NOTE: Only used for zig-zag or connected fills. for (size_t infill_step = 0; infill_step < max_infill_steps; infill_step++) { LayerIndex min_layer = layer_idx + infill_step * gradual_infill_step_layer_count + static_cast(layer_skip_count); @@ -526,11 +531,16 @@ 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 Polygons more_dense_infill = infill_area.difference(less_dense_infill); - infill_area_per_combine_current_density.push_back(more_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))); + if (is_connected) + { + sum_more_dense = sum_more_dense.unionPolygons(more_dense_infill); + } } 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(infill_area); + infill_area_per_combine_current_density.push_back(simplifier.polygon(infill_area.difference(sum_more_dense).offset(-infill_wall_width).offset(infill_wall_width))); 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"); } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index b369dbb082..cfc4d32c08 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -362,13 +362,9 @@ Polygons SliceDataStorage::getLayerOutlines( total.add(support_layer.support_roof); } } - int prime_tower_outer_extruder_nr = primeTower.extruder_order_[0]; - if (include_prime_tower && (extruder_nr == -1 || extruder_nr == prime_tower_outer_extruder_nr)) + if (include_prime_tower && primeTower.enabled_ && (extruder_nr == -1 || (! primeTower.extruder_order_.empty() && extruder_nr == primeTower.extruder_order_[0]))) { - if (primeTower.enabled_) - { - total.add(primeTower.getOuterPoly(layer_nr)); - } + total.add(primeTower.getOuterPoly(layer_nr)); } return total; } diff --git a/src/support.cpp b/src/support.cpp index 7488c6832f..f5ad514faa 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -187,6 +187,8 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) const size_t max_density_steps = infill_extruder.settings_.get("gradual_support_infill_steps"); const coord_t wall_width = infill_extruder.settings_.get("support_line_width"); + const bool is_connected = infill_extruder.settings_.get("zig_zaggify_infill") || infill_extruder.settings_.get("infill_pattern") == EFillMethod::ZIG_ZAG; + const Simplify simplifier(infill_extruder.settings_); // no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density] double layer_skip_count{ 8.0 }; // skip every so many layers as to ignore small gaps in the model making computation more easy @@ -234,6 +236,7 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) // calculate density areas for this island Polygons less_dense_support = infill_area; // one step less dense with each density_step + Polygons sum_more_dense; // NOTE: Only used for zig-zag or connected fills. for (unsigned int density_step = 0; density_step < max_density_steps; ++density_step) { LayerIndex actual_min_layer{ layer_nr + density_step * gradual_support_step_layer_count + static_cast(layer_skip_count) }; @@ -292,12 +295,16 @@ void AreaSupport::generateGradualSupport(SliceDataStorage& storage) support_infill_part.infill_area_per_combine_per_density_.emplace_back(); std::vector& support_area_current_density = support_infill_part.infill_area_per_combine_per_density_.back(); const Polygons more_dense_support = infill_area.difference(less_dense_support); - support_area_current_density.push_back(more_dense_support); + support_area_current_density.push_back(simplifier.polygon(more_dense_support.difference(sum_more_dense))); + if (is_connected) + { + sum_more_dense = sum_more_dense.unionPolygons(more_dense_support); + } } support_infill_part.infill_area_per_combine_per_density_.emplace_back(); std::vector& support_area_current_density = support_infill_part.infill_area_per_combine_per_density_.back(); - support_area_current_density.push_back(infill_area); + support_area_current_density.push_back(simplifier.polygon(infill_area.difference(sum_more_dense))); assert(support_infill_part.infill_area_per_combine_per_density_.size() != 0 && "support_infill_part.infill_area_per_combine_per_density should now be initialized"); #ifdef DEBUG