From 0017f57ea8ff5ed4405ef7a710e3a44812663089 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 17 Apr 2024 21:29:23 +0200 Subject: [PATCH 01/49] Remove dead code --- include/PrimeTower.h | 1 - src/PrimeTower.cpp | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index ef3cb78859..4ad34f8c94 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -60,7 +60,6 @@ class PrimeTower public: bool enabled_; //!< Whether the prime tower is enabled. bool would_have_actual_tower_; //!< Whether there is an actual tower. - bool multiple_extruders_on_first_layer_; //!< Whether multiple extruders are allowed on the first layer of the prime tower (e.g. when a raft is there) /* * In which order, from outside to inside, will we be printing the prime diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index ca3c898101..5c76e40d08 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -32,22 +32,6 @@ PrimeTower::PrimeTower() const Scene& scene = Application::getInstance().current_slice_->scene; PrimeTowerMethod method = scene.current_mesh_group->settings.get("prime_tower_mode"); - { - EPlatformAdhesion adhesion_type = scene.current_mesh_group->settings.get("adhesion_type"); - - // When we have multiple extruders sharing the same heater/nozzle, we expect that all the extruders have been - //'primed' by the print-start gcode script, but we don't know which one has been left at the tip of the nozzle - // and whether it needs 'purging' (before extruding a pure material) or not, so we need to prime (actually purge) - // each extruder before it is used for the model. This can done by the (per-extruder) brim lines or (per-extruder) - // skirt lines when they are used, but we need to do that inside the first prime-tower layer when they are not - // used (sacrifying for this purpose the usual single-extruder first layer, that would be better for prime-tower - // adhesion). - - multiple_extruders_on_first_layer_ = (method == PrimeTowerMethod::INTERLEAVED) || (method == PrimeTowerMethod::NORMAL) - || (scene.current_mesh_group->settings.get("machine_extruders_share_nozzle") - && ((adhesion_type != EPlatformAdhesion::SKIRT) && (adhesion_type != EPlatformAdhesion::BRIM))); - } - enabled_ = scene.current_mesh_group->settings.get("prime_tower_enable") && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 && scene.current_mesh_group->settings.get("prime_tower_size") > 10; From ec0567e408bf0928d860229a273315d394cf1dae Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 18 Apr 2024 09:06:38 +0200 Subject: [PATCH 02/49] Code simplification --- include/PrimeTower.h | 1 - src/PrimeTower.cpp | 11 +++++------ src/raft.cpp | 14 -------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 4ad34f8c94..8c1d6d4435 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -59,7 +59,6 @@ class PrimeTower public: bool enabled_; //!< Whether the prime tower is enabled. - bool would_have_actual_tower_; //!< Whether there is an actual tower. /* * In which order, from outside to inside, will we be printing the prime diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index 5c76e40d08..82af3cf239 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -35,8 +35,6 @@ PrimeTower::PrimeTower() enabled_ = scene.current_mesh_group->settings.get("prime_tower_enable") && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 && 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++) @@ -94,10 +92,11 @@ void PrimeTower::generatePaths(const SliceDataStorage& storage) { checkUsed(storage); + // Maybe it turns out that we don't need a prime tower after all because there are no layer switches. const int raft_total_extra_layers = Raft::getTotalExtraLayers(); - would_have_actual_tower_ = storage.max_print_height_second_to_last_extruder - >= -raft_total_extra_layers; // Maybe it turns out that we don't need a prime tower after all because there are no layer switches. - if (would_have_actual_tower_ && enabled_) + enabled_ &= storage.max_print_height_second_to_last_extruder >= -raft_total_extra_layers; + + if (enabled_) { generateGroundpoly(); @@ -308,7 +307,7 @@ void PrimeTower::addToGcode( const size_t prev_extruder_nr, const size_t new_extruder_nr) const { - if (! (enabled_ && would_have_actual_tower_)) + if (! enabled_) { return; } diff --git a/src/raft.cpp b/src/raft.cpp index 4eb329964f..a5c1426fd5 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -125,20 +125,6 @@ void Raft::generate(SliceDataStorage& storage) settings.get("raft_surface_remove_inside_corners"), settings.get("raft_surface_smoothing"), nominal_raft_line_width); - - if (storage.primeTower.enabled_ && ! storage.primeTower.would_have_actual_tower_) - { - // Find out if the prime-tower part of the raft still needs to be printed, even if there is no actual tower. - // This will only happen if the different raft layers are printed by different extruders. - const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; - const size_t base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr_; - const size_t interface_extruder_nr = mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr_; - const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr_; - if (base_extruder_nr == interface_extruder_nr && base_extruder_nr == surface_extruder_nr) - { - return; - } - } } coord_t Raft::getTotalThickness() From c65d92b13c7fda1c1f7d3c174fdf4f1078f78ff3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 18 Apr 2024 21:26:51 +0200 Subject: [PATCH 03/49] Fixed missing interleaved prime tower raft layer --- include/raft.h | 15 +++++++++++++++ src/FffGcodeWriter.cpp | 21 +++------------------ src/raft.cpp | 35 +++++++++++++++++++++++++++++------ src/sliceDataStorage.cpp | 6 +++--- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/include/raft.h b/include/raft.h index b6f837f53e..67eb7145c5 100644 --- a/include/raft.h +++ b/include/raft.h @@ -55,6 +55,18 @@ class Raft */ static size_t getTotalExtraLayers(); + /*! + * \brief Get the amount of bottom for the raft interface. + * \note This is currently hard-coded to 1 because we have yet no setting for the bottom + */ + static size_t getBottomLayers(); + + /*! \brief Get the amount of layers for the raft interface. */ + static size_t getInterfaceLayers(); + + /*! \brief Get the amount of layers for the raft top. */ + static size_t getSurfaceLayers(); + enum LayerType { RaftBase, @@ -70,6 +82,9 @@ class Raft * \return The type of layer at the given layer index. */ static LayerType getLayerType(LayerIndex layer_index); + +private: + static size_t getLayersAmount(const std::string& extruder_nr, const std::string& layers_nr); }; } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 74c6a90ed9..23ebc3fd3d 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -572,7 +572,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) const size_t base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr_; const size_t interface_extruder_nr = mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr_; const size_t surface_extruder_nr = mesh_group_settings.get("raft_surface_extruder_nr").extruder_nr_; - const size_t prime_tower_extruder_nr = storage.primeTower.extruder_order_.front(); coord_t z = 0; const LayerIndex initial_raft_layer_nr = -Raft::getTotalExtraLayers(); @@ -784,12 +783,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) interface_line_width, interface_avoid_distance); - if (! prime_tower_added_on_this_layer && current_extruder_nr == prime_tower_extruder_nr) - { - addPrimeTower(storage, gcode_layer, current_extruder_nr); - prime_tower_added_on_this_layer = true; - } - gcode_layer.setIsInside(true); if (interface_extruder_nr != current_extruder_nr) { @@ -910,8 +903,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (! prime_tower_added_on_this_layer) { - setExtruder_addPrime(storage, gcode_layer, prime_tower_extruder_nr); - current_extruder_nr = prime_tower_extruder_nr; + addPrimeTower(storage, gcode_layer, current_extruder_nr); } layer_plan_buffer.handle(gcode_layer, gcode); @@ -954,12 +946,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) surface_line_width, surface_avoid_distance); - if (! prime_tower_added_on_this_layer && current_extruder_nr == prime_tower_extruder_nr) - { - addPrimeTower(storage, gcode_layer, current_extruder_nr); - prime_tower_added_on_this_layer = true; - } - gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft @@ -1100,8 +1086,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (! prime_tower_added_on_this_layer) { - setExtruder_addPrime(storage, gcode_layer, prime_tower_extruder_nr); - current_extruder_nr = prime_tower_extruder_nr; + addPrimeTower(storage, gcode_layer, current_extruder_nr); } layer_plan_buffer.handle(gcode_layer, gcode); @@ -1629,7 +1614,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer(const SliceData } } - // Now check whether extuders should really used, and how + // Now check whether extuders should really be used, and how size_t last_extruder = start_extruder; for (size_t extruder_nr : ordered_extruders) { diff --git a/src/raft.cpp b/src/raft.cpp index a5c1426fd5..bf30f5b58e 100644 --- a/src/raft.cpp +++ b/src/raft.cpp @@ -168,18 +168,29 @@ coord_t Raft::getFillerLayerHeight() return round_divide(getZdiffBetweenRaftAndLayer0(), getFillerLayerCount()); } - size_t Raft::getTotalExtraLayers() +{ + return getBottomLayers() + getInterfaceLayers() + getSurfaceLayers() + getFillerLayerCount(); +} + +size_t Raft::getBottomLayers() { const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; - const ExtruderTrain& base_train = mesh_group_settings.get("raft_base_extruder_nr"); - const ExtruderTrain& interface_train = mesh_group_settings.get("raft_interface_extruder_nr"); - const ExtruderTrain& surface_train = mesh_group_settings.get("raft_surface_extruder_nr"); - if (base_train.settings_.get("adhesion_type") != EPlatformAdhesion::RAFT) + if (mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) { return 0; } - return 1 + interface_train.settings_.get("raft_interface_layers") + surface_train.settings_.get("raft_surface_layers") + getFillerLayerCount(); + return 1; +} + +size_t Raft::getInterfaceLayers() +{ + return getLayersAmount("raft_interface_extruder_nr", "raft_interface_layers"); +} + +size_t Raft::getSurfaceLayers() +{ + return getLayersAmount("raft_surface_extruder_nr", "raft_surface_layers"); } Raft::LayerType Raft::getLayerType(LayerIndex layer_index) @@ -214,5 +225,17 @@ Raft::LayerType Raft::getLayerType(LayerIndex layer_index) } } +size_t Raft::getLayersAmount(const std::string& extruder_nr, const std::string& layers_nr) +{ + const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; + if (mesh_group_settings.get("adhesion_type") != EPlatformAdhesion::RAFT) + { + return 0; + } + + const ExtruderTrain& train = mesh_group_settings.get(extruder_nr); + return train.settings_.get(layers_nr); +} + } // namespace cura diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 636f6ca0f7..3c5865eb26 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -486,8 +486,8 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) } if (adhesion_type == EPlatformAdhesion::RAFT) { - const LayerIndex raft_layers = Raft::getTotalExtraLayers(); - if (layer_nr == -raft_layers) // Base layer. + const LayerIndex raft_base_layer_index = -Raft::getTotalExtraLayers(); + if (layer_nr < raft_base_layer_index + Raft::getBottomLayers()) // Base layer. { ret[mesh_group_settings.get("raft_base_extruder_nr").extruder_nr_] = true; // When using a raft, all prime blobs need to be on the lowest layer (the build plate). @@ -499,7 +499,7 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) } } } - else if (layer_nr == -raft_layers + 1) // Interface layer. + else if (layer_nr < raft_base_layer_index + Raft::getBottomLayers() + Raft::getInterfaceLayers()) // Interface layer. { ret[mesh_group_settings.get("raft_interface_extruder_nr").extruder_nr_] = true; } From 7812a0e3ad0f30506145bbf5bcb71db20a6c34a1 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 19 Apr 2024 15:27:42 +0200 Subject: [PATCH 04/49] Fixed missing prime tower layers when using raft --- include/FffGcodeWriter.h | 4 ++ src/FffGcodeWriter.cpp | 109 +++++++++++++++++++++++---------------- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 42a97fedaf..acec8b81e8 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -207,6 +207,10 @@ class FffGcodeWriter : public NoCopy */ void processRaft(const SliceDataStorage& storage); + void startRaftLayer(const SliceDataStorage& storage, LayerPlan& gcode_layer, const LayerIndex layer_nr, size_t layer_extruder, size_t& current_extruder); + + void endRaftLayer(const SliceDataStorage& storage, LayerPlan& gcode_layer, const LayerIndex layer_nr, size_t& current_extruder); + /*! * Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer * diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 23ebc3fd3d..531d1f2737 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -591,7 +591,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) Shape raft_polygons; std::optional last_planned_position = std::optional(); - unsigned int current_extruder_nr = base_extruder_nr; + size_t current_extruder_nr = base_extruder_nr; { // raft base layer const Settings& base_settings = mesh_group_settings.get("raft_base_extruder_nr").settings_; @@ -758,7 +758,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (LayerIndex raft_interface_layer = 1; static_cast(raft_interface_layer) <= num_interface_layers; ++raft_interface_layer) { // raft interface layer - bool prime_tower_added_on_this_layer = ! storage.primeTower.enabled_; const LayerIndex layer_nr = initial_raft_layer_nr + raft_interface_layer; z += interface_layer_height; @@ -784,11 +783,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) interface_avoid_distance); gcode_layer.setIsInside(true); - if (interface_extruder_nr != current_extruder_nr) - { - setExtruder_addPrime(storage, gcode_layer, interface_extruder_nr); - current_extruder_nr = interface_extruder_nr; - } + + startRaftLayer(storage, gcode_layer, layer_nr, interface_extruder_nr, current_extruder_nr); Application::getInstance().communication_->sendLayerComplete(layer_nr, z, interface_layer_height); @@ -901,10 +897,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_polygons.clear(); raft_lines.clear(); - if (! prime_tower_added_on_this_layer) - { - addPrimeTower(storage, gcode_layer, current_extruder_nr); - } + endRaftLayer(storage, gcode_layer, layer_nr, current_extruder_nr); layer_plan_buffer.handle(gcode_layer, gcode); last_planned_position = gcode_layer.getLastPlannedPositionOrStartingPosition(); @@ -921,7 +914,6 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) for (LayerIndex raft_surface_layer = 1; static_cast(raft_surface_layer) <= num_surface_layers; raft_surface_layer++) { // raft surface layers - bool prime_tower_added_on_this_layer = ! storage.primeTower.enabled_; const LayerIndex layer_nr = initial_raft_layer_nr + 1 + num_interface_layers + raft_surface_layer - 1; // +1: 1 base layer z += surface_layer_height; @@ -949,11 +941,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) gcode_layer.setIsInside(true); // make sure that we are using the correct extruder to print raft - if (current_extruder_nr != surface_extruder_nr) - { - setExtruder_addPrime(storage, gcode_layer, surface_extruder_nr); - current_extruder_nr = surface_extruder_nr; - } + startRaftLayer(storage, gcode_layer, layer_nr, surface_extruder_nr, current_extruder_nr); + Application::getInstance().communication_->sendLayerComplete(layer_nr, z, surface_layer_height); Shape raft_outline_path; @@ -1084,15 +1073,41 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_lines.clear(); } - if (! prime_tower_added_on_this_layer) - { - addPrimeTower(storage, gcode_layer, current_extruder_nr); - } + endRaftLayer(storage, gcode_layer, layer_nr, current_extruder_nr); layer_plan_buffer.handle(gcode_layer, gcode); } } +void FffGcodeWriter::startRaftLayer(const SliceDataStorage& storage, LayerPlan& gcode_layer, const LayerIndex layer_nr, size_t layer_extruder, size_t& current_extruder) +{ + // If required, fill prime tower with previous extruder + setExtruder_addPrime(storage, gcode_layer, current_extruder); + + if (current_extruder != layer_extruder) + { + // Switch to new extruder and prime + setExtruder_addPrime(storage, gcode_layer, layer_extruder); + current_extruder = layer_extruder; + } +} + +void FffGcodeWriter::endRaftLayer(const SliceDataStorage& storage, LayerPlan& gcode_layer, const LayerIndex layer_nr, size_t& current_extruder) +{ + // If required, fill prime tower with current extruder + setExtruder_addPrime(storage, gcode_layer, current_extruder); + + // If required, fill prime tower for other extruders + for (const ExtruderUse& extruder_use : getExtruderUse(layer_nr)) + { + if (! gcode_layer.getPrimeTowerIsPlanned(extruder_use.extruder_nr) && extruder_use.prime != ExtruderPrime::None) + { + setExtruder_addPrime(storage, gcode_layer, extruder_use.extruder_nr); + current_extruder = extruder_use.extruder_nr; + } + } +} + FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIndex layer_nr, const size_t total_layers) const { spdlog::debug("GcodeWriter processing layer {} of {}", layer_nr, total_layers); @@ -1212,19 +1227,11 @@ FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataS for (const ExtruderUse& extruder_use : extruder_order) { size_t extruder_nr = extruder_use.extruder_nr; - // Everytime you start with a new extruder you want to add a prime tower, unless: - // - prime tower is disabled (setExtruder_addPrime takes care of this) - // - this is the first (and not the only!) extruder in this layer. Since the previous - // layer always ends with this extruder. If the first extruder is the only extruder, - // the prime tower needs to be added anyways, in order to support the prime tower if - // later in the print a prime tower is needed. - // - prime tower is already printed this layer (only applicable for more than 2 extruders). - // The setExtruder_addPrime takes care of this. - if (extruder_nr != extruder_order.front().extruder_nr || (extruder_order.size() == 1 && layer_nr >= 0) || extruder_nr == 0) - { - setExtruder_addPrime(storage, gcode_layer, extruder_nr); - time_keeper.registerTime("Prime tower pre"); - } + + // Set extruder (if needed) and prime (if needed) + setExtruder_addPrime(storage, gcode_layer, extruder_nr); + time_keeper.registerTime("Prime tower"); + if (include_helper_parts && (extruder_nr == support_infill_extruder_nr || extruder_nr == support_roof_extruder_nr || extruder_nr == support_bottom_extruder_nr)) { addSupportToGCode(storage, gcode_layer, extruder_nr); @@ -1251,14 +1258,6 @@ FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataS time_keeper.registerTime(fmt::format("Mesh {}", mesh_idx)); } } - // Always print a prime tower before switching extruder. Unless: - // - The prime tower is already printed this layer (setExtruder_addPrime takes care of this). - // - this is the last extruder of the layer, since the next layer will start with the same extruder. - if (extruder_nr != extruder_order.back().extruder_nr && layer_nr >= 0) - { - setExtruder_addPrime(storage, gcode_layer, extruder_nr); - time_keeper.registerTime("Prime tower post"); - } } gcode_layer.applyModifyPlugin(); @@ -1589,9 +1588,31 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer(const SliceData std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); const auto method = mesh_group_settings.get("prime_tower_mode"); const auto prime_tower_enable = mesh_group_settings.get("prime_tower_enable"); + const LayerIndex raft_base_layer_nr = -Raft::getTotalExtraLayers(); + + if (layer_nr < 0 && layer_nr < raft_base_layer_nr + Raft::getBottomLayers()) + { + // Raft base layers area treated apart because they don't have a proper prime tower + const size_t raft_base_extruder_nr = mesh_group_settings.get("raft_base_extruder_nr").extruder_nr_; + ret.push_back(ExtruderUse{ raft_base_extruder_nr, ExtruderPrime::None }); + + // check if we need prime blob on the first layer + if (layer_nr == raft_base_layer_nr) + { + for (size_t extruder_nr = 0; extruder_nr < extruder_is_used_on_this_layer.size(); extruder_nr++) + { + if (extruder_nr != raft_base_extruder_nr && getExtruderNeedPrimeBlobDuringFirstLayer(storage, extruder_nr)) + { + ret.push_back(ExtruderUse{ extruder_nr, ExtruderPrime::None }); + } + } + } + + return ret; + } // check if we are on the first layer - if (layer_nr == -static_cast(Raft::getTotalExtraLayers())) + if (layer_nr == raft_base_layer_nr) { // check if we need prime blob on the first layer for (size_t used_idx = 0; used_idx < extruder_is_used_on_this_layer.size(); used_idx++) @@ -1614,7 +1635,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer(const SliceData } } - // Now check whether extuders should really be used, and how + // Now check whether extruders should really be used, and how size_t last_extruder = start_extruder; for (size_t extruder_nr : ordered_extruders) { From e58b257737ddd8bff250cccbfa9296cdfdc4a5ea Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 07:45:29 +0200 Subject: [PATCH 05/49] Fix possibly wrong generated circle The previous implementation could generate a circle with a final point very close to the first one, or not, depending on double-precision rouding imprecision. This new version makes sure that the last generated point is always the same (non-explicitely closed polygon) --- include/utils/polygonUtils.h | 4 ++-- src/utils/polygonUtils.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 825bc62e79..95c9408a31 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -671,10 +671,10 @@ class PolygonUtils * This creates a regular polygon that is supposed to approximate a circle. * \param mid The center of the circle. * \param radius The radius of the circle. - * \param a_step The angle between segments of the circle. + * \param step_angle The angle between segments of the circle. * \return A new Polygon containing the circle. */ - static Polygon makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians a_step = std::numbers::pi / 8); + static Polygon makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians step_angle = std::numbers::pi / 8); /*! * Create a "wheel" shape. diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 085bca049c..22f03ddfff 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1431,12 +1431,14 @@ double PolygonUtils::relativeHammingDistance(const Shape& poly_a, const Shape& p return hamming_distance / total_area; } -Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians a_step) +Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians step_angle) { Polygon circle; - for (double a = 0; a < 2 * std::numbers::pi; a += a_step) + const size_t steps = (2 * std::numbers::pi) / step_angle; + for (size_t step = 0; step < steps; ++step) { - circle.emplace_back(mid + Point2LL(radius * cos(a), radius * sin(a))); + const double angle = step * step_angle; + circle.emplace_back(mid + Point2LL(radius * cos(angle), radius * sin(angle))); } return circle; } From c063eb48a42bf5d47676b1def6180fe871074fee Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 08:38:29 +0200 Subject: [PATCH 06/49] Fix crash due to previous merge --- include/PrimeTower.h | 2 -- src/FffPolygonGenerator.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 4de22dfa57..fe74d09ec6 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -77,8 +77,6 @@ class PrimeTower */ PrimeTower(); - void initializeExtruders(const std::vector& used_extruders); - /*! * Check whether we actually use the prime tower. */ diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index e7baf8e0a2..58e6ef5a9d 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -405,8 +405,6 @@ 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); From 6ca20e4009132cd196be9c593da265500b8d12da Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 11:03:17 +0200 Subject: [PATCH 07/49] Clean code --- src/FffGcodeWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1cd4e4a842..627a8d3e24 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1523,7 +1523,6 @@ 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; @@ -1563,7 +1562,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( assert(static_cast(extruder_count) > 0); std::vector ret; std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); - const auto method = mesh_group_settings.get("prime_tower_mode"); + const auto prime_tower_mode = mesh_group_settings.get("prime_tower_mode"); const auto prime_tower_enable = mesh_group_settings.get("prime_tower_enable"); const LayerIndex raft_base_layer_nr = -Raft::getTotalExtraLayers(); @@ -1620,7 +1619,7 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( if (prime_tower_enable) { - switch (method) + switch (prime_tower_mode) { case PrimeTowerMethod::NORMAL: if (extruder_is_used_on_this_layer[extruder_nr] && extruder_nr != last_extruder) @@ -1649,7 +1648,8 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( } } - if (method == PrimeTowerMethod::INTERLEAVED && ret.size() == 1 && ret.front().prime == ExtruderPrime::None && layer_nr <= storage.max_print_height_second_to_last_extruder) + if (prime_tower_mode == PrimeTowerMethod::INTERLEAVED && ret.size() == 1 && ret.front().prime == ExtruderPrime::None + && layer_nr <= storage.max_print_height_second_to_last_extruder) { ret.front().prime = ExtruderPrime::Sparse; } From bca6edd4df4e191a89ba5301a61df35dd1f54f4b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 12:08:08 +0200 Subject: [PATCH 08/49] Fix crash when using support_z_seam_away_from_model and raft --- src/FffGcodeWriter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 627a8d3e24..4e71c36c35 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3447,15 +3447,16 @@ 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 LayerIndex layer_nr = gcode_layer.getLayerNr(); ZSeamConfig z_seam_config = ZSeamConfig(EZSeamType::SHORTEST, 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")) + if (infill_extruder.settings_.get("support_z_seam_away_from_model") && layer_nr >= 0) { for (std::shared_ptr mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; - for (auto& part : mesh.layers[gcode_layer.getLayerNr()].parts) + for (auto& part : mesh.layers[layer_nr].parts) { disallowed_area_for_seams.push_back(part.print_outline); } From 14fca541b8db031bba5b413caeae4a374c6a92c3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 12:13:37 +0200 Subject: [PATCH 09/49] Use a pointer for the primer tower handler object The previous versions used an embedded object which was always present as a member variable. We now instantiate the primer tower object only if a prime tower will be actually generated, and use this pointer presence as a test to having a prime tower. This is also in prevision of a future change where multiple implementations of the prime tower will exist. --- include/PrimeTower.h | 13 ++----- include/sliceDataStorage.h | 6 +++- src/FffGcodeWriter.cpp | 22 ++++++------ src/FffPolygonGenerator.cpp | 13 ++++--- src/PrimeTower.cpp | 72 ++++++++++++++++--------------------- src/TreeModelVolumes.cpp | 4 +-- src/sliceDataStorage.cpp | 9 +++-- 7 files changed, 63 insertions(+), 76 deletions(-) diff --git a/include/PrimeTower.h b/include/PrimeTower.h index fe74d09ec6..13dd0f41f4 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -31,8 +31,6 @@ class PrimeTower using MovesByExtruder = std::map; using MovesByLayer = std::map>; - size_t extruder_count_; //!< Number of extruders - bool wipe_from_middle_; //!< Whether to wipe on the inside of the hollow prime tower Point2LL middle_; //!< The middle of the prime tower @@ -58,8 +56,6 @@ class PrimeTower std::vector outer_poly_base_; //!< The outline of the layers having extra width for the base public: - bool enabled_; //!< Whether the prime tower is enabled. - /* * In which order, from outside to inside, will we be printing the prime * towers for maximum strength? @@ -75,12 +71,7 @@ class PrimeTower * * \param storage A storage where it retrieves the prime tower settings. */ - PrimeTower(); - - /*! - * Check whether we actually use the prime tower. - */ - void checkUsed(); + PrimeTower(SliceDataStorage& storage, size_t extruder_count); /*! * Generate the prime tower area to be used on each layer @@ -131,6 +122,8 @@ class PrimeTower */ const Shape& getGroundPoly() const; + static PrimeTower* createPrimeTower(SliceDataStorage& storage); + private: /*! * \see PrimeTower::generatePaths diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 669e6829d0..f8309587f7 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -391,7 +391,8 @@ class SliceDataStorage : public NoCopy std::vector spiralize_seam_vertex_indices; //!< the index of the seam vertex for each layer std::vector spiralize_wall_outlines; //!< the wall outline polygons for each layer - PrimeTower primeTower; + //!< Pointer to primer tower handler object (a null pointer indicates that there is no prime tower) + PrimeTower* prime_tower_{ nullptr }; std::vector ooze_shield; // oozeShield per layer Shape draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber. @@ -404,6 +405,7 @@ class SliceDataStorage : public NoCopy ~SliceDataStorage() { + delete prime_tower_; } /*! @@ -457,6 +459,8 @@ class SliceDataStorage : public NoCopy */ Shape getMachineBorder(int extruder_nr = -1) const; + void initializePrimeTower(); + private: /*! * Construct the retraction_wipe_config_per_extruder diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 4e71c36c35..17cb0bbe79 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -644,9 +644,9 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) std::vector raft_outline_paths; raft_outline_paths.emplace_back(ParameterizedRaftPath{ line_spacing, storage.raft_base_outline }); - if (storage.primeTower.enabled_) + if (storage.prime_tower_) { - const Shape& raft_outline_prime_tower = storage.primeTower.getOuterPoly(layer_nr); + const Shape& raft_outline_prime_tower = storage.prime_tower_->getOuterPoly(layer_nr); if (line_spacing_prime_tower == line_spacing) { // Base layer is shared with prime tower base @@ -809,10 +809,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; - if (storage.primeTower.enabled_) + if (storage.prime_tower_) { // Interface layer excludes prime tower base - raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); + raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getOuterPoly(layer_nr)); } Infill infill_comp( @@ -969,10 +969,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) constexpr size_t zag_skip_count = 0; constexpr coord_t pocket_size = 0; - if (storage.primeTower.enabled_) + if (storage.prime_tower_) { // Surface layers exclude prime tower base - raft_outline_path = raft_outline_path.difference(storage.primeTower.getOuterPoly(layer_nr)); + raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getOuterPoly(layer_nr)); } for (const Shape& raft_island : raft_outline_path.splitIntoParts()) @@ -3961,14 +3961,12 @@ void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, Layer void FffGcodeWriter::addPrimeTower(const SliceDataStorage& storage, LayerPlan& gcode_layer, const size_t prev_extruder) const { - if (! storage.primeTower.enabled_) + if (storage.prime_tower_) { - return; + LayerIndex layer_nr = gcode_layer.getLayerNr(); + const std::vector extruder_order = getExtruderUse(layer_nr); + storage.prime_tower_->addToGcode(storage, gcode_layer, extruder_order, prev_extruder, gcode_layer.getExtruder()); } - - LayerIndex layer_nr = gcode_layer.getLayerNr(); - const std::vector extruder_order = getExtruderUse(layer_nr); - storage.primeTower.addToGcode(storage, gcode_layer, extruder_order, prev_extruder, gcode_layer.getExtruder()); } void FffGcodeWriter::finalize() diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 58e6ef5a9d..aff790ab44 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -413,8 +413,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& computePrintHeightStatistics(storage); // handle helpers - storage.primeTower.generatePaths(storage); - storage.primeTower.subtractFromSupport(storage); + storage.initializePrimeTower(); spdlog::debug("Processing ooze shield"); processOozeShield(storage); @@ -424,7 +423,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // This catches a special case in which the models are in the air, and then // the adhesion mustn't be calculated. - if (! isEmptyLayer(storage, 0) || storage.primeTower.enabled_) + if (! isEmptyLayer(storage, 0) || storage.prime_tower_) { spdlog::debug("Processing platform adhesion"); processPlatformAdhesion(storage); @@ -981,7 +980,7 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) { storage.ooze_shield[layer_nr].removeSmallAreas(largest_printed_area); } - if (mesh_group_settings.get("prime_tower_enable")) + if (storage.prime_tower_) { coord_t max_line_width = 0; { // compute max_line_width @@ -996,7 +995,7 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) } for (LayerIndex layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++) { - storage.ooze_shield[layer_nr] = storage.ooze_shield[layer_nr].difference(storage.primeTower.getOuterPoly(layer_nr).offset(max_line_width / 2)); + storage.ooze_shield[layer_nr] = storage.ooze_shield[layer_nr].difference(storage.prime_tower_->getOuterPoly(layer_nr).offset(max_line_width / 2)); } } } @@ -1033,7 +1032,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) maximum_deviation = std::min(maximum_deviation, extruder.settings_.get("meshfix_maximum_deviation")); } storage.draft_protection_shield = Simplify(maximum_resolution, maximum_deviation, 0).polygon(storage.draft_protection_shield); - if (mesh_group_settings.get("prime_tower_enable")) + if (storage.prime_tower_) { coord_t max_line_width = 0; { // compute max_line_width @@ -1046,7 +1045,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) max_line_width = std::max(max_line_width, extruders[extruder_nr].settings_.get("skirt_brim_line_width")); } } - storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.primeTower.getGroundPoly().offset(max_line_width / 2)); + storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.prime_tower_->getGroundPoly().offset(max_line_width / 2)); } } diff --git a/src/PrimeTower.cpp b/src/PrimeTower.cpp index d81b65fb00..ac3051a7aa 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower.cpp @@ -26,22 +26,19 @@ namespace cura { -PrimeTower::PrimeTower() +PrimeTower::PrimeTower(SliceDataStorage& storage, size_t extruder_count) : wipe_from_middle_(false) + , extruder_order_(extruder_count) { const Scene& scene = Application::getInstance().current_slice_->scene; - enabled_ = scene.current_mesh_group->settings.get("prime_tower_enable") && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 - && scene.current_mesh_group->settings.get("prime_tower_size") > 10; - - extruder_count_ = scene.extruders.size(); - extruder_order_.resize(extruder_count_); - for (unsigned int extruder_nr = 0; extruder_nr < extruder_count_; extruder_nr++) + // First by a basic list of used extruders numbers + for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { - extruder_order_[extruder_nr] = extruder_nr; // Start with default order, then sort. + extruder_order_[extruder_nr] = extruder_nr; } - // Sort from high adhesion to low adhesion. + // Then sort from high adhesion to low adhesion. std::stable_sort( extruder_order_.begin(), extruder_order_.end(), @@ -51,23 +48,13 @@ PrimeTower::PrimeTower() const Ratio adhesion_b = scene.extruders[extruder_nr_b].settings_.get("material_adhesion_tendency"); return adhesion_a < adhesion_b; }); -} -void PrimeTower::checkUsed() -{ - if (extruder_count_ <= 1) - { - enabled_ = false; - } + generatePaths(storage); + subtractFromSupport(storage); } void PrimeTower::generateGroundpoly() { - if (! enabled_) - { - return; - } - const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); @@ -83,23 +70,14 @@ void PrimeTower::generateGroundpoly() void PrimeTower::generatePaths(const SliceDataStorage& storage) { - checkUsed(); - - // Maybe it turns out that we don't need a prime tower after all because there are no layer switches. - const int raft_total_extra_layers = Raft::getTotalExtraLayers(); - enabled_ &= storage.max_print_height_second_to_last_extruder >= -raft_total_extra_layers; - - if (enabled_) - { - generateGroundpoly(); + generateGroundpoly(); - std::vector cumulative_insets; - generatePaths_denseInfill(cumulative_insets); + std::vector cumulative_insets; + generatePaths_denseInfill(cumulative_insets); - generateStartLocations(); + generateStartLocations(); - generatePaths_sparseInfill(cumulative_insets); - } + generatePaths_sparseInfill(cumulative_insets); } void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_insets) @@ -219,9 +197,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 < extruder_count_; ++first_extruder_idx) + for (size_t first_extruder_idx = 0; first_extruder_idx < extruder_order_.size(); ++first_extruder_idx) { - size_t nb_extruders_sparse = method == PrimeTowerMethod::NORMAL ? first_extruder_idx + 1 : extruder_count_; + size_t nb_extruders_sparse = method == PrimeTowerMethod::NORMAL ? first_extruder_idx + 1 : extruder_order_.size(); for (size_t last_extruder_idx = first_extruder_idx; last_extruder_idx < nb_extruders_sparse; ++last_extruder_idx) { @@ -301,11 +279,6 @@ void PrimeTower::addToGcode( const size_t prev_extruder_nr, const size_t new_extruder_nr) const { - if (! enabled_) - { - return; - } - if (gcode_layer.getPrimeTowerIsPlanned(new_extruder_nr)) { // don't print the prime tower if it has been printed already with this extruder. return; @@ -599,6 +572,21 @@ const Shape& PrimeTower::getGroundPoly() const return getOuterPoly(-Raft::getTotalExtraLayers()); } +PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) +{ + PrimeTower* prime_tower = nullptr; + const Scene& scene = Application::getInstance().current_slice_->scene; + const int raft_total_extra_layers = Raft::getTotalExtraLayers(); + + if (scene.extruders.size() > 1 && scene.current_mesh_group->settings.get("prime_tower_enable") + && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 && scene.current_mesh_group->settings.get("prime_tower_size") > 10 + && storage.max_print_height_second_to_last_extruder >= -raft_total_extra_layers) + { + prime_tower = new PrimeTower(storage, scene.extruders.size()); + } + return prime_tower; +} + void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr) const { if (gcode_layer.getLayerNr() != 0) diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index a6d12804a6..a968837403 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -148,9 +148,9 @@ TreeModelVolumes::TreeModelVolumes( anti_overhang_[layer_idx].push_back(storage.support.supportLayers[layer_idx].anti_overhang); } - if (storage.primeTower.enabled_) + if (storage.prime_tower_) { - anti_overhang_[layer_idx].push_back(storage.primeTower.getGroundPoly()); + anti_overhang_[layer_idx].push_back(storage.prime_tower_->getGroundPoly()); } anti_overhang_[layer_idx] = anti_overhang_[layer_idx].unionPolygons(); }); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 252b020a38..b667d42716 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -364,9 +364,9 @@ Shape SliceDataStorage::getLayerOutlines( total.push_back(support_layer.support_roof); } } - if (include_prime_tower && primeTower.enabled_ && (extruder_nr == -1 || (! primeTower.extruder_order_.empty() && extruder_nr == primeTower.extruder_order_[0]))) + if (include_prime_tower && prime_tower_ && (extruder_nr == -1 || (! prime_tower_->extruder_order_.empty() && extruder_nr == prime_tower_->extruder_order_[0]))) { - total.push_back(primeTower.getOuterPoly(layer_nr)); + total.push_back(prime_tower_->getOuterPoly(layer_nr)); } return total; } @@ -691,6 +691,11 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const return border; } +void SliceDataStorage::initializePrimeTower() +{ + prime_tower_ = PrimeTower::createPrimeTower(*this); +} + void SupportLayer::excludeAreasFromSupportInfillAreas(const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box) { From 00127163394e5026cac2d2c2ceb143d6f571a648 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 12:35:43 +0200 Subject: [PATCH 10/49] Remove unused file --- include/utils/SymmetricPair.h | 89 ----------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 include/utils/SymmetricPair.h diff --git a/include/utils/SymmetricPair.h b/include/utils/SymmetricPair.h deleted file mode 100644 index b7e3e1dc4e..0000000000 --- a/include/utils/SymmetricPair.h +++ /dev/null @@ -1,89 +0,0 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. - -#ifndef UTILS_SYMMETRIC_PAIR -#define UTILS_SYMMETRIC_PAIR - -#include // pair - -namespace cura -{ - -/*! - * A utility class for a pair of which the order between the first and the second doesn't matter. - * - * \tparam A The type of both elements of the pair. - */ -template -class SymmetricPair : public std::pair -{ -public: - /*! - * Forwarding std::pair constructor - */ - template - SymmetricPair(const SymmetricPair& pr) - : std::pair(pr) - { - } - /*! - * Forwarding std::pair constructor - */ - template - SymmetricPair(SymmetricPair&& pr) - : std::pair(pr) - { - } - /*! - * Forwarding std::pair constructor - */ - SymmetricPair(const A& first, const A& second) - : std::pair(first, second) - { - } - /*! - * Forwarding std::pair constructor - */ - template - SymmetricPair(U&& first, U&& second) - : std::pair(first, second) - { - } - /*! - * Forwarding std::pair constructor - */ - template - SymmetricPair(std::piecewise_construct_t pwc, std::tuple first_args, std::tuple second_args) - : std::pair(pwc, first_args, second_args) - { - } - - /*! - * Equality operator which checks if two SymmetricPairs are equal regardless of the order between first and second - */ - bool operator==(const SymmetricPair& other) const - { - return (std::pair::first == other.first && std::pair::second == other.second) || (std::pair::first == other.second && std::pair::second == other.first); - } -}; - -}//namespace cura - -namespace std -{ - -/*! - * Hash operator which creates a hash regardless of the order between first and second - */ -template -struct hash> -{ - size_t operator()(const cura::SymmetricPair& pr) const - { // has to be symmetric wrt a and b! - return std::hash()(pr.first) + std::hash()(pr.second); - } -}; -}//namespace std - - -#endif // UTILS_SYMMETRIC_PAIR \ No newline at end of file From ec67b5289c069f4bd0dc6c596f9966f93377cc37 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 14:54:22 +0200 Subject: [PATCH 11/49] Remove some headers inclusions --- include/PrimeTower.h | 1 - include/TreeModelVolumes.h | 11 ++++--- include/sliceDataStorage.h | 6 +--- include/utils/PairHash.h | 24 +++++++++++++++ include/utils/polygonUtils.h | 57 +++++++++++++++--------------------- src/FffGcodeWriter.cpp | 1 + src/FffPolygonGenerator.cpp | 1 + src/TreeModelVolumes.cpp | 1 + src/sliceDataStorage.cpp | 2 ++ 9 files changed, 61 insertions(+), 43 deletions(-) create mode 100644 include/utils/PairHash.h diff --git a/include/PrimeTower.h b/include/PrimeTower.h index 13dd0f41f4..4155b50271 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower.h @@ -228,7 +228,6 @@ class PrimeTower void gotoStartLocation(LayerPlan& gcode_layer, const int extruder) const; }; - } // namespace cura #endif // PRIME_TOWER_H diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index 0dee5f62c1..e95d754770 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -6,22 +6,25 @@ #include #include +#include #include #include -#include "TreeSupportSettings.h" -#include "geometry/Polygon.h" //For polygon parameters. +#include "TreeSupportEnums.h" +#include "geometry/Shape.h" #include "settings/EnumSettings.h" //To store whether X/Y or Z distance gets priority. #include "settings/types/LayerIndex.h" //Part of the RadiusLayerPair. -#include "sliceDataStorage.h" +#include "utils/PairHash.h" #include "utils/Simplify.h" + namespace cura { constexpr coord_t EPSILON = 5; constexpr coord_t FUDGE_LENGTH = 50; class SliceDataStorage; +class SliceMeshStorage; class LayerIndex; class Settings; @@ -169,7 +172,6 @@ class TreeModelVolumes */ Shape extractOutlineFromMesh(const SliceMeshStorage& mesh, LayerIndex layer_idx) const; - /*! * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. * @@ -526,4 +528,5 @@ class TreeModelVolumes } // namespace cura + #endif // TREEMODELVOLUMES_H diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index f8309587f7..0c36cfbc54 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -8,8 +8,6 @@ #include #include -#include "PrimeTower.h" -#include "RetractionConfig.h" #include "SupportInfillPart.h" #include "TopSurface.h" #include "WipeScriptConfig.h" @@ -27,15 +25,13 @@ #include "utils/AABB3D.h" #include "utils/NoCopy.h" -// libArachne -#include "utils/ExtrusionLine.h" - namespace cura { class Mesh; class SierpinskiFillProvider; class LightningGenerator; +class PrimeTower; /*! * A SkinPart is a connected area designated as top and/or bottom skin. diff --git a/include/utils/PairHash.h b/include/utils/PairHash.h new file mode 100644 index 0000000000..a94993ed9a --- /dev/null +++ b/include/utils/PairHash.h @@ -0,0 +1,24 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_PAIRHASH_H +#define UTILS_PAIRHASH_H + +#include +#include + +namespace std +{ + +template +struct hash> +{ + size_t operator()(const std::pair& pair) const + { + return 31 * std::hash()(pair.first) + 59 * std::hash()(pair.second); + } +}; + +} // namespace std + +#endif // UTILS_PAIRHASH_H diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 95c9408a31..4a83c8ec97 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -78,37 +78,6 @@ struct ClosestPoint using ClosestPointPolygon = ClosestPoint; -} // namespace cura - -namespace std -{ -template<> -struct hash -{ - size_t operator()(const cura::ClosestPointPolygon& cpp) const - { - return std::hash()(cpp.p()); - } -}; -} // namespace std - - -namespace std -{ -template -struct hash> -{ - size_t operator()(const std::pair& pair) const - { - return 31 * std::hash()(pair.first) + 59 * std::hash()(pair.second); - } -}; -} // namespace std - - -namespace cura -{ - /*! * A point within a polygon and the index of which segment in the polygon the point lies on. */ @@ -741,7 +710,29 @@ class PolygonUtils static ClosestPointPolygon _moveInside2(const ClosestPointPolygon& closest_polygon_point, const int distance, Point2LL& from, const int64_t max_dist2); }; - } // namespace cura -#endif // POLYGON_OPTIMIZER_H +namespace std +{ + +/*template +struct hash> +{ + size_t operator()(const std::pair& pair) const + { + return 31 * std::hash()(pair.first) + 59 * std::hash()(pair.second); + } +};*/ + +template<> +struct hash +{ + size_t operator()(const cura::ClosestPointPolygon& cpp) const + { + return std::hash()(cpp.p()); + } +}; + +} // namespace std + +#endif // UTILS_POLYGON_UTILS_H diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 17cb0bbe79..afebe06478 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,6 +21,7 @@ #include "InsetOrderOptimizer.h" #include "LayerPlan.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. +#include "PrimeTower.h" #include "Slice.h" #include "WallToolPaths.h" #include "bridge.h" diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index aff790ab44..d4a6ee853a 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -49,6 +49,7 @@ #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/math.h" +#include "PrimeTower.h" #include "geometry/OpenPolyline.h" #include "utils/Simplify.h" // clang-format on diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index a968837403..1ce9e47bdd 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -8,6 +8,7 @@ #include #include +#include "PrimeTower.h" #include "TreeSupport.h" #include "TreeSupportEnums.h" #include "progress/Progress.h" diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index b667d42716..59000ae7e7 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -10,6 +10,7 @@ #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "FffProcessor.h" //To create a mesh group with if none is provided. +#include "PrimeTower.h" #include "Slice.h" #include "geometry/OpenPolyline.h" #include "infill/DensityProvider.h" // for destructor @@ -17,6 +18,7 @@ #include "infill/SierpinskiFillProvider.h" #include "infill/SubDivCube.h" // For the destructor #include "raft.h" +#include "utils/ExtrusionLine.h" #include "utils/math.h" //For PI. namespace cura From 3e52bc125cf5397e3723ea6d4549c72fd0f372c0 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 15:25:15 +0200 Subject: [PATCH 12/49] Split PrimeTower class in (placeholder) subclasses We now have specific classes for normal and interleaved prime towers, so that it will become easier to fine-tune their behavior without breaking each other. --- CMakeLists.txt | 4 ++- include/{ => PrimeTower}/PrimeTower.h | 40 +++++++++++----------- include/PrimeTower/PrimeTowerInterleaved.h | 26 ++++++++++++++ include/PrimeTower/PrimeTowerNormal.h | 26 ++++++++++++++ src/FffGcodeWriter.cpp | 2 +- src/FffPolygonGenerator.cpp | 2 +- src/{ => PrimeTower}/PrimeTower.cpp | 21 +++++++++--- src/PrimeTower/PrimeTowerInterleaved.cpp | 14 ++++++++ src/PrimeTower/PrimeTowerNormal.cpp | 14 ++++++++ src/TreeModelVolumes.cpp | 2 +- src/sliceDataStorage.cpp | 2 +- 11 files changed, 124 insertions(+), 29 deletions(-) rename include/{ => PrimeTower}/PrimeTower.h (100%) create mode 100644 include/PrimeTower/PrimeTowerInterleaved.h create mode 100644 include/PrimeTower/PrimeTowerNormal.h rename src/{ => PrimeTower}/PrimeTower.cpp (97%) create mode 100644 src/PrimeTower/PrimeTowerInterleaved.cpp create mode 100644 src/PrimeTower/PrimeTowerNormal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d3cb270ed..aafc99e602 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,9 @@ set(engine_SRCS # Except main.cpp. src/multiVolumes.cpp src/path_ordering.cpp src/Preheat.cpp - src/PrimeTower.cpp + src/PrimeTower/PrimeTower.cpp + src/PrimeTower/PrimeTowerNormal.cpp + src/PrimeTower/PrimeTowerInterleaved.cpp src/raft.cpp src/Scene.cpp src/SkeletalTrapezoidation.cpp diff --git a/include/PrimeTower.h b/include/PrimeTower/PrimeTower.h similarity index 100% rename from include/PrimeTower.h rename to include/PrimeTower/PrimeTower.h index 4155b50271..baae6dd4d3 100644 --- a/include/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -73,18 +73,6 @@ class PrimeTower */ PrimeTower(SliceDataStorage& storage, size_t extruder_count); - /*! - * Generate the prime tower area to be used on each layer - * - * Fills \ref PrimeTower::inner_poly and sets \ref PrimeTower::middle - */ - void generateGroundpoly(); - - /*! - * Generate the area where the prime tower should be. - */ - void generatePaths(const SliceDataStorage& storage); - /*! * Add path plans for the prime tower to the \p gcode_layer * @@ -101,14 +89,6 @@ class PrimeTower const size_t prev_extruder_nr, const size_t new_extruder_nr) const; - /*! - * \brief Subtract the prime tower from the support areas in storage. - * - * \param storage The storage where to find the support from which to - * subtract a prime tower. - */ - void subtractFromSupport(SliceDataStorage& storage); - /*! * Get the outer polygon for the given layer, which may be the priming polygon only, or a larger polygon for layers with a base * @@ -226,6 +206,26 @@ class PrimeTower * starting at the location everytime which can result in z-seam blobs. */ void gotoStartLocation(LayerPlan& gcode_layer, const int extruder) const; + + /*! + * Generate the prime tower area to be used on each layer + * + * Fills \ref PrimeTower::inner_poly and sets \ref PrimeTower::middle + */ + void generateGroundpoly(); + + /*! + * Generate the area where the prime tower should be. + */ + void generatePaths(const SliceDataStorage& storage); + + /*! + * \brief Subtract the prime tower from the support areas in storage. + * + * \param storage The storage where to find the support from which to + * subtract a prime tower. + */ + void subtractFromSupport(SliceDataStorage& storage); }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h new file mode 100644 index 0000000000..db030f39d0 --- /dev/null +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -0,0 +1,26 @@ +// Copyright (c) 2024 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef PRIME_TOWER_INTERLEAVED_H +#define PRIME_TOWER_INTERLEAVED_H + +#include "PrimeTower/PrimeTower.h" + +namespace cura +{ + +class PrimeTowerInterleaved : public PrimeTower +{ +public: + /*! + * \brief Creates a prime tower instance that will determine where and how + * the prime tower gets printed. + * + * \param storage A storage where it retrieves the prime tower settings. + */ + PrimeTowerInterleaved(SliceDataStorage& storage, size_t extruder_count); +}; + +} // namespace cura + +#endif // PRIME_TOWER_INTERLEAVED_H diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h new file mode 100644 index 0000000000..fb70d40775 --- /dev/null +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -0,0 +1,26 @@ +// Copyright (c) 2024 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef PRIME_TOWER_NORMAL_H +#define PRIME_TOWER_NORMAL_H + +#include "PrimeTower/PrimeTower.h" + +namespace cura +{ + +class PrimeTowerNormal : public PrimeTower +{ +public: + /*! + * \brief Creates a prime tower instance that will determine where and how + * the prime tower gets printed. + * + * \param storage A storage where it retrieves the prime tower settings. + */ + PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_count); +}; + +} // namespace cura + +#endif // PRIME_TOWER_NORMAL_H diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index afebe06478..f765d09aa3 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,7 +21,7 @@ #include "InsetOrderOptimizer.h" #include "LayerPlan.h" #include "PathOrderMonotonic.h" //Monotonic ordering of skin lines. -#include "PrimeTower.h" +#include "PrimeTower/PrimeTower.h" #include "Slice.h" #include "WallToolPaths.h" #include "bridge.h" diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index d4a6ee853a..a7336b3aff 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -49,7 +49,7 @@ #include "utils/ThreadPool.h" #include "utils/gettime.h" #include "utils/math.h" -#include "PrimeTower.h" +#include "PrimeTower/PrimeTower.h" #include "geometry/OpenPolyline.h" #include "utils/Simplify.h" // clang-format on diff --git a/src/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp similarity index 97% rename from src/PrimeTower.cpp rename to src/PrimeTower/PrimeTower.cpp index ac3051a7aa..f8aac43878 100644 --- a/src/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2024 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. -#include "PrimeTower.h" +#include "PrimeTower/PrimeTower.h" #include #include @@ -12,6 +12,8 @@ #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "LayerPlan.h" +#include "PrimeTower/PrimeTowerInterleaved.h" +#include "PrimeTower/PrimeTowerNormal.h" #include "Scene.h" #include "Slice.h" #include "gcodeExport.h" @@ -576,13 +578,24 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) { PrimeTower* prime_tower = nullptr; const Scene& scene = Application::getInstance().current_slice_->scene; - const int raft_total_extra_layers = Raft::getTotalExtraLayers(); + const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); if (scene.extruders.size() > 1 && scene.current_mesh_group->settings.get("prime_tower_enable") && scene.current_mesh_group->settings.get("prime_tower_min_volume") > 10 && scene.current_mesh_group->settings.get("prime_tower_size") > 10 - && storage.max_print_height_second_to_last_extruder >= -raft_total_extra_layers) + && storage.max_print_height_second_to_last_extruder >= -static_cast(raft_total_extra_layers)) { - prime_tower = new PrimeTower(storage, scene.extruders.size()); + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + const PrimeTowerMethod method = mesh_group_settings.get("prime_tower_mode"); + + switch (method) + { + case PrimeTowerMethod::NORMAL: + prime_tower = new PrimeTowerNormal(storage, scene.extruders.size()); + break; + case PrimeTowerMethod::INTERLEAVED: + prime_tower = new PrimeTowerInterleaved(storage, scene.extruders.size()); + break; + } } return prime_tower; } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp new file mode 100644 index 0000000000..382d06756f --- /dev/null +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "PrimeTower/PrimeTowerInterleaved.h" + +namespace cura +{ + +PrimeTowerInterleaved::PrimeTowerInterleaved(SliceDataStorage& storage, size_t extruder_count) + : PrimeTower(storage, extruder_count) +{ +} + +} // namespace cura diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp new file mode 100644 index 0000000000..d93fc9271f --- /dev/null +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "PrimeTower/PrimeTowerNormal.h" + +namespace cura +{ + +PrimeTowerNormal::PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_count) + : PrimeTower(storage, extruder_count) +{ +} + +} // namespace cura diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 1ce9e47bdd..67e37d3aa3 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -8,7 +8,7 @@ #include #include -#include "PrimeTower.h" +#include "PrimeTower/PrimeTower.h" #include "TreeSupport.h" #include "TreeSupportEnums.h" #include "progress/Progress.h" diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 59000ae7e7..bfd63e388b 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -10,7 +10,7 @@ #include "Application.h" //To get settings. #include "ExtruderTrain.h" #include "FffProcessor.h" //To create a mesh group with if none is provided. -#include "PrimeTower.h" +#include "PrimeTower/PrimeTower.h" #include "Slice.h" #include "geometry/OpenPolyline.h" #include "infill/DensityProvider.h" // for destructor From 6eaa91596df0539bd088ed9111702b3c96882fdb Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 9 May 2024 21:41:14 +0200 Subject: [PATCH 13/49] Move extruder prime logic calculation to PrimeTower* classes Now the whole logic of computation of how each extruder should be primed is done inside the PrimeTowerInterleaved and PrimeTowerNormal classes, because this is very related to how they will process this data later. This will later allow for more fine-tuning of these behaviors without a big risk of breaking each other. --- include/PrimeTower/PrimeTower.h | 14 ++++++++++ include/PrimeTower/PrimeTowerInterleaved.h | 9 +++++++ include/PrimeTower/PrimeTowerNormal.h | 7 +++++ src/FffGcodeWriter.cpp | 30 +++------------------- src/PrimeTower/PrimeTower.cpp | 5 ++++ src/PrimeTower/PrimeTowerInterleaved.cpp | 27 +++++++++++++++++++ src/PrimeTower/PrimeTowerNormal.cpp | 23 +++++++++++++++++ 7 files changed, 89 insertions(+), 26 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index baae6dd4d3..45db28bdc2 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -102,8 +102,22 @@ class PrimeTower */ const Shape& getGroundPoly() const; + virtual ExtruderPrime getExtruderPrime( + const std::vector& extruder_is_used_on_this_layer, + size_t extruder_nr, + size_t last_extruder, + const SliceDataStorage& storage, + const LayerIndex& layer_nr) const = 0; + + virtual void polishExtruderUse(std::vector& /*extruder_use*/, const SliceDataStorage& /*storage*/, const LayerIndex& /*layer_nr*/) const + { + } + static PrimeTower* createPrimeTower(SliceDataStorage& storage); +protected: + static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); + private: /*! * \see PrimeTower::generatePaths diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index db030f39d0..4629fa3704 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -19,6 +19,15 @@ class PrimeTowerInterleaved : public PrimeTower * \param storage A storage where it retrieves the prime tower settings. */ PrimeTowerInterleaved(SliceDataStorage& storage, size_t extruder_count); + + virtual ExtruderPrime getExtruderPrime( + const std::vector& extruder_is_used_on_this_layer, + size_t extruder_nr, + size_t last_extruder, + const SliceDataStorage& storage, + const LayerIndex& layer_nr) const override; + + virtual void polishExtruderUse(std::vector& extruder_use, const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index fb70d40775..2a29bb5713 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -19,6 +19,13 @@ class PrimeTowerNormal : public PrimeTower * \param storage A storage where it retrieves the prime tower settings. */ PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_count); + + virtual ExtruderPrime getExtruderPrime( + const std::vector& extruder_is_used_on_this_layer, + size_t extruder_nr, + size_t last_extruder, + const SliceDataStorage& storage, + const LayerIndex& layer_nr) const override; }; } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index f765d09aa3..dc43b7a325 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1563,8 +1563,6 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( assert(static_cast(extruder_count) > 0); std::vector ret; std::vector extruder_is_used_on_this_layer = storage.getExtrudersUsed(layer_nr); - const auto prime_tower_mode = mesh_group_settings.get("prime_tower_mode"); - const auto prime_tower_enable = mesh_group_settings.get("prime_tower_enable"); const LayerIndex raft_base_layer_nr = -Raft::getTotalExtraLayers(); if (layer_nr < 0 && layer_nr < raft_base_layer_nr + Raft::getBottomLayers()) @@ -1618,28 +1616,9 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( { ExtruderPrime prime = ExtruderPrime::None; - if (prime_tower_enable) + if (storage.prime_tower_) { - switch (prime_tower_mode) - { - case PrimeTowerMethod::NORMAL: - if (extruder_is_used_on_this_layer[extruder_nr] && extruder_nr != last_extruder) - { - prime = ExtruderPrime::Prime; - } - else if (layer_nr < storage.max_print_height_second_to_last_extruder) - { - prime = ExtruderPrime::Sparse; - } - break; - - case PrimeTowerMethod::INTERLEAVED: - if (extruder_is_used_on_this_layer[extruder_nr] && extruder_nr != last_extruder) - { - prime = ExtruderPrime::Prime; - } - break; - } + prime = storage.prime_tower_->getExtruderPrime(extruder_is_used_on_this_layer, extruder_nr, last_extruder, storage, layer_nr); } if (extruder_is_used_on_this_layer[extruder_nr] || prime != ExtruderPrime::None) @@ -1649,10 +1628,9 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( } } - if (prime_tower_mode == PrimeTowerMethod::INTERLEAVED && ret.size() == 1 && ret.front().prime == ExtruderPrime::None - && layer_nr <= storage.max_print_height_second_to_last_extruder) + if (storage.prime_tower_) { - ret.front().prime = ExtruderPrime::Sparse; + storage.prime_tower_->polishExtruderUse(ret, storage, layer_nr); } assert(ret.size() <= (size_t)extruder_count && "Not more extruders may be planned in a layer than there are extruders!"); diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index f8aac43878..a9ac45bb36 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -600,6 +600,11 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) return prime_tower; } +bool PrimeTower::extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder) +{ + return extruder_is_used_on_this_layer[extruder_nr] && extruder_nr != last_extruder; +} + void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr) const { if (gcode_layer.getLayerNr() != 0) diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 382d06756f..fe69a545c8 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -3,6 +3,8 @@ #include "PrimeTower/PrimeTowerInterleaved.h" +#include "sliceDataStorage.h" + namespace cura { @@ -11,4 +13,29 @@ PrimeTowerInterleaved::PrimeTowerInterleaved(SliceDataStorage& storage, size_t e { } +ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( + const std::vector& extruder_is_used_on_this_layer, + size_t extruder_nr, + size_t last_extruder, + const SliceDataStorage& storage, + const LayerIndex& layer_nr) const +{ + if (extruderRequiresPrime(extruder_is_used_on_this_layer, extruder_nr, last_extruder)) + { + return ExtruderPrime::Prime; + } + else + { + return ExtruderPrime::None; + } +} + +void PrimeTowerInterleaved::polishExtruderUse(std::vector& extruder_use, const SliceDataStorage& storage, const LayerIndex& layer_nr) const +{ + if (extruder_use.size() == 1 && extruder_use.front().prime == ExtruderPrime::None && layer_nr <= storage.max_print_height_second_to_last_extruder) + { + extruder_use.front().prime = ExtruderPrime::Sparse; + } +} + } // namespace cura diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index d93fc9271f..9e71c415fd 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -3,6 +3,8 @@ #include "PrimeTower/PrimeTowerNormal.h" +#include "sliceDataStorage.h" + namespace cura { @@ -11,4 +13,25 @@ PrimeTowerNormal::PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_co { } +ExtruderPrime PrimeTowerNormal::getExtruderPrime( + const std::vector& extruder_is_used_on_this_layer, + size_t extruder_nr, + size_t last_extruder, + const SliceDataStorage& storage, + const LayerIndex& layer_nr) const +{ + if (extruderRequiresPrime(extruder_is_used_on_this_layer, extruder_nr, last_extruder)) + { + return ExtruderPrime::Prime; + } + else if (layer_nr < storage.max_print_height_second_to_last_extruder) + { + return ExtruderPrime::Sparse; + } + else + { + return ExtruderPrime::None; + } +} + } // namespace cura From 7dc995eaf41dd18c8314dc2a3671896bb17011ef Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 11 May 2024 15:19:44 +0200 Subject: [PATCH 14/49] Add a utility vector to store items by layers This new vector allows for storing items using a LayerIndex, which can be negative. Thus we can store items also on negative (raft) layers in the same list and avoid having two lists to store the same items. --- include/FffGcodeWriter.h | 18 +-- include/PrimeTower/PrimeTower.h | 5 +- include/PrimeTower/PrimeTowerInterleaved.h | 2 +- include/utils/LayerVector.h | 137 +++++++++++++++++++++ src/FffGcodeWriter.cpp | 59 +++------ src/PrimeTower/PrimeTowerInterleaved.cpp | 14 ++- 6 files changed, 169 insertions(+), 66 deletions(-) create mode 100644 include/utils/LayerVector.h diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 040a6eb1ee..69f808c85a 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -12,6 +12,7 @@ #include "GCodePathConfig.h" #include "LayerPlanBuffer.h" #include "gcodeExport.h" +#include "utils/LayerVector.h" #include "utils/NoCopy.h" #include "utils/gettime.h" @@ -60,13 +61,8 @@ class FffGcodeWriter : public NoCopy */ std::ofstream output_file; - /*! - * For each raft/filler layer, the extruders to be used in that layer in the order in which they are going to be used. - * The first number is the first raft layer. Indexing is shifted compared to normal negative layer numbers for raft/filler layers. - */ - std::vector> extruder_order_per_layer_negative_layers; - - std::vector> extruder_order_per_layer; //!< For each layer, the extruders to be used in that layer in the order in which they are going to be used + //!< For each layer, the extruders to be used in that layer in the order in which they are going to be used + LayerVector> extruder_order_per_layer; std::vector> mesh_order_per_extruder; //!< For each extruder, the order of the meshes (first element is first mesh to be printed) @@ -732,14 +728,6 @@ class FffGcodeWriter : public NoCopy * \return The first or last exruder used at the given index */ size_t findUsedExtruderIndex(const SliceDataStorage& storage, const LayerIndex& layer_nr, bool last) const; - - /*! - * Get the extruders use at the given layer - * - * \param layer_nr The index of the layer at which we want the extruders uses - * \return The extruders use at the given layer, which may be empty in some cases - */ - std::vector getExtruderUse(const LayerIndex& layer_nr) const; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 45db28bdc2..6210b6f7ac 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -11,6 +11,7 @@ #include "geometry/Polygon.h" #include "settings/EnumSettings.h" #include "settings/types/LayerIndex.h" +#include "utils/LayerVector.h" #include "utils/polygonUtils.h" namespace cura @@ -73,6 +74,8 @@ class PrimeTower */ PrimeTower(SliceDataStorage& storage, size_t extruder_count); + virtual ~PrimeTower() = default; + /*! * Add path plans for the prime tower to the \p gcode_layer * @@ -109,7 +112,7 @@ class PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const = 0; - virtual void polishExtruderUse(std::vector& /*extruder_use*/, const SliceDataStorage& /*storage*/, const LayerIndex& /*layer_nr*/) const + virtual void polishExtrudersUse(LayerVector>& /*extruders_use*/, const SliceDataStorage& /*storage*/) const { } diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 4629fa3704..3d53daa973 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -27,7 +27,7 @@ class PrimeTowerInterleaved : public PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; - virtual void polishExtruderUse(std::vector& extruder_use, const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; + virtual void polishExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage) const override; }; } // namespace cura diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h new file mode 100644 index 0000000000..e1de5f1354 --- /dev/null +++ b/include/utils/LayerVector.h @@ -0,0 +1,137 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef UTILS_LAYER_VECTOR_H +#define UTILS_LAYER_VECTOR_H + +#include + +#include "raft.h" +#include "settings/types/LayerIndex.h" + +namespace cura +{ + +/*! + * \brief The LayerVector class mimics a std::vector but with the index being a LayerIndex, thus it can have negative + * values (for raft layers). + * \note At instantiation, LayerVector will call Raft::getTotalExtraLayers() so it requires the settings to be setup. + * After that, it is assumed that this value will not change while the vector is used. + * \warning It is assumed that items are inserted in layer order, and without missing items, like a std::vector would do + */ +template +class LayerVector +{ + // Required for some std calls as a container + using value_type = T; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + using reference = value_type&; + using const_reference = value_type&; + +public: + LayerVector() + { + } + + /*! + * \brief Initializes the vector for use + * \param contains_raft_layers Indicates whether this vector will contain raft layers + * \param total_print_layers The number of print layers (not including raft layers). This is only for pre-reserving + * stored data and can be safely omitted. + * \note It is not mandatory to call this method if you do not intend to store raft layers, but still good practice + */ + void init(bool contains_raft_layers, size_t print_layers = 0) + { + delta_ = contains_raft_layers ? Raft::getTotalExtraLayers() : 0; + + vector_.clear(); + if (print_layers) + { + vector_.reserve(print_layers + delta_); + } + } + + const_reference operator[](const LayerIndex& pos) const + { + return vector_[static_cast(pos + delta_)]; + } + + reference operator[](const LayerIndex& pos) + { + return vector_[static_cast(pos + delta_)]; + } + + const_reference at(const LayerIndex& pos) const + { + return vector_.at(static_cast(pos + delta_)); + } + + reference at(const LayerIndex& pos) + { + return vector_.at(static_cast(pos + delta_)); + } + + value_type get(const LayerIndex& pos) const noexcept + { + LayerIndex::value_type index = pos + delta_; + if (index >= 0 && static_cast(index) < vector_.size()) + { + return vector_[static_cast(index)]; + } + return value_type{}; + } + + void pop_back() + { + vector_.pop_back(); + } + + void push_back(const_reference item) + { + vector_.push_back(item); + } + + void push_back(T&& item) + { + vector_.push_back(std::move(item)); + } + + [[nodiscard]] size_t size() const + { + return vector_.size(); + } + + [[nodiscard]] bool empty() const + { + return vector_.empty(); + } + + void reserve(size_t size) + { + vector_.reserve(size); + } + + void resize(size_t size) + { + vector_.resize(size); + } + + void clear() + { + vector_.clear(); + } + + void emplace_back(auto&&... args) + { + vector_.emplace_back(std::forward(args)...); + } + +private: + std::vector vector_; + LayerIndex::value_type delta_{ 0 }; +}; + +} // namespace cura + +#endif // UTILS_LAYER_VECTOR_H diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index dc43b7a325..8eeafe2aad 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -281,7 +281,7 @@ void FffGcodeWriter::findLayerSeamsForSpiralize(SliceDataStorage& storage, size_ bool done_this_layer = false; // iterate through extruders until we find a mesh that has a part with insets - const std::vector extruder_order = getExtruderUse(layer_nr); + const std::vector extruder_order = extruder_order_per_layer.get(layer_nr); for (unsigned int extruder_idx = 0; ! done_this_layer && extruder_idx < extruder_order.size(); ++extruder_idx) { const size_t extruder_nr = extruder_order[extruder_idx].extruder_nr; @@ -1099,7 +1099,7 @@ void FffGcodeWriter::endRaftLayer(const SliceDataStorage& storage, LayerPlan& gc setExtruder_addPrime(storage, gcode_layer, current_extruder); // If required, fill prime tower for other extruders - for (const ExtruderUse& extruder_use : getExtruderUse(layer_nr)) + for (const ExtruderUse& extruder_use : extruder_order_per_layer.get(layer_nr)) { if (! gcode_layer.getPrimeTowerIsPlanned(extruder_use.extruder_nr) && extruder_use.prime != ExtruderPrime::None) { @@ -1182,12 +1182,9 @@ FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataS } const coord_t comb_offset_from_outlines = max_inner_wall_width * 2; - assert( - static_cast(extruder_order_per_layer_negative_layers.size()) + layer_nr >= 0 && "Layer numbers shouldn't get more negative than there are raft/filler layers"); - const size_t first_extruder = findUsedExtruderIndex(storage, layer_nr, false); - const std::vector extruder_order = getExtruderUse(layer_nr); + const std::vector extruder_order = extruder_order_per_layer.get(layer_nr); const coord_t first_outer_wall_line_width = scene.extruders[first_extruder].settings_.get("wall_line_width_0"); LayerPlan& gcode_layer = *new LayerPlan( @@ -1227,7 +1224,7 @@ FffGcodeWriter::ProcessLayerResult FffGcodeWriter::processLayer(const SliceDataS for (const ExtruderUse& extruder_use : extruder_order) { - size_t extruder_nr = extruder_use.extruder_nr; + const size_t& extruder_nr = extruder_use.extruder_nr; // Set extruder (if needed) and prime (if needed) setExtruder_addPrime(storage, gcode_layer, extruder_nr); @@ -1521,20 +1518,26 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor last_extruder = gcode.getExtruderNr(); } + extruder_order_per_layer.init(true, storage.print_layer_count); + 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; 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, extruders_used); - extruder_order_per_layer_here.push_back(extruder_order); + extruder_order_per_layer.push_back(extruder_order); if (! extruder_order.empty()) { last_extruder = extruder_order.back().extruder_nr; } } + + if (storage.prime_tower_) + { + storage.prime_tower_->polishExtrudersUse(extruder_order_per_layer, storage); + } } void FffGcodeWriter::calculatePrimeLayerPerExtruder(const SliceDataStorage& storage) @@ -1628,11 +1631,6 @@ std::vector FffGcodeWriter::getUsedExtrudersOnLayer( } } - if (storage.prime_tower_) - { - storage.prime_tower_->polishExtruderUse(ret, storage, layer_nr); - } - assert(ret.size() <= (size_t)extruder_count && "Not more extruders may be planned in a layer than there are extruders!"); return ret; } @@ -2440,13 +2438,13 @@ bool FffGcodeWriter::partitionInfillBySkinAbove( size_t FffGcodeWriter::findUsedExtruderIndex(const SliceDataStorage& storage, const LayerIndex& layer_nr, bool last) const { - const std::vector extruder_use = getExtruderUse(layer_nr); + const std::vector extruder_use = extruder_order_per_layer.get(layer_nr); if (! extruder_use.empty()) { return last ? extruder_use.back().extruder_nr : extruder_use.front().extruder_nr; } - else if (layer_nr <= -extruder_order_per_layer_negative_layers.size()) + else if (layer_nr <= -Raft::getTotalExtraLayers()) { // Asking for extruder use below first layer, give first extruder return getStartExtruder(storage); @@ -2458,33 +2456,6 @@ size_t FffGcodeWriter::findUsedExtruderIndex(const SliceDataStorage& storage, co } } -std::vector FffGcodeWriter::getExtruderUse(const LayerIndex& layer_nr) const -{ - int layer_index; - const std::vector>* extruder_order; - - if (layer_nr >= 0) - { - layer_index = layer_nr; - extruder_order = &extruder_order_per_layer; - } - else - { - layer_index = extruder_order_per_layer_negative_layers.size() + layer_nr; - extruder_order = &extruder_order_per_layer_negative_layers; - } - - if (layer_index >= 0 && layer_index < extruder_order->size()) - { - return (*extruder_order)[layer_index]; - } - else - { - // No extruder use registered for this layer, which may happen in some edge-cases - return {}; - } -} - void FffGcodeWriter::processSpiralizedWall( const SliceDataStorage& storage, LayerPlan& gcode_layer, @@ -3943,7 +3914,7 @@ void FffGcodeWriter::addPrimeTower(const SliceDataStorage& storage, LayerPlan& g if (storage.prime_tower_) { LayerIndex layer_nr = gcode_layer.getLayerNr(); - const std::vector extruder_order = getExtruderUse(layer_nr); + const std::vector extruder_order = extruder_order_per_layer.get(layer_nr); storage.prime_tower_->addToGcode(storage, gcode_layer, extruder_order, prev_extruder, gcode_layer.getExtruder()); } } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index fe69a545c8..ea92b30971 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -17,8 +17,8 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder, - const SliceDataStorage& storage, - const LayerIndex& layer_nr) const + const SliceDataStorage& /*storage*/, + const LayerIndex& /*layer_nr*/) const { if (extruderRequiresPrime(extruder_is_used_on_this_layer, extruder_nr, last_extruder)) { @@ -30,11 +30,15 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( } } -void PrimeTowerInterleaved::polishExtruderUse(std::vector& extruder_use, const SliceDataStorage& storage, const LayerIndex& layer_nr) const +void PrimeTowerInterleaved::polishExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage) const { - if (extruder_use.size() == 1 && extruder_use.front().prime == ExtruderPrime::None && layer_nr <= storage.max_print_height_second_to_last_extruder) + for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr < storage.print_layer_count; ++layer_nr) { - extruder_use.front().prime = ExtruderPrime::Sparse; + std::vector& extruders_use_at_layer = extruders_use[layer_nr]; + if (extruders_use_at_layer.size() == 1 && extruders_use_at_layer.front().prime == ExtruderPrime::None && layer_nr <= storage.max_print_height_second_to_last_extruder) + { + extruders_use_at_layer.front().prime = ExtruderPrime::Sparse; + } } } From 41efc4c01171cc9024cbd59882d1360236cfa7d7 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 11 May 2024 21:52:44 +0200 Subject: [PATCH 15/49] Use LayerVector when possible in PrimeTower This simplifies some code logic in the prime tower that is common and can now be delegated to LayerVector --- include/PrimeTower/PrimeTower.h | 4 +-- include/utils/LayerVector.h | 45 +++++++++++++++++++++++++++++++-- src/PrimeTower/PrimeTower.cpp | 19 +++++++------- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 6210b6f7ac..7a72275efd 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -30,7 +30,7 @@ class PrimeTower { private: using MovesByExtruder = std::map; - using MovesByLayer = std::map>; + using MovesByLayer = std::map>; bool wipe_from_middle_; //!< Whether to wipe on the inside of the hollow prime tower Point2LL middle_; //!< The middle of the prime tower @@ -54,7 +54,7 @@ class PrimeTower MovesByExtruder inset_extra_moves_; //!< For each extruder, the extra inset moves to be processed for better adhesion on initial layer Shape outer_poly_; //!< The outline of the outermost prime tower. - std::vector outer_poly_base_; //!< The outline of the layers having extra width for the base + LayerVector outer_poly_base_; //!< The outline of the layers having extra width for the base public: /* diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index e1de5f1354..efbee9cc3c 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -27,7 +27,8 @@ class LayerVector using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; using reference = value_type&; - using const_reference = value_type&; + using const_reference = const value_type&; + using difference_type = typename std::vector::difference_type; public: LayerVector() @@ -52,6 +53,26 @@ class LayerVector } } + const_iterator begin() const + { + return vector_.begin(); + } + + iterator begin() + { + return vector_.begin(); + } + + const_iterator end() const + { + return vector_.end(); + } + + iterator end() + { + return vector_.end(); + } + const_reference operator[](const LayerIndex& pos) const { return vector_[static_cast(pos + delta_)]; @@ -72,10 +93,30 @@ class LayerVector return vector_.at(static_cast(pos + delta_)); } + [[nodiscard]] const_iterator iterator_at(const LayerIndex& pos) const + { + LayerIndex::value_type index = pos + delta_; + if (index >= 0 && static_cast(index) < size()) + { + return std::next(begin(), static_cast(index)); + } + return end(); + } + + [[nodiscard]] iterator iterator_at(const LayerIndex& pos) + { + LayerIndex::value_type index = pos + delta_; + if (index >= 0 && static_cast(index) < size()) + { + return std::next(begin(), static_cast(index)); + } + return end(); + } + value_type get(const LayerIndex& pos) const noexcept { LayerIndex::value_type index = pos + delta_; - if (index >= 0 && static_cast(index) < vector_.size()) + if (index >= 0 && static_cast(index) < size()) { return vector_[static_cast(index)]; } diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index a9ac45bb36..36457a2efd 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -98,9 +98,10 @@ void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_inse { // By default, add empty moves for every extruder prime_moves_[extruder_nr]; - base_extra_moves_[extruder_nr]; + base_extra_moves_[extruder_nr].init(true); inset_extra_moves_[extruder_nr]; } + outer_poly_base_.init(true); 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_) @@ -403,14 +404,12 @@ void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t ext bool PrimeTower::addToGcode_base(LayerPlan& gcode_layer, const size_t extruder_nr) const { - const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); - LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + raft_total_extra_layers; - - const auto& pattern_extra_brim = base_extra_moves_.at(extruder_nr); - if (absolute_layer_number < pattern_extra_brim.size()) + const LayerVector& pattern_extra_brim = base_extra_moves_.at(extruder_nr); + auto iterator = pattern_extra_brim.iterator_at(gcode_layer.getLayerNr()); + if (iterator != pattern_extra_brim.end()) { // Extra rings for stronger base - const auto& pattern = pattern_extra_brim[absolute_layer_number]; + const Shape& pattern = *iterator; if (! pattern.empty()) { const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[extruder_nr]; @@ -558,10 +557,10 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) const Shape& PrimeTower::getOuterPoly(const LayerIndex& layer_nr) const { - const LayerIndex absolute_layer_nr = layer_nr + Raft::getTotalExtraLayers(); - if (absolute_layer_nr < outer_poly_base_.size()) + auto iterator = outer_poly_base_.iterator_at(layer_nr); + if (iterator != outer_poly_base_.end()) { - return outer_poly_base_[absolute_layer_nr]; + return *iterator; } else { From d882198dfa3e34688233510d3615ff533d722abd Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 11 May 2024 22:02:48 +0200 Subject: [PATCH 16/49] Fix some (mostly type-related) warnings --- include/LayerPlan.h | 4 ++-- include/PrimeTower/PrimeTower.h | 4 ++-- include/utils/LayerVector.h | 2 +- src/LayerPlan.cpp | 4 ++-- src/PrimeTower/PrimeTower.cpp | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index b1377d4f3d..31e164956d 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -200,14 +200,14 @@ class LayerPlan : public NoCopy * Whether the prime tower is already planned for the specified extruder. * \param extruder_nr The extruder to check. */ - bool getPrimeTowerIsPlanned(unsigned int extruder_nr) const; + bool getPrimeTowerIsPlanned(size_t extruder_nr) const; /*! * Mark the prime tower as planned for the specified extruder. * \param extruder_nr The extruder to mark as having its prime tower * planned. */ - void setPrimeTowerIsPlanned(unsigned int extruder_nr); + void setPrimeTowerIsPlanned(size_t extruder_nr); /*! * Whether the prime tower extra base is already planned. diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 7a72275efd..0b2d3cf321 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -222,7 +222,7 @@ class PrimeTower * This function picks a start location for this extruder on the prime tower's perimeter and travels there to avoid * starting at the location everytime which can result in z-seam blobs. */ - void gotoStartLocation(LayerPlan& gcode_layer, const int extruder) const; + void gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder) const; /*! * Generate the prime tower area to be used on each layer @@ -234,7 +234,7 @@ class PrimeTower /*! * Generate the area where the prime tower should be. */ - void generatePaths(const SliceDataStorage& storage); + void generatePaths(); /*! * \brief Subtract the prime tower from the support areas in storage. diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index efbee9cc3c..031fd387c9 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -170,7 +170,7 @@ class LayerVector private: std::vector vector_; - LayerIndex::value_type delta_{ 0 }; + size_t delta_{ 0 }; }; } // namespace cura diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 7f923ef879..a28434fddc 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -330,12 +330,12 @@ void LayerPlan::moveInsideCombBoundary(const coord_t distance, const std::option } } -bool LayerPlan::getPrimeTowerIsPlanned(unsigned int extruder_nr) const +bool LayerPlan::getPrimeTowerIsPlanned(size_t extruder_nr) const { return has_prime_tower_planned_per_extruder_[extruder_nr]; } -void LayerPlan::setPrimeTowerIsPlanned(unsigned int extruder_nr) +void LayerPlan::setPrimeTowerIsPlanned(size_t extruder_nr) { has_prime_tower_planned_per_extruder_[extruder_nr] = true; } diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 36457a2efd..cdaee6dfd5 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -51,7 +51,7 @@ PrimeTower::PrimeTower(SliceDataStorage& storage, size_t extruder_count) return adhesion_a < adhesion_b; }); - generatePaths(storage); + generatePaths(); subtractFromSupport(storage); } @@ -70,7 +70,7 @@ void PrimeTower::generateGroundpoly() post_wipe_point_ = Point2LL(x - tower_size / 2, y + tower_size / 2); } -void PrimeTower::generatePaths(const SliceDataStorage& storage) +void PrimeTower::generatePaths() { generateGroundpoly(); @@ -545,7 +545,7 @@ std::vector PrimeTower::findExtrudersSparseInfill( void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { - for (size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) + for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) { const Shape outside_polygon = getOuterPoly(layer).getOutsidePolygons(); AABB outside_polygon_boundary_box(outside_polygon); @@ -604,12 +604,12 @@ bool PrimeTower::extruderRequiresPrime(const std::vector& extruder_is_used return extruder_is_used_on_this_layer[extruder_nr] && extruder_nr != last_extruder; } -void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const int extruder_nr) const +void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder_nr) const { if (gcode_layer.getLayerNr() != 0) { - int current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations_) + number_of_prime_tower_start_locations_) - % number_of_prime_tower_start_locations_; + size_t current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations_) + number_of_prime_tower_start_locations_) + % number_of_prime_tower_start_locations_; const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; From 34d38a21f833139eff1fc93808c9018bb0220fc8 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 13 May 2024 09:03:15 +0200 Subject: [PATCH 17/49] Split normal/interleaved behaviors into dedicated classes This simplifies the common behavior in PrimeTower and allows for more modifications inside each class without breaking each other --- include/PrimeTower/PrimeTower.h | 72 +++++----- include/PrimeTower/PrimeTowerInterleaved.h | 10 ++ include/PrimeTower/PrimeTowerNormal.h | 10 ++ include/settings/EnumSettings.h | 4 +- src/PrimeTower/PrimeTower.cpp | 151 +++++---------------- src/PrimeTower/PrimeTowerInterleaved.cpp | 106 +++++++++++++++ src/PrimeTower/PrimeTowerNormal.cpp | 46 +++++++ src/settings/Settings.cpp | 6 +- 8 files changed, 246 insertions(+), 159 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 0b2d3cf321..c10fb3d236 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -119,26 +119,6 @@ class PrimeTower static PrimeTower* createPrimeTower(SliceDataStorage& storage); protected: - static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); - -private: - /*! - * \see PrimeTower::generatePaths - * - * Generate the extrude paths for each extruder on even and odd layers - * Fill the ground poly with dense infill. - * \param cumulative_insets [in, out] The insets added to each extruder to compute the radius of its ring - */ - void generatePaths_denseInfill(std::vector& cumulative_insets); - - /*! - * \see WipeTower::generatePaths - * - * \brief Generate the sparse extrude paths for each extruders combination - * \param cumulative_insets The insets added to each extruder to compute the radius of its ring - */ - void generatePaths_sparseInfill(const std::vector& cumulative_insets); - /*! * \brief Generate the sparse extrude paths for an extruders combination * @@ -153,7 +133,41 @@ class PrimeTower const size_t last_extruder_idx, const std::vector& rings_radii, const coord_t line_width, - const size_t actual_extruder_nr); + const size_t actual_extruder_nr) const; + + virtual bool requiresBaseExtraPrint(size_t extruder_nr) const = 0; + + virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const = 0; + + virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const = 0; + + /*! + * \brief Find the list of extruders that don't actually need to be primed during this layer, and for which + * we want to print only the sparse infill to keep the prime tower consistent. + * \param gcode_layer The current gcode export + * \param required_extruder_prime The pre-computed list of extruders uses during this layer + * \param method The current prime tower strategy + * \param initial_list_idx A list potentially containing extruders that we already know can be used for + * sparse infill + * \return The indexes of extruders to be used for sparse infill + */ + virtual std::vector + findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const = 0; + + static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); + +private: + /*! + * \brief Generate the extrude paths for each extruder on even and odd layers. Fill the ground poly with dense infill. + * \param cumulative_insets [in, out] The insets added to each extruder to compute the radius of its ring + */ + void generateDenseInfill(std::vector& cumulative_insets); + + /*! + * \brief Generate the sparse extrude paths for each extruders combination + * \param cumulative_insets The insets added to each extruder to compute the radius of its ring + */ + void generateSparseInfill(const std::vector& cumulative_insets); /*! * Generate start locations on the prime tower. The locations are evenly spread around the prime tower's perimeter. @@ -201,22 +215,6 @@ class PrimeTower */ void addToGcode_sparseInfill(LayerPlan& gcode_layer, const std::vector& extruders_to_prime_idx, const size_t current_extruder_nr) const; - /*! - * \brief Find the list of extruders that don't actually need to be primed during this layer, and for which - * we want to print only the sparse infill to keep the prime tower consistent. - * \param gcode_layer The current gcode export - * \param required_extruder_prime The pre-computed list of extruders uses during this layer - * \param method The current prime tower strategy - * \param initial_list_idx A list potentially containing extruders that we already know can be used for - * sparse infill - * \return The indexes of extruders to be used for sparse infill - */ - std::vector findExtrudersSparseInfill( - LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - cura::PrimeTowerMethod method, - const std::vector& initial_list_idx = {}) const; - /*! * For an extruder switch that happens not on the first layer, the extruder needs to be primed on the prime tower. * This function picks a start location for this extruder on the prime tower's perimeter and travels there to avoid diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 3d53daa973..f31da8b140 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -28,6 +28,16 @@ class PrimeTowerInterleaved : public PrimeTower const LayerIndex& layer_nr) const override; virtual void polishExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage) const override; + +protected: + virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; + + virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const override; + + virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; + + virtual std::vector + findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 2a29bb5713..49322ee67d 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -26,6 +26,16 @@ class PrimeTowerNormal : public PrimeTower size_t last_extruder, const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; + +protected: + virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; + + virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const override; + + virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; + + virtual std::vector + findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const override; }; } // namespace cura diff --git a/include/settings/EnumSettings.h b/include/settings/EnumSettings.h index bc6ea72cee..847ccb10b8 100644 --- a/include/settings/EnumSettings.h +++ b/include/settings/EnumSettings.h @@ -246,9 +246,9 @@ enum class InsetDirection }; /*! - * Method used for prime tower generation + * Prime tower generation mode */ -enum class PrimeTowerMethod +enum class PrimeTowerMode { /*! * Prime tower that minimizes time and used filament as much as possible. diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index cdaee6dfd5..23b49ab8da 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -50,9 +50,6 @@ PrimeTower::PrimeTower(SliceDataStorage& storage, size_t extruder_count) const Ratio adhesion_b = scene.extruders[extruder_nr_b].settings_.get("material_adhesion_tendency"); return adhesion_a < adhesion_b; }); - - generatePaths(); - subtractFromSupport(storage); } void PrimeTower::generateGroundpoly() @@ -75,19 +72,18 @@ void PrimeTower::generatePaths() generateGroundpoly(); std::vector cumulative_insets; - generatePaths_denseInfill(cumulative_insets); + generateDenseInfill(cumulative_insets); generateStartLocations(); - generatePaths_sparseInfill(cumulative_insets); + generateSparseInfill(cumulative_insets); } -void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_insets) +void PrimeTower::generateDenseInfill(std::vector& cumulative_insets) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); - const PrimeTowerMethod method = mesh_group_settings.get("prime_tower_mode"); const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; @@ -127,8 +123,7 @@ void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_inse } // Generate the base outside extra rings - if ((method == PrimeTowerMethod::INTERLEAVED || (extruder_nr == extruder_order_.front() && method == PrimeTowerMethod::NORMAL)) && (base_enabled || has_raft) - && base_extra_radius > 0 && base_height > 0) + if (requiresBaseExtraPrint(extruder_nr) && (base_enabled || has_raft) && base_extra_radius > 0 && base_height > 0) { for (coord_t z = 0; z < base_height; z += layer_height) { @@ -152,7 +147,7 @@ void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_inse // Now we have the total cumulative inset, generate the base inside extra rings for (size_t extruder_nr : extruder_order_) { - if (extruder_nr == extruder_order_.back() || method == PrimeTowerMethod::INTERLEAVED) + if (requiresFirstLayerExtraInnerPrint(extruder_nr)) { const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); Shape pattern = PolygonUtils::generateInset(outer_poly_, line_width, cumulative_inset); @@ -164,69 +159,23 @@ void PrimeTower::generatePaths_denseInfill(std::vector& cumulative_inse } } -void PrimeTower::generatePaths_sparseInfill(const std::vector& cumulative_insets) +void PrimeTower::generateSparseInfill(const std::vector& cumulative_insets) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const PrimeTowerMethod method = mesh_group_settings.get("prime_tower_mode"); - struct ActualExtruder - { - size_t number; - coord_t line_width; - }; + // Pre-compute radiuses of each extruder ring + std::vector rings_radii; + const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); + const coord_t tower_radius = tower_size / 2; - std::vector actual_extruders; - actual_extruders.reserve(extruder_order_.size()); - for (size_t extruder_nr : extruder_order_) + rings_radii.push_back(tower_radius); + for (const coord_t& cumulative_inset : cumulative_insets) { - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - actual_extruders.push_back({ extruder_nr, line_width }); + rings_radii.push_back(tower_radius - cumulative_inset); } - if (method == PrimeTowerMethod::INTERLEAVED || method == PrimeTowerMethod::NORMAL) - { - // Pre-compute radiuses of each extruder ring - std::vector rings_radii; - const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); - const coord_t tower_radius = tower_size / 2; - - rings_radii.push_back(tower_radius); - for (const coord_t& cumulative_inset : cumulative_insets) - { - rings_radii.push_back(tower_radius - cumulative_inset); - } - - // 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 < extruder_order_.size(); ++first_extruder_idx) - { - size_t nb_extruders_sparse = method == PrimeTowerMethod::NORMAL ? first_extruder_idx + 1 : extruder_order_.size(); - - for (size_t last_extruder_idx = first_extruder_idx; last_extruder_idx < nb_extruders_sparse; ++last_extruder_idx) - { - size_t extruders_combination = 0; - for (size_t extruder_idx = first_extruder_idx; extruder_idx <= last_extruder_idx; ++extruder_idx) - { - size_t extruder_nr = extruder_order_.at(extruder_idx); - extruders_combination |= (1 << extruder_nr); - } - - std::map infills_for_combination; - for (const ActualExtruder& actual_extruder : actual_extruders) - { - if (method == PrimeTowerMethod::INTERLEAVED || actual_extruder.number == extruder_order_.at(first_extruder_idx)) - { - Shape infill = generatePath_sparseInfill(first_extruder_idx, last_extruder_idx, rings_radii, actual_extruder.line_width, actual_extruder.number); - infills_for_combination[actual_extruder.number] = infill; - } - } - - sparse_pattern_per_extruders_[extruders_combination] = infills_for_combination; - } - } - } + sparse_pattern_per_extruders_ = generateSparseInfillImpl(rings_radii); } Shape PrimeTower::generatePath_sparseInfill( @@ -234,7 +183,7 @@ Shape PrimeTower::generatePath_sparseInfill( const size_t last_extruder_idx, const std::vector& rings_radii, const coord_t line_width, - const size_t actual_extruder_nr) + const size_t actual_extruder_nr) const { const Scene& scene = Application::getInstance().current_slice_->scene; const coord_t max_bridging_distance = scene.extruders[actual_extruder_nr].settings_.get("prime_tower_max_bridging_distance"); @@ -327,13 +276,13 @@ void PrimeTower::addToGcode( return; } - PrimeTowerMethod method = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("prime_tower_mode"); + PrimeTowerMode prime_tower_mode = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("prime_tower_mode"); std::vector extra_primed_extruders_idx; switch (extruder_iterator->prime) { case ExtruderPrime::None: - if (method != PrimeTowerMethod::INTERLEAVED) + if (prime_tower_mode != PrimeTowerMode::INTERLEAVED) { gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); } @@ -341,7 +290,7 @@ void PrimeTower::addToGcode( case ExtruderPrime::Sparse: gotoStartLocation(gcode_layer, new_extruder_nr); - extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime, method, { new_extruder_idx }); + extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime, { new_extruder_idx }); addToGcode_sparseInfill(gcode_layer, extra_primed_extruders_idx, new_extruder_nr); break; @@ -351,11 +300,14 @@ void PrimeTower::addToGcode( addToGcode_denseInfill(gcode_layer, new_extruder_nr); gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); - if (method == PrimeTowerMethod::INTERLEAVED && gcode_layer.getLayerNr() <= storage.max_print_height_second_to_last_extruder) + if (gcode_layer.getLayerNr() <= storage.max_print_height_second_to_last_extruder) { // Whatever happens before and after, use the current extruder to prime all the non-required extruders now - extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime, method); - addToGcode_sparseInfill(gcode_layer, extra_primed_extruders_idx, new_extruder_nr); + extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime); + if (! extra_primed_extruders_idx.empty()) + { + addToGcode_sparseInfill(gcode_layer, extra_primed_extruders_idx, new_extruder_nr); + } } break; } @@ -501,48 +453,6 @@ void PrimeTower::addToGcode_sparseInfill(LayerPlan& gcode_layer, const std::vect } } -std::vector PrimeTower::findExtrudersSparseInfill( - LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - PrimeTowerMethod method, - const std::vector& initial_list_idx) const -{ - std::vector extruders_to_prime_idx; - - for (size_t extruder_idx = 0; extruder_idx < extruder_order_.size(); extruder_idx++) - { - auto iterator_initial_list = std::find(initial_list_idx.begin(), initial_list_idx.end(), extruder_idx); - bool is_in_initial_list = iterator_initial_list != initial_list_idx.end(); - - if (is_in_initial_list) - { - extruders_to_prime_idx.push_back(extruder_idx); - } - else - { - size_t extruder_nr = extruder_order_.at(extruder_idx); - if (method == PrimeTowerMethod::INTERLEAVED && ! gcode_layer.getPrimeTowerIsPlanned(extruder_nr)) - { - auto iterator_required_list = std::find_if( - required_extruder_prime.begin(), - required_extruder_prime.end(), - [extruder_nr](const ExtruderUse& extruder_use) - { - return extruder_use.extruder_nr == extruder_nr && extruder_use.prime == ExtruderPrime::Prime; - }); - bool is_in_required_list = iterator_required_list != required_extruder_prime.end(); - - if (! is_in_required_list) - { - extruders_to_prime_idx.push_back(extruder_idx); - } - } - } - } - - return extruders_to_prime_idx; -} - void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) @@ -584,18 +494,25 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) && storage.max_print_height_second_to_last_extruder >= -static_cast(raft_total_extra_layers)) { const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const PrimeTowerMethod method = mesh_group_settings.get("prime_tower_mode"); + const PrimeTowerMode method = mesh_group_settings.get("prime_tower_mode"); switch (method) { - case PrimeTowerMethod::NORMAL: + case PrimeTowerMode::NORMAL: prime_tower = new PrimeTowerNormal(storage, scene.extruders.size()); break; - case PrimeTowerMethod::INTERLEAVED: + case PrimeTowerMode::INTERLEAVED: prime_tower = new PrimeTowerInterleaved(storage, scene.extruders.size()); break; } } + + if (prime_tower) + { + prime_tower->generatePaths(); + prime_tower->subtractFromSupport(storage); + } + return prime_tower; } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index ea92b30971..c45a8f0e76 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -3,6 +3,10 @@ #include "PrimeTower/PrimeTowerInterleaved.h" +#include "Application.h" +#include "LayerPlan.h" +#include "Scene.h" +#include "Slice.h" #include "sliceDataStorage.h" namespace cura @@ -42,4 +46,106 @@ void PrimeTowerInterleaved::polishExtrudersUse(LayerVector> PrimeTowerInterleaved::generateSparseInfillImpl(const std::vector& rings_radii) const +{ + const Scene& scene = Application::getInstance().current_slice_->scene; + + struct ActualExtruder + { + size_t number; + coord_t line_width; + }; + + std::vector actual_extruders; + actual_extruders.reserve(extruder_order_.size()); + for (size_t extruder_nr : extruder_order_) + { + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + actual_extruders.push_back({ extruder_nr, line_width }); + } + + // 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 + std::map> sparse_pattern_per_extruders; + for (size_t first_extruder_idx = 0; first_extruder_idx < extruder_order_.size(); ++first_extruder_idx) + { + for (size_t last_extruder_idx = first_extruder_idx; last_extruder_idx < extruder_order_.size(); ++last_extruder_idx) + { + size_t extruders_combination = 0; + for (size_t extruder_idx = first_extruder_idx; extruder_idx <= last_extruder_idx; ++extruder_idx) + { + size_t extruder_nr = extruder_order_.at(extruder_idx); + extruders_combination |= (1 << extruder_nr); + } + + std::map infills_for_combination; + for (const ActualExtruder& actual_extruder : actual_extruders) + { + Shape infill = generatePath_sparseInfill(first_extruder_idx, last_extruder_idx, rings_radii, actual_extruder.line_width, actual_extruder.number); + infills_for_combination[actual_extruder.number] = infill; + } + + sparse_pattern_per_extruders[extruders_combination] = infills_for_combination; + } + } + + return sparse_pattern_per_extruders; +} + +std::vector PrimeTowerInterleaved::findExtrudersSparseInfill( + LayerPlan& gcode_layer, + const std::vector& required_extruder_prime, + const std::vector& initial_list_idx) const +{ + std::vector extruders_to_prime_idx; + + for (size_t extruder_idx = 0; extruder_idx < extruder_order_.size(); extruder_idx++) + { + auto iterator_initial_list = std::find(initial_list_idx.begin(), initial_list_idx.end(), extruder_idx); + bool is_in_initial_list = iterator_initial_list != initial_list_idx.end(); + + if (is_in_initial_list) + { + extruders_to_prime_idx.push_back(extruder_idx); + } + else + { + // If extruder is not the current used one, try to see if it requires a sparse prime that we could do + // at the same time + size_t extruder_nr = extruder_order_.at(extruder_idx); + if (! gcode_layer.getPrimeTowerIsPlanned(extruder_nr)) + { + auto iterator_required_list = std::find_if( + required_extruder_prime.begin(), + required_extruder_prime.end(), + [extruder_nr](const ExtruderUse& extruder_use) + { + return extruder_use.extruder_nr == extruder_nr && extruder_use.prime == ExtruderPrime::Prime; + }); + bool is_in_required_list = iterator_required_list != required_extruder_prime.end(); + + if (! is_in_required_list) + { + extruders_to_prime_idx.push_back(extruder_idx); + } + } + } + } + + return extruders_to_prime_idx; +} + } // namespace cura diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 9e71c415fd..036fbf2aca 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -3,6 +3,9 @@ #include "PrimeTower/PrimeTowerNormal.h" +#include "Application.h" +#include "Scene.h" +#include "Slice.h" #include "sliceDataStorage.h" namespace cura @@ -34,4 +37,47 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( } } +bool PrimeTowerNormal::requiresBaseExtraPrint(size_t extruder_nr) const +{ + // Generate base extra rings only for the outermost printed extruder + return extruder_nr == extruder_order_.front(); +} + +bool PrimeTowerNormal::requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const +{ + // Generate extra inner rings only for the innermost printed extruder + return extruder_nr == extruder_order_.back(); +} + +std::map> PrimeTowerNormal::generateSparseInfillImpl(const std::vector& rings_radii) const +{ + const Scene& scene = Application::getInstance().current_slice_->scene; + + // Generate a sparse infill for each extruder + std::map> sparse_pattern_per_extruders; + for (size_t extruder_idx = 0; extruder_idx < extruder_order_.size(); ++extruder_idx) + { + const size_t extruder_nr = extruder_order_[extruder_idx]; + const size_t extruders_combination = (1 << extruder_nr); + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + + std::map infills_for_combination; + Shape infill = generatePath_sparseInfill(extruder_idx, extruder_idx, rings_radii, line_width, extruder_nr); + infills_for_combination[extruder_nr] = infill; + + sparse_pattern_per_extruders[extruders_combination] = infills_for_combination; + } + + return sparse_pattern_per_extruders; +} + +std::vector PrimeTowerNormal::findExtrudersSparseInfill( + LayerPlan& /*gcode_layer*/, + const std::vector& /*required_extruder_prime*/, + const std::vector& initial_list_idx) const +{ + // In normal mode we only print what is required + return initial_list_idx; +} + } // namespace cura diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 136606cde2..8742268c4f 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -680,15 +680,15 @@ InsetDirection Settings::get(const std::string& key) const } template<> -PrimeTowerMethod Settings::get(const std::string& key) const +PrimeTowerMode Settings::get(const std::string& key) const { const std::string& value = get(key); if (value == "interleaved") { - return PrimeTowerMethod::INTERLEAVED; + return PrimeTowerMode::INTERLEAVED; } - return PrimeTowerMethod::NORMAL; + return PrimeTowerMode::NORMAL; } template<> From 1e29c8285f3dd49f0f7dd2f5ed0c07b0e1eb5f6e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 29 May 2024 18:14:08 +0200 Subject: [PATCH 18/49] Finalized seperation of prime tower implementations --- include/PrimeTower/PrimeTower.h | 8 ++++++-- include/PrimeTower/PrimeTowerInterleaved.h | 8 ++++++-- include/PrimeTower/PrimeTowerNormal.h | 8 ++++++-- src/PrimeTower/PrimeTower.cpp | 8 ++------ src/PrimeTower/PrimeTowerInterleaved.cpp | 7 ++++++- src/PrimeTower/PrimeTowerNormal.cpp | 7 ++++++- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index c10fb3d236..808004c145 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -151,8 +151,12 @@ class PrimeTower * sparse infill * \return The indexes of extruders to be used for sparse infill */ - virtual std::vector - findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const = 0; + virtual std::vector findExtrudersSparseInfill( + const LayerPlan& gcode_layer, + const std::vector& required_extruder_prime, + const std::vector& initial_list_idx = {}) const = 0; + + virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const = 0; static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index f31da8b140..f805cc1094 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -36,8 +36,12 @@ class PrimeTowerInterleaved : public PrimeTower virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; - virtual std::vector - findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const override; + virtual std::vector findExtrudersSparseInfill( + const LayerPlan& gcode_layer, + const std::vector& required_extruder_prime, + const std::vector& initial_list_idx = {}) const override; + + virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 49322ee67d..86d16724f2 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -34,8 +34,12 @@ class PrimeTowerNormal : public PrimeTower virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; - virtual std::vector - findExtrudersSparseInfill(LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx = {}) const override; + virtual std::vector findExtrudersSparseInfill( + const LayerPlan& gcode_layer, + const std::vector& required_extruder_prime, + const std::vector& initial_list_idx = {}) const override; + + virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const override; }; } // namespace cura diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 23b49ab8da..23d30ce4f8 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -34,7 +34,7 @@ PrimeTower::PrimeTower(SliceDataStorage& storage, size_t extruder_count) { const Scene& scene = Application::getInstance().current_slice_->scene; - // First by a basic list of used extruders numbers + // First make a basic list of used extruders numbers for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { extruder_order_[extruder_nr] = extruder_nr; @@ -276,16 +276,12 @@ void PrimeTower::addToGcode( return; } - PrimeTowerMode prime_tower_mode = Application::getInstance().current_slice_->scene.current_mesh_group->settings.get("prime_tower_mode"); std::vector extra_primed_extruders_idx; switch (extruder_iterator->prime) { case ExtruderPrime::None: - if (prime_tower_mode != PrimeTowerMode::INTERLEAVED) - { - gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); - } + processExtruderNoPrime(new_extruder_nr, gcode_layer); break; case ExtruderPrime::Sparse: diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index c45a8f0e76..49b981233b 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -106,7 +106,7 @@ std::map> PrimeTowerInterleaved::generateSparseI } std::vector PrimeTowerInterleaved::findExtrudersSparseInfill( - LayerPlan& gcode_layer, + const LayerPlan& gcode_layer, const std::vector& required_extruder_prime, const std::vector& initial_list_idx) const { @@ -148,4 +148,9 @@ std::vector PrimeTowerInterleaved::findExtrudersSparseInfill( return extruders_to_prime_idx; } +void PrimeTowerInterleaved::processExtruderNoPrime(const size_t /*extruder_nr*/, LayerPlan& /*gcode_layer*/) const +{ + // Do nothing because we want to know which extruder has been additionally processed +} + } // namespace cura diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 036fbf2aca..536167b89e 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -72,7 +72,7 @@ std::map> PrimeTowerNormal::generateSparseInfill } std::vector PrimeTowerNormal::findExtrudersSparseInfill( - LayerPlan& /*gcode_layer*/, + const LayerPlan& /*gcode_layer*/, const std::vector& /*required_extruder_prime*/, const std::vector& initial_list_idx) const { @@ -80,4 +80,9 @@ std::vector PrimeTowerNormal::findExtrudersSparseInfill( return initial_list_idx; } +void PrimeTowerNormal::processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const +{ + gcode_layer.setPrimeTowerIsPlanned(extruder_nr); +} + } // namespace cura From 4a753f2df6790d6a6d621cc204a05c8ecbd3b730 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 29 May 2024 18:19:30 +0200 Subject: [PATCH 19/49] Remove some now useless variables --- include/PrimeTower/PrimeTower.h | 2 +- include/PrimeTower/PrimeTowerInterleaved.h | 8 +------- include/PrimeTower/PrimeTowerNormal.h | 8 +------- src/FffGcodeWriter.cpp | 2 -- src/PrimeTower/PrimeTower.cpp | 6 +++--- src/PrimeTower/PrimeTowerInterleaved.cpp | 4 ++-- src/PrimeTower/PrimeTowerNormal.cpp | 5 +++-- 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 808004c145..78f1cd340f 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -72,7 +72,7 @@ class PrimeTower * * \param storage A storage where it retrieves the prime tower settings. */ - PrimeTower(SliceDataStorage& storage, size_t extruder_count); + PrimeTower(size_t extruder_count); virtual ~PrimeTower() = default; diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index f805cc1094..442b82a512 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -12,13 +12,7 @@ namespace cura class PrimeTowerInterleaved : public PrimeTower { public: - /*! - * \brief Creates a prime tower instance that will determine where and how - * the prime tower gets printed. - * - * \param storage A storage where it retrieves the prime tower settings. - */ - PrimeTowerInterleaved(SliceDataStorage& storage, size_t extruder_count); + PrimeTowerInterleaved(size_t extruder_count); virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 86d16724f2..6dec7b51bc 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -12,13 +12,7 @@ namespace cura class PrimeTowerNormal : public PrimeTower { public: - /*! - * \brief Creates a prime tower instance that will determine where and how - * the prime tower gets printed. - * - * \param storage A storage where it retrieves the prime tower settings. - */ - PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_count); + PrimeTowerNormal(size_t extruder_count); virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 8eeafe2aad..75ca167644 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1520,9 +1520,7 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor extruder_order_per_layer.init(true, storage.print_layer_count); - 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; for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr < static_cast(storage.print_layer_count); layer_nr++) { std::vector extruder_order = getUsedExtrudersOnLayer(storage, last_extruder, layer_nr, extruders_used); diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 23d30ce4f8..42f75a87b6 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -28,7 +28,7 @@ namespace cura { -PrimeTower::PrimeTower(SliceDataStorage& storage, size_t extruder_count) +PrimeTower::PrimeTower(size_t extruder_count) : wipe_from_middle_(false) , extruder_order_(extruder_count) { @@ -495,10 +495,10 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) switch (method) { case PrimeTowerMode::NORMAL: - prime_tower = new PrimeTowerNormal(storage, scene.extruders.size()); + prime_tower = new PrimeTowerNormal(scene.extruders.size()); break; case PrimeTowerMode::INTERLEAVED: - prime_tower = new PrimeTowerInterleaved(storage, scene.extruders.size()); + prime_tower = new PrimeTowerInterleaved(scene.extruders.size()); break; } } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 49b981233b..29a8f55eaa 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -12,8 +12,8 @@ namespace cura { -PrimeTowerInterleaved::PrimeTowerInterleaved(SliceDataStorage& storage, size_t extruder_count) - : PrimeTower(storage, extruder_count) +PrimeTowerInterleaved::PrimeTowerInterleaved(size_t extruder_count) + : PrimeTower(extruder_count) { } diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 536167b89e..9bd0ed33ef 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -4,6 +4,7 @@ #include "PrimeTower/PrimeTowerNormal.h" #include "Application.h" +#include "LayerPlan.h" #include "Scene.h" #include "Slice.h" #include "sliceDataStorage.h" @@ -11,8 +12,8 @@ namespace cura { -PrimeTowerNormal::PrimeTowerNormal(SliceDataStorage& storage, size_t extruder_count) - : PrimeTower(storage, extruder_count) +PrimeTowerNormal::PrimeTowerNormal(size_t extruder_count) + : PrimeTower(extruder_count) { } From 7b8105995cf6ccd83d806b3d236b96acb004253b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 29 May 2024 22:25:29 +0200 Subject: [PATCH 20/49] Implement basically working version of smaller PT --- include/PrimeTower/PrimeTower.h | 21 ++++- include/PrimeTower/PrimeTowerInterleaved.h | 7 +- include/PrimeTower/PrimeTowerNormal.h | 3 + src/FffGcodeWriter.cpp | 8 +- src/PrimeTower/PrimeTower.cpp | 96 ++++++++++++++++++++-- src/PrimeTower/PrimeTowerInterleaved.cpp | 70 ++++++++++++++-- src/PrimeTower/PrimeTowerNormal.cpp | 5 ++ src/sliceDataStorage.cpp | 1 - 8 files changed, 192 insertions(+), 19 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 78f1cd340f..c804961bd5 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -50,6 +50,13 @@ class PrimeTower */ std::map> sparse_pattern_per_extruders_; + /* + * The first index is the layer number + * The second index is the extruder number + * The shape represents what should be printed for the given extruder at the given layer + */ + std::map> moves_; + MovesByLayer base_extra_moves_; //!< For each layer and each extruder, the extra moves to be processed for better adhesion/strength MovesByExtruder inset_extra_moves_; //!< For each extruder, the extra inset moves to be processed for better adhesion on initial layer @@ -112,9 +119,7 @@ class PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const = 0; - virtual void polishExtrudersUse(LayerVector>& /*extruders_use*/, const SliceDataStorage& /*storage*/) const - { - } + void processExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder); static PrimeTower* createPrimeTower(SliceDataStorage& storage); @@ -158,6 +163,16 @@ class PrimeTower virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const = 0; + virtual void polishExtrudersUses(LayerVector>& /*extruders_use*/, const SliceDataStorage& /*storage*/, const size_t /*start_extruder*/) + { + } + + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) = 0; + + std::tuple generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius); + + Shape generateSupportMoves(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius); + static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); private: diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 442b82a512..454cf51f41 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -21,8 +21,6 @@ class PrimeTowerInterleaved : public PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; - virtual void polishExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage) const override; - protected: virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; @@ -36,6 +34,11 @@ class PrimeTowerInterleaved : public PrimeTower const std::vector& initial_list_idx = {}) const override; virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const override; + + virtual void polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) override; + + virtual std::map> + generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 6dec7b51bc..9a986332ed 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -21,6 +21,9 @@ class PrimeTowerNormal : public PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; + virtual std::map> + generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; + protected: virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 75ca167644..1187db0f08 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1509,14 +1509,16 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor size_t last_extruder; // set the initial extruder of this meshgroup Scene& scene = Application::getInstance().current_slice_->scene; + size_t start_extruder; if (scene.current_mesh_group == scene.mesh_groups.begin()) { // first meshgroup - last_extruder = getStartExtruder(storage); + start_extruder = getStartExtruder(storage); } else { - last_extruder = gcode.getExtruderNr(); + start_extruder = gcode.getExtruderNr(); } + last_extruder = start_extruder; extruder_order_per_layer.init(true, storage.print_layer_count); @@ -1534,7 +1536,7 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor if (storage.prime_tower_) { - storage.prime_tower_->polishExtrudersUse(extruder_order_per_layer, storage); + storage.prime_tower_->processExtrudersUse(extruder_order_per_layer, storage, start_extruder); } } diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 42f75a87b6..20d40538f6 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -71,11 +71,11 @@ void PrimeTower::generatePaths() { generateGroundpoly(); + generateStartLocations(); + std::vector cumulative_insets; generateDenseInfill(cumulative_insets); - generateStartLocations(); - generateSparseInfill(cumulative_insets); } @@ -214,6 +214,69 @@ Shape PrimeTower::generatePath_sparseInfill( return pattern; } +std::tuple PrimeTower::generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius) +{ + const Scene& scene = Application::getInstance().current_slice_->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + const coord_t layer_height = mesh_group_settings.get("layer_height"); + const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + const double required_volume = scene.extruders[extruder_nr].settings_.get("prime_tower_min_volume") * 1000000000; + const Ratio flow = scene.extruders[extruder_nr].settings_.get("prime_tower_flow"); + + double current_volume = 0; + coord_t current_outer_diameter = outer_radius; + Shape moves; + do + { + Shape shape = outer_poly_.offset(-(tower_radius - current_outer_diameter + line_width / 2)); + + if (! shape.empty()) + { + moves.push_back(shape); + current_volume += static_cast(shape.length() * line_width * layer_height) * flow; + current_outer_diameter -= line_width; + } + else + { + // Don't continue. We won't ever reach the required volume because it doesn't fit. + break; + } + } while (current_volume < required_volume); + + return std::make_tuple(moves, current_outer_diameter); +} + +Shape PrimeTower::generateSupportMoves(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) +{ + const Scene& scene = Application::getInstance().current_slice_->scene; + const double max_bridging_distance = static_cast(scene.extruders[extruder_nr].settings_.get("prime_tower_max_bridging_distance")); + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + const coord_t radius_delta = outer_radius - inner_radius; + const coord_t semi_line_width = line_width / 2; + + Shape moves; + + // Split ring according to max bridging distance + const coord_t nb_rings = static_cast(std::ceil(static_cast(radius_delta) / max_bridging_distance)); + if (nb_rings > 0) + { + const coord_t actual_radius_step = radius_delta / nb_rings; + + for (coord_t i = 0; i < nb_rings; ++i) + { + const coord_t ring_inner_radius = (inner_radius + i * actual_radius_step) + semi_line_width; + const coord_t ring_outer_radius = (inner_radius + (i + 1) * actual_radius_step) - semi_line_width; + + const size_t semi_nb_spokes = static_cast(std::ceil((std::numbers::pi * static_cast(ring_outer_radius)) / max_bridging_distance)); + + moves.push_back(PolygonUtils::makeWheel(middle_, ring_inner_radius, ring_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); + } + } + + return moves; +} + void PrimeTower::generateStartLocations() { // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, @@ -276,7 +339,7 @@ void PrimeTower::addToGcode( return; } - std::vector extra_primed_extruders_idx; + /*std::vector extra_primed_extruders_idx; switch (extruder_iterator->prime) { @@ -306,6 +369,23 @@ void PrimeTower::addToGcode( } } break; + }*/ + + const Shape* moves = nullptr; + auto iterator_layer = moves_.find(layer_nr); + if (iterator_layer != moves_.end()) + { + auto iterator_extruder = iterator_layer->second.find(new_extruder_nr); + if (iterator_extruder != iterator_layer->second.end()) + { + moves = &iterator_extruder->second; + } + } + + if (moves && ! moves->empty()) + { + const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[new_extruder_nr]; + gcode_layer.addPolygonsByOptimizer(*moves, config); } if (! gcode_layer.getPrimeTowerBaseIsPlanned() && addToGcode_base(gcode_layer, new_extruder_nr)) @@ -318,10 +398,10 @@ void PrimeTower::addToGcode( gcode_layer.setPrimeTowerInsetIsPlanned(); } - for (const size_t& primed_extruder_idx : extra_primed_extruders_idx) + /*for (const size_t& primed_extruder_idx : extra_primed_extruders_idx) { gcode_layer.setPrimeTowerIsPlanned(extruder_order_.at(primed_extruder_idx)); - } + }*/ // post-wipe: if (post_wipe) @@ -479,6 +559,12 @@ const Shape& PrimeTower::getGroundPoly() const return getOuterPoly(-Raft::getTotalExtraLayers()); } +void PrimeTower::processExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) +{ + polishExtrudersUses(extruders_use, storage, start_extruder); + moves_ = generateExtrusionsMoves(extruders_use, storage); +} + PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) { PrimeTower* prime_tower = nullptr; diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 29a8f55eaa..2d730cac9b 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -34,16 +34,47 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( } } -void PrimeTowerInterleaved::polishExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage) const +std::map> + PrimeTowerInterleaved::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) { - for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr < storage.print_layer_count; ++layer_nr) + const Scene& scene = Application::getInstance().current_slice_->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; + coord_t support_radius = tower_radius; + std::map> moves; + + // Now loop again, but from top bo bottom, so that the required support increases with what is actually required + for (LayerIndex layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr >= -Raft::getTotalExtraLayers(); --layer_nr) { - std::vector& extruders_use_at_layer = extruders_use[layer_nr]; - if (extruders_use_at_layer.size() == 1 && extruders_use_at_layer.front().prime == ExtruderPrime::None && layer_nr <= storage.max_print_height_second_to_last_extruder) + const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; + std::map moves_at_layer; + + // Now generate actual priming patterns + coord_t outer_radius = tower_radius; + for (const ExtruderUse& extruder_use : extruders_use_at_layer) { - extruders_use_at_layer.front().prime = ExtruderPrime::Sparse; + if (extruder_use.prime == ExtruderPrime::Prime) + { + Shape extruder_moves; + std::tie(extruder_moves, outer_radius) = generatePrimeMoves(extruder_use.extruder_nr, outer_radius); + moves_at_layer[extruder_use.extruder_nr] = extruder_moves; + } } + + // Generate extra "support" sparse pattern if required + if (support_radius < outer_radius) + { + Shape support_moves = generateSupportMoves(extruders_use_at_layer.back().extruder_nr, outer_radius, support_radius); + moves_at_layer[extruders_use_at_layer.back().extruder_nr].push_back(support_moves); + } + + // Now decrease support radius if required + support_radius = std::min(support_radius, outer_radius); + + moves[layer_nr] = moves_at_layer; } + + return moves; } bool PrimeTowerInterleaved::requiresBaseExtraPrint(size_t /*extruder_nr*/) const @@ -153,4 +184,33 @@ void PrimeTowerInterleaved::processExtruderNoPrime(const size_t /*extruder_nr*/, // Do nothing because we want to know which extruder has been additionally processed } +void PrimeTowerInterleaved::polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) +{ + size_t last_used_extruder = start_extruder; + + // Loop through the extruders uses from bottom to top to find the last used extruder at each layer, and make sure we always have some support to print + for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr <= storage.max_print_height_second_to_last_extruder; ++layer_nr) + { + std::vector& extruders_use_at_layer = extruders_use[layer_nr]; + + // Make sure we always have something to print + if (extruders_use_at_layer.empty()) + { + extruders_use_at_layer.emplace_back(last_used_extruder, ExtruderPrime::Sparse); + } + else if (std::all_of( + extruders_use_at_layer.begin(), + extruders_use_at_layer.end(), + [](const ExtruderUse& extruder_use) + { + return extruder_use.prime == ExtruderPrime::None; + })) + { + extruders_use_at_layer.back().prime = ExtruderPrime::Sparse; + } + + last_used_extruder = extruders_use_at_layer.back().extruder_nr; + } +} + } // namespace cura diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 9bd0ed33ef..83ae82769f 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -38,6 +38,11 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( } } +std::map > PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) +{ + return {}; +} + bool PrimeTowerNormal::requiresBaseExtraPrint(size_t extruder_nr) const { // Generate base extra rings only for the outermost printed extruder diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index bfd63e388b..e1a6c1cc3a 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -698,7 +698,6 @@ void SliceDataStorage::initializePrimeTower() prime_tower_ = PrimeTower::createPrimeTower(*this); } - void SupportLayer::excludeAreasFromSupportInfillAreas(const Shape& exclude_polygons, const AABB& exclude_polygons_boundary_box) { // record the indexes that need to be removed and do that after From 4efe481e9a3b7724654d7af873af85d38b85af12 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Wed, 29 May 2024 20:25:59 +0000 Subject: [PATCH 21/49] Applied clang-format. --- src/PrimeTower/PrimeTowerNormal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 83ae82769f..0c3a3ca712 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -38,7 +38,7 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( } } -std::map > PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) +std::map> PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) { return {}; } From f5fa05d314cafc5ca396dd97ffc4557b69e8f624 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 30 May 2024 21:44:31 +0200 Subject: [PATCH 22/49] Fix inset and base printing --- include/LayerPlan.h | 2 - include/PrimeTower/PrimeTower.h | 111 +------- include/PrimeTower/PrimeTowerInterleaved.h | 17 +- include/PrimeTower/PrimeTowerNormal.h | 25 +- include/utils/LayerVector.h | 10 + include/utils/polygonUtils.h | 2 +- src/LayerPlan.cpp | 22 -- src/PrimeTower/PrimeTower.cpp | 314 ++++----------------- src/PrimeTower/PrimeTowerInterleaved.cpp | 134 ++------- src/PrimeTower/PrimeTowerNormal.cpp | 71 ++--- src/sliceDataStorage.cpp | 2 +- src/utils/polygonUtils.cpp | 2 +- 12 files changed, 127 insertions(+), 585 deletions(-) diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 31e164956d..5fab3d86ee 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -71,8 +71,6 @@ class LayerPlan : public NoCopy std::vector layer_start_pos_per_extruder_; //!< The starting position of a layer for each extruder std::vector has_prime_tower_planned_per_extruder_; //!< For each extruder, whether the prime tower is planned yet or not. - bool has_prime_tower_base_planned_; //!< Whether the prime tower base is planned yet or not. - bool has_prime_tower_inset_planned_; //!< Whether the prime tower inset is planned yet or not. std::optional last_planned_position_; //!< The last planned XY position of the print head (if known) std::shared_ptr current_mesh_; //!< The mesh of the last planned move. diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index c804961bd5..00656bf0e7 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -28,8 +28,14 @@ class LayerPlan; */ class PrimeTower { +protected: + struct ExtruderMoves + { + size_t extruder_nr; + Shape moves; + }; + private: - using MovesByExtruder = std::map; using MovesByLayer = std::map>; bool wipe_from_middle_; //!< Whether to wipe on the inside of the hollow prime tower @@ -40,46 +46,24 @@ class PrimeTower std::vector prime_tower_start_locations_; //!< The differernt locations where to pre-wipe the active nozzle const unsigned int number_of_prime_tower_start_locations_ = 21; //!< The required size of \ref PrimeTower::wipe_locations - MovesByExtruder prime_moves_; //!< For each extruder, the moves to be processed for actual priming. - - /* - * The first index is a bitmask representing an extruder combination, e.g. 0x05 for extruders 1+3. - * The second index is the used extruder index, e.g. 1 - * The polygons represent the sparse pattern to be printed when all the given extruders are unused for this layer - * and the given extruder is currently in use - */ - std::map> sparse_pattern_per_extruders_; - /* * The first index is the layer number * The second index is the extruder number * The shape represents what should be printed for the given extruder at the given layer */ - std::map> moves_; - - MovesByLayer base_extra_moves_; //!< For each layer and each extruder, the extra moves to be processed for better adhesion/strength - MovesByExtruder inset_extra_moves_; //!< For each extruder, the extra inset moves to be processed for better adhesion on initial layer + std::map> moves_; Shape outer_poly_; //!< The outline of the outermost prime tower. LayerVector outer_poly_base_; //!< The outline of the layers having extra width for the base public: - /* - * In which order, from outside to inside, will we be printing the prime - * towers for maximum strength? - * - * This is the spatial order from outside to inside. This is NOT the actual - * order in time in which they are printed. - */ - std::vector extruder_order_; - /*! * \brief Creates a prime tower instance that will determine where and how * the prime tower gets printed. * * \param storage A storage where it retrieves the prime tower settings. */ - PrimeTower(size_t extruder_count); + PrimeTower(); virtual ~PrimeTower() = default; @@ -140,34 +124,12 @@ class PrimeTower const coord_t line_width, const size_t actual_extruder_nr) const; - virtual bool requiresBaseExtraPrint(size_t extruder_nr) const = 0; - - virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const = 0; - - virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const = 0; - - /*! - * \brief Find the list of extruders that don't actually need to be primed during this layer, and for which - * we want to print only the sparse infill to keep the prime tower consistent. - * \param gcode_layer The current gcode export - * \param required_extruder_prime The pre-computed list of extruders uses during this layer - * \param method The current prime tower strategy - * \param initial_list_idx A list potentially containing extruders that we already know can be used for - * sparse infill - * \return The indexes of extruders to be used for sparse infill - */ - virtual std::vector findExtrudersSparseInfill( - const LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - const std::vector& initial_list_idx = {}) const = 0; - - virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const = 0; - virtual void polishExtrudersUses(LayerVector>& /*extruders_use*/, const SliceDataStorage& /*storage*/, const size_t /*start_extruder*/) { } - virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) = 0; + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) + = 0; std::tuple generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius); @@ -176,17 +138,9 @@ class PrimeTower static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); private: - /*! - * \brief Generate the extrude paths for each extruder on even and odd layers. Fill the ground poly with dense infill. - * \param cumulative_insets [in, out] The insets added to each extruder to compute the radius of its ring - */ - void generateDenseInfill(std::vector& cumulative_insets); + void generateBase(); - /*! - * \brief Generate the sparse extrude paths for each extruders combination - * \param cumulative_insets The insets added to each extruder to compute the radius of its ring - */ - void generateSparseInfill(const std::vector& cumulative_insets); + void generateFirtLayerInset(); /*! * Generate start locations on the prime tower. The locations are evenly spread around the prime tower's perimeter. @@ -195,45 +149,6 @@ class PrimeTower */ void generateStartLocations(); - /*! - * \see PrimeTower::addToGcode - * - * Add path plans for the prime tower to the \p gcode_layer - * - * \param[in,out] gcode_layer Where to get the current extruder from. Where - * to store the generated layer paths. - * \param extruder The extruder we just switched to, with which the prime - * tower paths should be drawn. - */ - void addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder) const; - - /*! - * \brief Add path plans for the prime tower extra outer rings to make the stronger base - * \param gcode_layer The gcode export to add the paths plans to - * \param extruder_nr The current extruder number - * \return True if something has actually been added, according to the extruder number - * and current layer. - */ - bool addToGcode_base(LayerPlan& gcode_layer, const size_t extruder_nr) const; - - /*! - * \brief Add path plans for the prime tower extra inner rings to increase bed adhesion - * \param gcode_layer The gcode export to add the paths plans to - * \param extruder_nr The current extruder number - * \return True if something has actually been added, according to the extruder number - * and current layer. - */ - bool addToGcode_inset(LayerPlan& gcode_layer, const size_t extruder_nr) const; - - /*! - * \brief Add path plans in the case an extruder is not to be actually primed, but we still - * want to print something to make the prime tower consistent. - * \param gcode_layer The gcode export to add the paths plans to - * \param extruders_to_prime_idx The indexes of the extra extruders which also don't require being primed on this layer - * \param current_extruder_nr The extruder currently being used - */ - void addToGcode_sparseInfill(LayerPlan& gcode_layer, const std::vector& extruders_to_prime_idx, const size_t current_extruder_nr) const; - /*! * For an extruder switch that happens not on the first layer, the extruder needs to be primed on the prime tower. * This function picks a start location for this extruder on the prime tower's perimeter and travels there to avoid diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 454cf51f41..4267bb9006 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -12,7 +12,7 @@ namespace cura class PrimeTowerInterleaved : public PrimeTower { public: - PrimeTowerInterleaved(size_t extruder_count); + PrimeTowerInterleaved(); virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, @@ -22,22 +22,9 @@ class PrimeTowerInterleaved : public PrimeTower const LayerIndex& layer_nr) const override; protected: - virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; - - virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const override; - - virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; - - virtual std::vector findExtrudersSparseInfill( - const LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - const std::vector& initial_list_idx = {}) const override; - - virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const override; - virtual void polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) override; - virtual std::map> + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; }; diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 9a986332ed..085e6b3667 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -21,22 +21,19 @@ class PrimeTowerNormal : public PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const override; - virtual std::map> - generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; - protected: - virtual bool requiresBaseExtraPrint(size_t extruder_nr) const override; - - virtual bool requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const override; - - virtual std::map> generateSparseInfillImpl(const std::vector& rings_radii) const override; - - virtual std::vector findExtrudersSparseInfill( - const LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - const std::vector& initial_list_idx = {}) const override; + virtual std::map> + generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; - virtual void processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const override; +private: + /* + * In which order, from outside to inside, will we be printing the prime + * towers for maximum strength? + * + * This is the spatial order from outside to inside. This is NOT the actual + * order in time in which they are printed. + */ + std::vector extruder_order_; }; } // namespace cura diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index 031fd387c9..4d81759963 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -93,6 +93,16 @@ class LayerVector return vector_.at(static_cast(pos + delta_)); } + const_reference front() const + { + return vector_.front(); + } + + reference front() + { + return vector_.front(); + } + [[nodiscard]] const_iterator iterator_at(const LayerIndex& pos) const { LayerIndex::value_type index = pos + delta_; diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 4a83c8ec97..f4b3e7e5fc 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -694,7 +694,7 @@ class PolygonUtils * \param initial_inset The inset distance to be added to the first generated polygon * \return The generated inset polygons */ - static Shape generateInset(const Shape& outer_poly, coord_t line_width, coord_t initial_inset = 0); + static Shape generateInset(const Polygon &outer_poly, coord_t line_width, coord_t initial_inset = 0); private: /*! diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index a28434fddc..05d336da9c 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -100,8 +100,6 @@ LayerPlan::LayerPlan( , layer_type_(Raft::getLayerType(layer_nr)) , layer_thickness_(layer_thickness) , has_prime_tower_planned_per_extruder_(Application::getInstance().current_slice_->scene.extruders.size(), false) - , has_prime_tower_base_planned_(false) - , has_prime_tower_inset_planned_(false) , current_mesh_(nullptr) , last_extruder_previous_layer_(start_extruder) , last_planned_extruder_(&Application::getInstance().current_slice_->scene.extruders[start_extruder]) @@ -340,26 +338,6 @@ void LayerPlan::setPrimeTowerIsPlanned(size_t extruder_nr) has_prime_tower_planned_per_extruder_[extruder_nr] = true; } -bool LayerPlan::getPrimeTowerBaseIsPlanned() const -{ - return has_prime_tower_base_planned_; -} - -void LayerPlan::setPrimeTowerBaseIsPlanned() -{ - has_prime_tower_base_planned_ = true; -} - -bool LayerPlan::getPrimeTowerInsetIsPlanned() const -{ - return has_prime_tower_inset_planned_; -} - -void LayerPlan::setPrimeTowerInsetIsPlanned() -{ - has_prime_tower_inset_planned_ = true; -} - std::optional> LayerPlan::getFirstTravelDestinationState() const { std::optional> ret; diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 20d40538f6..36124b37b4 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -28,28 +28,9 @@ namespace cura { -PrimeTower::PrimeTower(size_t extruder_count) +PrimeTower::PrimeTower() : wipe_from_middle_(false) - , extruder_order_(extruder_count) { - const Scene& scene = Application::getInstance().current_slice_->scene; - - // First make a basic list of used extruders numbers - for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) - { - extruder_order_[extruder_nr] = extruder_nr; - } - - // Then sort from high adhesion to low adhesion. - std::stable_sort( - extruder_order_.begin(), - extruder_order_.end(), - [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool - { - 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::generateGroundpoly() @@ -70,63 +51,35 @@ void PrimeTower::generateGroundpoly() void PrimeTower::generatePaths() { generateGroundpoly(); - generateStartLocations(); - - std::vector cumulative_insets; - generateDenseInfill(cumulative_insets); - - generateSparseInfill(cumulative_insets); } -void PrimeTower::generateDenseInfill(std::vector& cumulative_insets) +void PrimeTower::generateBase() { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); - const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; - const coord_t base_height = std::max(scene.settings.get("prime_tower_base_height"), has_raft ? layer_height : 0); + // const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; + const coord_t base_height = scene.settings.get("prime_tower_base_height"); const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); - for (size_t extruder_nr : extruder_order_) + if (base_enabled && base_extra_radius > 0 && base_height > 0) { - // By default, add empty moves for every extruder - prime_moves_[extruder_nr]; - base_extra_moves_[extruder_nr].init(true); - inset_extra_moves_[extruder_nr]; - } - outer_poly_base_.init(true); + outer_poly_base_.init(true); - 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_) - { - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - const coord_t required_volume = MM3_2INT(scene.extruders[extruder_nr].settings_.get("prime_tower_min_volume")); - const Ratio flow = scene.extruders[extruder_nr].settings_.get("prime_tower_flow"); - coord_t current_volume = 0; - Shape& prime_moves = prime_moves_[extruder_nr]; - - // Create the walls of the prime tower. - unsigned int wall_nr = 0; - for (; current_volume < required_volume; wall_nr++) + // Generate the base outside extra rings for the first extruder of each layer + auto iterator = moves_.begin(); + for (coord_t z = 0; z < base_height && iterator != moves_.end(); z += layer_height, ++iterator) { - // Create a new polygon with an offset from the outer polygon. - Shape polygons = outer_poly_.offset(-cumulative_inset - wall_nr * line_width - line_width / 2); - prime_moves.push_back(polygons); - current_volume += polygons.length() * line_width * layer_height * flow; - if (polygons.empty()) // Don't continue. We won't ever reach the required volume because it doesn't fit. + std::vector& moves_at_this_layer = iterator->second; + if (! moves_at_this_layer.empty()) { - break; - } - } + ExtruderMoves& extruder_moves = moves_at_this_layer.front(); + const size_t extruder_nr = extruder_moves.extruder_nr; + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - // Generate the base outside extra rings - if (requiresBaseExtraPrint(extruder_nr) && (base_enabled || has_raft) && base_extra_radius > 0 && base_height > 0) - { - for (coord_t z = 0; z < base_height; z += layer_height) - { double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), base_curve_magnitude); coord_t extra_radius = base_extra_radius * brim_radius_factor; size_t extra_rings = extra_radius / line_width; @@ -136,48 +89,37 @@ void PrimeTower::generateDenseInfill(std::vector& cumulative_insets) } extra_radius = line_width * extra_rings; outer_poly_base_.push_back(outer_poly_.offset(extra_radius)); - base_extra_moves_[extruder_nr].push_back(PolygonUtils::generateOutset(outer_poly_, extra_rings, line_width)); + extruder_moves.moves.push_back(PolygonUtils::generateOutset(outer_poly_, extra_rings, line_width)); } } - - cumulative_inset += wall_nr * line_width; - cumulative_insets.push_back(cumulative_inset); } +} - // Now we have the total cumulative inset, generate the base inside extra rings - for (size_t extruder_nr : extruder_order_) +void PrimeTower::generateFirtLayerInset() +{ + // Generate the base inside extra rings for the last extruder of the first layer + if (! moves_.empty()) { - if (requiresFirstLayerExtraInnerPrint(extruder_nr)) + const std::vector& moves_first_layer = moves_.begin()->second; + if (! moves_first_layer.empty()) { - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape pattern = PolygonUtils::generateInset(outer_poly_, line_width, cumulative_inset); - if (! pattern.empty()) + ExtruderMoves extruder_moves = moves_first_layer.back(); + const Scene& scene = Application::getInstance().current_slice_->scene; + const size_t extruder_nr = extruder_moves.extruder_nr; + Shape& moves_last_extruder = extruder_moves.moves; + if (! moves_last_extruder.empty()) { - inset_extra_moves_[extruder_nr].push_back(pattern); + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + Shape pattern = PolygonUtils::generateInset(moves_last_extruder.back(), line_width, line_width / 2); + if (! pattern.empty()) + { + moves_last_extruder.push_back(pattern); + } } } } } -void PrimeTower::generateSparseInfill(const std::vector& cumulative_insets) -{ - const Scene& scene = Application::getInstance().current_slice_->scene; - const Settings& mesh_group_settings = scene.current_mesh_group->settings; - - // Pre-compute radiuses of each extruder ring - std::vector rings_radii; - const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); - const coord_t tower_radius = tower_size / 2; - - rings_radii.push_back(tower_radius); - for (const coord_t& cumulative_inset : cumulative_insets) - { - rings_radii.push_back(tower_radius - cumulative_inset); - } - - sparse_pattern_per_extruders_ = generateSparseInfillImpl(rings_radii); -} - Shape PrimeTower::generatePath_sparseInfill( const size_t first_extruder_idx, const size_t last_extruder_idx, @@ -327,81 +269,33 @@ void PrimeTower::addToGcode( return; } - size_t new_extruder_idx; - auto iterator = std::find(extruder_order_.begin(), extruder_order_.end(), new_extruder_nr); - if (iterator != extruder_order_.end()) - { - new_extruder_idx = iterator - extruder_order_.begin(); - } - else - { - // Given extruder nr is not registered ?! - return; - } - - /*std::vector extra_primed_extruders_idx; - - switch (extruder_iterator->prime) - { - case ExtruderPrime::None: - processExtruderNoPrime(new_extruder_nr, gcode_layer); - break; - - case ExtruderPrime::Sparse: - gotoStartLocation(gcode_layer, new_extruder_nr); - extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime, { new_extruder_idx }); - addToGcode_sparseInfill(gcode_layer, extra_primed_extruders_idx, new_extruder_nr); - break; - - case ExtruderPrime::Prime: - gotoStartLocation(gcode_layer, new_extruder_nr); - - addToGcode_denseInfill(gcode_layer, new_extruder_nr); - gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); - - if (gcode_layer.getLayerNr() <= storage.max_print_height_second_to_last_extruder) - { - // Whatever happens before and after, use the current extruder to prime all the non-required extruders now - extra_primed_extruders_idx = findExtrudersSparseInfill(gcode_layer, required_extruder_prime); - if (! extra_primed_extruders_idx.empty()) - { - addToGcode_sparseInfill(gcode_layer, extra_primed_extruders_idx, new_extruder_nr); - } - } - break; - }*/ - const Shape* moves = nullptr; auto iterator_layer = moves_.find(layer_nr); if (iterator_layer != moves_.end()) { - auto iterator_extruder = iterator_layer->second.find(new_extruder_nr); + const std::vector& moves_at_this_layer = iterator_layer->second; + auto iterator_extruder = std::find_if( + moves_at_this_layer.begin(), + moves_at_this_layer.end(), + [new_extruder_nr](const ExtruderMoves& extruder_moves) + { + return extruder_moves.extruder_nr == new_extruder_nr; + }); if (iterator_extruder != iterator_layer->second.end()) { - moves = &iterator_extruder->second; + moves = &(iterator_extruder->moves); } } if (moves && ! moves->empty()) { + gotoStartLocation(gcode_layer, new_extruder_nr); + const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[new_extruder_nr]; gcode_layer.addPolygonsByOptimizer(*moves, config); } - if (! gcode_layer.getPrimeTowerBaseIsPlanned() && addToGcode_base(gcode_layer, new_extruder_nr)) - { - gcode_layer.setPrimeTowerBaseIsPlanned(); - } - - if (! gcode_layer.getPrimeTowerInsetIsPlanned() && addToGcode_inset(gcode_layer, new_extruder_nr)) - { - gcode_layer.setPrimeTowerInsetIsPlanned(); - } - - /*for (const size_t& primed_extruder_idx : extra_primed_extruders_idx) - { - gcode_layer.setPrimeTowerIsPlanned(extruder_order_.at(primed_extruder_idx)); - }*/ + gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); // post-wipe: if (post_wipe) @@ -415,120 +309,6 @@ void PrimeTower::addToGcode( } } -void PrimeTower::addToGcode_denseInfill(LayerPlan& gcode_layer, const size_t extruder_nr) const -{ - const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); - const bool adhesion_raft = raft_total_extra_layers > 0; - LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + raft_total_extra_layers; - - if (! adhesion_raft || absolute_layer_number > 0) - { - // Actual prime pattern - const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[extruder_nr]; - const Shape& pattern = prime_moves_.at(extruder_nr); - gcode_layer.addPolygonsByOptimizer(pattern, config); - } -} - -bool PrimeTower::addToGcode_base(LayerPlan& gcode_layer, const size_t extruder_nr) const -{ - const LayerVector& pattern_extra_brim = base_extra_moves_.at(extruder_nr); - auto iterator = pattern_extra_brim.iterator_at(gcode_layer.getLayerNr()); - if (iterator != pattern_extra_brim.end()) - { - // Extra rings for stronger base - const Shape& pattern = *iterator; - if (! pattern.empty()) - { - const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[extruder_nr]; - gcode_layer.addPolygonsByOptimizer(pattern, config); - return true; - } - } - - return false; -} - -bool PrimeTower::addToGcode_inset(LayerPlan& gcode_layer, const size_t extruder_nr) const -{ - const size_t raft_total_extra_layers = Raft::getTotalExtraLayers(); - LayerIndex absolute_layer_number = gcode_layer.getLayerNr() + raft_total_extra_layers; - - if (absolute_layer_number == 0) // Extra-adhesion on very first layer only - { - const Shape& 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]; - gcode_layer.addPolygonsByOptimizer(pattern_extra_inset, config); - return true; - } - } - - return false; -} - -void PrimeTower::addToGcode_sparseInfill(LayerPlan& gcode_layer, const std::vector& extruders_to_prime_idx, const size_t current_extruder_nr) const -{ - std::vector> extruders_to_prime_idx_grouped; - - // Group extruders which are besides each other - for (size_t extruder_to_prime_idx : extruders_to_prime_idx) - { - if (extruders_to_prime_idx_grouped.empty()) - { - // First extruder : create new group - extruders_to_prime_idx_grouped.push_back({ extruder_to_prime_idx }); - } - else - { - std::vector& last_group = extruders_to_prime_idx_grouped.back(); - if (last_group.back() == extruder_to_prime_idx - 1) - { - // New extruders which belongs to same group - last_group.push_back(extruder_to_prime_idx); - } - else - { - // New extruders which belongs to new group - extruders_to_prime_idx_grouped.push_back({ extruder_to_prime_idx }); - } - } - } - - // And finally, append patterns for each group - const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[current_extruder_nr]; - - for (const std::vector& group_idx : extruders_to_prime_idx_grouped) - { - size_t mask = 0; - for (const size_t& extruder_idx : group_idx) - { - mask |= (1 << extruder_order_.at(extruder_idx)); - } - - auto iterator_combination = sparse_pattern_per_extruders_.find(mask); - if (iterator_combination != sparse_pattern_per_extruders_.end()) - { - const std::map& infill_for_combination = iterator_combination->second; - - auto iterator_extruder_nr = infill_for_combination.find(current_extruder_nr); - if (iterator_extruder_nr != infill_for_combination.end()) - { - gcode_layer.addPolygonsByOptimizer(iterator_extruder_nr->second, config); - } - else - { - spdlog::warn("Sparse pattern not found for extruder {}, skipping\n", current_extruder_nr); - } - } - else - { - spdlog::warn("Sparse pattern not found for group {}, skipping\n", mask); - } - } -} - void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) @@ -563,6 +343,8 @@ void PrimeTower::processExtrudersUse(LayerVector>& extr { polishExtrudersUses(extruders_use, storage, start_extruder); moves_ = generateExtrusionsMoves(extruders_use, storage); + generateBase(); + generateFirtLayerInset(); } PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) @@ -584,7 +366,7 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) prime_tower = new PrimeTowerNormal(scene.extruders.size()); break; case PrimeTowerMode::INTERLEAVED: - prime_tower = new PrimeTowerInterleaved(scene.extruders.size()); + prime_tower = new PrimeTowerInterleaved(); break; } } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 2d730cac9b..cbdde17d6b 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -12,8 +12,8 @@ namespace cura { -PrimeTowerInterleaved::PrimeTowerInterleaved(size_t extruder_count) - : PrimeTower(extruder_count) +PrimeTowerInterleaved::PrimeTowerInterleaved() + : PrimeTower() { } @@ -34,20 +34,21 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( } } -std::map> +std::map> PrimeTowerInterleaved::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; coord_t support_radius = tower_radius; - std::map> moves; + std::map> moves; // Now loop again, but from top bo bottom, so that the required support increases with what is actually required for (LayerIndex layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr >= -Raft::getTotalExtraLayers(); --layer_nr) { const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; - std::map moves_at_layer; + std::vector moves_at_layer; + size_t last_extruder_sparse = 0; // Now generate actual priming patterns coord_t outer_radius = tower_radius; @@ -57,15 +58,25 @@ std::map> { Shape extruder_moves; std::tie(extruder_moves, outer_radius) = generatePrimeMoves(extruder_use.extruder_nr, outer_radius); - moves_at_layer[extruder_use.extruder_nr] = extruder_moves; + moves_at_layer.emplace_back(extruder_use.extruder_nr, extruder_moves); + } + else if (extruder_use.prime == ExtruderPrime::Sparse) + { + last_extruder_sparse = extruder_use.extruder_nr; } } // Generate extra "support" sparse pattern if required if (support_radius < outer_radius) { - Shape support_moves = generateSupportMoves(extruders_use_at_layer.back().extruder_nr, outer_radius, support_radius); - moves_at_layer[extruders_use_at_layer.back().extruder_nr].push_back(support_moves); + if (moves_at_layer.empty()) + { + moves_at_layer.emplace_back(last_extruder_sparse, Shape()); + } + + ExtruderMoves& last_extruder_moves = moves_at_layer.back(); + Shape support_moves = generateSupportMoves(last_extruder_moves.extruder_nr, outer_radius, support_radius); + last_extruder_moves.moves.push_back(support_moves); } // Now decrease support radius if required @@ -77,113 +88,6 @@ std::map> return moves; } -bool PrimeTowerInterleaved::requiresBaseExtraPrint(size_t /*extruder_nr*/) const -{ - // We don't know yet which extruder is going to be used to print the base outer rings, so generate them for all extruders - return true; -} - -bool PrimeTowerInterleaved::requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const -{ - // We don't know yet which extruder is going to be used to print the extra inner rings, so generate them for all extruders - return true; -} - -std::map> PrimeTowerInterleaved::generateSparseInfillImpl(const std::vector& rings_radii) const -{ - const Scene& scene = Application::getInstance().current_slice_->scene; - - struct ActualExtruder - { - size_t number; - coord_t line_width; - }; - - std::vector actual_extruders; - actual_extruders.reserve(extruder_order_.size()); - for (size_t extruder_nr : extruder_order_) - { - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - actual_extruders.push_back({ extruder_nr, line_width }); - } - - // 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 - std::map> sparse_pattern_per_extruders; - for (size_t first_extruder_idx = 0; first_extruder_idx < extruder_order_.size(); ++first_extruder_idx) - { - for (size_t last_extruder_idx = first_extruder_idx; last_extruder_idx < extruder_order_.size(); ++last_extruder_idx) - { - size_t extruders_combination = 0; - for (size_t extruder_idx = first_extruder_idx; extruder_idx <= last_extruder_idx; ++extruder_idx) - { - size_t extruder_nr = extruder_order_.at(extruder_idx); - extruders_combination |= (1 << extruder_nr); - } - - std::map infills_for_combination; - for (const ActualExtruder& actual_extruder : actual_extruders) - { - Shape infill = generatePath_sparseInfill(first_extruder_idx, last_extruder_idx, rings_radii, actual_extruder.line_width, actual_extruder.number); - infills_for_combination[actual_extruder.number] = infill; - } - - sparse_pattern_per_extruders[extruders_combination] = infills_for_combination; - } - } - - return sparse_pattern_per_extruders; -} - -std::vector PrimeTowerInterleaved::findExtrudersSparseInfill( - const LayerPlan& gcode_layer, - const std::vector& required_extruder_prime, - const std::vector& initial_list_idx) const -{ - std::vector extruders_to_prime_idx; - - for (size_t extruder_idx = 0; extruder_idx < extruder_order_.size(); extruder_idx++) - { - auto iterator_initial_list = std::find(initial_list_idx.begin(), initial_list_idx.end(), extruder_idx); - bool is_in_initial_list = iterator_initial_list != initial_list_idx.end(); - - if (is_in_initial_list) - { - extruders_to_prime_idx.push_back(extruder_idx); - } - else - { - // If extruder is not the current used one, try to see if it requires a sparse prime that we could do - // at the same time - size_t extruder_nr = extruder_order_.at(extruder_idx); - if (! gcode_layer.getPrimeTowerIsPlanned(extruder_nr)) - { - auto iterator_required_list = std::find_if( - required_extruder_prime.begin(), - required_extruder_prime.end(), - [extruder_nr](const ExtruderUse& extruder_use) - { - return extruder_use.extruder_nr == extruder_nr && extruder_use.prime == ExtruderPrime::Prime; - }); - bool is_in_required_list = iterator_required_list != required_extruder_prime.end(); - - if (! is_in_required_list) - { - extruders_to_prime_idx.push_back(extruder_idx); - } - } - } - } - - return extruders_to_prime_idx; -} - -void PrimeTowerInterleaved::processExtruderNoPrime(const size_t /*extruder_nr*/, LayerPlan& /*gcode_layer*/) const -{ - // Do nothing because we want to know which extruder has been additionally processed -} - void PrimeTowerInterleaved::polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) { size_t last_used_extruder = start_extruder; diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 0c3a3ca712..e1af95c8d2 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -13,8 +13,26 @@ namespace cura { PrimeTowerNormal::PrimeTowerNormal(size_t extruder_count) - : PrimeTower(extruder_count) + : PrimeTower() { + const Scene& scene = Application::getInstance().current_slice_->scene; + + // First make a basic list of used extruders numbers + for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) + { + extruder_order_[extruder_nr] = extruder_nr; + } + + // Then sort from high adhesion to low adhesion. + std::stable_sort( + extruder_order_.begin(), + extruder_order_.end(), + [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool + { + 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; + }); } ExtruderPrime PrimeTowerNormal::getExtruderPrime( @@ -38,57 +56,10 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( } } -std::map> PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) +std::map> + PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) { return {}; } -bool PrimeTowerNormal::requiresBaseExtraPrint(size_t extruder_nr) const -{ - // Generate base extra rings only for the outermost printed extruder - return extruder_nr == extruder_order_.front(); -} - -bool PrimeTowerNormal::requiresFirstLayerExtraInnerPrint(size_t extruder_nr) const -{ - // Generate extra inner rings only for the innermost printed extruder - return extruder_nr == extruder_order_.back(); -} - -std::map> PrimeTowerNormal::generateSparseInfillImpl(const std::vector& rings_radii) const -{ - const Scene& scene = Application::getInstance().current_slice_->scene; - - // Generate a sparse infill for each extruder - std::map> sparse_pattern_per_extruders; - for (size_t extruder_idx = 0; extruder_idx < extruder_order_.size(); ++extruder_idx) - { - const size_t extruder_nr = extruder_order_[extruder_idx]; - const size_t extruders_combination = (1 << extruder_nr); - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - - std::map infills_for_combination; - Shape infill = generatePath_sparseInfill(extruder_idx, extruder_idx, rings_radii, line_width, extruder_nr); - infills_for_combination[extruder_nr] = infill; - - sparse_pattern_per_extruders[extruders_combination] = infills_for_combination; - } - - return sparse_pattern_per_extruders; -} - -std::vector PrimeTowerNormal::findExtrudersSparseInfill( - const LayerPlan& /*gcode_layer*/, - const std::vector& /*required_extruder_prime*/, - const std::vector& initial_list_idx) const -{ - // In normal mode we only print what is required - return initial_list_idx; -} - -void PrimeTowerNormal::processExtruderNoPrime(const size_t extruder_nr, LayerPlan& gcode_layer) const -{ - gcode_layer.setPrimeTowerIsPlanned(extruder_nr); -} - } // namespace cura diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index e1a6c1cc3a..cce418cd66 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -366,7 +366,7 @@ Shape SliceDataStorage::getLayerOutlines( total.push_back(support_layer.support_roof); } } - if (include_prime_tower && prime_tower_ && (extruder_nr == -1 || (! prime_tower_->extruder_order_.empty() && extruder_nr == prime_tower_->extruder_order_[0]))) + if (include_prime_tower && prime_tower_) { total.push_back(prime_tower_->getOuterPoly(layer_nr)); } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index c0a1e2fc4b..2e5fe9b5c4 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1631,7 +1631,7 @@ Shape PolygonUtils::generateOutset(const Shape& inner_poly, size_t count, coord_ return outset; } -Shape PolygonUtils::generateInset(const Shape& outer_poly, coord_t line_width, coord_t initial_inset) +Shape PolygonUtils::generateInset(const Polygon& outer_poly, coord_t line_width, coord_t initial_inset) { Shape inset; From fdc169b2e42080e53b3e719d540f288f4cc76729 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 30 May 2024 19:45:02 +0000 Subject: [PATCH 23/49] Applied clang-format. --- include/utils/polygonUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index f4b3e7e5fc..3b06428167 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -694,7 +694,7 @@ class PolygonUtils * \param initial_inset The inset distance to be added to the first generated polygon * \return The generated inset polygons */ - static Shape generateInset(const Polygon &outer_poly, coord_t line_width, coord_t initial_inset = 0); + static Shape generateInset(const Polygon& outer_poly, coord_t line_width, coord_t initial_inset = 0); private: /*! From 1f72f19ce32a18af0ab4d15a77bcac2c81acd4d1 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 30 May 2024 21:51:46 +0200 Subject: [PATCH 24/49] Simplify generateGroundpoly code --- src/PrimeTower/PrimeTower.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 36124b37b4..8779d00ea8 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -37,15 +37,13 @@ void PrimeTower::generateGroundpoly() { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const coord_t tower_size = mesh_group_settings.get("prime_tower_size"); - + const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; const coord_t x = mesh_group_settings.get("prime_tower_position_x"); const coord_t y = mesh_group_settings.get("prime_tower_position_y"); - const coord_t tower_radius = tower_size / 2; - outer_poly_.push_back(PolygonUtils::makeCircle(Point2LL(x - tower_radius, y + tower_radius), tower_radius, TAU / CIRCLE_RESOLUTION)); - middle_ = Point2LL(x - tower_size / 2, y + tower_size / 2); - post_wipe_point_ = Point2LL(x - tower_size / 2, y + tower_size / 2); + middle_ = Point2LL(x - tower_radius, y + tower_radius); + outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, TAU / CIRCLE_RESOLUTION)); + post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); } void PrimeTower::generatePaths() From 893a7d9aed492e67014f154841264ba7d438f7b5 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 31 May 2024 22:02:52 +0200 Subject: [PATCH 25/49] Fix interaction between prime tower base and support/adhesion With the new prime tower, the base is now calculated much later on the process, because it requires to know the extruders uses orders. However, the interation with support and bed adhesion features is calculated before that. So we can calculate an approximate outline as soon as the tower is initialized, and later we calculate the exact toolpaths. --- include/PrimeTower/PrimeTower.h | 55 +++++++-------- include/utils/polygonUtils.h | 4 +- src/FffGcodeWriter.cpp | 7 +- src/FffPolygonGenerator.cpp | 4 +- src/PrimeTower/PrimeTower.cpp | 115 ++++++++++++++++++-------------- src/TreeModelVolumes.cpp | 2 +- src/sliceDataStorage.cpp | 2 +- src/utils/polygonUtils.cpp | 11 +-- 8 files changed, 110 insertions(+), 90 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 00656bf0e7..e371267db7 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -53,8 +53,12 @@ class PrimeTower */ std::map> moves_; - Shape outer_poly_; //!< The outline of the outermost prime tower. - LayerVector outer_poly_base_; //!< The outline of the layers having extra width for the base + Shape outer_poly_; //!< The outline of the prime tower, not including the base + + //!< This is the exact outline of the extrusions lines of each layer, for layers having extra width for the base + LayerVector base_extrusion_outline_; + //!< This is the approximate outline of the area filled at each layer, for layers having extra width for the base + LayerVector base_occupied_outline_; public: /*! @@ -84,17 +88,33 @@ class PrimeTower const size_t new_extruder_nr) const; /*! - * Get the outer polygon for the given layer, which may be the priming polygon only, or a larger polygon for layers with a base + * Get the occupied outline of the prime tower at the given layer * * \param[in] layer_nr The index of the layer * \return The outer polygon for the prime tower at the given layer + * \note The returned outline is a close approximation of the actual toolpaths. The actual extrusion area may be slightly smaller. + * Use this method only if you need to get the exclusion area of the prime tower. Otherwise use getExtrusionOutline(). + * This method exists because this approximate area can be calculated as soon as the prime tower is initialized. */ - const Shape& getOuterPoly(const LayerIndex& layer_nr) const; + const Shape& getOccupiedOutline(const LayerIndex& layer_nr) const; /*! - * Get the outer polygon for the very first layer, which may be the priming polygon only, or a larger polygon if there is a base + * Get the occupied outline of the prime tower at the first layer + * + * \note @sa getOccupiedOutline() */ - const Shape& getGroundPoly() const; + const Shape& getOccupiedGroundOutline() const; + + /*! + * Get the extrusion outline of the prime tower at the given layer + * + * \param[in] layer_nr The index of the layer + * \return The extrusion outline for the prime tower at the given layer + * \note The returned outline is the exact outline of the extrusion path, which is useful if you need to generate a toolpath + * touching the prime tower. Otherwise use getExtrusionOutline(). This method will return the valid result only after + * processExtrudersUse() has been called, which is "late" is the global slicing operation. + */ + const Shape& getExtrusionOutline(const LayerIndex& layer_nr) const; virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, @@ -138,17 +158,8 @@ class PrimeTower static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); private: - void generateBase(); - void generateFirtLayerInset(); - /*! - * Generate start locations on the prime tower. The locations are evenly spread around the prime tower's perimeter. - * The number of starting points is defined by "number_of_prime_tower_start_locations". The generated points will - * be stored in "prime_tower_start_locations". - */ - void generateStartLocations(); - /*! * For an extruder switch that happens not on the first layer, the extruder needs to be primed on the prime tower. * This function picks a start location for this extruder on the prime tower's perimeter and travels there to avoid @@ -156,18 +167,6 @@ class PrimeTower */ void gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder) const; - /*! - * Generate the prime tower area to be used on each layer - * - * Fills \ref PrimeTower::inner_poly and sets \ref PrimeTower::middle - */ - void generateGroundpoly(); - - /*! - * Generate the area where the prime tower should be. - */ - void generatePaths(); - /*! * \brief Subtract the prime tower from the support areas in storage. * @@ -175,6 +174,8 @@ class PrimeTower * subtract a prime tower. */ void subtractFromSupport(SliceDataStorage& storage); + + void generateBase(); }; } // namespace cura diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 3b06428167..80bb0109f2 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -680,11 +680,11 @@ class PolygonUtils * Generate a few outset polygons around the given base, according to the given line width * * \param inner_poly The inner polygon to start generating the outset from - * \param count The number of outer polygons to add + * \param outer_poly The outer polygon to fit the outset into * \param line_width The actual line width to distance the polygons from each other (and from the base) * \return The generated outset polygons */ - static Shape generateOutset(const Shape& inner_poly, size_t count, coord_t line_width); + static Shape generateOutset(const Shape& inner_poly, const Shape& outer_poly, coord_t line_width); /*! * Generate inset polygons inside the given base, until there is no space left, according to the given line width diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1187db0f08..3cff97b93e 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -153,7 +153,6 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep gcode.writeTravel(p, extruder_settings.get("speed_travel")); } - calculateExtruderOrderPerLayer(storage); calculatePrimeLayerPerExtruder(storage); @@ -647,7 +646,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_outline_paths.emplace_back(ParameterizedRaftPath{ line_spacing, storage.raft_base_outline }); if (storage.prime_tower_) { - const Shape& raft_outline_prime_tower = storage.prime_tower_->getOuterPoly(layer_nr); + const Shape& raft_outline_prime_tower = storage.prime_tower_->getExtrusionOutline(layer_nr); if (line_spacing_prime_tower == line_spacing) { // Base layer is shared with prime tower base @@ -813,7 +812,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (storage.prime_tower_) { // Interface layer excludes prime tower base - raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getOuterPoly(layer_nr)); + raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getExtrusionOutline(layer_nr)); } Infill infill_comp( @@ -973,7 +972,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) if (storage.prime_tower_) { // Surface layers exclude prime tower base - raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getOuterPoly(layer_nr)); + raft_outline_path = raft_outline_path.difference(storage.prime_tower_->getExtrusionOutline(layer_nr)); } for (const Shape& raft_island : raft_outline_path.splitIntoParts()) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index a7336b3aff..10a1336995 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -996,7 +996,7 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage) } for (LayerIndex layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++) { - storage.ooze_shield[layer_nr] = storage.ooze_shield[layer_nr].difference(storage.prime_tower_->getOuterPoly(layer_nr).offset(max_line_width / 2)); + storage.ooze_shield[layer_nr] = storage.ooze_shield[layer_nr].difference(storage.prime_tower_->getOccupiedOutline(layer_nr).offset(max_line_width / 2)); } } } @@ -1046,7 +1046,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage) max_line_width = std::max(max_line_width, extruders[extruder_nr].settings_.get("skirt_brim_line_width")); } } - storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.prime_tower_->getGroundPoly().offset(max_line_width / 2)); + storage.draft_protection_shield = storage.draft_protection_shield.difference(storage.prime_tower_->getOccupiedGroundOutline().offset(max_line_width / 2)); } } diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 8779d00ea8..2f3e11a455 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -30,64 +30,72 @@ namespace cura PrimeTower::PrimeTower() : wipe_from_middle_(false) -{ -} - -void PrimeTower::generateGroundpoly() { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; const coord_t x = mesh_group_settings.get("prime_tower_position_x"); const coord_t y = mesh_group_settings.get("prime_tower_position_y"); + const coord_t layer_height = mesh_group_settings.get("layer_height"); + const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); + const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); + const coord_t base_height = scene.settings.get("prime_tower_base_height"); + const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); middle_ = Point2LL(x - tower_radius, y + tower_radius); outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, TAU / CIRCLE_RESOLUTION)); post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); -} -void PrimeTower::generatePaths() -{ - generateGroundpoly(); - generateStartLocations(); + // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, + // so use the same start and end segments for this. + PolygonsPointIndex segment_start = PolygonsPointIndex(&outer_poly_, 0, 0); + PolygonsPointIndex segment_end = segment_start; + + PolygonUtils::spreadDots(segment_start, segment_end, number_of_prime_tower_start_locations_, prime_tower_start_locations_); + + // Generate the base outline + if (base_enabled && base_extra_radius > 0 && base_height > 0) + { + base_occupied_outline_.init(true); + + for (coord_t z = 0; z < base_height; z += layer_height) + { + double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), base_curve_magnitude); + coord_t extra_radius = base_extra_radius * brim_radius_factor; + base_occupied_outline_.push_back(outer_poly_.offset(extra_radius)); + } + } } void PrimeTower::generateBase() { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; - const coord_t layer_height = mesh_group_settings.get("layer_height"); const bool base_enabled = mesh_group_settings.get("prime_tower_brim_enable"); const coord_t base_extra_radius = scene.settings.get("prime_tower_base_size"); - // const bool has_raft = mesh_group_settings.get("adhesion_type") == EPlatformAdhesion::RAFT; const coord_t base_height = scene.settings.get("prime_tower_base_height"); - const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); if (base_enabled && base_extra_radius > 0 && base_height > 0) { - outer_poly_base_.init(true); + base_extrusion_outline_.init(true); // Generate the base outside extra rings for the first extruder of each layer - auto iterator = moves_.begin(); - for (coord_t z = 0; z < base_height && iterator != moves_.end(); z += layer_height, ++iterator) + auto iterator_extrusion_paths = moves_.begin(); + auto iterator_base_outline = base_occupied_outline_.begin(); + for (; iterator_extrusion_paths != moves_.end() && iterator_base_outline != base_occupied_outline_.end(); ++iterator_extrusion_paths, ++iterator_base_outline) { - std::vector& moves_at_this_layer = iterator->second; + std::vector& moves_at_this_layer = iterator_extrusion_paths->second; if (! moves_at_this_layer.empty()) { + const Shape& base_ouline_at_this_layer = *iterator_base_outline; ExtruderMoves& extruder_moves = moves_at_this_layer.front(); const size_t extruder_nr = extruder_moves.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), base_curve_magnitude); - coord_t extra_radius = base_extra_radius * brim_radius_factor; - size_t extra_rings = extra_radius / line_width; - if (extra_rings == 0) - { - break; - } - extra_radius = line_width * extra_rings; - outer_poly_base_.push_back(outer_poly_.offset(extra_radius)); - extruder_moves.moves.push_back(PolygonUtils::generateOutset(outer_poly_, extra_rings, line_width)); + Shape outset = PolygonUtils::generateOutset(outer_poly_, base_ouline_at_this_layer, line_width); + extruder_moves.moves.push_back(outset); + + base_extrusion_outline_.push_back(outset.offset(line_width / 2)); } } } @@ -217,16 +225,6 @@ Shape PrimeTower::generateSupportMoves(const size_t extruder_nr, const coord_t o return moves; } -void PrimeTower::generateStartLocations() -{ - // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, - // so use the same start and end segments for this. - PolygonsPointIndex segment_start = PolygonsPointIndex(&outer_poly_, 0, 0); - PolygonsPointIndex segment_end = segment_start; - - PolygonUtils::spreadDots(segment_start, segment_end, number_of_prime_tower_start_locations_, prime_tower_start_locations_); -} - void PrimeTower::addToGcode( const SliceDataStorage& storage, LayerPlan& gcode_layer, @@ -307,22 +305,35 @@ void PrimeTower::addToGcode( } } -void PrimeTower::subtractFromSupport(SliceDataStorage& storage) +const Shape& PrimeTower::getOccupiedOutline(const LayerIndex& layer_nr) const { - for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) + auto iterator = base_occupied_outline_.iterator_at(layer_nr); + if (iterator != base_occupied_outline_.end()) { - const Shape outside_polygon = getOuterPoly(layer).getOutsidePolygons(); - AABB outside_polygon_boundary_box(outside_polygon); - SupportLayer& support_layer = storage.support.supportLayers[layer]; - // take the differences of the support infill parts and the prime tower area - support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box); + return *iterator; + } + else + { + return outer_poly_; + } +} + +const Shape& PrimeTower::getOccupiedGroundOutline() const +{ + if (! base_extrusion_outline_.empty()) + { + return base_extrusion_outline_.front(); + } + else + { + return outer_poly_; } } -const Shape& PrimeTower::getOuterPoly(const LayerIndex& layer_nr) const +const Shape& PrimeTower::getExtrusionOutline(const LayerIndex& layer_nr) const { - auto iterator = outer_poly_base_.iterator_at(layer_nr); - if (iterator != outer_poly_base_.end()) + auto iterator = base_extrusion_outline_.iterator_at(layer_nr); + if (iterator != base_extrusion_outline_.end()) { return *iterator; } @@ -332,9 +343,16 @@ const Shape& PrimeTower::getOuterPoly(const LayerIndex& layer_nr) const } } -const Shape& PrimeTower::getGroundPoly() const +void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { - return getOuterPoly(-Raft::getTotalExtraLayers()); + for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) + { + const Shape& outside_polygon = getOccupiedOutline(layer).getOutsidePolygons(); + AABB outside_polygon_boundary_box(outside_polygon); + SupportLayer& support_layer = storage.support.supportLayers[layer]; + // take the differences of the support infill parts and the prime tower area + support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box); + } } void PrimeTower::processExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) @@ -371,7 +389,6 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) if (prime_tower) { - prime_tower->generatePaths(); prime_tower->subtractFromSupport(storage); } diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index 67e37d3aa3..3dade07b66 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -151,7 +151,7 @@ TreeModelVolumes::TreeModelVolumes( if (storage.prime_tower_) { - anti_overhang_[layer_idx].push_back(storage.prime_tower_->getGroundPoly()); + anti_overhang_[layer_idx].push_back(storage.prime_tower_->getOccupiedOutline(layer_idx)); } anti_overhang_[layer_idx] = anti_overhang_[layer_idx].unionPolygons(); }); diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index cce418cd66..0270a3ffb7 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -368,7 +368,7 @@ Shape SliceDataStorage::getLayerOutlines( } if (include_prime_tower && prime_tower_) { - total.push_back(prime_tower_->getOuterPoly(layer_nr)); + total.push_back(prime_tower_->getOccupiedOutline(layer_nr)); } return total; } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 2e5fe9b5c4..5217f9bbe0 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1617,15 +1617,18 @@ Shape PolygonUtils::clipPolygonWithAABB(const Shape& src, const AABB& aabb) return out; } -Shape PolygonUtils::generateOutset(const Shape& inner_poly, size_t count, coord_t line_width) +Shape PolygonUtils::generateOutset(const Shape& inner_poly, const Shape& outer_poly, coord_t line_width) { Shape outset; - Shape current_outset; - for (size_t index = 0; index < count; ++index) + coord_t offset = line_width / 2; + Shape current_outset = inner_poly.offset(offset); + + while (! current_outset.empty() && current_outset.front().isValid() && outer_poly.inside(current_outset.front().front())) { - current_outset = index == 0 ? inner_poly.offset(line_width / 2) : current_outset.offset(line_width); outset.push_back(current_outset); + offset += line_width; + current_outset = inner_poly.offset(offset); } return outset; From dab68dfd5730603de484ccae62d55790258f32d3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 31 May 2024 22:38:32 +0200 Subject: [PATCH 26/49] Restored normal prime tower --- include/PrimeTower/PrimeTowerNormal.h | 12 +--- include/utils/LayerVector.h | 10 +++ src/PrimeTower/PrimeTower.cpp | 2 +- src/PrimeTower/PrimeTowerInterleaved.cpp | 2 +- src/PrimeTower/PrimeTowerNormal.cpp | 88 ++++++++++++++++++------ 5 files changed, 81 insertions(+), 33 deletions(-) diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 085e6b3667..67bfe34cae 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -12,7 +12,7 @@ namespace cura class PrimeTowerNormal : public PrimeTower { public: - PrimeTowerNormal(size_t extruder_count); + PrimeTowerNormal(); virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, @@ -24,16 +24,6 @@ class PrimeTowerNormal : public PrimeTower protected: virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; - -private: - /* - * In which order, from outside to inside, will we be printing the prime - * towers for maximum strength? - * - * This is the spatial order from outside to inside. This is NOT the actual - * order in time in which they are printed. - */ - std::vector extruder_order_; }; } // namespace cura diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index 4d81759963..7090024df9 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -133,6 +133,16 @@ class LayerVector return value_type{}; } + [[nodiscard]] LayerIndex getLayer(const const_iterator& iterator) const + { + return std::distance(begin(), iterator) - delta_; + } + + [[nodiscard]] LayerIndex getLayer(const iterator& iterator) const + { + return std::distance(begin(), iterator) - delta_; + } + void pop_back() { vector_.pop_back(); diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 2f3e11a455..4990727ca0 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -379,7 +379,7 @@ PrimeTower* PrimeTower::createPrimeTower(SliceDataStorage& storage) switch (method) { case PrimeTowerMode::NORMAL: - prime_tower = new PrimeTowerNormal(scene.extruders.size()); + prime_tower = new PrimeTowerNormal(); break; case PrimeTowerMode::INTERLEAVED: prime_tower = new PrimeTowerInterleaved(); diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index cbdde17d6b..260150ebcc 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -43,7 +43,7 @@ std::map> coord_t support_radius = tower_radius; std::map> moves; - // Now loop again, but from top bo bottom, so that the required support increases with what is actually required + // Loop from top bo bottom, so that the required support increases with what is actually required for (LayerIndex layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr >= -Raft::getTotalExtraLayers(); --layer_nr) { const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index e1af95c8d2..c103d8f00a 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -12,27 +12,9 @@ namespace cura { -PrimeTowerNormal::PrimeTowerNormal(size_t extruder_count) +PrimeTowerNormal::PrimeTowerNormal() : PrimeTower() { - const Scene& scene = Application::getInstance().current_slice_->scene; - - // First make a basic list of used extruders numbers - for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) - { - extruder_order_[extruder_nr] = extruder_nr; - } - - // Then sort from high adhesion to low adhesion. - std::stable_sort( - extruder_order_.begin(), - extruder_order_.end(), - [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool - { - 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; - }); } ExtruderPrime PrimeTowerNormal::getExtruderPrime( @@ -59,7 +41,73 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( std::map> PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) { - return {}; + const Scene& scene = Application::getInstance().current_slice_->scene; + const Settings& mesh_group_settings = scene.current_mesh_group->settings; + const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; + std::map> moves; + + // First make a basic list of used extruders numbers + std::vector extruder_order; + extruder_order.reserve(scene.extruders.size()); + for (const ExtruderTrain& extruder : scene.extruders) + { + extruder_order.push_back(extruder.extruder_nr_); + } + + // Then sort from high adhesion to low adhesion. This will give us the outside to inside extruder processing order. + std::stable_sort( + extruder_order.begin(), + extruder_order.end(), + [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool + { + 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; + }); + + // For each extruder, generate the prime and support patterns, which will always be the same across layers + coord_t current_radius = tower_radius; + std::map extruders_prime_moves; + std::map extruders_support_moves; + for (size_t extruder_nr : extruder_order) + { + std::tuple prime_moves = generatePrimeMoves(extruder_nr, current_radius); + extruders_prime_moves[extruder_nr] = std::get<0>(prime_moves); + const coord_t inner_radius = std::get<1>(prime_moves); + + extruders_support_moves[extruder_nr] = generateSupportMoves(extruder_nr, current_radius, inner_radius); + + current_radius = inner_radius; + } + + // Now fill the extruders moves according to their use + for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) + { + const std::vector& extruders_use_at_layer = *iterator; + const LayerIndex layer_nr = extruders_use.getLayer(iterator); + std::vector moves_at_layer; + + for (const ExtruderUse& extruder_use : extruders_use_at_layer) + { + switch (extruder_use.prime) + { + case ExtruderPrime::None: + break; + + case ExtruderPrime::Prime: + moves_at_layer.emplace_back(extruder_use.extruder_nr, extruders_prime_moves[extruder_use.extruder_nr]); + break; + + case ExtruderPrime::Sparse: + moves_at_layer.emplace_back(extruder_use.extruder_nr, extruders_support_moves[extruder_use.extruder_nr]); + break; + } + } + + moves[layer_nr] = moves_at_layer; + } + + return moves; } } // namespace cura From 3dfa6d03f248dd163cf72eabe406b7158b81dc3e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 31 May 2024 22:39:47 +0200 Subject: [PATCH 27/49] Polish iteration loops --- src/PrimeTower/PrimeTowerInterleaved.cpp | 3 ++- src/PrimeTower/PrimeTowerNormal.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 260150ebcc..21711490fc 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -44,8 +44,9 @@ std::map> std::map> moves; // Loop from top bo bottom, so that the required support increases with what is actually required - for (LayerIndex layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr >= -Raft::getTotalExtraLayers(); --layer_nr) + for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) { + const LayerIndex layer_nr = extruders_use.getLayer(iterator); const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; std::vector moves_at_layer; size_t last_extruder_sparse = 0; diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index c103d8f00a..102954c665 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -83,8 +83,8 @@ std::map> // Now fill the extruders moves according to their use for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) { - const std::vector& extruders_use_at_layer = *iterator; const LayerIndex layer_nr = extruders_use.getLayer(iterator); + const std::vector& extruders_use_at_layer = *iterator; std::vector moves_at_layer; for (const ExtruderUse& extruder_use : extruders_use_at_layer) From 8819cc713509b6c84f49ce9415e67d0cde0d1840 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 31 May 2024 22:56:39 +0200 Subject: [PATCH 28/49] Fix the normal prime tower base --- include/PrimeTower/PrimeTower.h | 6 +++--- src/PrimeTower/PrimeTowerNormal.cpp | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index e371267db7..5a9a294c14 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -47,9 +47,9 @@ class PrimeTower const unsigned int number_of_prime_tower_start_locations_ = 21; //!< The required size of \ref PrimeTower::wipe_locations /* - * The first index is the layer number - * The second index is the extruder number - * The shape represents what should be printed for the given extruder at the given layer + * The map index is the layer number + * For each layer, the list contains the extruders moves to be processed. This list is sorted from outer rings to inner + * rings, which is not the printing chronological order, but the physical arrangement. */ std::map> moves_; diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 102954c665..5545e5b542 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -55,10 +55,10 @@ std::map> } // Then sort from high adhesion to low adhesion. This will give us the outside to inside extruder processing order. - std::stable_sort( + std::sort( extruder_order.begin(), extruder_order.end(), - [&scene](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool + [&scene](const size_t extruder_nr_a, const size_t extruder_nr_b) { 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"); @@ -84,9 +84,20 @@ std::map> for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) { const LayerIndex layer_nr = extruders_use.getLayer(iterator); - const std::vector& extruders_use_at_layer = *iterator; - std::vector moves_at_layer; + std::vector extruders_use_at_layer = *iterator; + + // Sort to fit the global order, in order to insert the moves in outside to inside order + std::sort( + extruders_use_at_layer.begin(), + extruders_use_at_layer.end(), + [extruder_order](const ExtruderUse& extruder_use1, const ExtruderUse& extruder_use2) + { + return std::find(extruder_order.begin(), extruder_order.end(), extruder_use1.extruder_nr) + < std::find(extruder_order.begin(), extruder_order.end(), extruder_use2.extruder_nr); + }); + // Now put the proper moves for each extruder use + std::vector moves_at_layer; for (const ExtruderUse& extruder_use : extruders_use_at_layer) { switch (extruder_use.prime) From 358d12eec253d8a8a4217f9e1e33ad0fd7cd0e46 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 31 May 2024 23:07:37 +0200 Subject: [PATCH 29/49] Remove useless code/variables --- include/PrimeTower/PrimeTower.h | 30 ++++++---------- include/PrimeTower/PrimeTowerInterleaved.h | 5 ++- include/PrimeTower/PrimeTowerNormal.h | 3 +- src/FffGcodeWriter.cpp | 2 +- src/PrimeTower/PrimeTower.cpp | 42 ++-------------------- src/PrimeTower/PrimeTowerInterleaved.cpp | 9 +++-- src/PrimeTower/PrimeTowerNormal.cpp | 3 +- 7 files changed, 22 insertions(+), 72 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 5a9a294c14..e561426cf8 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -123,33 +123,23 @@ class PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const = 0; - void processExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder); + void processExtrudersUse(LayerVector>& extruders_use, const size_t start_extruder); static PrimeTower* createPrimeTower(SliceDataStorage& storage); protected: - /*! - * \brief Generate the sparse extrude paths for an extruders combination - * - * \param first_extruder_nr The index of the first extruder to be pseudo-primed - * \param last_extruder_nr The index of the last extruder to be pseudo-primed - * \param rings_radii The external radii of each extruder ring, plus the internal radius of the internal ring - * \param line_width The actual line width of the extruder - * \param actual_extruder_nr The number of the actual extruder to be used - */ - Shape generatePath_sparseInfill( - const size_t first_extruder_idx, - const size_t last_extruder_idx, - const std::vector& rings_radii, - const coord_t line_width, - const size_t actual_extruder_nr) const; - - virtual void polishExtrudersUses(LayerVector>& /*extruders_use*/, const SliceDataStorage& /*storage*/, const size_t /*start_extruder*/) + virtual void polishExtrudersUses(LayerVector>& /*extruders_use*/, const size_t /*start_extruder*/) { } - virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) - = 0; + /*! + * \brief generateExtrusionsMoves + * \param extruders_use + * \param storage + * \return A map of extruders moves per layer. The inner list is sorted from outer rings to inner + * rings, which is not the printing chronological order, but the physical arrangement. @sa moves_ + */ + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) = 0; std::tuple generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius); diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 4267bb9006..a1a915439c 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -22,10 +22,9 @@ class PrimeTowerInterleaved : public PrimeTower const LayerIndex& layer_nr) const override; protected: - virtual void polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) override; + virtual void polishExtrudersUses(LayerVector>& extruders_use, const size_t start_extruder) override; - virtual std::map> - generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 67bfe34cae..2280a33029 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -22,8 +22,7 @@ class PrimeTowerNormal : public PrimeTower const LayerIndex& layer_nr) const override; protected: - virtual std::map> - generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) override; + virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) override; }; } // namespace cura diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 3cff97b93e..976b1d9c24 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1535,7 +1535,7 @@ void FffGcodeWriter::calculateExtruderOrderPerLayer(const SliceDataStorage& stor if (storage.prime_tower_) { - storage.prime_tower_->processExtrudersUse(extruder_order_per_layer, storage, start_extruder); + storage.prime_tower_->processExtrudersUse(extruder_order_per_layer, start_extruder); } } diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 4990727ca0..c0b7e49510 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -126,42 +126,6 @@ void PrimeTower::generateFirtLayerInset() } } -Shape PrimeTower::generatePath_sparseInfill( - const size_t first_extruder_idx, - const size_t last_extruder_idx, - const std::vector& rings_radii, - const coord_t line_width, - const size_t actual_extruder_nr) const -{ - const Scene& scene = Application::getInstance().current_slice_->scene; - const coord_t max_bridging_distance = scene.extruders[actual_extruder_nr].settings_.get("prime_tower_max_bridging_distance"); - const coord_t outer_radius = rings_radii[first_extruder_idx]; - const coord_t inner_radius = rings_radii[last_extruder_idx + 1]; - const coord_t radius_delta = outer_radius - inner_radius; - const coord_t semi_line_width = line_width / 2; - - Shape pattern; - - // Split ring according to max bridging distance - const size_t nb_rings = std::ceil(static_cast(radius_delta) / max_bridging_distance); - if (nb_rings) - { - const coord_t actual_radius_step = radius_delta / nb_rings; - - for (size_t i = 0; i < nb_rings; ++i) - { - const coord_t ring_inner_radius = (inner_radius + i * actual_radius_step) + semi_line_width; - const coord_t ring_outer_radius = (inner_radius + (i + 1) * actual_radius_step) - semi_line_width; - - const size_t semi_nb_spokes = std::ceil((std::numbers::pi * ring_outer_radius) / max_bridging_distance); - - pattern.push_back(PolygonUtils::makeWheel(middle_, ring_inner_radius, ring_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); - } - } - - return pattern; -} - std::tuple PrimeTower::generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius) { const Scene& scene = Application::getInstance().current_slice_->scene; @@ -355,10 +319,10 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) } } -void PrimeTower::processExtrudersUse(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) +void PrimeTower::processExtrudersUse(LayerVector>& extruders_use, const size_t start_extruder) { - polishExtrudersUses(extruders_use, storage, start_extruder); - moves_ = generateExtrusionsMoves(extruders_use, storage); + polishExtrudersUses(extruders_use, start_extruder); + moves_ = generateExtrusionsMoves(extruders_use); generateBase(); generateFirtLayerInset(); } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 21711490fc..14342daa75 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -34,8 +34,7 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( } } -std::map> - PrimeTowerInterleaved::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) +std::map> PrimeTowerInterleaved::generateExtrusionsMoves(const LayerVector>& extruders_use) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; @@ -89,14 +88,14 @@ std::map> return moves; } -void PrimeTowerInterleaved::polishExtrudersUses(LayerVector>& extruders_use, const SliceDataStorage& storage, const size_t start_extruder) +void PrimeTowerInterleaved::polishExtrudersUses(LayerVector>& extruders_use, const size_t start_extruder) { size_t last_used_extruder = start_extruder; // Loop through the extruders uses from bottom to top to find the last used extruder at each layer, and make sure we always have some support to print - for (LayerIndex layer_nr = -Raft::getTotalExtraLayers(); layer_nr <= storage.max_print_height_second_to_last_extruder; ++layer_nr) + for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) { - std::vector& extruders_use_at_layer = extruders_use[layer_nr]; + std::vector& extruders_use_at_layer = *iterator; // Make sure we always have something to print if (extruders_use_at_layer.empty()) diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 5545e5b542..b74d2cf3f1 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -38,8 +38,7 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( } } -std::map> - PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use, const SliceDataStorage& storage) +std::map> PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; From eb926d7fe48f27ca99e40b79bf5450ce52761408 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 1 Jun 2024 12:43:06 +0200 Subject: [PATCH 30/49] Fix missing segment on prime tower circles --- include/utils/polygonUtils.h | 4 ++-- src/sliceDataStorage.cpp | 2 +- src/utils/polygonUtils.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 80bb0109f2..cefc8e2fe6 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -640,10 +640,10 @@ class PolygonUtils * This creates a regular polygon that is supposed to approximate a circle. * \param mid The center of the circle. * \param radius The radius of the circle. - * \param step_angle The angle between segments of the circle. + * \param steps The numbers of segments (definition) of the generated circle. * \return A new Polygon containing the circle. */ - static Polygon makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians step_angle = std::numbers::pi / 8); + static Polygon makeCircle(const Point2LL mid, const coord_t radius, const size_t steps); /*! * Create a "wheel" shape. diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 0270a3ffb7..b16b130794 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -634,7 +634,7 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const Point2LL translation(extruder_settings.get("machine_nozzle_offset_x"), extruder_settings.get("machine_nozzle_offset_y")); prime_pos -= translation; Shape prime_polygons; - prime_polygons.emplace_back(PolygonUtils::makeCircle(prime_pos, prime_clearance, std::numbers::pi / 32)); + prime_polygons.emplace_back(PolygonUtils::makeCircle(prime_pos, prime_clearance, 32)); disallowed_areas = disallowed_areas.unionPolygons(prime_polygons); } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 5217f9bbe0..7695ca127d 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1386,14 +1386,14 @@ double PolygonUtils::relativeHammingDistance(const Shape& poly_a, const Shape& p return hamming_distance / total_area; } -Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians step_angle) +Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const size_t steps) { Polygon circle; - const size_t steps = (2 * std::numbers::pi) / step_angle; + const double step_angle = (std::numbers::pi * 2) / static_cast(steps); for (size_t step = 0; step < steps; ++step) { - const double angle = step * step_angle; - circle.emplace_back(mid + Point2LL(radius * cos(angle), radius * sin(angle))); + const double angle = static_cast(step) * step_angle; + circle.emplace_back(mid + Point2LL(std::llrint(static_cast(radius) * cos(angle)), std::llrint(static_cast(radius) * sin(angle)))); } return circle; } From 4776b31e11374104fc89818b31cece7ac6bbfafc Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 1 Jun 2024 12:43:17 +0200 Subject: [PATCH 31/49] Fix missing inset on first layer --- src/PrimeTower/PrimeTower.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index c0b7e49510..056cf2d9d2 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -43,7 +43,7 @@ PrimeTower::PrimeTower() const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); middle_ = Point2LL(x - tower_radius, y + tower_radius); - outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, TAU / CIRCLE_RESOLUTION)); + outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, CIRCLE_RESOLUTION)); post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, @@ -106,10 +106,10 @@ void PrimeTower::generateFirtLayerInset() // Generate the base inside extra rings for the last extruder of the first layer if (! moves_.empty()) { - const std::vector& moves_first_layer = moves_.begin()->second; + std::vector& moves_first_layer = moves_.begin()->second; if (! moves_first_layer.empty()) { - ExtruderMoves extruder_moves = moves_first_layer.back(); + ExtruderMoves& extruder_moves = moves_first_layer.back(); const Scene& scene = Application::getInstance().current_slice_->scene; const size_t extruder_nr = extruder_moves.extruder_nr; Shape& moves_last_extruder = extruder_moves.moves; From ab9147659158929ffce4b500ede5f65f17a7fe7c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 1 Jun 2024 17:09:47 +0200 Subject: [PATCH 32/49] Fix interleaved generation --- include/utils/LayerVector.h | 32 ++++++++++++++++++++++++ src/PrimeTower/PrimeTowerInterleaved.cpp | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index 7090024df9..ab00bbb363 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -26,6 +26,8 @@ class LayerVector using value_type = T; using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; + using reverse_iterator = typename std::vector::reverse_iterator; + using const_reverse_iterator = typename std::vector::const_reverse_iterator; using reference = value_type&; using const_reference = const value_type&; using difference_type = typename std::vector::difference_type; @@ -73,6 +75,26 @@ class LayerVector return vector_.end(); } + const_reverse_iterator rbegin() const + { + return vector_.rbegin(); + } + + reverse_iterator rbegin() + { + return vector_.rbegin(); + } + + const_reverse_iterator rend() const + { + return vector_.rend(); + } + + reverse_iterator rend() + { + return vector_.rend(); + } + const_reference operator[](const LayerIndex& pos) const { return vector_[static_cast(pos + delta_)]; @@ -143,6 +165,16 @@ class LayerVector return std::distance(begin(), iterator) - delta_; } + [[nodiscard]] LayerIndex getLayer(const const_reverse_iterator& iterator) const + { + return std::distance(iterator, rend()) - 1 - delta_; + } + + [[nodiscard]] LayerIndex getLayer(const reverse_iterator& iterator) const + { + return std::distance(iterator, rend()) - 1 - delta_; + } + void pop_back() { vector_.pop_back(); diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 14342daa75..8c4805e67b 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -43,7 +43,7 @@ std::map> PrimeTowerInterleav std::map> moves; // Loop from top bo bottom, so that the required support increases with what is actually required - for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) + for (auto iterator = extruders_use.rbegin(); iterator != extruders_use.rend(); ++iterator) { const LayerIndex layer_nr = extruders_use.getLayer(iterator); const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; From 80cffb6a9d8df779b3135cd8589756e367069952 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sat, 1 Jun 2024 17:10:08 +0200 Subject: [PATCH 33/49] Fix possibly missing support layer with normal prime tower --- src/PrimeTower/PrimeTowerNormal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index b74d2cf3f1..bdf4b7b4f0 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -28,7 +28,7 @@ ExtruderPrime PrimeTowerNormal::getExtruderPrime( { return ExtruderPrime::Prime; } - else if (layer_nr < storage.max_print_height_second_to_last_extruder) + else if (layer_nr <= storage.max_print_height_second_to_last_extruder) { return ExtruderPrime::Sparse; } From c9c45dc1584185284d976530b369fcff02145e1a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Sun, 2 Jun 2024 16:10:29 +0200 Subject: [PATCH 34/49] Fix remaining warning in prime tower classes --- include/sliceDataStorage.h | 5 +---- include/utils/LayerVector.h | 16 ++++++++-------- src/PrimeTower/PrimeTower.cpp | 33 ++++++++++++++++++--------------- src/sliceDataStorage.cpp | 5 +++++ 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 0c36cfbc54..749fb8bbbc 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -399,10 +399,7 @@ class SliceDataStorage : public NoCopy */ SliceDataStorage(); - ~SliceDataStorage() - { - delete prime_tower_; - } + ~SliceDataStorage(); /*! * Get all outlines within a given layer. diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index ab00bbb363..bdf99cd9ca 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -155,24 +155,24 @@ class LayerVector return value_type{}; } - [[nodiscard]] LayerIndex getLayer(const const_iterator& iterator) const + [[nodiscard]] LayerIndex getLayer(const const_iterator& it) const { - return std::distance(begin(), iterator) - delta_; + return std::distance(begin(), it) - static_cast(delta_); } - [[nodiscard]] LayerIndex getLayer(const iterator& iterator) const + [[nodiscard]] LayerIndex getLayer(const iterator& it) const { - return std::distance(begin(), iterator) - delta_; + return std::distance(begin(), it) - static_cast(delta_); } - [[nodiscard]] LayerIndex getLayer(const const_reverse_iterator& iterator) const + [[nodiscard]] LayerIndex getLayer(const const_reverse_iterator& it) const { - return std::distance(iterator, rend()) - 1 - delta_; + return std::distance(it, rend()) - 1 - static_cast(delta_); } - [[nodiscard]] LayerIndex getLayer(const reverse_iterator& iterator) const + [[nodiscard]] LayerIndex getLayer(const reverse_iterator& it) const { - return std::distance(iterator, rend()) - 1 - delta_; + return std::distance(it, rend()) - 1 - static_cast(delta_); } void pop_back() diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 056cf2d9d2..b58b9c7dd7 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -60,8 +60,8 @@ PrimeTower::PrimeTower() for (coord_t z = 0; z < base_height; z += layer_height) { - double brim_radius_factor = std::pow((1.0 - static_cast(z) / base_height), base_curve_magnitude); - coord_t extra_radius = base_extra_radius * brim_radius_factor; + double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); + coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); base_occupied_outline_.push_back(outer_poly_.offset(extra_radius)); } } @@ -366,21 +366,24 @@ bool PrimeTower::extruderRequiresPrime(const std::vector& extruder_is_used void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder_nr) const { - if (gcode_layer.getLayerNr() != 0) + // Layer number may be negative, make it positive (or null) to apply modulo operator + LayerIndex layer_nr = gcode_layer.getLayerNr(); + while (layer_nr < 0) { - size_t current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations_) + number_of_prime_tower_start_locations_) - % number_of_prime_tower_start_locations_; - - const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; - const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; - const coord_t inward_dist = train.settings_.get("machine_nozzle_size") * 3 / 2; - const coord_t start_dist = train.settings_.get("machine_nozzle_size") * 2; - const Point2LL prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); - const Point2LL outward_dir = wipe_location.location_ - prime_end; - const Point2LL prime_start = wipe_location.location_ + normal(outward_dir, start_dist); - - gcode_layer.addTravel(prime_start); + layer_nr += number_of_prime_tower_start_locations_; } + + size_t current_start_location_idx = ((extruder_nr + 1) * static_cast(layer_nr)) % number_of_prime_tower_start_locations_; + + const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; + const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; + const coord_t inward_dist = train.settings_.get("machine_nozzle_size") * 3 / 2; + const coord_t start_dist = train.settings_.get("machine_nozzle_size") * 2; + const Point2LL prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); + const Point2LL outward_dir = wipe_location.location_ - prime_end; + const Point2LL prime_start = wipe_location.location_ + normal(outward_dir, start_dist); + + gcode_layer.addTravel(prime_start); } } // namespace cura diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index b16b130794..c030fc403d 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -270,6 +270,11 @@ SliceDataStorage::SliceDataStorage() machine_size.include(machine_max); } +SliceDataStorage::~SliceDataStorage() +{ + delete prime_tower_; +} + Shape SliceDataStorage::getLayerOutlines( const LayerIndex layer_nr, const bool include_support, From a4b6c5a9e65c8b8833cbe799bf49675f3ad7855a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 12 Jun 2024 15:57:42 +0200 Subject: [PATCH 35/49] Remove unentionnally added empty lines --- include/TreeModelVolumes.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/TreeModelVolumes.h b/include/TreeModelVolumes.h index e95d754770..1774b24fdf 100644 --- a/include/TreeModelVolumes.h +++ b/include/TreeModelVolumes.h @@ -17,7 +17,6 @@ #include "utils/PairHash.h" #include "utils/Simplify.h" - namespace cura { constexpr coord_t EPSILON = 5; @@ -528,5 +527,4 @@ class TreeModelVolumes } // namespace cura - #endif // TREEMODELVOLUMES_H From ac2408af0fc9e21aef5812942d8a395462e63556 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 12 Jun 2024 15:58:09 +0200 Subject: [PATCH 36/49] Fix missing function call --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 1e422699f2..c2f48297ce 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1100,7 +1100,7 @@ void FffGcodeWriter::endRaftLayer(const SliceDataStorage& storage, LayerPlan& gc setExtruder_addPrime(storage, gcode_layer, current_extruder, append_to_prime_tower); // If required, fill prime tower for other extruders - for (const ExtruderUse& extruder_use : getExtruderUse(layer_nr)) + for (const ExtruderUse& extruder_use : extruder_order_per_layer.get(layer_nr)) { if (! append_to_prime_tower || (! gcode_layer.getPrimeTowerIsPlanned(extruder_use.extruder_nr) && extruder_use.prime != ExtruderPrime::None)) { From 9939da998e70221875e69ae913371e049b203c76 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 12 Jun 2024 16:27:36 +0200 Subject: [PATCH 37/49] Clean code and add documentation --- include/utils/LayerVector.h | 18 +++++++++++------- include/utils/polygonUtils.h | 9 --------- src/FffGcodeWriter.cpp | 2 +- src/sliceDataStorage.cpp | 2 +- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index bdf99cd9ca..40afd423b8 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -15,8 +15,9 @@ namespace cura /*! * \brief The LayerVector class mimics a std::vector but with the index being a LayerIndex, thus it can have negative * values (for raft layers). - * \note At instantiation, LayerVector will call Raft::getTotalExtraLayers() so it requires the settings to be setup. - * After that, it is assumed that this value will not change while the vector is used. + * \note When calling the init() method, LayerVector will call Raft::getTotalExtraLayers() so it requires the settings + * to be setup. This is the reason why this is not done in the constructor, and has to be called manually. + * After that, it is assumed that this value will not change as long as the vector is used. * \warning It is assumed that items are inserted in layer order, and without missing items, like a std::vector would do */ template @@ -33,15 +34,13 @@ class LayerVector using difference_type = typename std::vector::difference_type; public: - LayerVector() - { - } + LayerVector() = default; /*! * \brief Initializes the vector for use * \param contains_raft_layers Indicates whether this vector will contain raft layers - * \param total_print_layers The number of print layers (not including raft layers). This is only for pre-reserving - * stored data and can be safely omitted. + * \param print_layers The number of print layers (not including raft layers). This is only for pre-reserving + * stored data and can be safely omitted. * \note It is not mandatory to call this method if you do not intend to store raft layers, but still good practice */ void init(bool contains_raft_layers, size_t print_layers = 0) @@ -145,6 +144,11 @@ class LayerVector return end(); } + /*! + * \brief Safe method to retrieve an element from the list + * \param pos The position of the element to be retrieved + * \return The element at the given position, or if there is none, a default-constructed value. + */ value_type get(const LayerIndex& pos) const noexcept { LayerIndex::value_type index = pos + delta_; diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index cefc8e2fe6..80e8fc5801 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -715,15 +715,6 @@ class PolygonUtils namespace std { -/*template -struct hash> -{ - size_t operator()(const std::pair& pair) const - { - return 31 * std::hash()(pair.first) + 59 * std::hash()(pair.second); - } -};*/ - template<> struct hash { diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index c2f48297ce..413143f4e7 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2463,7 +2463,7 @@ size_t FffGcodeWriter::findUsedExtruderIndex(const SliceDataStorage& storage, co } else { - // Asking for extruder on an empty layer, get the one from layer below + // Asking for extruder on an empty layer, get the last one from layer below return findUsedExtruderIndex(storage, layer_nr - 1, true); } } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 9e479c6c2b..a419f75680 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -639,7 +639,7 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const Point2LL translation(extruder_settings.get("machine_nozzle_offset_x"), extruder_settings.get("machine_nozzle_offset_y")); prime_pos -= translation; Shape prime_polygons; - prime_polygons.emplace_back(PolygonUtils::makeCircle(prime_pos, prime_clearance, 32)); + prime_polygons.emplace_back(PolygonUtils::makeCircle(prime_pos, prime_clearance, 64)); disallowed_areas = disallowed_areas.unionPolygons(prime_polygons); } From ea20a56a0cbfb3ad275f82897448b214f9d79f44 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 12 Jun 2024 16:31:04 +0200 Subject: [PATCH 38/49] Restore previous behavior for prime tower start position --- src/PrimeTower/PrimeTower.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index b58b9c7dd7..877ed99e02 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -368,22 +368,25 @@ void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder { // Layer number may be negative, make it positive (or null) to apply modulo operator LayerIndex layer_nr = gcode_layer.getLayerNr(); - while (layer_nr < 0) + if (layer_nr != -Raft::getTotalExtraLayers()) { - layer_nr += number_of_prime_tower_start_locations_; - } + while (layer_nr < 0) + { + layer_nr += number_of_prime_tower_start_locations_; + } - size_t current_start_location_idx = ((extruder_nr + 1) * static_cast(layer_nr)) % number_of_prime_tower_start_locations_; + size_t current_start_location_idx = ((extruder_nr + 1) * static_cast(layer_nr)) % number_of_prime_tower_start_locations_; - const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; - const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; - const coord_t inward_dist = train.settings_.get("machine_nozzle_size") * 3 / 2; - const coord_t start_dist = train.settings_.get("machine_nozzle_size") * 2; - const Point2LL prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); - const Point2LL outward_dir = wipe_location.location_ - prime_end; - const Point2LL prime_start = wipe_location.location_ + normal(outward_dir, start_dist); + const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; + const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; + const coord_t inward_dist = train.settings_.get("machine_nozzle_size") * 3 / 2; + const coord_t start_dist = train.settings_.get("machine_nozzle_size") * 2; + const Point2LL prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); + const Point2LL outward_dir = wipe_location.location_ - prime_end; + const Point2LL prime_start = wipe_location.location_ + normal(outward_dir, start_dist); - gcode_layer.addTravel(prime_start); + gcode_layer.addTravel(prime_start); + } } } // namespace cura From 453fd31d90d4ba5b135035a3b24dffd1fd558184 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Jun 2024 09:28:47 +0200 Subject: [PATCH 39/49] Add code documentation and reword some code --- include/ExtruderPrime.h | 2 +- include/PrimeTower/PrimeTower.h | 93 ++++++++++++++++------ include/PrimeTower/PrimeTowerInterleaved.h | 2 +- include/PrimeTower/PrimeTowerNormal.h | 2 +- src/PrimeTower/PrimeTower.cpp | 56 ++++++------- src/PrimeTower/PrimeTowerInterleaved.cpp | 40 +++++----- src/PrimeTower/PrimeTowerNormal.cpp | 36 ++++----- 7 files changed, 137 insertions(+), 94 deletions(-) diff --git a/include/ExtruderPrime.h b/include/ExtruderPrime.h index f3c8d3cc48..e6cb89b3c4 100644 --- a/include/ExtruderPrime.h +++ b/include/ExtruderPrime.h @@ -10,7 +10,7 @@ namespace cura enum class ExtruderPrime { None, // Do not prime at all for this extruder on this layer - Sparse, // Just extrude a sparse priming which purpose is to make the tower stronger + Support, // Just extrude a sparse pattern which purpose is to support the upper parts of the prime tower Prime, // Do an actual prime }; diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index e561426cf8..fa48024adb 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -29,15 +29,13 @@ class LayerPlan; class PrimeTower { protected: - struct ExtruderMoves + struct ExtruderToolPaths { size_t extruder_nr; - Shape moves; + Shape toolpaths; }; private: - using MovesByLayer = std::map>; - bool wipe_from_middle_; //!< Whether to wipe on the inside of the hollow prime tower Point2LL middle_; //!< The middle of the prime tower @@ -48,10 +46,10 @@ class PrimeTower /* * The map index is the layer number - * For each layer, the list contains the extruders moves to be processed. This list is sorted from outer rings to inner - * rings, which is not the printing chronological order, but the physical arrangement. + * For each layer, the list contains the extruders moves to be processed. This list is sorted from outer annuli to inner + * annuli, which is not the printing chronological order, but the physical arrangement. */ - std::map> moves_; + std::map> toolpaths_; Shape outer_poly_; //!< The outline of the prime tower, not including the base @@ -61,12 +59,7 @@ class PrimeTower LayerVector base_occupied_outline_; public: - /*! - * \brief Creates a prime tower instance that will determine where and how - * the prime tower gets printed. - * - * \param storage A storage where it retrieves the prime tower settings. - */ + /*! \brief Creates a prime tower instance that will determine where and how the prime tower gets printed. */ PrimeTower(); virtual ~PrimeTower() = default; @@ -116,6 +109,15 @@ class PrimeTower */ const Shape& getExtrusionOutline(const LayerIndex& layer_nr) const; + /*! + * \brief Get the required priming for the given extruder at the given layer + * \param extruder_is_used_on_this_layer A list indicating which extruders are used at this layer + * \param extruder_nr The extruder for which we want the priming information + * \param last_extruder The extruder that was in use just before using the new one + * \param storage The storage containing all the slice data + * \param layer_nr The layer at which we want to use the extruder + * \return An enumeration indication how the extruder will be used by the prime tower at this layer + */ virtual ExtruderPrime getExtruderPrime( const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, @@ -123,33 +125,76 @@ class PrimeTower const SliceDataStorage& storage, const LayerIndex& layer_nr) const = 0; + /*! + * \brief This method has to be called once the extruders use for each layer have been calculated. From this point, + * we can start generating the prime tower, and also polish the given extruders uses. + * \param extruders_use The calculated extruders uses at each layer. This may be slightly changed to make sure that + * the prime tower can be properly printed. + * \param start_extruder The very first used extruder + */ void processExtrudersUse(LayerVector>& extruders_use, const size_t start_extruder); + /*! + * \brief Create the proper prime tower object according to the current settings + * \param storage The storage containing all the slice data + * \return The proper prime tower object, which may be null if prime tower is actually disabled or not required + */ static PrimeTower* createPrimeTower(SliceDataStorage& storage); protected: + /*! + * \brief Once all the extruders uses have been calculated for each layer, this method makes a global pass to make + * sure that the prime tower can be properly printed. This is required because we sometimes need to know what + * a layer above is made of to fix a layer below. + * \param extruders_use The calculated extruders uses at each layer + * \param start_extruder The very first used extruder + */ virtual void polishExtrudersUses(LayerVector>& /*extruders_use*/, const size_t /*start_extruder*/) { + // Default behavior is to keep the extruders uses as they were calculated } /*! - * \brief generateExtrusionsMoves - * \param extruders_use - * \param storage - * \return A map of extruders moves per layer. The inner list is sorted from outer rings to inner - * rings, which is not the printing chronological order, but the physical arrangement. @sa moves_ + * \brief Generated the extruders toolpaths for each layer of the prime tower + * \param extruders_use The calculated extruders uses at each layer + * \return A map of extruders toolpaths per layer. The inner list is sorted from outer annuli to inner + * annuli, which is not the printing chronological order, but the physical arrangement. @sa toolpaths_ */ - virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) = 0; + virtual std::map> generateToolPaths(const LayerVector>& extruders_use) = 0; - std::tuple generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius); + /*! + * \brief Generate the actual priming toolpaths for the given extruder, starting at the given outer circle radius + * \param extruder_nr The extruder for which we want the priming toolpath + * \param outer_radius The radius of the starting outer circle + * \return A tuple containing the newly generated toolpaths, and the inner radius of the newly generated annulus + */ + std::tuple generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius); - Shape generateSupportMoves(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius); + /*! + * \brief Generate support toolpaths using the wheel pattern applied on an annulus + * \param extruder_nr The extruder for which we want the support toolpath + * \param outer_radius The annulus outer radius + * \param inner_radius The annulis inner radius + * \return + */ + Shape generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius); + /*! + * \brief Calculates whether an extruder requires priming at a specific layer + * \param extruder_is_used_on_this_layer The list of used extruders at this layer + * \param extruder_nr The extruder we now want to use + * \param last_extruder The extruder that was in use before switching to the new one + * \return True if the extruder needs to be primed, false otherwise + */ static bool extruderRequiresPrime(const std::vector& extruder_is_used_on_this_layer, size_t extruder_nr, size_t last_extruder); private: + /*! \brief Generates the extra inset used for better adhesion at the first layer */ void generateFirtLayerInset(); + /*! \brief Generates the extra annuli around the first layers of the prime tower which help make it stronger */ + void generateBase(); + /*! * For an extruder switch that happens not on the first layer, the extruder needs to be primed on the prime tower. * This function picks a start location for this extruder on the prime tower's perimeter and travels there to avoid @@ -159,13 +204,9 @@ class PrimeTower /*! * \brief Subtract the prime tower from the support areas in storage. - * - * \param storage The storage where to find the support from which to - * subtract a prime tower. + * \param storage The storage where to find the support from which to subtract a prime tower. */ void subtractFromSupport(SliceDataStorage& storage); - - void generateBase(); }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index a1a915439c..45d06b1aca 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -24,7 +24,7 @@ class PrimeTowerInterleaved : public PrimeTower protected: virtual void polishExtrudersUses(LayerVector>& extruders_use, const size_t start_extruder) override; - virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) override; + virtual std::map> generateToolPaths(const LayerVector>& extruders_use) override; }; } // namespace cura diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 2280a33029..3037255e93 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -22,7 +22,7 @@ class PrimeTowerNormal : public PrimeTower const LayerIndex& layer_nr) const override; protected: - virtual std::map> generateExtrusionsMoves(const LayerVector>& extruders_use) override; + virtual std::map> generateToolPaths(const LayerVector>& extruders_use) override; }; } // namespace cura diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 877ed99e02..a2eb530f27 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -79,21 +79,21 @@ void PrimeTower::generateBase() { base_extrusion_outline_.init(true); - // Generate the base outside extra rings for the first extruder of each layer - auto iterator_extrusion_paths = moves_.begin(); + // Generate the base outside extra annuli for the first extruder of each layer + auto iterator_extrusion_paths = toolpaths_.begin(); auto iterator_base_outline = base_occupied_outline_.begin(); - for (; iterator_extrusion_paths != moves_.end() && iterator_base_outline != base_occupied_outline_.end(); ++iterator_extrusion_paths, ++iterator_base_outline) + for (; iterator_extrusion_paths != toolpaths_.end() && iterator_base_outline != base_occupied_outline_.end(); ++iterator_extrusion_paths, ++iterator_base_outline) { - std::vector& moves_at_this_layer = iterator_extrusion_paths->second; + std::vector& moves_at_this_layer = iterator_extrusion_paths->second; if (! moves_at_this_layer.empty()) { const Shape& base_ouline_at_this_layer = *iterator_base_outline; - ExtruderMoves& extruder_moves = moves_at_this_layer.front(); + ExtruderToolPaths& extruder_moves = moves_at_this_layer.front(); const size_t extruder_nr = extruder_moves.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); Shape outset = PolygonUtils::generateOutset(outer_poly_, base_ouline_at_this_layer, line_width); - extruder_moves.moves.push_back(outset); + extruder_moves.toolpaths.push_back(outset); base_extrusion_outline_.push_back(outset.offset(line_width / 2)); } @@ -103,16 +103,16 @@ void PrimeTower::generateBase() void PrimeTower::generateFirtLayerInset() { - // Generate the base inside extra rings for the last extruder of the first layer - if (! moves_.empty()) + // Generate the base inside extra disc for the last extruder of the first layer + if (! toolpaths_.empty()) { - std::vector& moves_first_layer = moves_.begin()->second; + std::vector& moves_first_layer = toolpaths_.begin()->second; if (! moves_first_layer.empty()) { - ExtruderMoves& extruder_moves = moves_first_layer.back(); + ExtruderToolPaths& extruder_moves = moves_first_layer.back(); const Scene& scene = Application::getInstance().current_slice_->scene; const size_t extruder_nr = extruder_moves.extruder_nr; - Shape& moves_last_extruder = extruder_moves.moves; + Shape& moves_last_extruder = extruder_moves.toolpaths; if (! moves_last_extruder.empty()) { const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); @@ -126,7 +126,7 @@ void PrimeTower::generateFirtLayerInset() } } -std::tuple PrimeTower::generatePrimeMoves(const size_t extruder_nr, const coord_t outer_radius) +std::tuple PrimeTower::generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; @@ -159,7 +159,7 @@ std::tuple PrimeTower::generatePrimeMoves(const size_t extruder_ return std::make_tuple(moves, current_outer_diameter); } -Shape PrimeTower::generateSupportMoves(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) +Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) { const Scene& scene = Application::getInstance().current_slice_->scene; const double max_bridging_distance = static_cast(scene.extruders[extruder_nr].settings_.get("prime_tower_max_bridging_distance")); @@ -169,20 +169,20 @@ Shape PrimeTower::generateSupportMoves(const size_t extruder_nr, const coord_t o Shape moves; - // Split ring according to max bridging distance - const coord_t nb_rings = static_cast(std::ceil(static_cast(radius_delta) / max_bridging_distance)); - if (nb_rings > 0) + // Split annuli according to max bridging distance + const coord_t nb_annuli = static_cast(std::ceil(static_cast(radius_delta) / max_bridging_distance)); + if (nb_annuli > 0) { - const coord_t actual_radius_step = radius_delta / nb_rings; + const coord_t actual_radius_step = radius_delta / nb_annuli; - for (coord_t i = 0; i < nb_rings; ++i) + for (coord_t i = 0; i < nb_annuli; ++i) { - const coord_t ring_inner_radius = (inner_radius + i * actual_radius_step) + semi_line_width; - const coord_t ring_outer_radius = (inner_radius + (i + 1) * actual_radius_step) - semi_line_width; + const coord_t annulus_inner_radius = (inner_radius + i * actual_radius_step) + semi_line_width; + const coord_t annulus_outer_radius = (inner_radius + (i + 1) * actual_radius_step) - semi_line_width; - const size_t semi_nb_spokes = static_cast(std::ceil((std::numbers::pi * static_cast(ring_outer_radius)) / max_bridging_distance)); + const size_t semi_nb_spokes = static_cast(std::ceil((std::numbers::pi * static_cast(annulus_outer_radius)) / max_bridging_distance)); - moves.push_back(PolygonUtils::makeWheel(middle_, ring_inner_radius, ring_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); + moves.push_back(PolygonUtils::makeWheel(middle_, annulus_inner_radius, annulus_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); } } @@ -230,20 +230,20 @@ void PrimeTower::addToGcode( } const Shape* moves = nullptr; - auto iterator_layer = moves_.find(layer_nr); - if (iterator_layer != moves_.end()) + auto iterator_layer = toolpaths_.find(layer_nr); + if (iterator_layer != toolpaths_.end()) { - const std::vector& moves_at_this_layer = iterator_layer->second; + const std::vector& moves_at_this_layer = iterator_layer->second; auto iterator_extruder = std::find_if( moves_at_this_layer.begin(), moves_at_this_layer.end(), - [new_extruder_nr](const ExtruderMoves& extruder_moves) + [new_extruder_nr](const ExtruderToolPaths& extruder_moves) { return extruder_moves.extruder_nr == new_extruder_nr; }); if (iterator_extruder != iterator_layer->second.end()) { - moves = &(iterator_extruder->moves); + moves = &(iterator_extruder->toolpaths); } } @@ -322,7 +322,7 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) void PrimeTower::processExtrudersUse(LayerVector>& extruders_use, const size_t start_extruder) { polishExtrudersUses(extruders_use, start_extruder); - moves_ = generateExtrusionsMoves(extruders_use); + toolpaths_ = generateToolPaths(extruders_use); generateBase(); generateFirtLayerInset(); } diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 8c4805e67b..2cc4bc538c 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -24,6 +24,8 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( const SliceDataStorage& /*storage*/, const LayerIndex& /*layer_nr*/) const { + // For now, just calculate prime or not. Support extrusion requires the whole extruders list to be calculted, and + // will be processed later. if (extruderRequiresPrime(extruder_is_used_on_this_layer, extruder_nr, last_extruder)) { return ExtruderPrime::Prime; @@ -34,21 +36,21 @@ ExtruderPrime PrimeTowerInterleaved::getExtruderPrime( } } -std::map> PrimeTowerInterleaved::generateExtrusionsMoves(const LayerVector>& extruders_use) +std::map> PrimeTowerInterleaved::generateToolPaths(const LayerVector>& extruders_use) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; coord_t support_radius = tower_radius; - std::map> moves; + std::map> toolpaths; // Loop from top bo bottom, so that the required support increases with what is actually required for (auto iterator = extruders_use.rbegin(); iterator != extruders_use.rend(); ++iterator) { const LayerIndex layer_nr = extruders_use.getLayer(iterator); const std::vector& extruders_use_at_layer = extruders_use[layer_nr]; - std::vector moves_at_layer; - size_t last_extruder_sparse = 0; + std::vector toolpaths_at_layer; + size_t last_extruder_support = 0; // Now generate actual priming patterns coord_t outer_radius = tower_radius; @@ -56,36 +58,36 @@ std::map> PrimeTowerInterleav { if (extruder_use.prime == ExtruderPrime::Prime) { - Shape extruder_moves; - std::tie(extruder_moves, outer_radius) = generatePrimeMoves(extruder_use.extruder_nr, outer_radius); - moves_at_layer.emplace_back(extruder_use.extruder_nr, extruder_moves); + Shape extruder_toolpaths; + std::tie(extruder_toolpaths, outer_radius) = generatePrimeToolpaths(extruder_use.extruder_nr, outer_radius); + toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruder_toolpaths); } - else if (extruder_use.prime == ExtruderPrime::Sparse) + else if (extruder_use.prime == ExtruderPrime::Support) { - last_extruder_sparse = extruder_use.extruder_nr; + last_extruder_support = extruder_use.extruder_nr; } } - // Generate extra "support" sparse pattern if required + // Generate extra support pattern if required if (support_radius < outer_radius) { - if (moves_at_layer.empty()) + if (toolpaths_at_layer.empty()) { - moves_at_layer.emplace_back(last_extruder_sparse, Shape()); + toolpaths_at_layer.emplace_back(last_extruder_support, Shape()); } - ExtruderMoves& last_extruder_moves = moves_at_layer.back(); - Shape support_moves = generateSupportMoves(last_extruder_moves.extruder_nr, outer_radius, support_radius); - last_extruder_moves.moves.push_back(support_moves); + ExtruderToolPaths& last_extruder_toolpaths = toolpaths_at_layer.back(); + Shape support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, outer_radius, support_radius); + last_extruder_toolpaths.toolpaths.push_back(support_toolpaths); } // Now decrease support radius if required support_radius = std::min(support_radius, outer_radius); - moves[layer_nr] = moves_at_layer; + toolpaths[layer_nr] = toolpaths_at_layer; } - return moves; + return toolpaths; } void PrimeTowerInterleaved::polishExtrudersUses(LayerVector>& extruders_use, const size_t start_extruder) @@ -100,7 +102,7 @@ void PrimeTowerInterleaved::polishExtrudersUses(LayerVector> PrimeTowerNormal::generateExtrusionsMoves(const LayerVector>& extruders_use) +std::map> PrimeTowerNormal::generateToolPaths(const LayerVector>& extruders_use) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; - std::map> moves; + std::map> toolpaths; // First make a basic list of used extruders numbers std::vector extruder_order; @@ -66,26 +66,26 @@ std::map> PrimeTowerNormal::g // For each extruder, generate the prime and support patterns, which will always be the same across layers coord_t current_radius = tower_radius; - std::map extruders_prime_moves; - std::map extruders_support_moves; + std::map extruders_prime_toolpaths; + std::map extruders_support_toolpaths; for (size_t extruder_nr : extruder_order) { - std::tuple prime_moves = generatePrimeMoves(extruder_nr, current_radius); - extruders_prime_moves[extruder_nr] = std::get<0>(prime_moves); - const coord_t inner_radius = std::get<1>(prime_moves); + std::tuple prime_toolpaths = generatePrimeToolpaths(extruder_nr, current_radius); + extruders_prime_toolpaths[extruder_nr] = std::get<0>(prime_toolpaths); + const coord_t inner_radius = std::get<1>(prime_toolpaths); - extruders_support_moves[extruder_nr] = generateSupportMoves(extruder_nr, current_radius, inner_radius); + extruders_support_toolpaths[extruder_nr] = generateSupportToolpaths(extruder_nr, current_radius, inner_radius); current_radius = inner_radius; } - // Now fill the extruders moves according to their use + // Now fill the extruders toolpaths according to their use for (auto iterator = extruders_use.begin(); iterator != extruders_use.end(); ++iterator) { const LayerIndex layer_nr = extruders_use.getLayer(iterator); std::vector extruders_use_at_layer = *iterator; - // Sort to fit the global order, in order to insert the moves in outside to inside order + // Sort to fit the global order, in order to insert the toolpaths in outside to inside order std::sort( extruders_use_at_layer.begin(), extruders_use_at_layer.end(), @@ -95,8 +95,8 @@ std::map> PrimeTowerNormal::g < std::find(extruder_order.begin(), extruder_order.end(), extruder_use2.extruder_nr); }); - // Now put the proper moves for each extruder use - std::vector moves_at_layer; + // Now put the proper toolpaths for each extruder use + std::vector toolpaths_at_layer; for (const ExtruderUse& extruder_use : extruders_use_at_layer) { switch (extruder_use.prime) @@ -105,19 +105,19 @@ std::map> PrimeTowerNormal::g break; case ExtruderPrime::Prime: - moves_at_layer.emplace_back(extruder_use.extruder_nr, extruders_prime_moves[extruder_use.extruder_nr]); + toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruders_prime_toolpaths[extruder_use.extruder_nr]); break; - case ExtruderPrime::Sparse: - moves_at_layer.emplace_back(extruder_use.extruder_nr, extruders_support_moves[extruder_use.extruder_nr]); + case ExtruderPrime::Support: + toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruders_support_toolpaths[extruder_use.extruder_nr]); break; } } - moves[layer_nr] = moves_at_layer; + toolpaths[layer_nr] = toolpaths_at_layer; } - return moves; + return toolpaths; } } // namespace cura From d1359484ede9181ff6284c1610b5dcb8d0969e48 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 13 Jun 2024 18:25:56 +0200 Subject: [PATCH 40/49] Generate only circular patterns for base and initial inset --- include/PrimeTower/PrimeTower.h | 10 +++- include/utils/polygonUtils.h | 23 ++++---- src/PrimeTower/PrimeTower.cpp | 75 +++++++++++------------- src/PrimeTower/PrimeTowerInterleaved.cpp | 14 +++-- src/PrimeTower/PrimeTowerNormal.cpp | 22 ++++--- src/utils/polygonUtils.cpp | 23 ++++---- 6 files changed, 90 insertions(+), 77 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index fa48024adb..8f0d8f38e3 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -29,10 +29,18 @@ class LayerPlan; class PrimeTower { protected: + struct OccupiedOutline + { + Shape outline; + coord_t outer_radius; + }; + struct ExtruderToolPaths { size_t extruder_nr; Shape toolpaths; + coord_t outer_radius; + coord_t inner_radius; }; private: @@ -56,7 +64,7 @@ class PrimeTower //!< This is the exact outline of the extrusions lines of each layer, for layers having extra width for the base LayerVector base_extrusion_outline_; //!< This is the approximate outline of the area filled at each layer, for layers having extra width for the base - LayerVector base_occupied_outline_; + LayerVector base_occupied_outline_; public: /*! \brief Creates a prime tower instance that will determine where and how the prime tower gets printed. */ diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 80e8fc5801..4b99f3b695 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -677,24 +677,27 @@ class PolygonUtils static Shape clipPolygonWithAABB(const Shape& src, const AABB& aabb); /*! - * Generate a few outset polygons around the given base, according to the given line width + * Generate a few outset circles around a base, according to the given line width * - * \param inner_poly The inner polygon to start generating the outset from - * \param outer_poly The outer polygon to fit the outset into + * \param center The center of the outset + * \param inner_radius The inner radius to start generating the outset from + * \param outer_radius The outer radius to fit the outset into * \param line_width The actual line width to distance the polygons from each other (and from the base) - * \return The generated outset polygons + * \param circle_definition The definition (number of segments) of the generated circles + * \return The generated outset circles */ - static Shape generateOutset(const Shape& inner_poly, const Shape& outer_poly, coord_t line_width); + static Shape generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); /*! - * Generate inset polygons inside the given base, until there is no space left, according to the given line width + * Generate inset circles inside the given base, until there is no space left, according to the given line width * - * \param outer_poly The outer polygon to start generating the inset from + * \param center The center of the inset + * \param outer_radius The outer radius to start generating the inset from * \param line_width The actual line width to distance the polygons from each other (and from the base) - * \param initial_inset The inset distance to be added to the first generated polygon - * \return The generated inset polygons + * \param circle_definition The definition (number of segments) of the generated circles + * \return The generated inset circles */ - static Shape generateInset(const Polygon& outer_poly, coord_t line_width, coord_t initial_inset = 0); + static Shape generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); private: /*! diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index a2eb530f27..e31902bef2 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -60,9 +60,10 @@ PrimeTower::PrimeTower() for (coord_t z = 0; z < base_height; z += layer_height) { - double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); - coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); - base_occupied_outline_.push_back(outer_poly_.offset(extra_radius)); + const double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); + const coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); + const coord_t total_radius = tower_radius + extra_radius; + base_occupied_outline_.emplace_back(std::vector({ PolygonUtils::makeCircle(middle_, total_radius, CIRCLE_RESOLUTION) }), total_radius); } } } @@ -84,16 +85,17 @@ void PrimeTower::generateBase() auto iterator_base_outline = base_occupied_outline_.begin(); for (; iterator_extrusion_paths != toolpaths_.end() && iterator_base_outline != base_occupied_outline_.end(); ++iterator_extrusion_paths, ++iterator_base_outline) { - std::vector& moves_at_this_layer = iterator_extrusion_paths->second; - if (! moves_at_this_layer.empty()) + std::vector& toolpaths_at_this_layer = iterator_extrusion_paths->second; + if (! toolpaths_at_this_layer.empty()) { - const Shape& base_ouline_at_this_layer = *iterator_base_outline; - ExtruderToolPaths& extruder_moves = moves_at_this_layer.front(); - const size_t extruder_nr = extruder_moves.extruder_nr; + const OccupiedOutline& base_ouline_at_this_layer = *iterator_base_outline; + ExtruderToolPaths& first_extruder_toolpaths = toolpaths_at_this_layer.front(); + const size_t extruder_nr = first_extruder_toolpaths.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape outset = PolygonUtils::generateOutset(outer_poly_, base_ouline_at_this_layer, line_width); - extruder_moves.toolpaths.push_back(outset); + Shape outset + = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, CIRCLE_RESOLUTION); + first_extruder_toolpaths.toolpaths.push_back(outset); base_extrusion_outline_.push_back(outset.offset(line_width / 2)); } @@ -106,22 +108,15 @@ void PrimeTower::generateFirtLayerInset() // Generate the base inside extra disc for the last extruder of the first layer if (! toolpaths_.empty()) { - std::vector& moves_first_layer = toolpaths_.begin()->second; - if (! moves_first_layer.empty()) + std::vector& toolpaths_first_layer = toolpaths_.begin()->second; + if (! toolpaths_first_layer.empty()) { - ExtruderToolPaths& extruder_moves = moves_first_layer.back(); + ExtruderToolPaths& last_extruder_toolpaths = toolpaths_first_layer.back(); const Scene& scene = Application::getInstance().current_slice_->scene; - const size_t extruder_nr = extruder_moves.extruder_nr; - Shape& moves_last_extruder = extruder_moves.toolpaths; - if (! moves_last_extruder.empty()) - { - const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape pattern = PolygonUtils::generateInset(moves_last_extruder.back(), line_width, line_width / 2); - if (! pattern.empty()) - { - moves_last_extruder.push_back(pattern); - } - } + const size_t extruder_nr = last_extruder_toolpaths.extruder_nr; + const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); + Shape pattern = PolygonUtils::generateCircularInset(middle_, last_extruder_toolpaths.inner_radius, line_width, CIRCLE_RESOLUTION); + last_extruder_toolpaths.toolpaths.push_back(pattern); } } } @@ -138,14 +133,14 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru double current_volume = 0; coord_t current_outer_diameter = outer_radius; - Shape moves; + Shape toolpaths; do { Shape shape = outer_poly_.offset(-(tower_radius - current_outer_diameter + line_width / 2)); if (! shape.empty()) { - moves.push_back(shape); + toolpaths.push_back(shape); current_volume += static_cast(shape.length() * line_width * layer_height) * flow; current_outer_diameter -= line_width; } @@ -156,7 +151,7 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru } } while (current_volume < required_volume); - return std::make_tuple(moves, current_outer_diameter); + return std::make_tuple(toolpaths, current_outer_diameter); } Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) @@ -167,7 +162,7 @@ Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord const coord_t radius_delta = outer_radius - inner_radius; const coord_t semi_line_width = line_width / 2; - Shape moves; + Shape toolpaths; // Split annuli according to max bridging distance const coord_t nb_annuli = static_cast(std::ceil(static_cast(radius_delta) / max_bridging_distance)); @@ -182,11 +177,11 @@ Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord const size_t semi_nb_spokes = static_cast(std::ceil((std::numbers::pi * static_cast(annulus_outer_radius)) / max_bridging_distance)); - moves.push_back(PolygonUtils::makeWheel(middle_, annulus_inner_radius, annulus_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); + toolpaths.push_back(PolygonUtils::makeWheel(middle_, annulus_inner_radius, annulus_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); } } - return moves; + return toolpaths; } void PrimeTower::addToGcode( @@ -229,30 +224,30 @@ void PrimeTower::addToGcode( return; } - const Shape* moves = nullptr; + const Shape* toolpaths = nullptr; auto iterator_layer = toolpaths_.find(layer_nr); if (iterator_layer != toolpaths_.end()) { - const std::vector& moves_at_this_layer = iterator_layer->second; + const std::vector& toolpaths_at_this_layer = iterator_layer->second; auto iterator_extruder = std::find_if( - moves_at_this_layer.begin(), - moves_at_this_layer.end(), - [new_extruder_nr](const ExtruderToolPaths& extruder_moves) + toolpaths_at_this_layer.begin(), + toolpaths_at_this_layer.end(), + [new_extruder_nr](const ExtruderToolPaths& extruder_toolpaths) { - return extruder_moves.extruder_nr == new_extruder_nr; + return extruder_toolpaths.extruder_nr == new_extruder_nr; }); if (iterator_extruder != iterator_layer->second.end()) { - moves = &(iterator_extruder->toolpaths); + toolpaths = &(iterator_extruder->toolpaths); } } - if (moves && ! moves->empty()) + if (toolpaths && ! toolpaths->empty()) { gotoStartLocation(gcode_layer, new_extruder_nr); const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[new_extruder_nr]; - gcode_layer.addPolygonsByOptimizer(*moves, config); + gcode_layer.addPolygonsByOptimizer(*toolpaths, config); } gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); @@ -274,7 +269,7 @@ const Shape& PrimeTower::getOccupiedOutline(const LayerIndex& layer_nr) const auto iterator = base_occupied_outline_.iterator_at(layer_nr); if (iterator != base_occupied_outline_.end()) { - return *iterator; + return iterator->outline; } else { diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 2cc4bc538c..3742c37151 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -58,9 +58,14 @@ std::map> PrimeTowerInter { if (extruder_use.prime == ExtruderPrime::Prime) { - Shape extruder_toolpaths; - std::tie(extruder_toolpaths, outer_radius) = generatePrimeToolpaths(extruder_use.extruder_nr, outer_radius); - toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruder_toolpaths); + ExtruderToolPaths extruder_toolpaths; + extruder_toolpaths.outer_radius = outer_radius; + extruder_toolpaths.extruder_nr = extruder_use.extruder_nr; + + std::tie(extruder_toolpaths.toolpaths, extruder_toolpaths.inner_radius) = generatePrimeToolpaths(extruder_use.extruder_nr, outer_radius); + toolpaths_at_layer.push_back(extruder_toolpaths); + + outer_radius = extruder_toolpaths.inner_radius; } else if (extruder_use.prime == ExtruderPrime::Support) { @@ -73,12 +78,13 @@ std::map> PrimeTowerInter { if (toolpaths_at_layer.empty()) { - toolpaths_at_layer.emplace_back(last_extruder_support, Shape()); + toolpaths_at_layer.emplace_back(last_extruder_support, Shape(), outer_radius, support_radius); } ExtruderToolPaths& last_extruder_toolpaths = toolpaths_at_layer.back(); Shape support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, outer_radius, support_radius); last_extruder_toolpaths.toolpaths.push_back(support_toolpaths); + last_extruder_toolpaths.inner_radius = support_radius; } // Now decrease support radius if required diff --git a/src/PrimeTower/PrimeTowerNormal.cpp b/src/PrimeTower/PrimeTowerNormal.cpp index 6f0f9d047a..eb99669423 100644 --- a/src/PrimeTower/PrimeTowerNormal.cpp +++ b/src/PrimeTower/PrimeTowerNormal.cpp @@ -66,17 +66,21 @@ std::map> PrimeTowerNorma // For each extruder, generate the prime and support patterns, which will always be the same across layers coord_t current_radius = tower_radius; - std::map extruders_prime_toolpaths; - std::map extruders_support_toolpaths; + std::map extruders_prime_toolpaths; + std::map extruders_support_toolpaths; for (size_t extruder_nr : extruder_order) { - std::tuple prime_toolpaths = generatePrimeToolpaths(extruder_nr, current_radius); - extruders_prime_toolpaths[extruder_nr] = std::get<0>(prime_toolpaths); - const coord_t inner_radius = std::get<1>(prime_toolpaths); + ExtruderToolPaths extruder_prime_toolpaths; + extruder_prime_toolpaths.extruder_nr = extruder_nr; + extruder_prime_toolpaths.outer_radius = current_radius; + std::tie(extruder_prime_toolpaths.toolpaths, extruder_prime_toolpaths.inner_radius) = generatePrimeToolpaths(extruder_nr, current_radius); + extruders_prime_toolpaths[extruder_nr] = extruder_prime_toolpaths; - extruders_support_toolpaths[extruder_nr] = generateSupportToolpaths(extruder_nr, current_radius, inner_radius); + ExtruderToolPaths extruder_support_toolpaths = extruder_prime_toolpaths; + extruder_support_toolpaths.toolpaths = generateSupportToolpaths(extruder_nr, current_radius, extruder_prime_toolpaths.inner_radius); + extruders_support_toolpaths[extruder_nr] = extruder_support_toolpaths; - current_radius = inner_radius; + current_radius = extruder_prime_toolpaths.inner_radius; } // Now fill the extruders toolpaths according to their use @@ -105,11 +109,11 @@ std::map> PrimeTowerNorma break; case ExtruderPrime::Prime: - toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruders_prime_toolpaths[extruder_use.extruder_nr]); + toolpaths_at_layer.push_back(extruders_prime_toolpaths[extruder_use.extruder_nr]); break; case ExtruderPrime::Support: - toolpaths_at_layer.emplace_back(extruder_use.extruder_nr, extruders_support_toolpaths[extruder_use.extruder_nr]); + toolpaths_at_layer.push_back(extruders_support_toolpaths[extruder_use.extruder_nr]); break; } } diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 7695ca127d..84f1e68160 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1617,32 +1617,29 @@ Shape PolygonUtils::clipPolygonWithAABB(const Shape& src, const AABB& aabb) return out; } -Shape PolygonUtils::generateOutset(const Shape& inner_poly, const Shape& outer_poly, coord_t line_width) +Shape PolygonUtils::generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, coord_t line_width, const size_t circle_definition) { Shape outset; + coord_t radius = inner_radius + line_width / 2; - coord_t offset = line_width / 2; - Shape current_outset = inner_poly.offset(offset); - - while (! current_outset.empty() && current_outset.front().isValid() && outer_poly.inside(current_outset.front().front())) + while (radius + line_width / 2 <= outer_radius) { - outset.push_back(current_outset); - offset += line_width; - current_outset = inner_poly.offset(offset); + outset.push_back(makeCircle(center, radius, circle_definition)); + radius += line_width; } return outset; } -Shape PolygonUtils::generateInset(const Polygon& outer_poly, coord_t line_width, coord_t initial_inset) +Shape PolygonUtils::generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition) { Shape inset; + coord_t radius = outer_radius - line_width / 2; - Shape current_inset = outer_poly.offset(-(initial_inset + line_width / 2)); - while (! current_inset.empty()) + while (radius - line_width / 2 >= line_width) { - inset.push_back(current_inset); - current_inset = current_inset.offset(-line_width); + inset.push_back(makeCircle(center, radius, circle_definition)); + radius -= line_width; } return inset; From 6c5dca6a1f3138c49a49a2e5796050d8cdf700e2 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 14 Jun 2024 10:34:52 +0200 Subject: [PATCH 41/49] Fix double base print and optimize execution We now generate a new circle every time we need to create an inset/ outset instead of offsetting the base shape. --- include/utils/polygonUtils.h | 5 +++-- src/PrimeTower/PrimeTower.cpp | 33 ++++++++++++--------------------- src/utils/polygonUtils.cpp | 15 +++++++++------ 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 4b99f3b695..df17d37938 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -684,9 +684,10 @@ class PolygonUtils * \param outer_radius The outer radius to fit the outset into * \param line_width The actual line width to distance the polygons from each other (and from the base) * \param circle_definition The definition (number of segments) of the generated circles - * \return The generated outset circles + * \return The generated outset circles, and the outer radius or the shape */ - static Shape generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); + static std::tuple + generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); /*! * Generate inset circles inside the given base, until there is no space left, according to the given line width diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index e31902bef2..025591328e 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -93,11 +93,11 @@ void PrimeTower::generateBase() const size_t extruder_nr = first_extruder_toolpaths.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape outset + std::tuple outset = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, CIRCLE_RESOLUTION); - first_extruder_toolpaths.toolpaths.push_back(outset); + first_extruder_toolpaths.toolpaths.push_back(std::get<0>(outset)); - base_extrusion_outline_.push_back(outset.offset(line_width / 2)); + base_extrusion_outline_.push_back(std::vector({ PolygonUtils::makeCircle(middle_, std::get<1>(outset), CIRCLE_RESOLUTION) })); } } } @@ -126,32 +126,23 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t layer_height = mesh_group_settings.get("layer_height"); - const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); const double required_volume = scene.extruders[extruder_nr].settings_.get("prime_tower_min_volume") * 1000000000; const Ratio flow = scene.extruders[extruder_nr].settings_.get("prime_tower_flow"); + const coord_t semi_line_width = line_width / 2; double current_volume = 0; - coord_t current_outer_diameter = outer_radius; + coord_t current_outer_radius = outer_radius - semi_line_width; Shape toolpaths; - do + while (current_volume < required_volume && current_outer_radius >= semi_line_width) { - Shape shape = outer_poly_.offset(-(tower_radius - current_outer_diameter + line_width / 2)); - - if (! shape.empty()) - { - toolpaths.push_back(shape); - current_volume += static_cast(shape.length() * line_width * layer_height) * flow; - current_outer_diameter -= line_width; - } - else - { - // Don't continue. We won't ever reach the required volume because it doesn't fit. - break; - } - } while (current_volume < required_volume); + Polygon circle = PolygonUtils::makeCircle(middle_, current_outer_radius, CIRCLE_RESOLUTION); + toolpaths.push_back(circle); + current_volume += static_cast(circle.length() * line_width * layer_height) * flow; + current_outer_radius -= line_width; + } - return std::make_tuple(toolpaths, current_outer_diameter); + return { toolpaths, current_outer_radius + semi_line_width }; } Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 84f1e68160..30ec761df8 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1617,26 +1617,29 @@ Shape PolygonUtils::clipPolygonWithAABB(const Shape& src, const AABB& aabb) return out; } -Shape PolygonUtils::generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, coord_t line_width, const size_t circle_definition) +std::tuple + PolygonUtils::generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, coord_t line_width, const size_t circle_definition) { Shape outset; - coord_t radius = inner_radius + line_width / 2; + const coord_t semi_line_width = line_width / 2; + coord_t radius = inner_radius + semi_line_width; - while (radius + line_width / 2 <= outer_radius) + while (radius + semi_line_width <= outer_radius) { outset.push_back(makeCircle(center, radius, circle_definition)); radius += line_width; } - return outset; + return { outset, radius - semi_line_width }; } Shape PolygonUtils::generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition) { Shape inset; - coord_t radius = outer_radius - line_width / 2; + const coord_t semi_line_width = line_width / 2; + coord_t radius = outer_radius - semi_line_width; - while (radius - line_width / 2 >= line_width) + while (radius - semi_line_width >= line_width) { inset.push_back(makeCircle(center, radius, circle_definition)); radius -= line_width; From 06e31696d80daa181623c07abc208b984b452d57 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 14 Jun 2024 14:46:08 +0200 Subject: [PATCH 42/49] Change constants as define to class member --- include/PrimeTower/PrimeTower.h | 3 +++ src/PrimeTower/PrimeTower.cpp | 17 +++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 8f0d8f38e3..6c9de36d15 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -66,6 +66,9 @@ class PrimeTower //!< This is the approximate outline of the area filled at each layer, for layers having extra width for the base LayerVector base_occupied_outline_; + static constexpr size_t circle_definition_{ 32 }; // The number of vertices in each circle. + static constexpr size_t arc_definition_{ 4 }; // The number of segments in each arc of a wheel + public: /*! \brief Creates a prime tower instance that will determine where and how the prime tower gets printed. */ PrimeTower(); diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 025591328e..d9e01505a8 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -21,9 +21,6 @@ #include "raft.h" #include "sliceDataStorage.h" -#define CIRCLE_RESOLUTION 32 // The number of vertices in each circle. -#define ARC_RESOLUTION 4 // The number of segments in each arc of a wheel - namespace cura { @@ -43,7 +40,7 @@ PrimeTower::PrimeTower() const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); middle_ = Point2LL(x - tower_radius, y + tower_radius); - outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, CIRCLE_RESOLUTION)); + outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, circle_definition_)); post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, @@ -63,7 +60,7 @@ PrimeTower::PrimeTower() const double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); const coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); const coord_t total_radius = tower_radius + extra_radius; - base_occupied_outline_.emplace_back(std::vector({ PolygonUtils::makeCircle(middle_, total_radius, CIRCLE_RESOLUTION) }), total_radius); + base_occupied_outline_.emplace_back(std::vector({ PolygonUtils::makeCircle(middle_, total_radius, circle_definition_) }), total_radius); } } } @@ -94,10 +91,10 @@ void PrimeTower::generateBase() const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); std::tuple outset - = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, CIRCLE_RESOLUTION); + = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, circle_definition_); first_extruder_toolpaths.toolpaths.push_back(std::get<0>(outset)); - base_extrusion_outline_.push_back(std::vector({ PolygonUtils::makeCircle(middle_, std::get<1>(outset), CIRCLE_RESOLUTION) })); + base_extrusion_outline_.push_back(std::vector({ PolygonUtils::makeCircle(middle_, std::get<1>(outset), circle_definition_) })); } } } @@ -115,7 +112,7 @@ void PrimeTower::generateFirtLayerInset() const Scene& scene = Application::getInstance().current_slice_->scene; const size_t extruder_nr = last_extruder_toolpaths.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape pattern = PolygonUtils::generateCircularInset(middle_, last_extruder_toolpaths.inner_radius, line_width, CIRCLE_RESOLUTION); + Shape pattern = PolygonUtils::generateCircularInset(middle_, last_extruder_toolpaths.inner_radius, line_width, circle_definition_); last_extruder_toolpaths.toolpaths.push_back(pattern); } } @@ -136,7 +133,7 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru Shape toolpaths; while (current_volume < required_volume && current_outer_radius >= semi_line_width) { - Polygon circle = PolygonUtils::makeCircle(middle_, current_outer_radius, CIRCLE_RESOLUTION); + Polygon circle = PolygonUtils::makeCircle(middle_, current_outer_radius, circle_definition_); toolpaths.push_back(circle); current_volume += static_cast(circle.length() * line_width * layer_height) * flow; current_outer_radius -= line_width; @@ -168,7 +165,7 @@ Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord const size_t semi_nb_spokes = static_cast(std::ceil((std::numbers::pi * static_cast(annulus_outer_radius)) / max_bridging_distance)); - toolpaths.push_back(PolygonUtils::makeWheel(middle_, annulus_inner_radius, annulus_outer_radius, semi_nb_spokes, ARC_RESOLUTION)); + toolpaths.push_back(PolygonUtils::makeWheel(middle_, annulus_inner_radius, annulus_outer_radius, semi_nb_spokes, arc_definition_)); } } From 992d187171d32d96008af487c5f5efc780a0a642 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 14 Jun 2024 17:16:26 +0200 Subject: [PATCH 43/49] Fix wipe out start positions with base The previous implementation was not updated following the addition of the prime tower base. --- include/PrimeTower/PrimeTower.h | 16 +++--- include/geometry/LinesSet.h | 13 +++++ include/geometry/Shape.h | 7 +++ include/settings/types/Angle.h | 8 +-- include/utils/polygonUtils.h | 48 ++++------------- src/FffGcodeWriter.cpp | 2 +- src/PrimeTower/PrimeTower.cpp | 55 +++++++++---------- src/geometry/LinesSet.cpp | 28 ++++++---- src/geometry/Shape.cpp | 45 ++++++++++++++++ src/utils/polygonUtils.cpp | 96 ++++----------------------------- 10 files changed, 144 insertions(+), 174 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 6c9de36d15..b294c03ee0 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -31,7 +31,7 @@ class PrimeTower protected: struct OccupiedOutline { - Shape outline; + Polygon outline; coord_t outer_radius; }; @@ -49,8 +49,8 @@ class PrimeTower Point2LL post_wipe_point_; //!< Location to post-wipe the unused nozzle off on - std::vector prime_tower_start_locations_; //!< The differernt locations where to pre-wipe the active nozzle - const unsigned int number_of_prime_tower_start_locations_ = 21; //!< The required size of \ref PrimeTower::wipe_locations + static constexpr size_t number_of_prime_tower_start_locations_ = 21; //!< The required size of \ref PrimeTower::wipe_locations + static constexpr AngleRadians start_locations_step_ = (std::numbers::pi * 2.0) / number_of_prime_tower_start_locations_; /* * The map index is the layer number @@ -59,10 +59,10 @@ class PrimeTower */ std::map> toolpaths_; - Shape outer_poly_; //!< The outline of the prime tower, not including the base + OccupiedOutline outer_poly_; //!< The outline of the prime tower, not including the base //!< This is the exact outline of the extrusions lines of each layer, for layers having extra width for the base - LayerVector base_extrusion_outline_; + LayerVector base_extrusion_outline_; //!< This is the approximate outline of the area filled at each layer, for layers having extra width for the base LayerVector base_occupied_outline_; @@ -100,14 +100,14 @@ class PrimeTower * Use this method only if you need to get the exclusion area of the prime tower. Otherwise use getExtrusionOutline(). * This method exists because this approximate area can be calculated as soon as the prime tower is initialized. */ - const Shape& getOccupiedOutline(const LayerIndex& layer_nr) const; + const Polygon& getOccupiedOutline(const LayerIndex& layer_nr) const; /*! * Get the occupied outline of the prime tower at the first layer * * \note @sa getOccupiedOutline() */ - const Shape& getOccupiedGroundOutline() const; + const Polygon& getOccupiedGroundOutline() const; /*! * Get the extrusion outline of the prime tower at the given layer @@ -118,7 +118,7 @@ class PrimeTower * touching the prime tower. Otherwise use getExtrusionOutline(). This method will return the valid result only after * processExtrudersUse() has been called, which is "late" is the global slicing operation. */ - const Shape& getExtrusionOutline(const LayerIndex& layer_nr) const; + const Polygon& getExtrusionOutline(const LayerIndex& layer_nr) const; /*! * \brief Get the required priming for the given extruder at the given layer diff --git a/include/geometry/LinesSet.h b/include/geometry/LinesSet.h index 1e885cb89c..7834b821eb 100644 --- a/include/geometry/LinesSet.h +++ b/include/geometry/LinesSet.h @@ -65,6 +65,12 @@ class LinesSet { } + /*! \brief Constructor with a single existing line */ + explicit LinesSet(const LineType& line) + : lines_({ line }) + { + } + /*! * \brief Constructor that takes ownership of the data from the given set of lines * \warning This constructor is actually only defined for a LinesSet containing OpenPolyline @@ -261,6 +267,13 @@ class LinesSet */ void addPaths(ClipperLib::Clipper& clipper, ClipperLib::PolyType poly_typ) const; + /*! + * \brief Utility method to add a line to a ClipperLib::Clipper object + * \note This method needs to be public but you shouldn't need to use it from outside + */ + template + void addPath(ClipperLib::Clipper& clipper, const OtherLineLine& line, ClipperLib::PolyType poly_typ) const; + /*! * \brief Utility method to add all the lines to a ClipperLib::ClipperOffset object * \note This method needs to be public but you shouldn't need to use it from outside diff --git a/include/geometry/Shape.h b/include/geometry/Shape.h index aaa373f4e1..552325676f 100644 --- a/include/geometry/Shape.h +++ b/include/geometry/Shape.h @@ -43,6 +43,9 @@ class Shape : public LinesSet /*! \brief Constructor with an existing set of polygons */ Shape(const std::vector& polygons); + /*! \brief Constructor with a single existing polygon */ + explicit Shape(const Polygon& polygon); + /*! * \brief Constructor that takes ownership of the given list of points * \param explicitely_closed Specify whether the given points form an explicitely closed line @@ -66,8 +69,12 @@ class Shape : public LinesSet [[nodiscard]] Shape difference(const Shape& other) const; + [[nodiscard]] Shape difference(const Polygon& polygon) const; + [[nodiscard]] Shape unionPolygons(const Shape& other, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero) const; + [[nodiscard]] Shape unionPolygons(const Polygon& polygon, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero) const; + /*! * Union all polygons with each other (When polygons.add(polygon) has been called for overlapping polygons) */ diff --git a/include/settings/types/Angle.h b/include/settings/types/Angle.h index f2814ee3c8..20b6cc2405 100644 --- a/include/settings/types/Angle.h +++ b/include/settings/types/Angle.h @@ -105,17 +105,17 @@ class AngleRadians /* * \brief Default constructor setting the angle to 0. */ - AngleRadians() noexcept = default; + constexpr AngleRadians() noexcept = default; /*! * \brief Converts an angle from degrees into radians. */ - AngleRadians(const AngleDegrees& value); + constexpr AngleRadians(const AngleDegrees& value); /* * \brief Translate the double value in degrees to an AngleRadians instance. */ - AngleRadians(double value) + constexpr AngleRadians(double value) : value_(std::fmod(std::fmod(value, TAU) + TAU, TAU)) { } @@ -166,7 +166,7 @@ inline AngleDegrees::AngleDegrees(const AngleRadians& value) { } -inline AngleRadians::AngleRadians(const AngleDegrees& value) +constexpr inline AngleRadians::AngleRadians(const AngleDegrees& value) : value_(static_cast(value) * TAU / 360.0) { } diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index df17d37938..2048580eeb 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -94,34 +94,6 @@ class PolygonUtils public: static const std::function no_penalty_function; //!< Function always returning zero - /*! - * compute the length of a segment of a polygon - * - * if \p end == \p start then the full polygon is taken - * - * \warning assumes that start and end lie on the same polygon! - * - * \param start The start vertex of the segment - * \param end the end vertex of the segment - * \return the total length of all the line segments in between the two vertices. - */ - static int64_t segmentLength(PolygonsPointIndex start, PolygonsPointIndex end); - - /*! - * Generate evenly spread out dots along a segment of a polygon - * - * Start at a distance from \p start and end at a distance from \p end, - * unless \p end == \p start; then that point is in the result - * - * \warning Assumes that start and end lie on the same polygon! - * - * \param start The start vertex of the segment - * \param end the end vertex of the segment - * \param n_dots number of dots to spread out - * \param result Where to store the generated points - */ - static void spreadDots(PolygonsPointIndex start, PolygonsPointIndex end, unsigned int n_dots, std::vector& result); - /*! * Generate a grid of dots inside of the area of the \p polygons. */ @@ -160,14 +132,6 @@ class PolygonUtils */ static Point2LL getBoundaryPointWithOffset(const Polyline& poly, unsigned int point_idx, int64_t offset); - /*! - * Move a point away from the boundary by looking at the boundary normal of the nearest vert. - * - * \param point_on_boundary The object holding the point on the boundary along with the information of which line segment the point is on. - * \param offset The distance the point has to be moved inward from the polygon. - */ - static Point2LL moveInsideDiagonally(ClosestPointPolygon point_on_boundary, int64_t inset); - /*! * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. * Given a \p distance more than zero, the point will end up inside, and conversely outside. @@ -643,7 +607,17 @@ class PolygonUtils * \param steps The numbers of segments (definition) of the generated circle. * \return A new Polygon containing the circle. */ - static Polygon makeCircle(const Point2LL mid, const coord_t radius, const size_t steps); + static Polygon makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps); + + /*! + * Create a point of a circle. + * + * \param mid The center of the circle. + * \param radius The radius of the circle. + * \param angle The point angular position + * \return The coordinates of the point on the circle. + */ + static Point2LL makeCirclePoint(const Point2LL& mid, const coord_t radius, const AngleRadians& angle); /*! * Create a "wheel" shape. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 413143f4e7..7d40ec06ad 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -646,7 +646,7 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) raft_outline_paths.emplace_back(ParameterizedRaftPath{ line_spacing, storage.raft_base_outline }); if (storage.prime_tower_) { - const Shape& raft_outline_prime_tower = storage.prime_tower_->getExtrusionOutline(layer_nr); + const Shape raft_outline_prime_tower = Shape(storage.prime_tower_->getExtrusionOutline(layer_nr)); if (line_spacing_prime_tower == line_spacing) { // Base layer is shared with prime tower base diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index d9e01505a8..89da6babdc 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -40,16 +40,9 @@ PrimeTower::PrimeTower() const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); middle_ = Point2LL(x - tower_radius, y + tower_radius); - outer_poly_.push_back(PolygonUtils::makeCircle(middle_, tower_radius, circle_definition_)); + outer_poly_ = { PolygonUtils::makeCircle(middle_, tower_radius, circle_definition_), tower_radius }; post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); - // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline, - // so use the same start and end segments for this. - PolygonsPointIndex segment_start = PolygonsPointIndex(&outer_poly_, 0, 0); - PolygonsPointIndex segment_end = segment_start; - - PolygonUtils::spreadDots(segment_start, segment_end, number_of_prime_tower_start_locations_, prime_tower_start_locations_); - // Generate the base outline if (base_enabled && base_extra_radius > 0 && base_height > 0) { @@ -60,7 +53,7 @@ PrimeTower::PrimeTower() const double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); const coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); const coord_t total_radius = tower_radius + extra_radius; - base_occupied_outline_.emplace_back(std::vector({ PolygonUtils::makeCircle(middle_, total_radius, circle_definition_) }), total_radius); + base_occupied_outline_.emplace_back(PolygonUtils::makeCircle(middle_, total_radius, circle_definition_), total_radius); } } } @@ -94,7 +87,7 @@ void PrimeTower::generateBase() = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, circle_definition_); first_extruder_toolpaths.toolpaths.push_back(std::get<0>(outset)); - base_extrusion_outline_.push_back(std::vector({ PolygonUtils::makeCircle(middle_, std::get<1>(outset), circle_definition_) })); + base_extrusion_outline_.push_back(PolygonUtils::makeCircle(middle_, std::get<1>(outset), circle_definition_)); } } } @@ -252,7 +245,7 @@ void PrimeTower::addToGcode( } } -const Shape& PrimeTower::getOccupiedOutline(const LayerIndex& layer_nr) const +const Polygon& PrimeTower::getOccupiedOutline(const LayerIndex& layer_nr) const { auto iterator = base_occupied_outline_.iterator_at(layer_nr); if (iterator != base_occupied_outline_.end()) @@ -261,11 +254,11 @@ const Shape& PrimeTower::getOccupiedOutline(const LayerIndex& layer_nr) const } else { - return outer_poly_; + return outer_poly_.outline; } } -const Shape& PrimeTower::getOccupiedGroundOutline() const +const Polygon& PrimeTower::getOccupiedGroundOutline() const { if (! base_extrusion_outline_.empty()) { @@ -273,11 +266,11 @@ const Shape& PrimeTower::getOccupiedGroundOutline() const } else { - return outer_poly_; + return outer_poly_.outline; } } -const Shape& PrimeTower::getExtrusionOutline(const LayerIndex& layer_nr) const +const Polygon& PrimeTower::getExtrusionOutline(const LayerIndex& layer_nr) const { auto iterator = base_extrusion_outline_.iterator_at(layer_nr); if (iterator != base_extrusion_outline_.end()) @@ -286,7 +279,7 @@ const Shape& PrimeTower::getExtrusionOutline(const LayerIndex& layer_nr) const } else { - return outer_poly_; + return outer_poly_.outline; } } @@ -294,11 +287,11 @@ void PrimeTower::subtractFromSupport(SliceDataStorage& storage) { for (size_t layer = 0; static_cast(layer) <= storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++) { - const Shape& outside_polygon = getOccupiedOutline(layer).getOutsidePolygons(); + const Polygon& outside_polygon = getOccupiedOutline(layer); AABB outside_polygon_boundary_box(outside_polygon); SupportLayer& support_layer = storage.support.supportLayers[layer]; // take the differences of the support infill parts and the prime tower area - support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box); + support_layer.excludeAreasFromSupportInfillAreas(Shape(outside_polygon), outside_polygon_boundary_box); } } @@ -349,24 +342,32 @@ bool PrimeTower::extruderRequiresPrime(const std::vector& extruder_is_used void PrimeTower::gotoStartLocation(LayerPlan& gcode_layer, const size_t extruder_nr) const { - // Layer number may be negative, make it positive (or null) to apply modulo operator LayerIndex layer_nr = gcode_layer.getLayerNr(); if (layer_nr != -Raft::getTotalExtraLayers()) { + coord_t wipe_radius; + auto iterator = base_occupied_outline_.iterator_at(gcode_layer.getLayerNr()); + if (iterator != base_occupied_outline_.end()) + { + wipe_radius = iterator->outer_radius; + } + else + { + wipe_radius = outer_poly_.outer_radius; + } + + const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; + wipe_radius += train.settings_.get("machine_nozzle_size") * 2; + + // Layer number may be negative, make it positive (or null) before using modulo operator while (layer_nr < 0) { layer_nr += number_of_prime_tower_start_locations_; } size_t current_start_location_idx = ((extruder_nr + 1) * static_cast(layer_nr)) % number_of_prime_tower_start_locations_; - - const ClosestPointPolygon wipe_location = prime_tower_start_locations_[current_start_location_idx]; - const ExtruderTrain& train = Application::getInstance().current_slice_->scene.extruders[extruder_nr]; - const coord_t inward_dist = train.settings_.get("machine_nozzle_size") * 3 / 2; - const coord_t start_dist = train.settings_.get("machine_nozzle_size") * 2; - const Point2LL prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist); - const Point2LL outward_dir = wipe_location.location_ - prime_end; - const Point2LL prime_start = wipe_location.location_ + normal(outward_dir, start_dist); + const AngleRadians angle = start_locations_step_ * current_start_location_idx; + const Point2LL prime_start = PolygonUtils::makeCirclePoint(middle_, wipe_radius, angle); gcode_layer.addTravel(prime_start); } diff --git a/src/geometry/LinesSet.cpp b/src/geometry/LinesSet.cpp index 039931812a..ccfd7259a6 100644 --- a/src/geometry/LinesSet.cpp +++ b/src/geometry/LinesSet.cpp @@ -276,21 +276,28 @@ void LinesSet::removeDegenerateVerts() } } +template +template +void LinesSet::addPath(ClipperLib::Clipper& clipper, const OtherLineLine& line, ClipperLib::PolyType poly_typ) const +{ + // In this context, the "Closed" argument means "Is a surface" so it should be only + // true for actual filled polygons. Closed polylines are to be treated as lines here. + if constexpr (std::is_same::value) + { + clipper.AddPath(line.getPoints(), poly_typ, true); + } + else + { + clipper.AddPath(line.getPoints(), poly_typ, false); + } +} + template void LinesSet::addPaths(ClipperLib::Clipper& clipper, ClipperLib::PolyType poly_typ) const { for (const LineType& line : getLines()) { - // In this context, the "Closed" argument means "Is a surface" so it should be only - // true for actual filled polygons. Closed polylines are to be treated as lines here. - if constexpr (std::is_same::value) - { - clipper.AddPath(line.getPoints(), poly_typ, true); - } - else - { - clipper.AddPath(line.getPoints(), poly_typ, false); - } + addPath(clipper, line, poly_typ); } } @@ -345,5 +352,6 @@ template void LinesSet::addPaths(ClipperLib::ClipperOffset& clipper, Cl template void LinesSet::push_back(const Polygon& line, CheckNonEmptyParam checkNonEmpty); template void LinesSet::push_back(Polygon&& line, CheckNonEmptyParam checkNonEmpty); template void LinesSet::push_back(LinesSet&& lines_set); +template void LinesSet::addPath(ClipperLib::Clipper& clipper, const Polygon& line, ClipperLib::PolyType poly_typ) const; } // namespace cura diff --git a/src/geometry/Shape.cpp b/src/geometry/Shape.cpp index 8156bb5fa2..ed5dffbbf0 100644 --- a/src/geometry/Shape.cpp +++ b/src/geometry/Shape.cpp @@ -47,6 +47,11 @@ Shape::Shape(const std::vector& polygons) { } +Shape::Shape(const Polygon& polygon) + : LinesSet(polygon) +{ +} + void Shape::emplace_back(ClipperLib::Paths&& paths, bool explicitely_closed) { reserve(size() + paths.size()); @@ -152,6 +157,24 @@ Shape Shape::difference(const Shape& other) const return Shape(std::move(ret)); } +Shape Shape::difference(const Polygon& other) const +{ + if (empty()) + { + return {}; + } + if (other.empty()) + { + return *this; + } + ClipperLib::Paths ret; + ClipperLib::Clipper clipper(clipper_init); + addPaths(clipper, ClipperLib::ptSubject); + addPath(clipper, other, ClipperLib::ptClip); + clipper.Execute(ClipperLib::ctDifference, ret); + return Shape(std::move(ret)); +} + Shape Shape::unionPolygons(const Shape& other, ClipperLib::PolyFillType fill_type) const { if (empty() && other.empty()) @@ -174,6 +197,28 @@ Shape Shape::unionPolygons(const Shape& other, ClipperLib::PolyFillType fill_typ return Shape{ std::move(ret) }; } +Shape Shape::unionPolygons(const Polygon& polygon, ClipperLib::PolyFillType fill_type) const +{ + if (empty() && polygon.empty()) + { + return {}; + } + if (empty()) + { + return Shape(polygon); + } + if (polygon.empty() && size() <= 1) + { + return *this; + } + ClipperLib::Paths ret; + ClipperLib::Clipper clipper(clipper_init); + addPaths(clipper, ClipperLib::ptSubject); + addPath(clipper, polygon, ClipperLib::ptSubject); + clipper.Execute(ClipperLib::ctUnion, ret, fill_type, fill_type); + return Shape{ std::move(ret) }; +} + Shape Shape::unionPolygons() const { return unionPolygons(Shape()); diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 30ec761df8..b7aad13923 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -32,69 +32,6 @@ const std::function PolygonUtils::no_penalty_function = [](Point2 return 0; }; -int64_t PolygonUtils::segmentLength(PolygonsPointIndex start, PolygonsPointIndex end) -{ - assert(start.poly_idx_ == end.poly_idx_); - int64_t segment_length = 0; - Point2LL prev_vert = start.p(); - const Polygon& poly = start.getPolygon(); - for (unsigned int point_idx = 1; point_idx <= poly.size(); point_idx++) - { - unsigned int vert_idx = (start.point_idx_ + point_idx) % poly.size(); - Point2LL vert = poly[vert_idx]; - segment_length += vSize(vert - prev_vert); - - if (vert_idx == end.point_idx_) - { // break at the end of the loop, so that [end] and [start] may be the same - return segment_length; - } - prev_vert = vert; - } - assert(false && "The segment end should have been encountered!"); - return segment_length; -} - -void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end, unsigned int n_dots, std::vector& result) -{ - assert(start.poly_idx_ == end.poly_idx_); - int64_t segment_length = segmentLength(start, end); - - const Polygon& poly = start.getPolygon(); - unsigned int n_dots_in_between = n_dots; - if (start == end) - { - result.emplace_back(start.p(), start.point_idx_, &poly); - n_dots_in_between--; // generate one less below, because we already pushed a point to the result - } - - int64_t wipe_point_dist = segment_length / (n_dots_in_between + 1); // distance between two wipe points; keep a distance at both sides of the segment - - int64_t dist_past_vert_to_insert_point = wipe_point_dist; - unsigned int n_points_generated = 0; - PolygonsPointIndex vert = start; - while (true) - { - Point2LL p0 = vert.p(); - Point2LL p1 = vert.next().p(); - Point2LL p0p1 = p1 - p0; - int64_t p0p1_length = vSize(p0p1); - - for (; dist_past_vert_to_insert_point < p0p1_length && n_points_generated < n_dots_in_between; dist_past_vert_to_insert_point += wipe_point_dist) - { - result.emplace_back(p0 + normal(p0p1, dist_past_vert_to_insert_point), vert.point_idx_, &poly); - n_points_generated++; - } - dist_past_vert_to_insert_point -= p0p1_length; - - ++vert; - if (vert == end) - { // break at end of loop to allow for [start] and [end] being the same, meaning the full polygon - break; - } - } - assert(result.size() == n_dots && "we didn't generate as many wipe locations as we asked for."); -} - std::vector PolygonUtils::spreadDotsArea(const Shape& polygons, coord_t grid_size) { return spreadDotsArea(polygons, Point2LL(grid_size, grid_size)); @@ -225,26 +162,6 @@ Point2LL PolygonUtils::getBoundaryPointWithOffset(const Polyline& poly, unsigned return poly[point_idx] + normal(getVertexInwardNormal(poly, point_idx), -offset); } -Point2LL PolygonUtils::moveInsideDiagonally(ClosestPointPolygon point_on_boundary, int64_t inset) -{ - if (! point_on_boundary.isValid()) - { - return no_point; - } - - const Polygon& poly = *point_on_boundary.poly_; - Point2LL p0 = poly[point_on_boundary.point_idx_]; - Point2LL p1 = poly[(point_on_boundary.point_idx_ + 1) % poly.size()]; - if (vSize2(p0 - point_on_boundary.location_) < vSize2(p1 - point_on_boundary.location_)) - { - return point_on_boundary.location_ + normal(getVertexInwardNormal(poly, point_on_boundary.point_idx_), inset); - } - else - { - return point_on_boundary.location_ + normal(getVertexInwardNormal(poly, (point_on_boundary.point_idx_ + 1) % poly.size()), inset); - } -} - unsigned int PolygonUtils::moveOutside(const Shape& polygons, Point2LL& from, int distance, int64_t maxDist2) { return moveInside(polygons, from, -distance, maxDist2); @@ -1386,18 +1303,23 @@ double PolygonUtils::relativeHammingDistance(const Shape& poly_a, const Shape& p return hamming_distance / total_area; } -Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const size_t steps) +Polygon PolygonUtils::makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps) { Polygon circle; - const double step_angle = (std::numbers::pi * 2) / static_cast(steps); + const AngleRadians step_angle = (std::numbers::pi * 2) / static_cast(steps); for (size_t step = 0; step < steps; ++step) { - const double angle = static_cast(step) * step_angle; - circle.emplace_back(mid + Point2LL(std::llrint(static_cast(radius) * cos(angle)), std::llrint(static_cast(radius) * sin(angle)))); + const AngleRadians angle = static_cast(step) * step_angle; + circle.emplace_back(makeCirclePoint(mid, radius, angle)); } return circle; } +Point2LL PolygonUtils::makeCirclePoint(const Point2LL& mid, const coord_t radius, const AngleRadians& angle) +{ + return mid + Point2LL(std::llrint(static_cast(radius) * cos(angle)), std::llrint(static_cast(radius) * sin(angle))); +} + Polygon PolygonUtils::makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution) { Polygon wheel; From eced18ebf65f95b94d86554836639d8c9bd1b7d3 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 17 Jun 2024 10:09:47 +0200 Subject: [PATCH 44/49] Use the most explicit geometric types in PrimeTower --- include/PrimeTower/PrimeTower.h | 7 +-- include/geometry/ClosedPolyline.h | 9 ++-- include/utils/polygonUtils.h | 30 ++++++++----- src/PrimeTower/PrimeTower.cpp | 24 +++++------ src/PrimeTower/PrimeTowerInterleaved.cpp | 4 +- src/sliceDataStorage.cpp | 5 +-- src/utils/polygonUtils.cpp | 55 ++++++++++-------------- 7 files changed, 68 insertions(+), 66 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index b294c03ee0..0c8343d48e 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -8,6 +8,7 @@ #include #include "ExtruderUse.h" +#include "geometry/ClosedLinesSet.h" #include "geometry/Polygon.h" #include "settings/EnumSettings.h" #include "settings/types/LayerIndex.h" @@ -38,7 +39,7 @@ class PrimeTower struct ExtruderToolPaths { size_t extruder_nr; - Shape toolpaths; + ClosedLinesSet toolpaths; coord_t outer_radius; coord_t inner_radius; }; @@ -179,7 +180,7 @@ class PrimeTower * \param outer_radius The radius of the starting outer circle * \return A tuple containing the newly generated toolpaths, and the inner radius of the newly generated annulus */ - std::tuple generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius); + std::tuple generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius); /*! * \brief Generate support toolpaths using the wheel pattern applied on an annulus @@ -188,7 +189,7 @@ class PrimeTower * \param inner_radius The annulis inner radius * \return */ - Shape generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius); + ClosedLinesSet generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius); /*! * \brief Calculates whether an extruder requires priming at a specific layer diff --git a/include/geometry/ClosedPolyline.h b/include/geometry/ClosedPolyline.h index 8f4bc3e753..979ed6f29c 100644 --- a/include/geometry/ClosedPolyline.h +++ b/include/geometry/ClosedPolyline.h @@ -32,14 +32,17 @@ class ClosedPolyline : public Polyline bool explicitely_closed_{ false }; public: + /*! + * \brief Builds an empty closed polyline + * \warning By default, the line is tagged as non explicitely closed. We need this default + * constructor in various places, but be careful that the interpretation of the points + * added later will depend on this. + */ ClosedPolyline() = default; /*! * \brief Builds an empty closed polyline * \param explicitely_closed Indicates whether the line will be explicitely closed - * \warning By default, the line is tagged as explicitely closed. We need this default - * constructor in various places, but be careful that the interpretation of the points - * added later will depend on this. */ explicit ClosedPolyline(const bool explicitely_closed) : explicitely_closed_{ explicitely_closed } diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 2048580eeb..43d96a5c5d 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -13,6 +13,7 @@ #include "PolygonsPointIndex.h" #include "SparseLineGrid.h" #include "SparsePointGridInclusive.h" +#include "geometry/ClosedLinesSet.h" #include "geometry/Polygon.h" namespace cura @@ -599,15 +600,24 @@ class PolygonUtils static double relativeHammingDistance(const Shape& poly_a, const Shape& poly_b); /*! - * Create an approximation of a circle. + * Creates a regular polygon that is supposed to approximate a disc. + * + * \param mid The center of the disc. + * \param radius The radius of the disc. + * \param steps The numbers of segments (definition) of the generated disc. + * \return A new Polygon containing the disc. + */ + static Polygon makeDisc(const Point2LL& mid, const coord_t radius, const size_t steps); + + /*! + * Creates a closed polyline that is supposed to approximate a circle. * - * This creates a regular polygon that is supposed to approximate a circle. * \param mid The center of the circle. * \param radius The radius of the circle. * \param steps The numbers of segments (definition) of the generated circle. * \return A new Polygon containing the circle. */ - static Polygon makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps); + static ClosedPolyline makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps); /*! * Create a point of a circle. @@ -620,17 +630,16 @@ class PolygonUtils static Point2LL makeCirclePoint(const Point2LL& mid, const coord_t radius, const AngleRadians& angle); /*! - * Create a "wheel" shape. + * This creates a polyline which represents the shape of a wheel, which is kind of a "circular zigzag" pattern. * - * This creates a polygon which represents the shape of a wheel. * \param mid The center of the circle. * \param inner_radius The radius of the wheel inner circle. * \param outer_radius The radius of the wheel outer circle. * \param semi_nb_spokes The semi number of spokes in the wheel. There will actually be N*2 spokes. - * \param arc_angle_resolution The number of segment on each arc. - * \return A new Polygon containing the circle. + * \param arc_angle_resolution The number of segments on each arc. + * \return A new Polyline containing the circle. */ - static Polygon makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution); + static ClosedPolyline makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution); /*! * Connect all polygons to their holes using zero widths hole channels, so that the polygons and their outlines are connected together @@ -641,7 +650,6 @@ class PolygonUtils static Shape unionManySmall(const Shape& polygon); - /*! * Intersects a polygon with an AABB. * \param src The polygon that has to be intersected with an AABB @@ -660,7 +668,7 @@ class PolygonUtils * \param circle_definition The definition (number of segments) of the generated circles * \return The generated outset circles, and the outer radius or the shape */ - static std::tuple + static std::tuple generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); /*! @@ -672,7 +680,7 @@ class PolygonUtils * \param circle_definition The definition (number of segments) of the generated circles * \return The generated inset circles */ - static Shape generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); + static ClosedLinesSet generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition); private: /*! diff --git a/src/PrimeTower/PrimeTower.cpp b/src/PrimeTower/PrimeTower.cpp index 89da6babdc..accfeaf374 100644 --- a/src/PrimeTower/PrimeTower.cpp +++ b/src/PrimeTower/PrimeTower.cpp @@ -40,7 +40,7 @@ PrimeTower::PrimeTower() const double base_curve_magnitude = mesh_group_settings.get("prime_tower_base_curve_magnitude"); middle_ = Point2LL(x - tower_radius, y + tower_radius); - outer_poly_ = { PolygonUtils::makeCircle(middle_, tower_radius, circle_definition_), tower_radius }; + outer_poly_ = { PolygonUtils::makeDisc(middle_, tower_radius, circle_definition_), tower_radius }; post_wipe_point_ = Point2LL(x - tower_radius, y + tower_radius); // Generate the base outline @@ -53,7 +53,7 @@ PrimeTower::PrimeTower() const double brim_radius_factor = std::pow((1.0 - static_cast(z) / static_cast(base_height)), base_curve_magnitude); const coord_t extra_radius = std::llrint(static_cast(base_extra_radius) * brim_radius_factor); const coord_t total_radius = tower_radius + extra_radius; - base_occupied_outline_.emplace_back(PolygonUtils::makeCircle(middle_, total_radius, circle_definition_), total_radius); + base_occupied_outline_.emplace_back(PolygonUtils::makeDisc(middle_, total_radius, circle_definition_), total_radius); } } } @@ -83,11 +83,11 @@ void PrimeTower::generateBase() const size_t extruder_nr = first_extruder_toolpaths.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - std::tuple outset + std::tuple outset = PolygonUtils::generateCirculatOutset(middle_, first_extruder_toolpaths.outer_radius, base_ouline_at_this_layer.outer_radius, line_width, circle_definition_); first_extruder_toolpaths.toolpaths.push_back(std::get<0>(outset)); - base_extrusion_outline_.push_back(PolygonUtils::makeCircle(middle_, std::get<1>(outset), circle_definition_)); + base_extrusion_outline_.push_back(PolygonUtils::makeDisc(middle_, std::get<1>(outset), circle_definition_)); } } } @@ -105,13 +105,13 @@ void PrimeTower::generateFirtLayerInset() const Scene& scene = Application::getInstance().current_slice_->scene; const size_t extruder_nr = last_extruder_toolpaths.extruder_nr; const coord_t line_width = scene.extruders[extruder_nr].settings_.get("prime_tower_line_width"); - Shape pattern = PolygonUtils::generateCircularInset(middle_, last_extruder_toolpaths.inner_radius, line_width, circle_definition_); + ClosedLinesSet pattern = PolygonUtils::generateCircularInset(middle_, last_extruder_toolpaths.inner_radius, line_width, circle_definition_); last_extruder_toolpaths.toolpaths.push_back(pattern); } } } -std::tuple PrimeTower::generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius) +std::tuple PrimeTower::generatePrimeToolpaths(const size_t extruder_nr, const coord_t outer_radius) { const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; @@ -123,10 +123,10 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru double current_volume = 0; coord_t current_outer_radius = outer_radius - semi_line_width; - Shape toolpaths; + ClosedLinesSet toolpaths; while (current_volume < required_volume && current_outer_radius >= semi_line_width) { - Polygon circle = PolygonUtils::makeCircle(middle_, current_outer_radius, circle_definition_); + ClosedPolyline circle = PolygonUtils::makeCircle(middle_, current_outer_radius, circle_definition_); toolpaths.push_back(circle); current_volume += static_cast(circle.length() * line_width * layer_height) * flow; current_outer_radius -= line_width; @@ -135,7 +135,7 @@ std::tuple PrimeTower::generatePrimeToolpaths(const size_t extru return { toolpaths, current_outer_radius + semi_line_width }; } -Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) +ClosedLinesSet PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord_t outer_radius, const coord_t inner_radius) { const Scene& scene = Application::getInstance().current_slice_->scene; const double max_bridging_distance = static_cast(scene.extruders[extruder_nr].settings_.get("prime_tower_max_bridging_distance")); @@ -143,7 +143,7 @@ Shape PrimeTower::generateSupportToolpaths(const size_t extruder_nr, const coord const coord_t radius_delta = outer_radius - inner_radius; const coord_t semi_line_width = line_width / 2; - Shape toolpaths; + ClosedLinesSet toolpaths; // Split annuli according to max bridging distance const coord_t nb_annuli = static_cast(std::ceil(static_cast(radius_delta) / max_bridging_distance)); @@ -205,7 +205,7 @@ void PrimeTower::addToGcode( return; } - const Shape* toolpaths = nullptr; + const ClosedLinesSet* toolpaths = nullptr; auto iterator_layer = toolpaths_.find(layer_nr); if (iterator_layer != toolpaths_.end()) { @@ -228,7 +228,7 @@ void PrimeTower::addToGcode( gotoStartLocation(gcode_layer, new_extruder_nr); const GCodePathConfig& config = gcode_layer.configs_storage_.prime_tower_config_per_extruder[new_extruder_nr]; - gcode_layer.addPolygonsByOptimizer(*toolpaths, config); + gcode_layer.addLinesByOptimizer(*toolpaths, config, SpaceFillType::PolyLines); } gcode_layer.setPrimeTowerIsPlanned(new_extruder_nr); diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 3742c37151..5973cca4b0 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -78,11 +78,11 @@ std::map> PrimeTowerInter { if (toolpaths_at_layer.empty()) { - toolpaths_at_layer.emplace_back(last_extruder_support, Shape(), outer_radius, support_radius); + toolpaths_at_layer.emplace_back(last_extruder_support, ClosedLinesSet(), outer_radius, support_radius); } ExtruderToolPaths& last_extruder_toolpaths = toolpaths_at_layer.back(); - Shape support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, outer_radius, support_radius); + ClosedLinesSet support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, outer_radius, support_radius); last_extruder_toolpaths.toolpaths.push_back(support_toolpaths); last_extruder_toolpaths.inner_radius = support_radius; } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index a419f75680..e5ba129b7f 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -638,9 +638,8 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const } Point2LL translation(extruder_settings.get("machine_nozzle_offset_x"), extruder_settings.get("machine_nozzle_offset_y")); prime_pos -= translation; - Shape prime_polygons; - prime_polygons.emplace_back(PolygonUtils::makeCircle(prime_pos, prime_clearance, 64)); - disallowed_areas = disallowed_areas.unionPolygons(prime_polygons); + Polygon prime_polygon = PolygonUtils::makeDisc(prime_pos, prime_clearance, 64); + disallowed_areas = disallowed_areas.unionPolygons(prime_polygon); } Shape disallowed_all_extruders; diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index b7aad13923..5ca80ac87c 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1303,16 +1303,21 @@ double PolygonUtils::relativeHammingDistance(const Shape& poly_a, const Shape& p return hamming_distance / total_area; } -Polygon PolygonUtils::makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps) +Polygon PolygonUtils::makeDisc(const Point2LL& mid, const coord_t radius, const size_t steps) { - Polygon circle; + Polygon disc; const AngleRadians step_angle = (std::numbers::pi * 2) / static_cast(steps); for (size_t step = 0; step < steps; ++step) { const AngleRadians angle = static_cast(step) * step_angle; - circle.emplace_back(makeCirclePoint(mid, radius, angle)); + disc.push_back(makeCirclePoint(mid, radius, angle)); } - return circle; + return disc; +} + +ClosedPolyline PolygonUtils::makeCircle(const Point2LL& mid, const coord_t radius, const size_t steps) +{ + return makeDisc(mid, radius, steps); } Point2LL PolygonUtils::makeCirclePoint(const Point2LL& mid, const coord_t radius, const AngleRadians& angle) @@ -1320,43 +1325,29 @@ Point2LL PolygonUtils::makeCirclePoint(const Point2LL& mid, const coord_t radius return mid + Point2LL(std::llrint(static_cast(radius) * cos(angle)), std::llrint(static_cast(radius) * sin(angle))); } -Polygon PolygonUtils::makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution) +ClosedPolyline PolygonUtils::makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution) { - Polygon wheel; - - std::vector> target_radii; - target_radii.push_back({ inner_radius, outer_radius }); - target_radii.push_back({ outer_radius, inner_radius }); + ClosedPolyline wheel; const size_t nb_spokes = semi_nb_spokes * 2; - const float angle_step = TAU / nb_spokes; - const float arc_step = angle_step / arc_angle_resolution; - float angle = 0.0; + const double spoke_angle_step = TAU / static_cast(nb_spokes); + const double arc_angle_step = spoke_angle_step / static_cast(arc_angle_resolution); + for (size_t spoke = 0; spoke < nb_spokes; ++spoke) { - const std::pair& radii = target_radii.at(spoke % 2); + const double spoke_angle = static_cast(spoke) * spoke_angle_step; + const coord_t radius = spoke % 2 == 0 ? inner_radius : outer_radius; - angle = spoke * angle_step; - float cos_angle = cos(angle); - float sin_angle = sin(angle); - wheel.emplace_back(mid + Point2LL(radii.first * cos_angle, radii.first * sin_angle)); - - for (size_t arc_part = 0; arc_part < arc_angle_resolution; ++arc_part) + for (size_t arc_part = 0; arc_part <= arc_angle_resolution; ++arc_part) { - wheel.emplace_back(mid + Point2LL(radii.second * cos_angle, radii.second * sin_angle)); - if (arc_part < arc_angle_resolution - 1) - { - angle += arc_step; - cos_angle = cos(angle); - sin_angle = sin(angle); - } + const double angle = spoke_angle + static_cast(arc_part) * arc_angle_step; + wheel.push_back(makeCirclePoint(mid, radius, angle)); } } return wheel; } - Shape PolygonUtils::connect(const Shape& input) { Shape ret; @@ -1539,10 +1530,10 @@ Shape PolygonUtils::clipPolygonWithAABB(const Shape& src, const AABB& aabb) return out; } -std::tuple +std::tuple PolygonUtils::generateCirculatOutset(const Point2LL& center, const coord_t inner_radius, const coord_t outer_radius, coord_t line_width, const size_t circle_definition) { - Shape outset; + ClosedLinesSet outset; const coord_t semi_line_width = line_width / 2; coord_t radius = inner_radius + semi_line_width; @@ -1555,9 +1546,9 @@ std::tuple return { outset, radius - semi_line_width }; } -Shape PolygonUtils::generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition) +ClosedLinesSet PolygonUtils::generateCircularInset(const Point2LL& center, const coord_t outer_radius, const coord_t line_width, const size_t circle_definition) { - Shape inset; + ClosedLinesSet inset; const coord_t semi_line_width = line_width / 2; coord_t radius = outer_radius - semi_line_width; From 5325ce01ffedfe37b6a548a2eca87365d52c507f Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 18 Jun 2024 09:14:12 +0200 Subject: [PATCH 45/49] Remove obsolete unit test --- tests/utils/PolygonUtilsTest.cpp | 39 -------------------------------- 1 file changed, 39 deletions(-) diff --git a/tests/utils/PolygonUtilsTest.cpp b/tests/utils/PolygonUtilsTest.cpp index cc1907390e..dd13f1e5ad 100644 --- a/tests/utils/PolygonUtilsTest.cpp +++ b/tests/utils/PolygonUtilsTest.cpp @@ -339,45 +339,6 @@ class PolygonUtilsTest : public testing::Test } }; -TEST_F(PolygonUtilsTest, spreadDotsSegment) -{ - std::vector supposed; - supposed.emplace_back(Point2LL(50, 0), 0, &test_squares[0], 0); - supposed.emplace_back(Point2LL(100, 0), 1, &test_squares[0], 0); - supposed.emplace_back(Point2LL(100, 50), 1, &test_squares[0], 0); - - std::vector result; - PolygonUtils::spreadDots(PolygonsPointIndex(&test_squares, 0, 0), PolygonsPointIndex(&test_squares, 0, 2), 3, result); - - ASSERT_EQ(result.size(), supposed.size()); - for (size_t point_idx = 0; point_idx < result.size(); point_idx++) - { - EXPECT_EQ(result[point_idx].p(), supposed[point_idx].p()); - } -} - -TEST_F(PolygonUtilsTest, spreadDotsFull) -{ - std::vector supposed; - supposed.emplace_back(Point2LL(0, 0), 0, &test_squares[0], 0); - supposed.emplace_back(Point2LL(50, 0), 0, &test_squares[0], 0); - supposed.emplace_back(Point2LL(100, 0), 1, &test_squares[0], 0); - supposed.emplace_back(Point2LL(100, 50), 1, &test_squares[0], 0); - supposed.emplace_back(Point2LL(100, 100), 2, &test_squares[0], 0); - supposed.emplace_back(Point2LL(50, 100), 2, &test_squares[0], 0); - supposed.emplace_back(Point2LL(0, 100), 3, &test_squares[0], 0); - supposed.emplace_back(Point2LL(0, 50), 3, &test_squares[0], 0); - - std::vector result; - PolygonUtils::spreadDots(PolygonsPointIndex(&test_squares, 0, 0), PolygonsPointIndex(&test_squares, 0, 0), 8, result); - - ASSERT_EQ(result.size(), supposed.size()); - for (size_t point_idx = 0; point_idx < result.size(); point_idx++) - { - EXPECT_EQ(result[point_idx].p(), supposed[point_idx].p()); - } -} - struct GetNextParallelIntersectionParameters { std::optional predicted; From 2b817f7a6e987765f5bdd9ef7a83094d7d954aa0 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 18 Jun 2024 09:14:29 +0200 Subject: [PATCH 46/49] Add documentation --- include/utils/LayerVector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index 40afd423b8..d03aa9b50d 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -14,7 +14,7 @@ namespace cura /*! * \brief The LayerVector class mimics a std::vector but with the index being a LayerIndex, thus it can have negative - * values (for raft layers). + * values (for raft layers). it also ensure that the first element in the list is always on the very first layer. * \note When calling the init() method, LayerVector will call Raft::getTotalExtraLayers() so it requires the settings * to be setup. This is the reason why this is not done in the constructor, and has to be called manually. * After that, it is assumed that this value will not change as long as the vector is used. From 8badc291eb56ab18e94305b49da38b7795147d17 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 18 Jun 2024 11:29:38 +0200 Subject: [PATCH 47/49] Handle prime_tower_min_shell_thickness settings --- src/PrimeTower/PrimeTowerInterleaved.cpp | 47 ++++++++++++++---------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/PrimeTower/PrimeTowerInterleaved.cpp b/src/PrimeTower/PrimeTowerInterleaved.cpp index 5973cca4b0..fabe0226ce 100644 --- a/src/PrimeTower/PrimeTowerInterleaved.cpp +++ b/src/PrimeTower/PrimeTowerInterleaved.cpp @@ -41,7 +41,8 @@ std::map> PrimeTowerInter const Scene& scene = Application::getInstance().current_slice_->scene; const Settings& mesh_group_settings = scene.current_mesh_group->settings; const coord_t tower_radius = mesh_group_settings.get("prime_tower_size") / 2; - coord_t support_radius = tower_radius; + const coord_t min_shell_thickness = mesh_group_settings.get("prime_tower_min_shell_thickness"); + coord_t shell_thickness = 0; std::map> toolpaths; // Loop from top bo bottom, so that the required support increases with what is actually required @@ -52,20 +53,20 @@ std::map> PrimeTowerInter std::vector toolpaths_at_layer; size_t last_extruder_support = 0; - // Now generate actual priming patterns - coord_t outer_radius = tower_radius; + // Generate actual priming patterns + coord_t prime_next_outer_radius = tower_radius; for (const ExtruderUse& extruder_use : extruders_use_at_layer) { if (extruder_use.prime == ExtruderPrime::Prime) { ExtruderToolPaths extruder_toolpaths; - extruder_toolpaths.outer_radius = outer_radius; + extruder_toolpaths.outer_radius = prime_next_outer_radius; extruder_toolpaths.extruder_nr = extruder_use.extruder_nr; - std::tie(extruder_toolpaths.toolpaths, extruder_toolpaths.inner_radius) = generatePrimeToolpaths(extruder_use.extruder_nr, outer_radius); + std::tie(extruder_toolpaths.toolpaths, extruder_toolpaths.inner_radius) = generatePrimeToolpaths(extruder_use.extruder_nr, prime_next_outer_radius); toolpaths_at_layer.push_back(extruder_toolpaths); - outer_radius = extruder_toolpaths.inner_radius; + prime_next_outer_radius = extruder_toolpaths.inner_radius; } else if (extruder_use.prime == ExtruderPrime::Support) { @@ -73,24 +74,32 @@ std::map> PrimeTowerInter } } - // Generate extra support pattern if required - if (support_radius < outer_radius) + // Increase shell thickness if required + const coord_t layer_prime_thickness = tower_radius - prime_next_outer_radius; + shell_thickness = std::max(shell_thickness, layer_prime_thickness); + + if (shell_thickness > 0) { - if (toolpaths_at_layer.empty()) + shell_thickness = std::max(shell_thickness, min_shell_thickness); + + // Generate extra inner support if required + const coord_t inner_support_radius = tower_radius - shell_thickness; + if (inner_support_radius < prime_next_outer_radius) { - toolpaths_at_layer.emplace_back(last_extruder_support, ClosedLinesSet(), outer_radius, support_radius); + if (toolpaths_at_layer.empty()) + { + toolpaths_at_layer.emplace_back(last_extruder_support, ClosedLinesSet(), prime_next_outer_radius, inner_support_radius); + } + + ExtruderToolPaths& last_extruder_toolpaths = toolpaths_at_layer.back(); + ClosedLinesSet support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, prime_next_outer_radius, inner_support_radius); + last_extruder_toolpaths.toolpaths.push_back(support_toolpaths); + last_extruder_toolpaths.outer_radius = prime_next_outer_radius; + last_extruder_toolpaths.inner_radius = inner_support_radius; } - ExtruderToolPaths& last_extruder_toolpaths = toolpaths_at_layer.back(); - ClosedLinesSet support_toolpaths = generateSupportToolpaths(last_extruder_toolpaths.extruder_nr, outer_radius, support_radius); - last_extruder_toolpaths.toolpaths.push_back(support_toolpaths); - last_extruder_toolpaths.inner_radius = support_radius; + toolpaths[layer_nr] = toolpaths_at_layer; } - - // Now decrease support radius if required - support_radius = std::min(support_radius, outer_radius); - - toolpaths[layer_nr] = toolpaths_at_layer; } return toolpaths; From 15d4e122593d481f638c1f56c28c7653762ca10c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 28 Jun 2024 11:30:41 +0200 Subject: [PATCH 48/49] Add/fix documentation --- include/PrimeTower/PrimeTower.h | 14 +++++++++++--- include/PrimeTower/PrimeTowerInterleaved.h | 8 ++++++++ include/PrimeTower/PrimeTowerNormal.h | 7 +++++++ include/utils/LayerVector.h | 2 +- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/PrimeTower/PrimeTower.h b/include/PrimeTower/PrimeTower.h index 0c8343d48e..e0bbf5d5a6 100644 --- a/include/PrimeTower/PrimeTower.h +++ b/include/PrimeTower/PrimeTower.h @@ -22,10 +22,18 @@ class SliceDataStorage; class LayerPlan; /*! - * Class for everything to do with the prime tower: - * - Generating the areas. + * Abstract class for everything to do with the prime tower: + * - Generating the occupation areas. * - Checking up untill which height the prime tower has to be printed. - * - Generating the paths and adding them to the layer plan. + * - Inserting priming commands in extruders uses + * - Generating priming paths and adding them to the layer plan. + * + * We may adopt different strategies to generate the prime tower, thus this class is abstract and different + * implementations may co-exist. The common behavior implemented in the main class is: + * - Generate occupation areas as a cylinder with a flared base + * - Generate the base extra extrusion discs around the base cylinder + * - Generate the first layer extra inset inside the base cylinder + * Then it is the job of the specific implementation to handle the generation of extrusion paths for the base cylinder */ class PrimeTower { diff --git a/include/PrimeTower/PrimeTowerInterleaved.h b/include/PrimeTower/PrimeTowerInterleaved.h index 45d06b1aca..d9d743ca4c 100644 --- a/include/PrimeTower/PrimeTowerInterleaved.h +++ b/include/PrimeTower/PrimeTowerInterleaved.h @@ -9,6 +9,14 @@ namespace cura { +/*! + * Specific prime tower implementation that generates interleaved priming paths. It is optimized to waste as few + * filament as possible, while ensuring that the prime tower is still robust even if it gets very high. + * When there is no actual priming required for extruders, it will create a kind of circular zigzag pattern that acts as + * a sparse support. Otherwise it will create priming annuli, stacked on top of each other. + * This is very effective when doing multi-color printing, however it can be used only if all the filaments properly + * adhere to each other. Otherwise there is a high risk that the tower will collapse during the print. + */ class PrimeTowerInterleaved : public PrimeTower { public: diff --git a/include/PrimeTower/PrimeTowerNormal.h b/include/PrimeTower/PrimeTowerNormal.h index 3037255e93..5235774252 100644 --- a/include/PrimeTower/PrimeTowerNormal.h +++ b/include/PrimeTower/PrimeTowerNormal.h @@ -9,6 +9,13 @@ namespace cura { +/*! + * Specific prime tower implementation that generates nested cylinders. Each layer, all the extruders will be used to + * contribute to the prime tower, even if they don't actually need priming. In this case, a circular zigzag pattern will + * be used to act as support for upper priming extrusions. + * Although this method is not very efficient, it is required when using different materials that don't properly adhere + * to each other. By nesting the cylinders, you make sure that the tower remains consistent and strong along the print. + */ class PrimeTowerNormal : public PrimeTower { public: diff --git a/include/utils/LayerVector.h b/include/utils/LayerVector.h index d03aa9b50d..929f916297 100644 --- a/include/utils/LayerVector.h +++ b/include/utils/LayerVector.h @@ -14,7 +14,7 @@ namespace cura /*! * \brief The LayerVector class mimics a std::vector but with the index being a LayerIndex, thus it can have negative - * values (for raft layers). it also ensure that the first element in the list is always on the very first layer. + * values (for raft layers). It also ensures that the first element in the list is always on the very first layer. * \note When calling the init() method, LayerVector will call Raft::getTotalExtraLayers() so it requires the settings * to be setup. This is the reason why this is not done in the constructor, and has to be called manually. * After that, it is assumed that this value will not change as long as the vector is used. From f0341ebbf863b6eed3f372dc0cdb4c70023bedcb Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 28 Jun 2024 11:34:32 +0200 Subject: [PATCH 49/49] Apply suggested code consistency --- src/FffGcodeWriter.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 481175354c..b1ebefe5a9 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3937,12 +3937,14 @@ void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, Layer void FffGcodeWriter::addPrimeTower(const SliceDataStorage& storage, LayerPlan& gcode_layer, const size_t prev_extruder) const { - if (storage.prime_tower_) + if (! storage.prime_tower_) { - LayerIndex layer_nr = gcode_layer.getLayerNr(); - const std::vector extruder_order = extruder_order_per_layer.get(layer_nr); - storage.prime_tower_->addToGcode(storage, gcode_layer, extruder_order, prev_extruder, gcode_layer.getExtruder()); + return; } + + const LayerIndex layer_nr = gcode_layer.getLayerNr(); + const std::vector extruder_order = extruder_order_per_layer.get(layer_nr); + storage.prime_tower_->addToGcode(storage, gcode_layer, extruder_order, prev_extruder, gcode_layer.getExtruder()); } void FffGcodeWriter::finalize()