From 4aa73919b616223d9bb9c00b6ecd4c02951ef5b6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 14 Sep 2023 21:01:40 +0200 Subject: [PATCH 1/4] Pass shared pointers, per preference, by reference. Otherwise they're passed by value. Not as bad as passing the entire _original_ object of course, but a ref is still a little lighter weight than a shared pointer object. done as part of, but not nescesary for, CURA-11019 --- include/SkeletalTrapezoidationEdge.h | 6 +++--- include/SkeletalTrapezoidationJoint.h | 2 +- include/infill.h | 10 +++++----- src/communication/ArcusCommunication.cpp | 2 +- src/infill.cpp | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/SkeletalTrapezoidationEdge.h b/include/SkeletalTrapezoidationEdge.h index aa897d830f..4c0bfb365c 100644 --- a/include/SkeletalTrapezoidationEdge.h +++ b/include/SkeletalTrapezoidationEdge.h @@ -80,7 +80,7 @@ class SkeletalTrapezoidationEdge { return transitions.use_count() > 0 && (ignore_empty || ! transitions.lock()->empty()); } - void setTransitions(std::shared_ptr> storage) + void setTransitions(std::shared_ptr>& storage) { transitions = storage; } @@ -93,7 +93,7 @@ class SkeletalTrapezoidationEdge { return transition_ends.use_count() > 0 && (ignore_empty || ! transition_ends.lock()->empty()); } - void setTransitionEnds(std::shared_ptr> storage) + void setTransitionEnds(std::shared_ptr>& storage) { transition_ends = storage; } @@ -106,7 +106,7 @@ class SkeletalTrapezoidationEdge { return extrusion_junctions.use_count() > 0 && (ignore_empty || ! extrusion_junctions.lock()->empty()); } - void setExtrusionJunctions(std::shared_ptr storage) + void setExtrusionJunctions(std::shared_ptr& storage) { extrusion_junctions = storage; } diff --git a/include/SkeletalTrapezoidationJoint.h b/include/SkeletalTrapezoidationJoint.h index 603b9b452f..7c7575a07b 100644 --- a/include/SkeletalTrapezoidationJoint.h +++ b/include/SkeletalTrapezoidationJoint.h @@ -43,7 +43,7 @@ class SkeletalTrapezoidationJoint { return beading.use_count() > 0; } - void setBeading(std::shared_ptr storage) + void setBeading(std::shared_ptr& storage) { beading = storage; } diff --git a/include/infill.h b/include/infill.h index 7c6a342499..40ca7fbd59 100644 --- a/include/infill.h +++ b/include/infill.h @@ -203,8 +203,8 @@ class Infill const Settings& settings, int layer_idx, SectionType section_type, - const std::shared_ptr cross_fill_provider = nullptr, - const std::shared_ptr lightning_layer = nullptr, + const std::shared_ptr& cross_fill_provider = nullptr, + const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, const Polygons& prevent_small_exposed_to_air = Polygons(), const bool is_bridge_skin = false); @@ -242,8 +242,8 @@ class Infill Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const std::shared_ptr cross_fill_pattern = nullptr, - const std::shared_ptr lightning_layer = nullptr, + const std::shared_ptr& cross_fill_pattern = nullptr, + const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr); /*! @@ -402,7 +402,7 @@ class Infill * see https://hal.archives-ouvertes.fr/hal-02155929/document * \param result (output) The resulting polygons */ - void generateLightningInfill(const std::shared_ptr lightning_layer, Polygons& result_lines); + void generateLightningInfill(const std::shared_ptr& lightning_layer, Polygons& result_lines); /*! * Generate sparse concentric infill diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index cf17d69454..44cc58cbe2 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -420,7 +420,7 @@ void ArcusCommunication::sendOptimizedLayerData() } spdlog::info("Sending {} layers.", data.current_layer_count); - for (std::pair> entry : data.slice_data) // Note: This is in no particular order! + for (const auto& entry : data.slice_data) // Note: This is in no particular order! { spdlog::debug("Sending layer data for layer {} of {}.", entry.first, data.slice_data.size()); private_data->socket->sendMessage(entry.second); // Send the actual layers. diff --git a/src/infill.cpp b/src/infill.cpp index 6bdd545aaa..9137aa66ca 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -88,8 +88,8 @@ void Infill::generate( const Settings& settings, int layer_idx, SectionType section_type, - const std::shared_ptr cross_fill_provider, - const std::shared_ptr lightning_trees, + const std::shared_ptr& cross_fill_provider, + const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh, const Polygons& prevent_small_exposed_to_air, const bool is_bridge_skin) @@ -255,8 +255,8 @@ void Infill::_generate( Polygons& result_polygons, Polygons& result_lines, const Settings& settings, - const std::shared_ptr cross_fill_provider, - const std::shared_ptr lightning_trees, + const std::shared_ptr& cross_fill_provider, + const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh) { if (inner_contour.empty()) @@ -432,7 +432,7 @@ void Infill::generateGyroidInfill(Polygons& result_lines, Polygons& result_polyg PolylineStitcher::stitch(line_segments, result_lines, result_polygons, infill_line_width); } -void Infill::generateLightningInfill(const std::shared_ptr trees, Polygons& result_lines) +void Infill::generateLightningInfill(const std::shared_ptr& trees, Polygons& result_lines) { // Don't need to support areas smaller than line width, as they are always within radius: if (std::abs(inner_contour.area()) < infill_line_width || ! trees) From 9b5da0ecf482c2d49164b19f1aea7eb2abb4b907 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 14 Sep 2023 21:39:25 +0200 Subject: [PATCH 2/4] Fix slowdown issue due to copy of large data. Since `make_shared` is a constructor, the mesh data was copied over each time we started a new layer. Properly fix this by using smart-pointers from the beginning, so we only have the creation overhead, which is nearly nothing. In fact, this solution may be _faster_ than the original, perhaps due to some constness that is now also enforced. fixes internal ticket CURA-11019 --- include/FffGcodeWriter.h | 5 ++-- include/LayerPlan.h | 4 +-- include/pathPlanning/GCodePath.h | 2 +- include/sliceDataStorage.h | 2 +- src/FffGcodeWriter.cpp | 39 +++++++++++++++++------------- src/FffPolygonGenerator.cpp | 30 +++++++++++++---------- src/LayerPlan.cpp | 11 +++++---- src/TreeModelVolumes.cpp | 5 ++-- src/TreeSupport.cpp | 16 ++++++------ src/bridge.cpp | 3 ++- src/plugins/converters.cpp | 2 +- src/settings/PathConfigStorage.cpp | 6 ++--- src/sliceDataStorage.cpp | 22 ++++++++--------- src/support.cpp | 19 ++++++++------- 14 files changed, 90 insertions(+), 76 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 2d2e7c7162..2cde3c40d4 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -328,13 +328,12 @@ class FffGcodeWriter : public NoCopy * This adds all features (e.g. walls, skin etc.) of this \p mesh to the gcode which are printed using \p extruder_nr * * \param[in] storage where the slice data is stored. - * \param mesh The mesh to add to the layer plan \p gcode_layer. + * \param mesh_ptr The mesh to add to the layer plan \p gcode_layer. * \param extruder_nr The extruder for which to print all features of the mesh which should be printed with this extruder * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) - const; + void addMeshLayerToGCode(const SliceDataStorage& storage, const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 3eda752178..c894ab2a27 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -70,7 +70,7 @@ class LayerPlan : public NoCopy std::vector has_prime_tower_planned_per_extruder; //!< For each extruder, whether the prime tower 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. + std::shared_ptr current_mesh; //!< The mesh of the last planned move. /*! * Whether the skirt or brim polygons have been processed into planned paths @@ -238,7 +238,7 @@ class LayerPlan : public NoCopy * Track the currently printing mesh. * \param mesh_id A unique ID indicating the current mesh. */ - void setMesh(const std::shared_ptr& mesh); + void setMesh(const std::shared_ptr& mesh); /*! * Set bridge_wall_mask. diff --git a/include/pathPlanning/GCodePath.h b/include/pathPlanning/GCodePath.h index 8c3c34dcfe..7bbd56c709 100644 --- a/include/pathPlanning/GCodePath.h +++ b/include/pathPlanning/GCodePath.h @@ -30,7 +30,7 @@ namespace cura struct GCodePath { GCodePathConfig config{}; //!< The configuration settings of the path. - std::shared_ptr mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; + std::shared_ptr mesh; //!< Which mesh this path belongs to, if any. If it's not part of any mesh, the mesh should be nullptr; SpaceFillType space_fill_type{}; //!< The type of space filling of which this path is a part Ratio flow{}; //!< A type-independent flow configuration Ratio width_factor{}; //!< Adjustment to the line width. Similar to flow, but causes the speed_back_pressure_factor to be adjusted. diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 32a391c24c..9b0661ed36 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -324,7 +324,7 @@ class SliceDataStorage : public NoCopy Point3 model_size, model_min, model_max; AABB3D machine_size; //!< The bounding box with the width, height and depth of the printer. - std::vector meshes; + std::vector> meshes; std::vector retraction_wipe_config_per_extruder; //!< Config for retractions, extruder switch retractions, and wipes, per extruder. diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 648e9082ce..0d6ea404fd 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -106,8 +106,9 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep } size_t total_layers = 0; - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; size_t mesh_layer_num = mesh.layers.size(); // calculation of _actual_ number of layers in loop. @@ -269,7 +270,7 @@ void FffGcodeWriter::findLayerSeamsForSpiralize(SliceDataStorage& storage, size_ const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (unsigned int mesh_idx : mesh_order) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + auto& mesh = *storage.meshes[mesh_idx]; // if this mesh has layer data for this layer process it if (! done_this_layer && mesh.layers.size() > layer_nr) { @@ -371,9 +372,9 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh : storage.meshes) { - retractionAndWipeConfigFromSettings(mesh.settings, &mesh.retraction_wipe_config); + retractionAndWipeConfigFromSettings(mesh->settings, &mesh->retraction_wipe_config); } } @@ -505,9 +506,9 @@ void FffGcodeWriter::setSupportAngles(SliceDataStorage& storage) } else { - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh : storage.meshes) { - if (mesh.settings.get(interface_height_setting) + if (mesh->settings.get(interface_height_setting) >= 2 * Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height")) { // Some roofs are quite thick. @@ -913,10 +914,11 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } else { - z = storage.meshes[0].layers[layer_nr].printZ; // stub default + z = storage.meshes[0]->layers[layer_nr].printZ; // stub default // find printZ of first actual printed mesh - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("cutting_mesh") || mesh.settings.get("infill_mesh")) { @@ -951,8 +953,9 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } coord_t max_inner_wall_width = 0; - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; coord_t mesh_inner_wall_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); if (layer_nr == 0) { @@ -1022,14 +1025,14 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (size_t mesh_idx : mesh_order) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const auto& mesh = storage.meshes[mesh_idx]; const MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; - if (mesh.settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE + if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE && extruder_nr - == mesh.settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! + == mesh->settings.get("wall_0_extruder_nr").extruder_nr // mesh surface mode should always only be printed with the outer wall extruder! ) { - addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer); + addMeshLayerToGCode_meshSurfaceMode(storage, *mesh, mesh_config, gcode_layer); } else { @@ -1382,7 +1385,7 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s std::vector::iterator mesh_group = Application::getInstance().current_slice->scene.current_mesh_group; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const auto& mesh = *storage.meshes[mesh_idx]; if (mesh.getExtruderIsUsed(extruder_nr)) { const Mesh& mesh_data = mesh_group->meshes[mesh_idx]; @@ -1449,11 +1452,12 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceMeshStorage& mesh, c void FffGcodeWriter::addMeshLayerToGCode( const SliceDataStorage& storage, - const SliceMeshStorage& mesh, + const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const { + const auto& mesh = *mesh_ptr; if (gcode_layer.getLayerNr() > mesh.layer_nr_max_filled_layer) { return; @@ -1471,7 +1475,7 @@ void FffGcodeWriter::addMeshLayerToGCode( return; } - gcode_layer.setMesh(std::make_shared(mesh)); + gcode_layer.setMesh(mesh_ptr); ZSeamConfig z_seam_config; if (mesh.isPrinted()) //"normal" meshes with walls, skin, infill, etc. get the traditional part ordering based on the z-seam settings. @@ -2259,8 +2263,9 @@ bool FffGcodeWriter::processInsets( Polygons outlines_below; AABB boundaryBox(part.outline); - for (const SliceMeshStorage& m : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& m = *mesh_ptr; if (m.isPrinted()) { for (const SliceLayerPart& prevLayerPart : m.layers[gcode_layer.getLayerNr() - 1].parts) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 03c83c0627..942345ccc1 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -275,8 +275,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe Mesh& mesh = scene.current_mesh_group->meshes[meshIdx]; // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes - storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh - SliceMeshStorage& meshStorage = storage.meshes.back(); + storage.meshes.push_back(std::make_shared(& meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh + auto& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes const bool is_support_modifier = AreaSupport::handleSupportModifierMesh(storage, mesh.settings, slicer); @@ -347,8 +347,9 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // compute layer count and remove first empty layers // there is no separate progress stage for removeEmptyFisrtLayer (TODO) unsigned int slice_layer_count = 0; - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (! mesh.settings.get("infill_mesh") && ! mesh.settings.get("anti_overhang_mesh")) { slice_layer_count = std::max(slice_layer_count, mesh.layers.size()); @@ -369,7 +370,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& std::multimap order_to_mesh_indices; for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - order_to_mesh_indices.emplace(storage.meshes[mesh_idx].settings.get("infill_mesh_order"), mesh_idx); + order_to_mesh_indices.emplace(storage.meshes[mesh_idx]->settings.get("infill_mesh_order"), mesh_idx); } for (std::pair& order_and_mesh_idx : order_to_mesh_indices) { @@ -432,9 +433,9 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& spdlog::debug("Meshes post-processing"); // meshes post processing - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh : storage.meshes) { - processDerivedWallsSkinInfill(mesh); + processDerivedWallsSkinInfill(*mesh); } spdlog::debug("Processing gradual support"); @@ -449,7 +450,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( ProgressStageEstimator& inset_skin_progress_estimate) { size_t mesh_idx = mesh_order[mesh_order_idx]; - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; size_t mesh_layer_count = mesh.layers.size(); if (mesh.settings.get("infill_mesh")) { @@ -513,7 +514,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( for (size_t other_mesh_order_idx = mesh_order_idx + 1; other_mesh_order_idx < mesh_order.size(); ++other_mesh_order_idx) { const size_t other_mesh_idx = mesh_order[other_mesh_order_idx]; - SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx]; + SliceMeshStorage& other_mesh = *storage.meshes[other_mesh_idx]; if (other_mesh.settings.get("infill_mesh")) { AABB3D aabb = scene.current_mesh_group->meshes[mesh_idx].getAABB(); @@ -553,7 +554,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order) { size_t mesh_idx = mesh_order[mesh_order_idx]; - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; coord_t surface_line_width = mesh.settings.get("wall_line_width_0"); mesh.layer_nr_max_filled_layer = -1; @@ -585,7 +586,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const siz { break; // all previous meshes have been processed } - SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx]; + SliceMeshStorage& other_mesh = *storage.meshes[other_mesh_idx]; if (layer_idx >= static_cast(other_mesh.layers.size())) { // there can be no interaction between the infill mesh and this other non-infill mesh continue; @@ -741,8 +742,9 @@ bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const LayerInd return false; } } - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (layer_idx >= mesh.layers.size()) { continue; @@ -782,8 +784,9 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, size { spdlog::info("Removing {} layers because they are empty", n_empty_first_layers); const coord_t layer_height = Application::getInstance().current_slice->scene.current_mesh_group->settings.get("layer_height"); - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; std::vector& layers = mesh.layers; if (layers.size() > n_empty_first_layers) { @@ -852,8 +855,9 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage max_print_height_per_extruder.resize(extruder_count, -(raft_layers + 1)); // Initialize all as -1 (or lower in case of raft). { // compute max_object_height_per_extruder // Height of the meshes themselves. - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh")) { continue; // Special type of mesh that doesn't get printed. diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index 600869daac..b7d24b1b11 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -159,8 +159,9 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) } else { - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; const SliceLayer& layer = mesh.layers[static_cast(layer_nr)]; // don't process infill_mesh or anti_overhang_mesh if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) @@ -282,7 +283,7 @@ bool LayerPlan::setExtruder(const size_t extruder_nr) } return true; } -void LayerPlan::setMesh(const std::shared_ptr& mesh) +void LayerPlan::setMesh(const std::shared_ptr& mesh) { current_mesh = mesh; } @@ -1187,9 +1188,9 @@ void LayerPlan::addLinesByOptimizer( if (layer_nr >= 0) { // determine how much the skin/infill lines overlap the combing boundary - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh : storage.meshes) { - const coord_t overlap = std::max(mesh.settings.get("skin_overlap_mm"), mesh.settings.get("infill_overlap_mm")); + const coord_t overlap = std::max(mesh->settings.get("skin_overlap_mm"), mesh->settings.get("infill_overlap_mm")); if (overlap > dist) { dist = overlap; @@ -1846,7 +1847,7 @@ void LayerPlan::writeGCode(GCodeExport& gcode) const bool acceleration_travel_enabled = mesh_group_settings.get("acceleration_travel_enabled"); const bool jerk_enabled = mesh_group_settings.get("jerk_enabled"); const bool jerk_travel_enabled = mesh_group_settings.get("jerk_travel_enabled"); - std::shared_ptr current_mesh; + std::shared_ptr current_mesh; for (size_t extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++) { diff --git a/src/TreeModelVolumes.cpp b/src/TreeModelVolumes.cpp index fef53433e7..6ddcef2c93 100644 --- a/src/TreeModelVolumes.cpp +++ b/src/TreeModelVolumes.cpp @@ -49,8 +49,9 @@ TreeModelVolumes::TreeModelVolumes( coord_t min_maximum_area_deviation = std::numeric_limits::max(); support_rests_on_model = false; - for (auto [mesh_idx, mesh] : storage.meshes | ranges::views::enumerate) + for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { + auto& mesh = *mesh_ptr; bool added = false; for (auto [idx, layer_outline] : layer_outlines_ | ranges::views::enumerate) { @@ -103,7 +104,7 @@ TreeModelVolumes::TreeModelVolumes( { // Workaround for compiler bug on apple-clang -- Closure won't properly capture variables in capture lists in outer scope. const auto& mesh_idx_l = mesh_idx; - const auto& mesh_l = mesh; + const auto& mesh_l = *mesh; // ^^^ Remove when fixed (and rename accordingly in the below parallel-for). cura::parallel_for( diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 5c7585cfbb..d9e56458c4 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -38,8 +38,9 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { size_t largest_printed_mesh_idx = 0; - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= @@ -48,8 +49,9 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) // Group all meshes that can be processed together. NOTE this is different from mesh-groups! // Only one setting object is needed per group, as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. - for (auto [mesh_idx, mesh] : storage.meshes | ranges::views::enumerate) + for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { + auto& mesh = *mesh_ptr; const bool non_supportable_mesh = mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh"); if (mesh.settings.get("support_structure") != ESupportStructure::TREE || ! mesh.settings.get("support_enable") || non_supportable_mesh) { @@ -76,14 +78,14 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) } // no need to do this per mesh group as adaptive layers and raft setting are not setable per mesh. - if (storage.meshes[largest_printed_mesh_idx].layers.back().printZ < mesh.layers.back().printZ) + if (storage.meshes[largest_printed_mesh_idx]->layers.back().printZ < mesh.layers.back().printZ) { largest_printed_mesh_idx = mesh_idx; } } - std::vector known_z(storage.meshes[largest_printed_mesh_idx].layers.size()); + std::vector known_z(storage.meshes[largest_printed_mesh_idx]->layers.size()); - for (auto [z, layer] : ranges::views::enumerate(storage.meshes[largest_printed_mesh_idx].layers)) + for (auto [z, layer] : ranges::views::enumerate(storage.meshes[largest_printed_mesh_idx]->layers)) { known_z[z] = layer.printZ; } @@ -152,7 +154,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) // ### Place tips of the support tree for (size_t mesh_idx : processing.second) { - generateInitialAreas(storage.meshes[mesh_idx], move_bounds, storage); + generateInitialAreas(*storage.meshes[mesh_idx], move_bounds, storage); } const auto t_gen = std::chrono::high_resolution_clock::now(); @@ -206,7 +208,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); const size_t z_distance_top_layers = round_up_divide(z_distance_top, diff --git a/src/bridge.cpp b/src/bridge.cpp index f6f4b41f04..a395f7bd79 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -24,8 +24,9 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl const Ratio sparse_infill_max_density = settings.get("bridge_sparse_infill_max_density"); // include parts from all meshes - for (const SliceMeshStorage& mesh : storage.meshes) + for (const auto& mesh_ptr : storage.meshes) { + const auto& mesh = *mesh_ptr; if (mesh.isPrinted()) { const coord_t infill_line_distance = mesh.settings.get("infill_line_distance"); diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 33e7d4882e..88a716e47a 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -430,7 +430,7 @@ gcode_paths_modify_response::native_value_type gcode_paths_modify_response::operator()(gcode_paths_modify_response::native_value_type& original_value, const gcode_paths_modify_response::value_type& message) const { std::vector paths; - using map_t = std::unordered_map>; + using map_t = std::unordered_map>; auto meshes = original_value | ranges::views::filter( [](const auto& path) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 2f9dd2acea..467b130f5c 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -124,9 +124,9 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } mesh_configs.reserve(storage.meshes.size()); - for (const SliceMeshStorage& mesh_storage : storage.meshes) + for (const auto& mesh_storage : storage.meshes) { - mesh_configs.emplace_back(mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); + mesh_configs.emplace_back(*mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); } support_infill_config.reserve(MAX_INFILL_COMBINE); @@ -224,7 +224,7 @@ void PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storag { // meshes for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const SpeedDerivatives initial_layer_speed_config{ .speed = mesh.settings.get("speed_print_layer_0"), .acceleration = mesh.settings.get("acceleration_print_layer_0"), diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index 788ac2e45a..c890783f8c 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -300,16 +300,16 @@ Polygons Polygons total; if (layer_nr >= 0) { - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { - if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") - || (extruder_nr != -1 && extruder_nr != int(mesh.settings.get("wall_0_extruder_nr").extruder_nr))) + if (mesh->settings.get("infill_mesh") || mesh->settings.get("anti_overhang_mesh") + || (extruder_nr != -1 && extruder_nr != int(mesh->settings.get("wall_0_extruder_nr").extruder_nr))) { continue; } - const SliceLayer& layer = mesh.layers[layer_nr]; + const SliceLayer& layer = mesh->layers[layer_nr]; layer.getOutlines(total, external_polys_only); - if (mesh.settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) + if (mesh->settings.get("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(MM2INT(0.1))); } @@ -381,9 +381,9 @@ std::vector SliceDataStorage::getExtrudersUsed() const // support // support is presupposed to be present... - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { - if (mesh.settings.get("support_enable") || mesh.settings.get("support_mesh")) + if (mesh->settings.get("support_enable") || mesh->settings.get("support_mesh")) { ret[mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr] = true; ret[mesh_group_settings.get("support_infill_extruder_nr").extruder_nr] = true; @@ -399,11 +399,11 @@ std::vector SliceDataStorage::getExtrudersUsed() const } // all meshes are presupposed to actually have content - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { - ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr); + ret[extruder_nr] = ret[extruder_nr] || mesh->getExtruderIsUsed(extruder_nr); } } return ret; @@ -508,11 +508,11 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) if (include_models) { - for (const SliceMeshStorage& mesh : meshes) + for (const auto& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { - ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr, layer_nr); + ret[extruder_nr] = ret[extruder_nr] || mesh->getExtruderIsUsed(extruder_nr, layer_nr); } } } diff --git a/src/support.cpp b/src/support.cpp index 1adb0b1ae7..a55d21b53f 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -599,8 +599,9 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp void AreaSupport::generateOverhangAreas(SliceDataStorage& storage) { - for (SliceMeshStorage& mesh : storage.meshes) + for (auto& mesh_ptr : storage.meshes) { + auto& mesh = *mesh_ptr; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; @@ -645,14 +646,14 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; } - Settings* infill_settings = &storage.meshes[mesh_idx].settings; - Settings* roof_settings = &storage.meshes[mesh_idx].settings; - Settings* bottom_settings = &storage.meshes[mesh_idx].settings; + Settings* infill_settings = &storage.meshes[mesh_idx]->settings; + Settings* roof_settings = &storage.meshes[mesh_idx]->settings; + Settings* bottom_settings = &storage.meshes[mesh_idx]->settings; if (mesh.settings.get("support_mesh")) { if ((mesh.settings.get("support_mesh_drop_down") && support_meshes_drop_down_handled) @@ -694,7 +695,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage) // handle support interface for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; @@ -725,12 +726,12 @@ void AreaSupport::precomputeCrossInfillTree(SliceDataStorage& storage) AABB3D aabb; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh")) { continue; } - Settings& infill_settings = storage.meshes[mesh_idx].settings; + Settings& infill_settings = storage.meshes[mesh_idx]->settings; if (mesh.settings.get("support_mesh")) { // use extruder train settings rather than the per-object settings of the first support mesh encountered. @@ -1039,7 +1040,7 @@ void AreaSupport::generateSupportAreasForMesh( const size_t layer_count, std::vector& support_areas) { - SliceMeshStorage& mesh = storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; const ESupportStructure support_structure = mesh.settings.get("support_structure"); const bool is_support_mesh_place_holder From e171220546f94a513991d83bc5dd87e2ca7f44dd Mon Sep 17 00:00:00 2001 From: rburema Date: Fri, 15 Sep 2023 07:21:23 +0000 Subject: [PATCH 3/4] Applied clang-format. --- include/FffGcodeWriter.h | 7 +- include/SkeletalTrapezoidationEdge.h | 38 +- include/SkeletalTrapezoidationJoint.h | 25 +- src/FffPolygonGenerator.cpp | 2 +- src/TreeSupport.cpp | 1280 +++++++++++++------------ src/bridge.cpp | 46 +- 6 files changed, 756 insertions(+), 642 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 2cde3c40d4..0e3dea645c 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -333,7 +333,12 @@ class FffGcodeWriter : public NoCopy * \param mesh_config the line config with which to print a print feature * \param gcode_layer The initial planning of the gcode of the layer. */ - void addMeshLayerToGCode(const SliceDataStorage& storage, const std::shared_ptr& mesh_ptr, const size_t extruder_nr, const MeshPathConfigs& mesh_config, LayerPlan& gcode_layer) const; + void addMeshLayerToGCode( + const SliceDataStorage& storage, + const std::shared_ptr& mesh_ptr, + const size_t extruder_nr, + const MeshPathConfigs& mesh_config, + LayerPlan& gcode_layer) const; /*! * Add all features of the given extruder from a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer. diff --git a/include/SkeletalTrapezoidationEdge.h b/include/SkeletalTrapezoidationEdge.h index 4c0bfb365c..60f464deb5 100644 --- a/include/SkeletalTrapezoidationEdge.h +++ b/include/SkeletalTrapezoidationEdge.h @@ -4,19 +4,24 @@ #ifndef SKELETAL_TRAPEZOIDATION_EDGE_H #define SKELETAL_TRAPEZOIDATION_EDGE_H -#include // smart pointers +#include "utils/ExtrusionJunction.h" + #include +#include // smart pointers #include -#include "utils/ExtrusionJunction.h" - namespace cura { class SkeletalTrapezoidationEdge { private: - enum class Central : int { UNKNOWN = -1, NO = 0, YES = 1}; + enum class Central : int + { + UNKNOWN = -1, + NO = 0, + YES = 1 + }; public: /*! @@ -28,9 +33,11 @@ class SkeletalTrapezoidationEdge int lower_bead_count; coord_t feature_radius; // The feature radius at which this transition is placed TransitionMiddle(coord_t pos, int lower_bead_count, coord_t feature_radius) - : pos(pos), lower_bead_count(lower_bead_count) + : pos(pos) + , lower_bead_count(lower_bead_count) , feature_radius(feature_radius) - {} + { + } }; /*! @@ -42,8 +49,11 @@ class SkeletalTrapezoidationEdge int lower_bead_count; bool is_lower_end; // Whether this is the ed of the transition with lower bead count TransitionEnd(coord_t pos, int lower_bead_count, bool is_lower_end) - : pos(pos), lower_bead_count(lower_bead_count), is_lower_end(is_lower_end) - {} + : pos(pos) + , lower_bead_count(lower_bead_count) + , is_lower_end(is_lower_end) + { + } }; enum class EdgeType : int @@ -55,12 +65,14 @@ class SkeletalTrapezoidationEdge EdgeType type; SkeletalTrapezoidationEdge() - : SkeletalTrapezoidationEdge(EdgeType::NORMAL) - {} + : SkeletalTrapezoidationEdge(EdgeType::NORMAL) + { + } SkeletalTrapezoidationEdge(const EdgeType& type) - : type(type) - , is_central(Central::UNKNOWN) - {} + : type(type) + , is_central(Central::UNKNOWN) + { + } bool isCentral() const { diff --git a/include/SkeletalTrapezoidationJoint.h b/include/SkeletalTrapezoidationJoint.h index 7c7575a07b..e8c5ad42a7 100644 --- a/include/SkeletalTrapezoidationJoint.h +++ b/include/SkeletalTrapezoidationJoint.h @@ -1,20 +1,21 @@ -//Copyright (c) 2020 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2020 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef SKELETAL_TRAPEZOIDATION_JOINT_H #define SKELETAL_TRAPEZOIDATION_JOINT_H -#include // smart pointers - #include "BeadingStrategy/BeadingStrategy.h" #include "utils/IntPoint.h" +#include // smart pointers + namespace cura { class SkeletalTrapezoidationJoint { using Beading = BeadingStrategy::Beading; + public: struct BeadingPropagation { @@ -27,17 +28,20 @@ class SkeletalTrapezoidationJoint , dist_to_bottom_source(0) , dist_from_top_source(0) , is_upward_propagated_only(false) - {} + { + } }; coord_t distance_to_boundary; coord_t bead_count; - float transition_ratio; //! The distance near the skeleton to leave free because this joint is in the middle of a transition, as a fraction of the inner bead width of the bead at the higher transition. + float transition_ratio; //! The distance near the skeleton to leave free because this joint is in the middle of a transition, as a fraction of the inner bead width of the bead + //! at the higher transition. SkeletalTrapezoidationJoint() - : distance_to_boundary(-1) - , bead_count(-1) - , transition_ratio(0) - {} + : distance_to_boundary(-1) + , bead_count(-1) + , transition_ratio(0) + { + } bool hasBeading() const { @@ -53,7 +57,6 @@ class SkeletalTrapezoidationJoint } private: - std::weak_ptr beading; }; diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index 942345ccc1..edd5e48d6e 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -275,7 +275,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe Mesh& mesh = scene.current_mesh_group->meshes[meshIdx]; // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes - storage.meshes.push_back(std::make_shared(& meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh + storage.meshes.push_back(std::make_shared(&meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh auto& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index d9e56458c4..9cc4c7d404 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -2,6 +2,7 @@ // CuraEngine is released under the terms of the AGPLv3 or higher #include "TreeSupport.h" + #include "Application.h" //To get settings. #include "TreeSupportTipGenerator.h" #include "TreeSupportUtils.h" @@ -17,20 +18,20 @@ #include "utils/polygonUtils.h" //For moveInside. #include "utils/section_type.h" -#include -#include -#include #include #include #include #include +#include #include + +#include +#include +#include #include #include #include -#include - namespace cura { @@ -41,10 +42,10 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) for (const auto& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; - TreeSupportSettings::some_model_contains_thick_roof |= - mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); - TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= - mesh.settings.get("support_top_distance") == 0 || mesh.settings.get("support_bottom_distance") == 0 || mesh.settings.get("min_feature_size") < (FUDGE_LENGTH * 2); + TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); + TreeSupportSettings::has_to_rely_on_min_xy_dist_only |= mesh.settings.get("support_top_distance") == 0 + || mesh.settings.get("support_bottom_distance") == 0 + || mesh.settings.get("min_feature_size") < (FUDGE_LENGTH * 2); } // Group all meshes that can be processed together. NOTE this is different from mesh-groups! @@ -68,8 +69,10 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { added = true; grouped_mesh.second.emplace_back(mesh_idx); - // Handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it drastically. - grouped_mesh.first.performance_interface_skip_layers = std::min(grouped_mesh.first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers); + // Handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it + // drastically. + grouped_mesh.first.performance_interface_skip_layers + = std::min(grouped_mesh.first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers); } } if (! added) @@ -95,8 +98,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) mesh.first.setActualZ(known_z); } - placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(),Polygons()); - + placed_support_lines_support_areas = std::vector(storage.support.supportLayers.size(), Polygons()); } void TreeSupport::generateSupportAreas(SliceDataStorage& storage) @@ -112,12 +114,14 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) } // Process every mesh group. These groups can not be processed parallel, as the processing in each group is parallelized, and nested parallelization is disables and slow. - for (auto [counter, processing] : grouped_meshes | ranges::views::enumerate ) + for (auto [counter, processing] : grouped_meshes | ranges::views::enumerate) { // process each combination of meshes - std::vector> move_bounds(storage.support.supportLayers.size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. + std::vector> move_bounds( + storage.support.supportLayers + .size()); // Value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in drawAreas. - additional_required_support_area=std::vector(storage.support.supportLayers.size(),Polygons()); + additional_required_support_area = std::vector(storage.support.supportLayers.size(), Polygons()); spdlog::info("Processing support tree mesh group {} of {} containing {} meshes.", counter + 1, grouped_meshes.size(), grouped_meshes[counter].second.size()); @@ -125,8 +129,7 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) auto t_start = std::chrono::high_resolution_clock::now(); // get all already existing support areas and exclude them - cura::parallel_for - ( + cura::parallel_for( LayerIndex(0), LayerIndex(storage.support.supportLayers.size()), [&](const LayerIndex layer_idx) @@ -140,12 +143,20 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) } exclude[layer_idx] = exlude_at_layer.unionPolygons(); scripta::log("tree_support_exclude", exclude[layer_idx], SectionType::SUPPORT, layer_idx); - } - ); - config = processing.first; // This struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generateInitialAreas have knowledge of the existence of multiple meshes being processed. + }); + config = processing.first; // This struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generateInitialAreas have knowledge of + // the existence of multiple meshes being processed. progress_multiplier = 1.0 / double(grouped_meshes.size()); progress_offset = counter == 0 ? 0 : TREE_PROGRESS_TOTAL * (double(counter) * progress_multiplier); - volumes_ = TreeModelVolumes(storage, config.maximum_move_distance, config.maximum_move_distance_slow, config.support_line_width / 2, processing.second.front(), progress_multiplier, progress_offset, exclude); + volumes_ = TreeModelVolumes( + storage, + config.maximum_move_distance, + config.maximum_move_distance_slow, + config.support_line_width / 2, + processing.second.front(), + progress_multiplier, + progress_offset, + exclude); // ### Precalculate avoidances, collision etc. precalculate(storage, processing.second); @@ -176,19 +187,18 @@ void TreeSupport::generateSupportAreas(SliceDataStorage& storage) const auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); const auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); const auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); - spdlog::info - ( + spdlog::info( "Total time used creating Tree support for the currently grouped meshes: {} ms. Different subtasks:\n" - "Calculating Avoidance: {} ms Creating inital influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as support {} ms", + "Calculating Avoidance: {} ms Creating inital influence areas: {} ms Influence area creation: {} ms Placement of Points in InfluenceAreas: {} ms Drawing result as " + "support {} ms", dur_total, dur_pre_gen, dur_gen, dur_path, dur_place, - dur_draw - ); + dur_draw); + - for (auto& layer : move_bounds) { for (auto elem : layer) @@ -212,7 +222,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector("layer_height"); const coord_t z_distance_top = mesh.settings.get("support_top_distance"); const size_t z_distance_top_layers = round_up_divide(z_distance_top, - layer_height) + 1; // Support must always be 1 layer below overhang. + layer_height) + 1; // Support must always be 1 layer below overhang. if (mesh.overhang_areas.size() <= z_distance_top_layers) { continue; @@ -222,7 +232,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector max_layer) // iterates over multiple meshes { @@ -244,8 +254,7 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector tip_gen.generateTips(storage, mesh, move_bounds, additional_required_support_area, placed_support_lines_support_areas); } -void TreeSupport::mergeHelper -( +void TreeSupport::mergeHelper( std::map& reduced_aabb, std::map& input_aabb, const PropertyAreasUnordered& to_bp_areas, @@ -254,12 +263,14 @@ void TreeSupport::mergeHelper PropertyAreasUnordered& insert_bp_areas, PropertyAreasUnordered& insert_model_areas, PropertyAreasUnordered& insert_influence, - std::vector& erase, const LayerIndex layer_idx -) + std::vector& erase, + const LayerIndex layer_idx) { const bool first_merge_iteration = reduced_aabb.empty(); // If this is the first iteration, all elements in input have to be merged with each other - const std::function getRadiusFunction = - [&](const size_t distance_to_top, const double buildplate_radius_increases) { return config.getRadius(distance_to_top, buildplate_radius_increases); }; + const std::function getRadiusFunction = [&](const size_t distance_to_top, const double buildplate_radius_increases) + { + return config.getRadius(distance_to_top, buildplate_radius_increases); + }; for (auto& influence : input_aabb) { bool merged = false; @@ -271,7 +282,7 @@ void TreeSupport::mergeHelper AABB aabb = reduced_check.second; if (aabb.hit(influence_aabb)) { - if (!first_merge_iteration && input_aabb.count(reduced_check.first)) + if (! first_merge_iteration && input_aabb.count(reduced_check.first)) { break; // Do not try to merge elements that already should have been merged. Done for potential performance improvement. } @@ -280,14 +291,15 @@ void TreeSupport::mergeHelper // ^^^ We do not want to merge a gracious with a non gracious area as bad placement could negatively impact the dependability of the whole subtree. const bool merging_to_bp = reduced_check.first.to_buildplate && influence.first.to_buildplate; const bool merging_min_and_regular_xy = reduced_check.first.use_min_xy_dist != influence.first.use_min_xy_dist; - // ^^^ Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased by the delta to the larger it is engulfed by it already. + // ^^^ Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased by the delta to the larger it is engulfed by it + // already. // But because a different collision may be removed from the in drawArea generated circles, this assumption could be wrong. - const bool merging_different_range_limits = - reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; + const bool merging_different_range_limits = reduced_check.first.influence_area_limit_active && influence.first.influence_area_limit_active + && influence.first.influence_area_limit_range != reduced_check.first.influence_area_limit_range; coord_t increased_to_model_radius = 0; size_t larger_to_model_dtt = 0; - if (!merging_to_bp) + if (! merging_to_bp) { const coord_t infl_radius = config.getRadius(influence.first); // Get the real radius increase as the user does not care for the collision model. const coord_t redu_radius = config.getRadius(reduced_check.first); @@ -313,14 +325,9 @@ void TreeSupport::mergeHelper // If a merge could place a stable branch on unstable ground, would be increasing the radius further than allowed to when merging to model and to_bp trees or // would merge to model before it is known they will even been drawn the merge is skipped - if - ( - merging_min_and_regular_xy || - merging_gracious_and_non_gracious || - increased_to_model_radius > config.max_to_model_radius_increase || - (!merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && !reduced_check.first.supports_roof && !influence.first.supports_roof) || - merging_different_range_limits - ) + if (merging_min_and_regular_xy || merging_gracious_and_non_gracious || increased_to_model_radius > config.max_to_model_radius_increase + || (! merging_to_bp && larger_to_model_dtt < config.min_dtt_to_model && ! reduced_check.first.supports_roof && ! influence.first.supports_roof) + || merging_different_range_limits) { continue; } @@ -329,41 +336,29 @@ void TreeSupport::mergeHelper Polygons relevant_redu; if (merging_to_bp) { - relevant_infl = to_bp_areas.count(influence.first) ? to_bp_areas.at(influence.first) : Polygons(); // influence.first is a new element => not required to check if it was changed - relevant_redu = - insert_bp_areas.count - ( - reduced_check.first) ? - insert_bp_areas[reduced_check.first] : - (to_bp_areas.count(reduced_check.first) ? to_bp_areas.at(reduced_check.first) : Polygons() - ); + relevant_infl = to_bp_areas.count(influence.first) ? to_bp_areas.at(influence.first) + : Polygons(); // influence.first is a new element => not required to check if it was changed + relevant_redu = insert_bp_areas.count(reduced_check.first) ? insert_bp_areas[reduced_check.first] + : (to_bp_areas.count(reduced_check.first) ? to_bp_areas.at(reduced_check.first) : Polygons()); } else { relevant_infl = to_model_areas.count(influence.first) ? to_model_areas.at(influence.first) : Polygons(); - relevant_redu = - insert_model_areas.count - ( - reduced_check.first) ? - insert_model_areas[reduced_check.first] : - (to_model_areas.count(reduced_check.first) ? to_model_areas.at(reduced_check.first) : Polygons() - ); + relevant_redu = insert_model_areas.count(reduced_check.first) + ? insert_model_areas[reduced_check.first] + : (to_model_areas.count(reduced_check.first) ? to_model_areas.at(reduced_check.first) : Polygons()); } const bool red_bigger = config.getCollisionRadius(reduced_check.first) > config.getCollisionRadius(influence.first); - std::pair smaller_rad = - red_bigger ? - std::pair(influence.first, relevant_infl) : - std::pair(reduced_check.first, relevant_redu); - std::pair bigger_rad = - red_bigger ? - std::pair(reduced_check.first, relevant_redu) : - std::pair(influence.first, relevant_infl); + std::pair smaller_rad = red_bigger ? std::pair(influence.first, relevant_infl) + : std::pair(reduced_check.first, relevant_redu); + std::pair bigger_rad = red_bigger ? std::pair(reduced_check.first, relevant_redu) + : std::pair(influence.first, relevant_infl); const coord_t real_radius_delta = std::abs(config.getRadius(bigger_rad.first) - config.getRadius(smaller_rad.first)); const coord_t smaller_collision_radius = config.getCollisionRadius(smaller_rad.first); // the area of the bigger radius is used to ensure correct placement regarding the relevant avoidance, so if that would change an invalid area may be created - if (!bigger_rad.first.can_use_safe_radius && smaller_rad.first.can_use_safe_radius) + if (! bigger_rad.first.can_use_safe_radius && smaller_rad.first.can_use_safe_radius) { continue; } @@ -378,23 +373,23 @@ void TreeSupport::mergeHelper // a branch (of the larger collision radius) placed in this intersection, has already engulfed the branch of the smaller collision radius. // Because of this a merge may happen even if the influence areas (that represent possible center points of branches) do not intersect yet. // Remember that collision radius <= real radius as otherwise this assumption would be false. - const Polygons small_rad_increased_by_big_minus_small = - TreeSupportUtils::safeOffsetInc - ( - smaller_rad.second, - real_radius_delta, volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), - 2 * (config.xy_distance + smaller_collision_radius - EPSILON), // Epsilon avoids possible rounding errors - 0, - 0, - config.support_line_distance / 2, - &config.simplifier - ); + const Polygons small_rad_increased_by_big_minus_small = TreeSupportUtils::safeOffsetInc( + smaller_rad.second, + real_radius_delta, + volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), + 2 * (config.xy_distance + smaller_collision_radius - EPSILON), // Epsilon avoids possible rounding errors + 0, + 0, + config.support_line_distance / 2, + &config.simplifier); Polygons intersect = small_rad_increased_by_big_minus_small.intersection(bigger_rad.second); - if (intersect.area() > 1) // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) + if (intersect.area() + > 1) // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) { - // Check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change it. - if (intersect.offset(-FUDGE_LENGTH/2).area() <= 1) + // Check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change + // it. + if (intersect.offset(-FUDGE_LENGTH / 2).area() <= 1) { continue; } @@ -415,8 +410,7 @@ void TreeSupport::mergeHelper increased_to_model_radius = std::max(reduced_check.first.increased_to_model_radius, influence.first.increased_to_model_radius); } - const TreeSupportElement key - ( + const TreeSupportElement key( reduced_check.first, influence.first, layer_idx - 1, @@ -425,31 +419,30 @@ void TreeSupport::mergeHelper getRadiusFunction, config.diameter_scale_bp_radius, config.branch_radius, - config.diameter_angle_scale_factor - ); + config.diameter_angle_scale_factor); - const auto getIntersectInfluence = - [&] (const PropertyAreasUnordered& insert_infl, const PropertyAreas& infl_areas) - { - const Polygons infl_small = insert_infl.count(smaller_rad.first) ? insert_infl.at(smaller_rad.first) : (infl_areas.count(smaller_rad.first) ? infl_areas.at(smaller_rad.first) : Polygons()); - const Polygons infl_big = insert_infl.count(bigger_rad.first) ? insert_infl.at(bigger_rad.first) : (infl_areas.count(bigger_rad.first) ? infl_areas.at(bigger_rad.first) : Polygons()); - const Polygons small_rad_increased_by_big_minus_small_infl = - TreeSupportUtils::safeOffsetInc - ( - infl_small, - real_radius_delta, - volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), - 2 * (config.xy_distance + smaller_collision_radius - EPSILON), - 0, - 0, - config.support_line_distance / 2, - &config.simplifier - ); - return small_rad_increased_by_big_minus_small_infl.intersection(infl_big); // If the one with the bigger radius with the lower radius removed overlaps we can merge. - }; + const auto getIntersectInfluence = [&](const PropertyAreasUnordered& insert_infl, const PropertyAreas& infl_areas) + { + const Polygons infl_small = insert_infl.count(smaller_rad.first) ? insert_infl.at(smaller_rad.first) + : (infl_areas.count(smaller_rad.first) ? infl_areas.at(smaller_rad.first) : Polygons()); + const Polygons infl_big = insert_infl.count(bigger_rad.first) ? insert_infl.at(bigger_rad.first) + : (infl_areas.count(bigger_rad.first) ? infl_areas.at(bigger_rad.first) : Polygons()); + const Polygons small_rad_increased_by_big_minus_small_infl = TreeSupportUtils::safeOffsetInc( + infl_small, + real_radius_delta, + volumes_.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius), + 2 * (config.xy_distance + smaller_collision_radius - EPSILON), + 0, + 0, + config.support_line_distance / 2, + &config.simplifier); + return small_rad_increased_by_big_minus_small_infl.intersection( + infl_big); // If the one with the bigger radius with the lower radius removed overlaps we can merge. + }; Polygons intersect_influence; - intersect_influence = TreeSupportUtils::safeUnion(intersect, getIntersectInfluence(insert_influence, influence_areas)); // Rounding errors again. Do not ask me where or why. + intersect_influence + = TreeSupportUtils::safeUnion(intersect, getIntersectInfluence(insert_influence, influence_areas)); // Rounding errors again. Do not ask me where or why. Polygons intersect_to_model; if (merging_to_bp && config.support_rests_on_model) @@ -475,8 +468,10 @@ void TreeSupport::mergeHelper erase.emplace_back(reduced_check.first); erase.emplace_back(influence.first); - const Polygons merge = intersect.unionPolygons(intersect_to_model).offset(config.getRadius(key), ClipperLib::jtRound).difference(volumes_.getCollision(0, layer_idx - 1)); - // ^^^ Regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). + const Polygons merge + = intersect.unionPolygons(intersect_to_model).offset(config.getRadius(key), ClipperLib::jtRound).difference(volumes_.getCollision(0, layer_idx - 1)); + // ^^^ Regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a + // negative area.). // And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer. reduced_aabb.erase(reduced_check.first); // This invalidates reduced_check. @@ -495,13 +490,7 @@ void TreeSupport::mergeHelper } } -void TreeSupport::mergeInfluenceAreas -( - PropertyAreasUnordered& to_bp_areas, - PropertyAreas& to_model_areas, - PropertyAreas& influence_areas, - LayerIndex layer_idx -) +void TreeSupport::mergeInfluenceAreas(PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, LayerIndex layer_idx) { /* * Idea behind this is that the calculation of merges can be accelerated a bit using divide and conquer: @@ -521,13 +510,15 @@ void TreeSupport::mergeInfluenceAreas // max_bucket_count is input_size/min_elements_per_bucket round down to the next 2^n. // The rounding to 2^n is to ensure improved performance, as every iteration two buckets will be merged, halving the amount of buckets. - // If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be optimal performance-wise. + // If halving would cause an uneven count, e.g. 3 Then bucket 0 and 1 would have to be merged, and in the next iteration the last remaining buckets. This is assumed to not be + // optimal performance-wise. const size_t max_bucket_count = std::pow(2, std::floor(std::log(round_up_divide(input_size, min_elements_per_bucket)))); int bucket_count = std::min(max_bucket_count, num_threads); // do not use more buckets than available threads. // To achieve that every element in a bucket is already correctly merged with other elements in this bucket // an extra empty bucket is created for each bucket, and the elements are merged into the empty one. - // Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the same bucket in this case. + // Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the + // same bucket in this case. std::vector buckets_area(2 * bucket_count); std::vector> buckets_aabb(2 * bucket_count); @@ -551,8 +542,7 @@ void TreeSupport::mergeInfluenceAreas } // Precalculate the AABBs from the influence areas. - cura::parallel_for - ( + cura::parallel_for( 0, buckets_area.size() / 2, [&](size_t idx) // +=2 as in the beginning only uneven buckets will be filled @@ -564,8 +554,7 @@ void TreeSupport::mergeInfluenceAreas outer_support_wall_aabb.expand(config.getRadius(input_pair.first)); buckets_aabb[idx].emplace(input_pair.first, outer_support_wall_aabb); } - } - ); + }); while (buckets_area.size() > 1) { @@ -575,16 +564,14 @@ void TreeSupport::mergeInfluenceAreas std::vector insert_influence(buckets_area.size() / 2); std::vector> erase(buckets_area.size() / 2); - cura::parallel_for - ( + cura::parallel_for( 0, (coord_t)buckets_area.size() / 2, [&](size_t bucket_pair_idx) { bucket_pair_idx *= 2; // this is eqivalent to a parallel for(size_t idx=0;idx x) mutable { return x.empty(); }); + const auto position_aabb = std::remove_if( + buckets_aabb.begin(), + buckets_aabb.end(), + [&](const std::map x) mutable + { + return x.empty(); + }); buckets_aabb.erase(position_aabb, buckets_aabb.end()); } } -std::optional TreeSupport::increaseSingleArea -( +std::optional TreeSupport::increaseSingleArea( AreaIncreaseSettings settings, LayerIndex layer_idx, TreeSupportElement* parent, const Polygons& relevant_offset, - Polygons& to_bp_data, Polygons& to_model_data, + Polygons& to_bp_data, + Polygons& to_model_data, Polygons& increased, const coord_t overspeed, - const bool mergelayer -) + const bool mergelayer) { TreeSupportElement current_elem(parent); // Also increases DTT by one. Polygons check_layer_data; @@ -659,22 +655,19 @@ std::optional TreeSupport::increaseSingleArea increased = relevant_offset; if (overspeed > 0) { - const coord_t safe_movement_distance = - (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + const coord_t safe_movement_distance = (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later. // The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later. - increased = - TreeSupportUtils::safeOffsetInc - ( - increased, - overspeed, - volumes_.getWallRestriction(config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist), - safe_movement_distance, - safe_movement_distance + radius, - 1, - config.support_line_distance / 2, - nullptr - ); + increased = TreeSupportUtils::safeOffsetInc( + increased, + overspeed, + volumes_.getWallRestriction(config.getCollisionRadius(*parent), layer_idx, parent->use_min_xy_dist), + safe_movement_distance, + safe_movement_distance + radius, + 1, + config.support_line_distance / 2, + nullptr); } if (settings.no_error && settings.move) { @@ -711,7 +704,8 @@ std::optional TreeSupport::increaseSingleArea } else { - to_model_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, AvoidanceType::COLLISION, true, settings.use_min_distance))); + to_model_data + = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, AvoidanceType::COLLISION, true, settings.use_min_distance))); } } } @@ -720,29 +714,35 @@ std::optional TreeSupport::increaseSingleArea if (settings.increase_radius && check_layer_data.area() > 1) { - std::function validWithRadius = - [&](coord_t next_radius) + std::function validWithRadius = [&](coord_t next_radius) + { + if (volumes_.ceilRadius(next_radius, settings.use_min_distance) <= volumes_.ceilRadius(radius, settings.use_min_distance)) { - if (volumes_.ceilRadius(next_radius, settings.use_min_distance) <= volumes_.ceilRadius(radius, settings.use_min_distance)) - { - return true; - } + return true; + } - Polygons to_bp_data_2; - if (current_elem.to_buildplate) - { - // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. - to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); - } - Polygons to_model_data_2; - if (config.support_rests_on_model && !current_elem.to_buildplate) - { - to_model_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, current_elem.to_model_gracious? settings.type:AvoidanceType::COLLISION, true, settings.use_min_distance)).unionPolygons(); - } - Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; + Polygons to_bp_data_2; + if (current_elem.to_buildplate) + { + // Regular union as output will not be used later => this area should always be a subset of the safeUnion one. + to_bp_data_2 = increased.difference(volumes_.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)).unionPolygons(); + } + Polygons to_model_data_2; + if (config.support_rests_on_model && ! current_elem.to_buildplate) + { + to_model_data_2 = increased + .difference(volumes_.getAvoidance( + next_radius, + layer_idx - 1, + current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, + true, + settings.use_min_distance)) + .unionPolygons(); + } + Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; - return check_layer_data_2.area() > 1; - }; + return check_layer_data_2.area() > 1; + }; coord_t ceil_radius_before = volumes_.ceilRadius(radius, settings.use_min_distance); // If the Collision Radius is smaller than the actual radius, check if it can catch up without violating the avoidance. @@ -756,18 +756,15 @@ std::optional TreeSupport::increaseSingleArea current_ceil_radius = volumes_.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance); } size_t resulting_eff_dtt = current_elem.effective_radius_height; - while - ( - resulting_eff_dtt + 1 < current_elem.distance_to_top && - config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= current_ceil_radius && - config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= config.getRadius(current_elem) - ) + while (resulting_eff_dtt + 1 < current_elem.distance_to_top && config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= current_ceil_radius + && config.getRadius(resulting_eff_dtt + 1, current_elem.buildplate_radius_increases) <= config.getRadius(current_elem)) { resulting_eff_dtt++; } current_elem.effective_radius_height = resulting_eff_dtt; - // If catchup is not possible, it is likely that there is a hole below. Assuming the branches are in some kind of bowl, the branches should still stay away from the wall of the bowl if possible. + // If catchup is not possible, it is likely that there is a hole below. Assuming the branches are in some kind of bowl, the branches should still stay away from the + // wall of the bowl if possible. if (config.getCollisionRadius(current_elem) < config.increase_radius_until_radius && config.getCollisionRadius(current_elem) < config.getRadius(current_elem)) { Polygons new_to_bp_data; @@ -781,7 +778,7 @@ std::optional TreeSupport::increaseSingleArea to_bp_data = new_to_bp_data; } } - if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) + if (config.support_rests_on_model && (! current_elem.to_buildplate || mergelayer)) { new_to_model_data = to_model_data.difference(volumes_.getCollision(config.getRadius(current_elem), layer_idx - 1, current_elem.use_min_xy_dist)); if (new_to_model_data.area() > EPSILON) @@ -790,21 +787,24 @@ std::optional TreeSupport::increaseSingleArea } } } - } radius = config.getCollisionRadius(current_elem); const coord_t foot_radius_increase = config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)); const double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase); - // ^^^ Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become bigger than precalculated. + // ^^^ Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become + // bigger than precalculated. - // If the support_rest_preference is GRACEFUL, increase buildplate_radius_increases anyway. This does ONLY affect the CollisionRadius, as the regular radius only includes the buildplate_radius_increases when the SupportElement is to_buildplate (which it can not be when support_rest_preference is GRACEFUL). - // If the branch later rests on the buildplate the to_buildplate flag will only need to be updated to ensure that the radius is also correctly increased. - // Downside is that the enlargement of the CollisionRadius can cause branches, that could rest on the model if the radius was not increased, to instead rest on the buildplate. - // A better way could be changing avoidance to model to not include the buildplate and then calculate avoidances by combining the to model avoidance without the radius increase with the to buildplate avoidance with the larger radius. - // This would require ensuring all requests for the avoidance would have to ensure that the correct hybrid avoidance is requested (which would only be relevant when support_rest_preference is GRACEFUL) - // Also unioning areas when an avoidance is requested may also have a relevant performance impact, so there can be an argument made that the current workaround is preferable. - const bool increase_bp_foot = planned_foot_increase > 0 && (current_elem.to_buildplate || (current_elem.to_model_gracious && config.support_rest_preference == RestPreference::GRACEFUL)); + // If the support_rest_preference is GRACEFUL, increase buildplate_radius_increases anyway. This does ONLY affect the CollisionRadius, as the regular radius only includes + // the buildplate_radius_increases when the SupportElement is to_buildplate (which it can not be when support_rest_preference is GRACEFUL). If the branch later rests on the + // buildplate the to_buildplate flag will only need to be updated to ensure that the radius is also correctly increased. Downside is that the enlargement of the + // CollisionRadius can cause branches, that could rest on the model if the radius was not increased, to instead rest on the buildplate. A better way could be changing + // avoidance to model to not include the buildplate and then calculate avoidances by combining the to model avoidance without the radius increase with the to buildplate + // avoidance with the larger radius. This would require ensuring all requests for the avoidance would have to ensure that the correct hybrid avoidance is requested (which + // would only be relevant when support_rest_preference is GRACEFUL) Also unioning areas when an avoidance is requested may also have a relevant performance impact, so there + // can be an argument made that the current workaround is preferable. + const bool increase_bp_foot + = planned_foot_increase > 0 && (current_elem.to_buildplate || (current_elem.to_model_gracious && config.support_rest_preference == RestPreference::GRACEFUL)); if (increase_bp_foot && config.getRadius(current_elem) >= config.branch_radius && config.getRadius(current_elem) >= config.increase_radius_until_radius) @@ -822,26 +822,30 @@ std::optional TreeSupport::increaseSingleArea { to_bp_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); } - if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) + if (config.support_rests_on_model && (! current_elem.to_buildplate || mergelayer)) { - to_model_data = TreeSupportUtils::safeUnion(increased.difference(volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious? settings.type:AvoidanceType::COLLISION, true, settings.use_min_distance))); + to_model_data = TreeSupportUtils::safeUnion(increased.difference( + volumes_.getAvoidance(radius, layer_idx - 1, current_elem.to_model_gracious ? settings.type : AvoidanceType::COLLISION, true, settings.use_min_distance))); } check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; if (check_layer_data.area() < 1) { - spdlog::error("Lost area by doing catch up from {} to radius {}", ceil_radius_before, volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance)); + spdlog::error( + "Lost area by doing catch up from {} to radius {}", + ceil_radius_before, + volumes_.ceilRadius(config.getCollisionRadius(current_elem), settings.use_min_distance)); } } } - if (current_elem.influence_area_limit_active && !current_elem.use_min_xy_dist && check_layer_data.area() > 1 && (current_elem.to_model_gracious || current_elem.distance_to_top <= config.min_dtt_to_model)) + if (current_elem.influence_area_limit_active && ! current_elem.use_min_xy_dist && check_layer_data.area() > 1 + && (current_elem.to_model_gracious || current_elem.distance_to_top <= config.min_dtt_to_model)) { - const coord_t max_radius_increase = - std::max - ( - static_cast((config.branch_radius - config.min_radius) / config.tip_layers), - static_cast((config.branch_radius * config.diameter_angle_scale_factor) + config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0))) - ); + const coord_t max_radius_increase = std::max( + static_cast((config.branch_radius - config.min_radius) / config.tip_layers), + static_cast( + (config.branch_radius * config.diameter_angle_scale_factor) + + config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)))); bool limit_range_validated = false; // Rounding errors in a while loop can cause non-termination, so better safe than sorry. See https://github.com/Ultimaker/Cura/issues/14133 for an example. to_bp_data = TreeSupportUtils::safeUnion(to_bp_data); @@ -868,7 +872,7 @@ std::optional TreeSupport::increaseSingleArea limit_range_validated = true; } } - if (!limit_range_validated) + if (! limit_range_validated) { const coord_t reach_increase = std::max(current_elem.influence_area_limit_range / 4, (config.maximum_move_distance + max_radius_increase)); current_elem.influence_area_limit_range += reach_increase; @@ -880,19 +884,17 @@ std::optional TreeSupport::increaseSingleArea return check_layer_data.area() > 1 ? std::optional(current_elem) : std::optional(); } -void TreeSupport::increaseAreas -( +void TreeSupport::increaseAreas( PropertyAreasUnordered& to_bp_areas, PropertyAreas& to_model_areas, PropertyAreas& influence_areas, std::vector& bypass_merge_areas, const std::vector& last_layer, - const LayerIndex layer_idx, const bool mergelayer -) + const LayerIndex layer_idx, + const bool mergelayer) { std::mutex critical_sections; - cura::parallel_for - ( + cura::parallel_for( 0, last_layer.size(), [&](const size_t idx) @@ -912,25 +914,28 @@ void TreeSupport::increaseAreas // As the branch may have become larger the distance between these 2 walls is smaller than the distance of the center points. // These extra distance is added to the movement distance possible for this layer. - coord_t extra_speed = EPSILON; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause issues at VERY VERY small layer heights. + coord_t extra_speed = EPSILON; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause + // issues at VERY VERY small layer heights. coord_t extra_slow_speed = 0; // Only added to the slow movement distance. const coord_t ceiled_parent_radius = volumes_.ceilRadius(config.getCollisionRadius(*parent), parent->use_min_xy_dist); const coord_t projected_radius_increased = config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases); const coord_t projected_radius_delta = projected_radius_increased - config.getCollisionRadius(*parent); - // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the xy_min_distance) of the layer above and below like this (d = blocked area because of z distance): + // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the + // xy_min_distance) of the layer above and below like this (d = blocked area because of z distance): /* * layer z+1:dddddiiiiiioooo * layer z+0:xxxxxdddddddddd * layer z-1:dddddxxxxxxxxxx * For more detailed visualisation see calculateWallRestrictions */ - const coord_t safe_movement_distance = - (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + - (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); - if (ceiled_parent_radius == volumes_.ceilRadius(projected_radius_increased, parent->use_min_xy_dist) || projected_radius_increased < config.increase_radius_until_radius) + const coord_t safe_movement_distance = (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); + if (ceiled_parent_radius == volumes_.ceilRadius(projected_radius_increased, parent->use_min_xy_dist) + || projected_radius_increased < config.increase_radius_until_radius) { - // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall + // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of + // the slower moving wall extra_speed += projected_radius_delta; } else @@ -940,16 +945,20 @@ void TreeSupport::increaseAreas extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed)); } - if (config.layer_start_bp_radius > layer_idx && config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.buildplate_radius_increases)) + if (config.layer_start_bp_radius > layer_idx + && config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.buildplate_radius_increases)) { // Can guarantee elephant foot radius increase. - if (ceiled_parent_radius == volumes_.ceilRadius(config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases + 1), parent->use_min_xy_dist)) + if (ceiled_parent_radius + == volumes_.ceilRadius(config.getRadius(parent->effective_radius_height + 1, parent->buildplate_radius_increases + 1), parent->use_min_xy_dist)) { extra_speed += config.branch_radius * config.diameter_scale_bp_radius; } else { - extra_slow_speed += std::min(coord_t(config.branch_radius * config.diameter_scale_bp_radius), config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed)); + extra_slow_speed += std::min( + coord_t(config.branch_radius * config.diameter_scale_bp_radius), + config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed)); } } @@ -970,60 +979,70 @@ void TreeSupport::increaseAreas // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found std::deque order; - std::function insertSetting = - [&](const AreaIncreaseSettings& settings, bool back) + std::function insertSetting = [&](const AreaIncreaseSettings& settings, bool back) + { + if (std::find(order.begin(), order.end(), settings) == order.end()) { - if (std::find(order.begin(), order.end(), settings) == order.end()) + if (back) { - if (back) - { - order.emplace_back(settings); - } - else - { - order.emplace_front(settings); - } + order.emplace_back(settings); } - }; + else + { + order.emplace_front(settings); + } + } + }; const bool parent_moved_slow = elem.last_area_increase.increase_speed < config.maximum_move_distance; const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::SLOW; - if - ( - elem.last_area_increase.move && - elem.last_area_increase.no_error && - elem.can_use_safe_radius && - ! mergelayer && - ! avoidance_speed_mismatch && - (elem.distance_to_top >= config.tip_layers || parent_moved_slow) - ) + if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && ! mergelayer && ! avoidance_speed_mismatch + && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) { // Assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster. const auto slow_or_fast = elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed; - insertSetting(AreaIncreaseSettings(elem.last_area_increase.type, slow_or_fast, increase_radius, elem.last_area_increase.no_error, ! use_min_radius, elem.last_area_increase.move), true); - insertSetting(AreaIncreaseSettings(elem.last_area_increase.type, slow_or_fast, ! increase_radius, elem.last_area_increase.no_error, ! use_min_radius, elem.last_area_increase.move), true); + insertSetting( + AreaIncreaseSettings( + elem.last_area_increase.type, + slow_or_fast, + increase_radius, + elem.last_area_increase.no_error, + ! use_min_radius, + elem.last_area_increase.move), + true); + insertSetting( + AreaIncreaseSettings( + elem.last_area_increase.type, + slow_or_fast, + ! increase_radius, + elem.last_area_increase.no_error, + ! use_min_radius, + elem.last_area_increase.move), + true); } // Branch may still go though a hole, so a check has to be done whether the hole was already passed, and the regular avoidance can be used. if (! elem.can_use_safe_radius) { // If the radius until which it is always increased can not be guaranteed, move fast. This is to avoid holes smaller than the real branch radius. // This does not guarantee the avoidance of such holes, but ensures they are avoided if possible. - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, !move), true); // Did we go through the hole. + insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); // Did we go through the hole. // In many cases the definition of hole is overly restrictive, so to avoid unnecessary fast movement in the tip, it is ignored there for a bit. // This CAN cause a branch to go though a hole it otherwise may have avoided. if (elem.distance_to_top < round_up_divide(config.tip_layers, 2)) { - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, !move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, slow_speed, increase_radius, no_error, ! use_min_radius, ! move), true); } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, !move), true); // Did we manage to avoid the hole, - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, !increase_radius, no_error, ! use_min_radius, move), true); - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, !increase_radius, no_error, ! use_min_radius, move), true); + insertSetting( + AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, increase_radius, no_error, ! use_min_radius, ! move), + true); // Did we manage to avoid the hole, + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST_SAFE, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, no_error, ! use_min_radius, move), true); } else { insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, increase_radius, no_error, ! use_min_radius, move), true); - // While moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, which looks similar to a layer shift and can reduce stability. - // As such idx have chosen to only use the user setting for radius increases as a friendly recommendation. + // While moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, which looks similar to a + // layer shift and can reduce stability. As such idx have chosen to only use the user setting for radius increases as a friendly recommendation. insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, slow_speed, ! increase_radius, no_error, ! use_min_radius, move), true); // a (See above.) if (elem.distance_to_top < config.tip_layers) { @@ -1046,43 +1065,48 @@ void TreeSupport::increaseAreas order = new_order; } - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move), true); //simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has caused issues + insertSetting( + AreaIncreaseSettings(AvoidanceType::FAST, fast_speed, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), + true); // simplifying is very important for performance, but before an error is compensated by moving faster it makes sense to check to see if the simplifying has + // caused issues // The getAccumulatedPlaceable0 intersection is just a quick and dirty check to see that at least a part of the branch would correctly rest on the model. - // Proper way would be to offset getAccumulatedPlaceable0 by -radius first, but the small benefit to maybe detect an error, that should not be happening anyway is not worth the performance impact in the expected case when a branch rests on the model. - if (elem.to_buildplate || (elem.to_model_gracious && (parent->area->intersection(volumes_.getPlaceableAreas(radius, layer_idx)).empty())) || (!elem.to_model_gracious && (parent->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty())) ) // Error case. + // Proper way would be to offset getAccumulatedPlaceable0 by -radius first, but the small benefit to maybe detect an error, that should not be happening anyway is not + // worth the performance impact in the expected case when a branch rests on the model. + if (elem.to_buildplate || (elem.to_model_gracious && (parent->area->intersection(volumes_.getPlaceableAreas(radius, layer_idx)).empty())) + || (! elem.to_model_gracious && (parent->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx)).empty()))) // Error case. { // It is normal that we won't be able to find a new area at some point in time if we won't be able to reach layer 0 aka have to connect with the model. - insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, !increase_radius, !no_error, elem.use_min_xy_dist, move), true); + insertSetting(AreaIncreaseSettings(AvoidanceType::FAST, fast_speed * 1.5, ! increase_radius, ! no_error, elem.use_min_xy_dist, move), true); } if (elem.distance_to_top < elem.dont_move_until && elem.can_use_safe_radius) // Only do not move when holes would be avoided in every case. { - insertSetting(AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, !use_min_radius, !move), false); // Only do not move when already in a no hole avoidance with the regular xy distance. + insertSetting( + AreaIncreaseSettings(AvoidanceType::SLOW, 0, increase_radius, no_error, ! use_min_radius, ! move), + false); // Only do not move when already in a no hole avoidance with the regular xy distance. } Polygons inc_wo_collision; - // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to calculate the fast one. - // Calculated by comparing the steps saved when calculating independently with the saved steps when not. - const bool offset_independent_faster = - (radius / safe_movement_distance - (((config.maximum_move_distance + extra_speed) < (radius + safe_movement_distance)) ? 1 : 0)) > - (round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance)); + // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to + // calculate the fast one. Calculated by comparing the steps saved when calculating independently with the saved steps when not. + const bool offset_independent_faster = (radius / safe_movement_distance - (((config.maximum_move_distance + extra_speed) < (radius + safe_movement_distance)) ? 1 : 0)) + > (round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance)); for (AreaIncreaseSettings settings : order) { if (settings.move) { if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independent_faster)) { - offset_slow = - TreeSupportUtils::safeOffsetInc - ( - *parent->area, - extra_speed + extra_slow_speed + config.maximum_move_distance_slow, - wall_restriction, - safe_movement_distance, offset_independent_faster ? safe_movement_distance + radius : 0, - 2, // Offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. - config.support_line_distance / 2, - &config.simplifier - ).unionPolygons(); + offset_slow = TreeSupportUtils::safeOffsetInc( + *parent->area, + extra_speed + extra_slow_speed + config.maximum_move_distance_slow, + wall_restriction, + safe_movement_distance, + offset_independent_faster ? safe_movement_distance + radius : 0, + 2, // Offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); // At this point one can see that the Polygons class was never made for precision in the single digit micron range. } @@ -1090,22 +1114,30 @@ void TreeSupport::increaseAreas { if (offset_independent_faster) { - offset_fast = - TreeSupportUtils::safeOffsetInc - ( - *parent->area, - extra_speed + config.maximum_move_distance, - wall_restriction, - safe_movement_distance, offset_independent_faster ? safe_movement_distance + radius : 0, - 1, - config.support_line_distance / 2, - &config.simplifier - ).unionPolygons(); + offset_fast = TreeSupportUtils::safeOffsetInc( + *parent->area, + extra_speed + config.maximum_move_distance, + wall_restriction, + safe_movement_distance, + offset_independent_faster ? safe_movement_distance + radius : 0, + 1, + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); } else { const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed); - offset_fast = TreeSupportUtils::safeOffsetInc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independent_faster ? 2 : 1, config.support_line_distance / 2, &config.simplifier).unionPolygons(); + offset_fast = TreeSupportUtils::safeOffsetInc( + offset_slow, + delta_slow_fast, + wall_restriction, + safe_movement_distance, + safe_movement_distance + radius, + offset_independent_faster ? 2 : 1, + config.support_line_distance / 2, + &config.simplifier) + .unionPolygons(); } } } @@ -1114,20 +1146,22 @@ void TreeSupport::increaseAreas // Check for errors! if (! settings.no_error) { - - // If the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased + // If the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, + // it still actually has an area that can be increased Polygons lines_offset = TreeSupportUtils::toPolylines(*parent->area).offsetPolyLine(EPSILON); Polygons base_error_area = parent->area->unionPolygons(lines_offset); result = increaseSingleArea(settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, settings.increase_speed, mergelayer); - if(fast_speed < settings.increase_speed) + if (fast_speed < settings.increase_speed) { - spdlog::warn - ( + spdlog::warn( "Influence area could not be increased! Data about the Influence area: " - "Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until move {} \n " - "Parent {}: Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until move {}", - radius, layer_idx - 1, + "Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} safe {} until " + "move {} \n " + "Parent {}: Radius: {} at layer: {} NextTarget: {} Distance to top: {} Elephant foot increases {} use_min_xy_dist {} to buildplate {} gracious {} " + "safe {} until move {}", + radius, + layer_idx - 1, elem.next_height, elem.distance_to_top, elem.buildplate_radius_increases, @@ -1138,20 +1172,29 @@ void TreeSupport::increaseAreas elem.dont_move_until, fmt::ptr(parent), config.getCollisionRadius(*parent), - layer_idx, parent->next_height, + layer_idx, + parent->next_height, parent->distance_to_top, parent->buildplate_radius_increases, parent->use_min_xy_dist, parent->to_buildplate, parent->to_model_gracious, parent->can_use_safe_radius, - parent->dont_move_until - ); + parent->dont_move_until); } } else { - result = increaseSingleArea(settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, std::max(settings.increase_speed-fast_speed,coord_t(0)), mergelayer); + result = increaseSingleArea( + settings, + layer_idx, + parent, + settings.increase_speed == slow_speed ? offset_slow : offset_fast, + to_bp_data, + to_model_data, + inc_wo_collision, + std::max(settings.increase_speed - fast_speed, coord_t(0)), + mergelayer); } if (result) @@ -1160,7 +1203,10 @@ void TreeSupport::increaseAreas radius = config.getCollisionRadius(elem); elem.last_area_increase = settings; add = true; - bypass_merge = ! settings.move || (settings.use_min_distance && elem.distance_to_top < config.tip_layers); // Do not merge if the branch should not move or the priority has to be to get farther away from the model. + bypass_merge + = ! settings.move + || (settings.use_min_distance + && elem.distance_to_top < config.tip_layers); // Do not merge if the branch should not move or the priority has to be to get farther away from the model. if (settings.move) { elem.dont_move_until = 0; @@ -1190,7 +1236,9 @@ void TreeSupport::increaseAreas if (add) { - Polygons max_influence_area = TreeSupportUtils::safeUnion(inc_wo_collision.difference(volumes_.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), TreeSupportUtils::safeUnion(to_bp_data, to_model_data)); + Polygons max_influence_area = TreeSupportUtils::safeUnion( + inc_wo_collision.difference(volumes_.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), + TreeSupportUtils::safeUnion(to_bp_data, to_model_data)); // ^^^ Note: union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be { @@ -1222,8 +1270,7 @@ void TreeSupport::increaseAreas // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent->result_on_layer = Point(-1, -1); } - } - ); + }); } void TreeSupport::createLayerPathing(std::vector>& move_bounds) @@ -1241,7 +1288,9 @@ void TreeSupport::createLayerPathing(std::vector>& bool new_element = false; // Ensure at least one merge operation per 3mm height, 50 layers, 1 mm movement of slow speed or 5mm movement of fast speed (whatever is lowest). Values were guessed. - size_t max_merge_every_x_layers = std::min(std::min(5000 / (std::max(config.maximum_move_distance, static_cast(100))), 1000 / std::max(config.maximum_move_distance_slow, static_cast(20))), 3000 / config.layer_height); + size_t max_merge_every_x_layers = std::min( + std::min(5000 / (std::max(config.maximum_move_distance, static_cast(100))), 1000 / std::max(config.maximum_move_distance_slow, static_cast(20))), + 3000 / config.layer_height); size_t merge_every_x_layers = 1; // Calculate the influence areas for each layer below (Top down) @@ -1259,7 +1308,8 @@ void TreeSupport::createLayerPathing(std::vector>& PropertyAreas influence_areas; // Over this map will be iterated when merging, as such it has to be ordered to ensure deterministic results. PropertyAreas to_model_areas; // The area of these SupportElement is not set, to avoid to much allocation and deallocation on the heap. PropertyAreasUnordered to_bp_areas; // Same. - std::vector bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. + std::vector + bypass_merge_areas; // Different to the other maps of SupportElements as these here have the area already set, as they are already to be inserted into move_bounds. const auto time_a = std::chrono::high_resolution_clock::now(); @@ -1279,7 +1329,7 @@ void TreeSupport::createLayerPathing(std::vector>& last_merge = layer_idx; reduced_by_merging = count_before_merge > influence_areas.size(); - if (! reduced_by_merging && !new_element) + if (! reduced_by_merging && ! new_element) { merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1); } @@ -1334,16 +1384,21 @@ void TreeSupport::setPointsOnAreas(const TreeSupportElement* elem) for (TreeSupportElement* next_elem : elem->parents) { - if (next_elem->result_on_layer != Point(-1, -1)) // If the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. + if (next_elem->result_on_layer + != Point(-1, -1)) // If the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. { continue; } Point from = elem->result_on_layer; - if (!(next_elem->area->inside(from, true))) + if (! (next_elem->area->inside(from, true))) { - PolygonUtils::moveInside(*next_elem->area, from, 0); // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 - // It is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. While this seems like a problem it may for example occur after merges. + PolygonUtils::moveInside( + *next_elem->area, + from, + 0); // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 + // It is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. While this seems like a problem it may for example + // occur after merges. } next_elem->result_on_layer = from; // Do not call recursive because then amount of layers would be restricted by the stack size. @@ -1367,7 +1422,8 @@ bool TreeSupport::setToModelContact(std::vector>& Polygons valid_place_area; - // Check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest up layer index. + // Check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest + // up layer index. for (LayerIndex layer_check = layer_idx; check->next_height >= layer_check; layer_check++) { Polygons check_valid_place_area = check->area->intersection(volumes_.getPlaceableAreas(config.getCollisionRadius(*check), layer_check)); @@ -1411,7 +1467,8 @@ bool TreeSupport::setToModelContact(std::vector>& } } - for (LayerIndex layer = layer_idx + 1; layer < last_successfull_layer - 1; ++layer) // NOTE: Use of 'itoa' will make this crash in the loop, even though the operation should be equivalent. + for (LayerIndex layer = layer_idx + 1; layer < last_successfull_layer - 1; + ++layer) // NOTE: Use of 'itoa' will make this crash in the loop, even though the operation should be equivalent. { move_bounds[layer].erase(checked[layer - layer_idx]); delete checked[layer - layer_idx]->area; @@ -1440,29 +1497,36 @@ bool TreeSupport::setToModelContact(std::vector>& else // can not add graceful => just place it here and hope for the best { Point best = first_elem->next_position; - Polygons valid_place_area = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); + Polygons valid_place_area + = first_elem->area->difference(volumes_.getAvoidance(config.getCollisionRadius(first_elem), layer_idx, AvoidanceType::COLLISION, first_elem->use_min_xy_dist)); - if (!valid_place_area.inside(best, true)) + if (! valid_place_area.inside(best, true)) { - if (!valid_place_area.empty()) + if (! valid_place_area.empty()) { PolygonUtils::moveInside(valid_place_area, best); } else { bool found_partial_placement; - for (coord_t radius_offset : { -config.getCollisionRadius(first_elem), -config.getCollisionRadius(first_elem) / 2, coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. + for (coord_t radius_offset : { -config.getCollisionRadius(first_elem), + -config.getCollisionRadius(first_elem) / 2, + coord_t(0) }) // Interestingly the first radius is working most of the time, even though it seems like it shouldn't. { valid_place_area = first_elem->area->intersection(volumes_.getAccumulatedPlaceable0(layer_idx).offset(radius_offset)); - if (!valid_place_area.empty()) + if (! valid_place_area.empty()) { PolygonUtils::moveInside(valid_place_area, best); - spdlog::warn("Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", layer_idx, radius_offset, config.getCollisionRadius(first_elem)); + spdlog::warn( + "Not able to place branch fully on non support blocker at layer {} using offset {} for radius {}", + layer_idx, + radius_offset, + config.getCollisionRadius(first_elem)); found_partial_placement = true; break; } } - if (!found_partial_placement) + if (! found_partial_placement) { PolygonUtils::moveInside(*first_elem->area, best); spdlog::warn("Not able to place branch on non support blocker at layer {}", layer_idx); @@ -1478,7 +1542,8 @@ bool TreeSupport::setToModelContact(std::vector>& void TreeSupport::createNodesFromArea(std::vector>& move_bounds) { - // Initialize points on layer 0, with a "random" point in the influence area. Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result. + // Initialize points on layer 0, with a "random" point in the influence area. Point is chosen based on an inaccurate estimate where the branches will split into two, but every + // point inside the influence area would produce a valid result. std::unordered_set remove; for (TreeSupportElement* init : move_bounds[0]) { @@ -1499,7 +1564,8 @@ void TreeSupport::createNodesFromArea(std::vector> } else { - // If the support_rest_preference is GRACEFUL the collision radius is increased, but the radius will only be increased if the element is to_buildplate, so if the branch rests on the buildplate, the element will have to be updated to include this information. + // If the support_rest_preference is GRACEFUL the collision radius is increased, but the radius will only be increased if the element is to_buildplate, so if the + // branch rests on the buildplate, the element will have to be updated to include this information. init->setToBuildplateForAllParents(true); } } @@ -1520,17 +1586,23 @@ void TreeSupport::createNodesFromArea(std::vector> bool removed = false; if (elem->result_on_layer == Point(-1, -1)) // Check if the resulting center point is not yet set. { - if (elem->to_buildplate || (!elem->to_buildplate && elem->distance_to_top < config.min_dtt_to_model && !elem->supports_roof)) + if (elem->to_buildplate || (! elem->to_buildplate && elem->distance_to_top < config.min_dtt_to_model && ! elem->supports_roof)) { if (elem->to_buildplate) { - spdlog::error("Uninitialized Influence area targeting ({},{}) at target_height: {} layer: {}", elem->target_position.X, elem->target_position.Y, elem->target_height, layer_idx); + spdlog::error( + "Uninitialized Influence area targeting ({},{}) at target_height: {} layer: {}", + elem->target_position.X, + elem->target_position.Y, + elem->target_height, + layer_idx); } remove.emplace(elem); // We dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set. removed = true; for (TreeSupportElement* parent : elem->parents) { - // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set. As this branch needs to be removed => all parents result_on_layer have to be invalidated. + // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set. As this branch needs + // to be removed => all parents result_on_layer have to be invalidated. parent->result_on_layer = Point(-1, -1); } continue; @@ -1546,7 +1618,7 @@ void TreeSupport::createNodesFromArea(std::vector> } } - if (!removed) + if (! removed) { setPointsOnAreas(elem); // Element is valid now setting points in the layer above. } @@ -1563,7 +1635,10 @@ void TreeSupport::createNodesFromArea(std::vector> } } -void TreeSupport::generateBranchAreas(std::vector>& linear_data, std::vector>& layer_tree_polygons, const std::map& inverse_tree_order) +void TreeSupport::generateBranchAreas( + std::vector>& linear_data, + std::vector>& layer_tree_polygons, + const std::map& inverse_tree_order) { double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC; constexpr int progress_report_steps = 10; @@ -1582,136 +1657,135 @@ void TreeSupport::generateBranchAreas(std::vector - ( - 0, - linear_data.size(), - [&](const size_t idx) - { - TreeSupportElement* elem = linear_data[idx].second; - coord_t radius = config.getRadius(*elem); - bool parent_uses_min = false; - TreeSupportElement* child_elem = inverse_tree_order.count(elem) ? inverse_tree_order.at(elem) : nullptr; + cura::parallel_for( + 0, + linear_data.size(), + [&](const size_t idx) + { + TreeSupportElement* elem = linear_data[idx].second; + coord_t radius = config.getRadius(*elem); + bool parent_uses_min = false; + TreeSupportElement* child_elem = inverse_tree_order.count(elem) ? inverse_tree_order.at(elem) : nullptr; - // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. - std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; - if (! elem->skip_ovalisation) + // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. + std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; + if (! elem->skip_ovalisation) + { + if (child_elem != nullptr) { - if (child_elem != nullptr) - { - Point movement = (child_elem->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, radius); - } - for (TreeSupportElement* parent : elem->parents) - { - Point movement = (parent->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(config.getRadius(parent), config.support_line_width)); - parent_uses_min |= parent->use_min_xy_dist; - } - - for (Point target: elem->additional_ovalization_targets) - { - Point movement = (target - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(radius, config.support_line_width)); - } - + Point movement = (child_elem->result_on_layer - elem->result_on_layer); + movement_directions.emplace_back(movement, radius); } - - coord_t max_speed_sqd = 0; - std::function generateArea = - [&](coord_t offset) + for (TreeSupportElement* parent : elem->parents) { - Polygons poly; + Point movement = (parent->result_on_layer - elem->result_on_layer); + movement_directions.emplace_back(movement, std::max(config.getRadius(parent), config.support_line_width)); + parent_uses_min |= parent->use_min_xy_dist; + } - for (std::pair movement : movement_directions) - { - max_speed_sqd = std::max(max_speed_sqd, vSize2(movement.first)); + for (Point target : elem->additional_ovalization_targets) + { + Point movement = (target - elem->result_on_layer); + movement_directions.emplace_back(movement, std::max(radius, config.support_line_width)); + } + } - // Visualization: https://jsfiddle.net/0zvcq39L/2/ - // Ovalizes the circle to an ellipse, that contains both old center and new target position. - double used_scale = (movement.second + offset) / (1.0 * config.branch_radius); - Point center_position = elem->result_on_layer + movement.first / 2; - const double moveX = movement.first.X / (used_scale * config.branch_radius); - const double moveY = movement.first.Y / (used_scale * config.branch_radius); - const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); + coord_t max_speed_sqd = 0; + std::function generateArea = [&](coord_t offset) + { + Polygons poly; - std::array matrix = - { - used_scale * (1 + moveX * moveX * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (1 + moveY * moveY * vsize_inv), - }; - Polygon circle; - for (Point vertex : branch_circle) - { - vertex = Point(matrix[0] * vertex.X + matrix[1] * vertex.Y, matrix[2] * vertex.X + matrix[3] * vertex.Y); - circle.add(center_position + vertex); - } - poly.add(circle.offset(0)); + for (std::pair movement : movement_directions) + { + max_speed_sqd = std::max(max_speed_sqd, vSize2(movement.first)); + + // Visualization: https://jsfiddle.net/0zvcq39L/2/ + // Ovalizes the circle to an ellipse, that contains both old center and new target position. + double used_scale = (movement.second + offset) / (1.0 * config.branch_radius); + Point center_position = elem->result_on_layer + movement.first / 2; + const double moveX = movement.first.X / (used_scale * config.branch_radius); + const double moveY = movement.first.Y / (used_scale * config.branch_radius); + const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); + + std::array matrix = { + used_scale * (1 + moveX * moveX * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (0 + moveX * moveY * vsize_inv), + used_scale * (1 + moveY * moveY * vsize_inv), + }; + Polygon circle; + for (Point vertex : branch_circle) + { + vertex = Point(matrix[0] * vertex.X + matrix[1] * vertex.Y, matrix[2] * vertex.X + matrix[3] * vertex.Y); + circle.add(center_position + vertex); } + poly.add(circle.offset(0)); + } - poly = poly.unionPolygons().offset(std::min(static_cast(FUDGE_LENGTH), config.support_line_width / 4)).difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); - // ^^^ There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. - // This fixes it, and for every other part, 0.05mm will not be noticed. - return poly; - }; + poly = poly.unionPolygons() + .offset(std::min(static_cast(FUDGE_LENGTH), config.support_line_width / 4)) + .difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); + // ^^^ There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly + // further away front the overhang (x/y wise) than optimal. + // This fixes it, and for every other part, 0.05mm will not be noticed. + return poly; + }; - constexpr auto three_quarters_sqd = 0.75 * 0.75; - const bool fast_relative_movement = max_speed_sqd > (radius * radius * three_quarters_sqd); + constexpr auto three_quarters_sqd = 0.75 * 0.75; + const bool fast_relative_movement = max_speed_sqd > (radius * radius * three_quarters_sqd); - // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. - linear_inserts[idx] = generateArea(0); + // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. + linear_inserts[idx] = generateArea(0); - if (fast_relative_movement || config.getRadius(*elem) - config.getCollisionRadius(*elem) > config.support_line_width) + if (fast_relative_movement || config.getRadius(*elem) - config.getCollisionRadius(*elem) > config.support_line_width) + { + // Simulate the path the nozzle will take on the outermost wall. + // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air. + Polygons nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + if (nozzle_path.splitIntoParts(false).size() > 1) { - // Simulate the path the nozzle will take on the outermost wall. - // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air. - Polygons nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + // Just try to make the area a tiny bit larger. + linear_inserts[idx] = generateArea(config.support_line_width / 2); + nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); + + // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best if (nozzle_path.splitIntoParts(false).size() > 1) { - // Just try to make the area a tiny bit larger. - linear_inserts[idx] = generateArea(config.support_line_width / 2); - nozzle_path = linear_inserts[idx].offset(-config.support_line_width / 2); - - // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best - if (nozzle_path.splitIntoParts(false).size() > 1) + Polygons polygons_with_correct_center; + for (PolygonsPart part : nozzle_path.splitIntoParts(false)) { - Polygons polygons_with_correct_center; - for (PolygonsPart part : nozzle_path.splitIntoParts(false)) + if (part.inside(elem->result_on_layer, true)) { - if (part.inside(elem->result_on_layer, true)) + polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); + } + else + { + // Try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... + Point from = elem->result_on_layer; + PolygonUtils::moveInside(part, from, 0); + if (vSize2(elem->result_on_layer - from) < (FUDGE_LENGTH * FUDGE_LENGTH) / 4) { polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); } - else - { - // Try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... - Point from = elem->result_on_layer; - PolygonUtils::moveInside(part, from, 0); - if (vSize2(elem->result_on_layer - from) < (FUDGE_LENGTH * FUDGE_LENGTH) / 4) - { - polygons_with_correct_center = polygons_with_correct_center.unionPolygons(part); - } - } } - // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. - linear_inserts[idx] = polygons_with_correct_center.offset(config.support_line_width / 2).unionPolygons(); - linear_inserts[idx] = linear_inserts[idx].difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)).unionPolygons(); } + // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. + linear_inserts[idx] = polygons_with_correct_center.offset(config.support_line_width / 2).unionPolygons(); + linear_inserts[idx] + = linear_inserts[idx].difference(volumes_.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)).unionPolygons(); } } + } - if (idx % progress_inserts_check_interval == 0) + if (idx % progress_inserts_check_interval == 0) + { { - { - std::lock_guard critical_section_progress(critical_sections); - progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); - } + std::lock_guard critical_section_progress(critical_sections); + progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; + Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * progress_multiplier + progress_offset, TREE_PROGRESS_TOTAL); } } - ); + }); // Single threaded combining all elements to the right layers. Only copies data! for (const coord_t i : ranges::views::iota(0UL, linear_data.size())) @@ -1731,8 +1805,7 @@ void TreeSupport::smoothBranchAreas(std::vector> processing; processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); std::vector>> update_next(processing.size()); // With this a lock can be avoided. - cura::parallel_for - ( + cura::parallel_for( 0, processing.size(), [&](const size_t processing_idx) @@ -1746,10 +1819,13 @@ void TreeSupport::smoothBranchAreas(std::vectorresult_on_layer - parent->result_on_layer) - (config.getRadius(*data_pair.first) - config.getRadius(*parent))); + max_outer_wall_distance = std::max( + max_outer_wall_distance, + vSize(data_pair.first->result_on_layer - parent->result_on_layer) - (config.getRadius(*data_pair.first) - config.getRadius(*parent))); } } - max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. + max_outer_wall_distance + += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. if (do_something) { Polygons max_allowed_area = data_pair.second.offset(max_outer_wall_distance); @@ -1757,12 +1833,12 @@ void TreeSupport::smoothBranchAreas(std::vector(parent, layer_tree_polygons[layer_idx + 1][parent].intersection(max_allowed_area))); + update_next[processing_idx].emplace_back( + std::pair(parent, layer_tree_polygons[layer_idx + 1][parent].intersection(max_allowed_area))); } } } - } - ); + }); for (std::vector> data_vector : update_next) { @@ -1784,10 +1860,11 @@ void TreeSupport::smoothBranchAreas(std::vector> processing; processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end()); - std::vector> update_next(processing.size(), std::pair(nullptr, Polygons())); // With this a lock can be avoided. + std::vector> update_next( + processing.size(), + std::pair(nullptr, Polygons())); // With this a lock can be avoided. - cura::parallel_for - ( + cura::parallel_for( 0, processing.size(), [&](const size_t processing_idx) @@ -1821,8 +1898,7 @@ void TreeSupport::smoothBranchAreas(std::vector(data_pair.first, result); } } - } - ); + }); updated_last_iteration.clear(); for (std::pair data_pair : update_next) @@ -1839,22 +1915,21 @@ void TreeSupport::smoothBranchAreas(std::vector>& layer_tree_polygons, const std::vector>& linear_data, std::vector>>& dropped_down_areas, - const std::map& inverse_tree_order -) + const std::map& inverse_tree_order) { - cura::parallel_for - ( + cura::parallel_for( 0, linear_data.size(), [&](const size_t idx) { TreeSupportElement* elem = linear_data[idx].second; - bool non_gracious_model_contact = ! elem->to_model_gracious && ! inverse_tree_order.count(elem); // If an element has no child, it connects to whatever is below as no support further down for it will exist. + bool non_gracious_model_contact + = ! elem->to_model_gracious + && ! inverse_tree_order.count(elem); // If an element has no child, it connects to whatever is below as no support further down for it will exist. if (non_gracious_model_contact) { Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem].intersection(volumes_.getAccumulatedPlaceable0(linear_data[idx].first)); @@ -1864,8 +1939,7 @@ void TreeSupport::dropNonGraciousAreas dropped_down_areas[idx].emplace_back(linear_data[idx].first - counter, rest_support); } } - } - ); + }); } @@ -1873,8 +1947,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora { const auto t_start = std::chrono::high_resolution_clock::now(); - const coord_t closing_dist=config.support_line_width*config.support_wall_count; - const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size/ 2 - 5 : config.min_wall_line_width/ 2 - 5; // based on calculation in WallToolPath + const coord_t closing_dist = config.support_line_width * config.support_wall_count; + const coord_t open_close_distance = config.fill_outline_gaps ? config.min_feature_size / 2 - 5 : config.min_wall_line_width / 2 - 5; // based on calculation in WallToolPath const double small_area_length = INT2MM(static_cast(config.support_line_width) / 2); std::function reversePolygon = [&](Polygons& poly) @@ -1886,107 +1960,103 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora }; - std::vector support_holes(support_layer_storage.size(),Polygons()); - //Extract all holes as polygon objects - cura::parallel_for - ( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) - { - - - support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))).offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); + std::vector support_holes(support_layer_storage.size(), Polygons()); + // Extract all holes as polygon objects + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + support_layer_storage[layer_idx] = config.simplifier.polygon(PolygonUtils::unionManySmall(support_layer_storage[layer_idx].smooth(FUDGE_LENGTH))) + .offset(-open_close_distance) + .offset(open_close_distance * 2) + .offset(-open_close_distance); + support_layer_storage[layer_idx].removeSmallAreas(small_area_length * small_area_length, false); - std::vector parts = support_layer_storage[layer_idx].sortByNesting(); + std::vector parts = support_layer_storage[layer_idx].sortByNesting(); - if (parts.size() <= 1) - { - return; - } + if (parts.size() <= 1) + { + return; + } - Polygons holes_original; - for (const size_t idx : ranges::views::iota(1UL, parts.size())) - { - Polygons area = parts[idx]; - reversePolygon(area); - holes_original.add(area); - } - support_holes[layer_idx] = holes_original; + Polygons holes_original; + for (const size_t idx : ranges::views::iota(1UL, parts.size())) + { + Polygons area = parts[idx]; + reversePolygon(area); + holes_original.add(area); } - ); + support_holes[layer_idx] = holes_original; + }); const auto t_union = std::chrono::high_resolution_clock::now(); std::vector> holeparts(support_layer_storage.size()); - //Split all holes into parts - cura::parallel_for - ( - 0, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Split all holes into parts + cura::parallel_for( + 0, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + for (Polygons hole : support_holes[layer_idx].splitIntoParts()) { - for (Polygons hole:support_holes[layer_idx].splitIntoParts()) - { - holeparts[layer_idx].emplace_back(hole); - } + holeparts[layer_idx].emplace_back(hole); } - ); - std::vector>> hole_rest_map (holeparts.size()); - std::vector> holes_resting_outside (holeparts.size()); + }); + std::vector>> hole_rest_map(holeparts.size()); + std::vector> holes_resting_outside(holeparts.size()); - //Figure out which hole rests on which other hole - cura::parallel_for - ( - 1, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Figure out which hole rests on which other hole + cura::parallel_for( + 1, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + if (holeparts[layer_idx].empty()) { - if (holeparts[layer_idx].empty()) - { - return; - } + return; + } - Polygons outer_walls = - TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()).tubeShape(closing_dist,0);//.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); + Polygons outer_walls + = TreeSupportUtils::toPolylines(support_layer_storage[layer_idx - 1].getOutsidePolygons()) + .tubeShape(closing_dist, 0); //.unionPolygons(volumes_.getCollision(0, layer_idx - 1, true).offset(-(config.support_line_width+config.xy_min_distance))); - Polygons holes_below; + Polygons holes_below; - for (auto poly: holeparts[layer_idx - 1]) + for (auto poly : holeparts[layer_idx - 1]) + { + holes_below.add(poly); + } + + for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) + { + AABB hole_aabb = AABB(hole); + hole_aabb.expand(EPSILON); + if (! hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls, hole_aabb)).empty()) { - holes_below.add(poly); + holes_resting_outside[layer_idx].emplace(idx); } - - for (auto [idx, hole] : holeparts[layer_idx] | ranges::views::enumerate) + else { - AABB hole_aabb = AABB(hole); - hole_aabb.expand(EPSILON); - if (!hole.intersection(PolygonUtils::clipPolygonWithAABB(outer_walls,hole_aabb)).empty()) - { - holes_resting_outside[layer_idx].emplace(idx); - } - else + for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) { - for (auto [idx2, hole2] : holeparts[layer_idx - 1] | ranges::views::enumerate) + if (hole_aabb.hit(AABB(hole2)) + && ! hole.intersection(hole2).empty()) // TODO should technically be outline: Check if this is fine either way as it would save an offset { - - if (hole_aabb.hit(AABB(hole2)) && ! hole.intersection(hole2).empty() ) // TODO should technically be outline: Check if this is fine either way as it would save an offset - { - hole_rest_map[layer_idx][idx].emplace_back(idx2); - } + hole_rest_map[layer_idx][idx].emplace_back(idx2); } } } } - ); + }); const auto t_hole_rest_ordering = std::chrono::high_resolution_clock::now(); std::unordered_set removed_holes_by_idx; std::vector valid_holes(support_holes.size(), Polygons()); - //Check which holes have to be removed as they do not rest on anything. Only keep holes that have to be removed + // Check which holes have to be removed as they do not rest on anything. Only keep holes that have to be removed for (const size_t layer_idx : ranges::views::iota(1UL, support_holes.size())) { std::unordered_set next_removed_holes_by_idx; @@ -2000,7 +2070,8 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } else { - if(hole_rest_map[layer_idx].contains(idx)){ + if (hole_rest_map[layer_idx].contains(idx)) + { for (size_t resting_idx : hole_rest_map[layer_idx][idx]) { if (! removed_holes_by_idx.contains(resting_idx)) @@ -2025,24 +2096,22 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora } const auto t_hole_removal_tagging = std::chrono::high_resolution_clock::now(); - //Check if holes are so close to each other that two lines will be printed directly next to each other, which is assumed stable (as otherwise the simulated support pattern will not work correctly) and remove all remaining, invalid holes - cura::parallel_for - ( - 1, - support_layer_storage.size(), - [&](const LayerIndex layer_idx) + // Check if holes are so close to each other that two lines will be printed directly next to each other, which is assumed stable (as otherwise the simulated support pattern + // will not work correctly) and remove all remaining, invalid holes + cura::parallel_for( + 1, + support_layer_storage.size(), + [&](const LayerIndex layer_idx) + { + if (holeparts[layer_idx].empty()) { - if (holeparts[layer_idx].empty()) - { - return; - } - - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); - reversePolygon(valid_holes[layer_idx]); - support_layer_storage[layer_idx].add(valid_holes[layer_idx]); + return; } - ); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].getOutsidePolygons(); + reversePolygon(valid_holes[layer_idx]); + support_layer_storage[layer_idx].add(valid_holes[layer_idx]); + }); const auto t_end = std::chrono::high_resolution_clock::now(); @@ -2052,19 +2121,24 @@ void TreeSupport::filterFloatingLines(std::vector& support_layer_stora const auto dur_hole_removal_tagging = 0.001 * std::chrono::duration_cast(t_hole_removal_tagging - t_hole_rest_ordering).count(); const auto dur_hole_removal = 0.001 * std::chrono::duration_cast(t_end - t_hole_removal_tagging).count(); - spdlog::debug("Time to union areas: {} ms Time to evaluate which hole rest on which other hole: {} ms Time to see which holes are not resting on anything valid: {} ms remove all holes that are invalid and not close enough to a valid hole: {} ms", dur_union,dur_hole_rest_ordering,dur_hole_removal_tagging, dur_hole_removal); - + spdlog::debug( + "Time to union areas: {} ms Time to evaluate which hole rest on which other hole: {} ms Time to see which holes are not resting on anything valid: {} ms remove all holes " + "that are invalid and not close enough to a valid hole: {} ms", + dur_union, + dur_hole_rest_ordering, + dur_hole_removal_tagging, + dur_hole_removal); } void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& support_layer_storage, std::vector& support_roof_storage, SliceDataStorage& storage) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; - double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; + double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; // Iterate over the generated circles in parallel and clean them up. Also add support floor. std::mutex critical_sections; - cura::parallel_for - ( + cura::parallel_for( 0, support_layer_storage.size(), [&](const LayerIndex layer_idx) @@ -2073,43 +2147,53 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor // Subtract support lines of the branches from the roof storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.unionPolygons(support_roof_storage[layer_idx]); - if (!storage.support.supportLayers[layer_idx].support_roof.empty() && support_layer_storage[layer_idx].intersection(storage.support.supportLayers[layer_idx].support_roof).area() > 1) + if (! storage.support.supportLayers[layer_idx].support_roof.empty() + && support_layer_storage[layer_idx].intersection(storage.support.supportLayers[layer_idx].support_roof).area() > 1) { switch (interface_pref) { - case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); - break; - - case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]); - break; - - case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: - { - Polygons interface_lines = - TreeSupportUtils::generateSupportInfillLines(storage.support.supportLayers[layer_idx].support_roof, config, true, layer_idx, config.support_roof_line_distance, storage.support.cross_fill_provider, true) - .offsetPolyLine(config.support_roof_line_width / 2); - support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); - } + case InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT: + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(storage.support.supportLayers[layer_idx].support_roof); break; - case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: - { - Polygons tree_lines; - tree_lines = - tree_lines.unionPolygons - ( - TreeSupportUtils::generateSupportInfillLines(support_layer_storage[layer_idx], config, false, layer_idx, config.support_line_distance, storage.support.cross_fill_provider, true) - .offsetPolyLine(config.support_line_width / 2) - ); - storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); - // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. - } + case InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE: + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(support_layer_storage[layer_idx]); break; - case InterfacePreference::NOTHING: - break; + case InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT: + { + Polygons interface_lines = TreeSupportUtils::generateSupportInfillLines( + storage.support.supportLayers[layer_idx].support_roof, + config, + true, + layer_idx, + config.support_roof_line_distance, + storage.support.cross_fill_provider, + true) + .offsetPolyLine(config.support_roof_line_width / 2); + support_layer_storage[layer_idx] = support_layer_storage[layer_idx].difference(interface_lines); + } + break; + + case InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE: + { + Polygons tree_lines; + tree_lines = tree_lines.unionPolygons(TreeSupportUtils::generateSupportInfillLines( + support_layer_storage[layer_idx], + config, + false, + layer_idx, + config.support_line_distance, + storage.support.cross_fill_provider, + true) + .offsetPolyLine(config.support_line_width / 2)); + storage.support.supportLayers[layer_idx].support_roof = storage.support.supportLayers[layer_idx].support_roof.difference(tree_lines); + // Do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. + } + break; + + case InterfacePreference::NOTHING: + break; } } @@ -2121,8 +2205,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor size_t layers_below = 0; while (layers_below <= config.support_bottom_layers) { - // One sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other. - const size_t sample_layer = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(config.z_distance_bottom_layers))); + // One sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each + // other. + const size_t sample_layer + = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(config.z_distance_bottom_layers))); constexpr bool no_support = false; constexpr bool no_prime_tower = false; floor_layer.add(layer_outset.intersection(storage.getLayerOutlines(sample_layer, no_support, no_prime_tower))); @@ -2153,32 +2239,31 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(std::vector& suppor { std::lock_guard critical_section_storage(critical_sections); - if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty()) + if (! storage.support.supportLayers[layer_idx].support_infill_parts.empty() || ! storage.support.supportLayers[layer_idx].support_roof.empty()) { storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast(layer_idx)); } } - } - ); + }); } void TreeSupport::drawAreas(std::vector>& move_bounds, SliceDataStorage& storage) { std::vector support_layer_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); - std::map inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. - std::vector> linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance issues. - // These were fixed by creating less initial points, but i do not see a good reason to remove a working performance optimization. + std::map + inverse_tree_order; // In the tree structure only the parents can be accessed. Inverse this to be able to access the children. + std::vector> + linear_data; // All SupportElements are put into a layer independent storage to improve parallelization. Was added at a point in time where this function had performance + // issues. These were fixed by creating less initial points, but i do not see a good reason to remove a working performance optimization. for (const auto layer_idx : ranges::views::iota(0UL, move_bounds.size())) { for (TreeSupportElement* elem : move_bounds[layer_idx]) { // (Check if) We either come from nowhere at the final layer or we had invalid parents 2. should never happen but just to be sure: - if - ( - (layer_idx > 0 && ((!inverse_tree_order.count(elem) && elem->target_height == layer_idx && config.min_dtt_to_model > 0 && !elem->to_buildplate) || - (inverse_tree_order.count(elem) && inverse_tree_order[elem]->result_on_layer == Point(-1, -1)))) - ) + if ((layer_idx > 0 + && ((! inverse_tree_order.count(elem) && elem->target_height == layer_idx && config.min_dtt_to_model > 0 && ! elem->to_buildplate) + || (inverse_tree_order.count(elem) && inverse_tree_order[elem]->result_on_layer == Point(-1, -1))))) { continue; } @@ -2204,7 +2289,8 @@ void TreeSupport::drawAreas(std::vector>& move_bou generateBranchAreas(linear_data, layer_tree_polygons, inverse_tree_order); const auto t_generate = std::chrono::high_resolution_clock::now(); - // In some edge-cases a branch may go through a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out. + // In some edge-cases a branch may go through a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be + // caught and smoothed out. smoothBranchAreas(layer_tree_polygons); const auto t_smooth = std::chrono::high_resolution_clock::now(); @@ -2223,19 +2309,15 @@ void TreeSupport::drawAreas(std::vector>& move_bou } // ensure all branch areas added as roof actually cause a roofline to generate. Else disable turning the branch to roof going down - cura::parallel_for - ( + cura::parallel_for( 0, layer_tree_polygons.size(), [&](const size_t layer_idx) { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - if - ( - data_pair.first->missing_roof_layers > data_pair.first->distance_to_top && - TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, true).empty() - ) + if (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top + && TreeSupportUtils::generateSupportInfillLines(data_pair.second, config, true, layer_idx, config.support_roof_line_distance, nullptr, true).empty()) { std::vector to_disable_roofs; to_disable_roofs.emplace_back(data_pair.first); @@ -2254,8 +2336,7 @@ void TreeSupport::drawAreas(std::vector>& move_bou } } } - } - ); + }); // Single threaded combining all support areas to the right layers. // Only copies data! @@ -2263,15 +2344,13 @@ void TreeSupport::drawAreas(std::vector>& move_bou { for (std::pair data_pair : layer_tree_polygons[layer_idx]) { - ( - (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top) ? support_roof_storage : support_layer_storage - )[layer_idx].add(data_pair.second); + ((data_pair.first->missing_roof_layers > data_pair.first->distance_to_top) ? support_roof_storage : support_layer_storage)[layer_idx].add(data_pair.second); } } for (const auto layer_idx : ranges::views::iota(0UL, additional_required_support_area.size())) { - if(support_layer_storage.size() > layer_idx) + if (support_layer_storage.size() > layer_idx) { support_layer_storage[layer_idx].add(additional_required_support_area[layer_idx]); } @@ -2289,7 +2368,14 @@ void TreeSupport::drawAreas(std::vector>& move_bou const auto dur_drop = 0.001 * std::chrono::duration_cast(t_drop - t_smooth).count(); const auto dur_filter = 0.001 * std::chrono::duration_cast(t_filter - t_drop).count(); const auto dur_finalize = 0.001 * std::chrono::duration_cast(t_end - t_filter).count(); - spdlog::info("Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms filterFloatingLines: {} ms finalizeInterfaceAndSupportAreas {} ms", dur_gen_tips, dur_smooth, dur_drop, dur_filter, dur_finalize); + spdlog::info( + "Time used for drawing subfuctions: generateBranchAreas: {} ms smoothBranchAreas: {} ms dropNonGraciousAreas: {} ms filterFloatingLines: {} ms " + "finalizeInterfaceAndSupportAreas {} ms", + dur_gen_tips, + dur_smooth, + dur_drop, + dur_filter, + dur_finalize); } } // namespace cura diff --git a/src/bridge.cpp b/src/bridge.cpp index a395f7bd79..06ddbb6080 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -1,22 +1,30 @@ -//Copyright (c) 2018 Ultimaker B.V. -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// CuraEngine is released under the terms of the AGPLv3 or higher. #include "bridge.h" -#include "sliceDataStorage.h" + #include "settings/types/Ratio.h" +#include "sliceDataStorage.h" #include "utils/AABB.h" #include "utils/polygon.h" namespace cura { -int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const SliceDataStorage& storage, const unsigned layer_nr, const unsigned bridge_layer, const SupportLayer* support_layer, Polygons& supported_regions) +int bridgeAngle( + const Settings& settings, + const Polygons& skin_outline, + const SliceDataStorage& storage, + const unsigned layer_nr, + const unsigned bridge_layer, + const SupportLayer* support_layer, + Polygons& supported_regions) { assert(! skin_outline.empty()); AABB boundary_box(skin_outline); - //To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer. - // This gives us the islands that the layer rests on. + // To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer. + // This gives us the islands that the layer rests on. Polygons islands; Polygons prev_layer_outline; // we also want the complete outline of the previous layer @@ -42,7 +50,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl } prev_layer_outline.add(solid_below); // not intersected with skin - if (!boundary_box.hit(prev_layer_part.boundaryBox)) + if (! boundary_box.hit(prev_layer_part.boundaryBox)) continue; islands.add(skin_outline.intersection(solid_below)); @@ -59,7 +67,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl // the model on one side but the remainder of the skin is above support would look like // a bridge because it would have two islands) - FIXME more work required here? - if (!support_layer->support_roof.empty()) + if (! support_layer->support_roof.empty()) { AABB support_roof_bb(support_layer->support_roof); if (boundary_box.hit(support_roof_bb)) @@ -67,7 +75,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl prev_layer_outline.add(support_layer->support_roof); // not intersected with skin Polygons supported_skin(skin_outline.intersection(support_layer->support_roof)); - if (!supported_skin.empty()) + if (! supported_skin.empty()) { supported_regions.add(supported_skin); } @@ -83,7 +91,7 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl prev_layer_outline.add(support_part.getInfillArea()); // not intersected with skin Polygons supported_skin(skin_outline.intersection(support_part.getInfillArea())); - if (!supported_skin.empty()) + if (! supported_skin.empty()) { supported_regions.add(supported_skin); } @@ -115,7 +123,8 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl Polygons skin_perimeter_lines; for (ConstPolygonRef poly : skin_outline) { - if (poly.empty()) continue; + if (poly.empty()) + continue; skin_perimeter_lines.add(poly); skin_perimeter_lines.back().emplace_back(poly.front()); } @@ -157,15 +166,15 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl return -1; } - //Next find the 2 largest islands that we rest on. + // Next find the 2 largest islands that we rest on. double area1 = 0; double area2 = 0; int idx1 = -1; int idx2 = -1; - for(unsigned int n=0; n area1) @@ -184,15 +193,14 @@ int bridgeAngle(const Settings& settings, const Polygons& skin_outline, const Sl idx2 = n; } } - + if (idx1 < 0 || idx2 < 0) return -1; - + Point center1 = islands[idx1].centerOfMass(); Point center2 = islands[idx2].centerOfMass(); return angle(center2 - center1); } -}//namespace cura - +} // namespace cura From 74456d083079cc4025c7d7d68507d6a0152e97f6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 19 Sep 2023 12:36:39 +0200 Subject: [PATCH 4/4] Don't use auto when the type is not 'near'. Done as part of CURA-11019 --- src/FffGcodeWriter.cpp | 16 ++++++++-------- src/FffPolygonGenerator.cpp | 10 +++++----- src/LayerPlan.cpp | 4 ++-- src/TreeSupport.cpp | 4 ++-- src/bridge.cpp | 2 +- src/settings/PathConfigStorage.cpp | 2 +- src/sliceDataStorage.cpp | 8 ++++---- src/support.cpp | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0d6ea404fd..d5cbd83648 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -106,7 +106,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep } size_t total_layers = 0; - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; size_t mesh_layer_num = mesh.layers.size(); @@ -270,7 +270,7 @@ void FffGcodeWriter::findLayerSeamsForSpiralize(SliceDataStorage& storage, size_ const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (unsigned int mesh_idx : mesh_order) { - auto& mesh = *storage.meshes[mesh_idx]; + SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; // if this mesh has layer data for this layer process it if (! done_this_layer && mesh.layers.size() > layer_nr) { @@ -372,7 +372,7 @@ void FffGcodeWriter::setConfigRetractionAndWipe(SliceDataStorage& storage) ExtruderTrain& train = scene.extruders[extruder_index]; retractionAndWipeConfigFromSettings(train.settings, &storage.retraction_wipe_config_per_extruder[extruder_index]); } - for (auto& mesh : storage.meshes) + for (std::shared_ptr& mesh : storage.meshes) { retractionAndWipeConfigFromSettings(mesh->settings, &mesh->retraction_wipe_config); } @@ -916,7 +916,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn { z = storage.meshes[0]->layers[layer_nr].printZ; // stub default // find printZ of first actual printed mesh - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; if (layer_nr >= static_cast(mesh.layers.size()) || mesh.settings.get("support_mesh") || mesh.settings.get("anti_overhang_mesh") @@ -953,7 +953,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn } coord_t max_inner_wall_width = 0; - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; coord_t mesh_inner_wall_width = mesh.settings.get((mesh.settings.get("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"); @@ -1025,7 +1025,7 @@ LayerPlan& FffGcodeWriter::processLayer(const SliceDataStorage& storage, LayerIn const std::vector& mesh_order = mesh_order_per_extruder[extruder_nr]; for (size_t mesh_idx : mesh_order) { - const auto& mesh = storage.meshes[mesh_idx]; + const std::shared_ptr& mesh = storage.meshes[mesh_idx]; const MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx]; if (mesh->settings.get("magic_mesh_surface_mode") == ESurfaceMode::SURFACE && extruder_nr @@ -1385,7 +1385,7 @@ std::vector FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& s std::vector::iterator mesh_group = Application::getInstance().current_slice->scene.current_mesh_group; for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { - const auto& mesh = *storage.meshes[mesh_idx]; + const SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; if (mesh.getExtruderIsUsed(extruder_nr)) { const Mesh& mesh_data = mesh_group->meshes[mesh_idx]; @@ -2263,7 +2263,7 @@ bool FffGcodeWriter::processInsets( Polygons outlines_below; AABB boundaryBox(part.outline); - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& m = *mesh_ptr; if (m.isPrinted()) diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index edd5e48d6e..daff0196eb 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -276,7 +276,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe // always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes storage.meshes.push_back(std::make_shared(&meshgroup->meshes[meshIdx], slicer->layers.size())); // new mesh in storage had settings from the Mesh - auto& meshStorage = *storage.meshes.back(); + SliceMeshStorage& meshStorage = *storage.meshes.back(); // only create layer parts for normal meshes const bool is_support_modifier = AreaSupport::handleSupportModifierMesh(storage, mesh.settings, slicer); @@ -347,7 +347,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& // compute layer count and remove first empty layers // there is no separate progress stage for removeEmptyFisrtLayer (TODO) unsigned int slice_layer_count = 0; - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (! mesh.settings.get("infill_mesh") && ! mesh.settings.get("anti_overhang_mesh")) @@ -433,7 +433,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& spdlog::debug("Meshes post-processing"); // meshes post processing - for (auto& mesh : storage.meshes) + for (std::shared_ptr& mesh : storage.meshes) { processDerivedWallsSkinInfill(*mesh); } @@ -742,7 +742,7 @@ bool FffPolygonGenerator::isEmptyLayer(SliceDataStorage& storage, const LayerInd return false; } } - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (layer_idx >= mesh.layers.size()) @@ -855,7 +855,7 @@ void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage max_print_height_per_extruder.resize(extruder_count, -(raft_layers + 1)); // Initialize all as -1 (or lower in case of raft). { // compute max_object_height_per_extruder // Height of the meshes themselves. - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh")) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index b7d24b1b11..530ba6b63b 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -159,7 +159,7 @@ Polygons LayerPlan::computeCombBoundary(const CombBoundary boundary_type) } else { - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; const SliceLayer& layer = mesh.layers[static_cast(layer_nr)]; @@ -1188,7 +1188,7 @@ void LayerPlan::addLinesByOptimizer( if (layer_nr >= 0) { // determine how much the skin/infill lines overlap the combing boundary - for (const auto& mesh : storage.meshes) + for (const std::shared_ptr& mesh : storage.meshes) { const coord_t overlap = std::max(mesh->settings.get("skin_overlap_mm"), mesh->settings.get("infill_overlap_mm")); if (overlap > dist) diff --git a/src/TreeSupport.cpp b/src/TreeSupport.cpp index 9cc4c7d404..c226588ebe 100644 --- a/src/TreeSupport.cpp +++ b/src/TreeSupport.cpp @@ -39,7 +39,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) { size_t largest_printed_mesh_idx = 0; - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; TreeSupportSettings::some_model_contains_thick_roof |= mesh.settings.get("support_roof_height") >= 2 * mesh.settings.get("layer_height"); @@ -52,7 +52,7 @@ TreeSupport::TreeSupport(const SliceDataStorage& storage) // Only one setting object is needed per group, as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. for (auto [mesh_idx, mesh_ptr] : storage.meshes | ranges::views::enumerate) { - auto& mesh = *mesh_ptr; + SliceMeshStorage& mesh = *mesh_ptr; const bool non_supportable_mesh = mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh") || mesh.settings.get("support_mesh"); if (mesh.settings.get("support_structure") != ESupportStructure::TREE || ! mesh.settings.get("support_enable") || non_supportable_mesh) { diff --git a/src/bridge.cpp b/src/bridge.cpp index 06ddbb6080..75e42889e2 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -32,7 +32,7 @@ int bridgeAngle( const Ratio sparse_infill_max_density = settings.get("bridge_sparse_infill_max_density"); // include parts from all meshes - for (const auto& mesh_ptr : storage.meshes) + for (const std::shared_ptr& mesh_ptr : storage.meshes) { const auto& mesh = *mesh_ptr; if (mesh.isPrinted()) diff --git a/src/settings/PathConfigStorage.cpp b/src/settings/PathConfigStorage.cpp index 467b130f5c..f6e6d1b703 100644 --- a/src/settings/PathConfigStorage.cpp +++ b/src/settings/PathConfigStorage.cpp @@ -124,7 +124,7 @@ PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, const Laye } mesh_configs.reserve(storage.meshes.size()); - for (const auto& mesh_storage : storage.meshes) + for (const std::shared_ptr& mesh_storage : storage.meshes) { mesh_configs.emplace_back(*mesh_storage, layer_thickness, layer_nr, line_width_factor_per_extruder); } diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index c890783f8c..4533fbb8be 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -300,7 +300,7 @@ Polygons Polygons total; if (layer_nr >= 0) { - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { if (mesh->settings.get("infill_mesh") || mesh->settings.get("anti_overhang_mesh") || (extruder_nr != -1 && extruder_nr != int(mesh->settings.get("wall_0_extruder_nr").extruder_nr))) @@ -381,7 +381,7 @@ std::vector SliceDataStorage::getExtrudersUsed() const // support // support is presupposed to be present... - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { if (mesh->settings.get("support_enable") || mesh->settings.get("support_mesh")) { @@ -399,7 +399,7 @@ std::vector SliceDataStorage::getExtrudersUsed() const } // all meshes are presupposed to actually have content - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { @@ -508,7 +508,7 @@ std::vector SliceDataStorage::getExtrudersUsed(const LayerIndex layer_nr) if (include_models) { - for (const auto& mesh : meshes) + for (const std::shared_ptr& mesh : meshes) { for (unsigned int extruder_nr = 0; extruder_nr < ret.size(); extruder_nr++) { diff --git a/src/support.cpp b/src/support.cpp index a55d21b53f..188f2b486d 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -599,7 +599,7 @@ Polygons AreaSupport::join(const SliceDataStorage& storage, const Polygons& supp void AreaSupport::generateOverhangAreas(SliceDataStorage& storage) { - for (auto& mesh_ptr : storage.meshes) + for (std::shared_ptr& mesh_ptr : storage.meshes) { auto& mesh = *mesh_ptr; if (mesh.settings.get("infill_mesh") || mesh.settings.get("anti_overhang_mesh"))