From 6ef4e96dad2e593e5d96b6b4c30211c7cd34ede9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 30 Apr 2025 15:39:21 +0200 Subject: [PATCH 01/22] Split up parts into top/bottom/side if the wall-count is different. This commit doesn't yet actually _use_ the wall-difference mind you. -- Note also that the top/bottom has to be calculated here from the rough outlines (as opposed to the actually printed outlines) since normally that would be calculated later. (And due to the previously mentioned print-outlines vs. outlines, it's not the same calculation, so we can't just move it back to here.) part of CURA-12446 --- include/sliceDataStorage.h | 8 +++++ src/layerPart.cpp | 68 ++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 590ce5297a..bcb43bff1b 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -56,6 +56,14 @@ class SkinPart class SliceLayerPart { public: + enum class WallExposedType + { + BOTTOM, + TOP, + SIDE_ONLY, + }; + WallExposedType wall_exposed = WallExposedType::SIDE_ONLY; + AABB boundaryBox; //!< The boundaryBox is an axis-aligned boundary box which is used to quickly check for possible //!< collision between different parts on different layers. It's an optimization used during //!< skin calculations. diff --git a/src/layerPart.cpp b/src/layerPart.cpp index e1754b73fe..56bfb2c0b4 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -28,7 +28,7 @@ It's also the first step that stores the result in the "data storage" so all oth namespace cura { -void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, SlicerLayer* layer) +void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, SlicerLayer* layer, const Shape& bottom_parts, const Shape& top_parts) { OpenPolylineStitcher::stitch(layer->open_polylines_, storageLayer.open_polylines, layer->polygons_, settings.get("wall_line_width_0")); @@ -65,20 +65,62 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl result = layer->polygons_.splitIntoParts(union_layers || union_all_remove_holes); } - for (auto& part : result) + for (auto& main_part : result) { - storageLayer.parts.emplace_back(); - if (part.empty()) + std::vector>> parts_by_type = { + { SliceLayerPart::WallExposedType::BOTTOM, bottom_parts.splitIntoParts() }, + { SliceLayerPart::WallExposedType::TOP, top_parts.difference(bottom_parts).splitIntoParts() }, + { SliceLayerPart::WallExposedType::SIDE_ONLY, main_part.difference(bottom_parts).difference(top_parts).splitIntoParts() }, + }; + + for (auto& [wall_exposed, parts] : parts_by_type) { - continue; + for (auto& part : parts) + { + storageLayer.parts.emplace_back(); + if (part.empty()) + { + continue; + } + auto& back_part = storageLayer.parts.back(); + back_part.wall_exposed = wall_exposed; + back_part.outline = part; + back_part.boundaryBox.calculate(back_part.outline); + if (back_part.outline.empty()) + { + storageLayer.parts.pop_back(); + } + } } - storageLayer.parts.back().outline = part; - storageLayer.parts.back().boundaryBox.calculate(storageLayer.parts.back().outline); - if (storageLayer.parts.back().outline.empty()) + } +} + +Shape getBottom(size_t layer_nr, const std::vector& slayers, const Settings& settings) +{ + auto result = Shape(); + if (settings.get("wall_line_count_bottom") != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) + { + result = slayers[layer_nr].polygons_; + if (layer_nr > 0) + { + result = result.difference(slayers[layer_nr - 1].polygons_); + } + } + return result; +} + +Shape getTop(size_t layer_nr, const std::vector& slayers, const Settings& settings) +{ + auto result = Shape(); + if (settings.get("wall_line_count_top") != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) + { + result = slayers[layer_nr].polygons_; + if (layer_nr < slayers.size() - 1) { - storageLayer.parts.pop_back(); + result = result.difference(slayers[layer_nr + 1].polygons_); } } + return result; } void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) @@ -93,7 +135,13 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) { SliceLayer& layer_storage = mesh.layers[layer_nr]; SlicerLayer& slice_layer = slicer->layers[layer_nr]; - createLayerWithParts(mesh.settings, layer_storage, &slice_layer); + createLayerWithParts( + mesh.settings, + layer_storage, + &slice_layer, + getBottom(layer_nr, slicer->layers, mesh.settings), + getTop(layer_nr, slicer->layers, mesh.settings) + ); }); for (LayerIndex layer_nr = total_layers - 1; layer_nr >= 0; layer_nr--) From 694cb2b5fd44f0c049a027eb4e5b7dd3c2d427b0 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 30 Apr 2025 15:49:21 +0200 Subject: [PATCH 02/22] Small refactor: DRY up getting top/bottom layer outline diffs. part of CURA-12446 --- src/layerPart.cpp | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 56bfb2c0b4..5e50d59f47 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -95,29 +95,16 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl } } -Shape getBottom(size_t layer_nr, const std::vector& slayers, const Settings& settings) +Shape getTopOrBottom(int direction, const std::string& setting_name, size_t layer_nr, const std::vector& slayers, const Settings& settings) { auto result = Shape(); - if (settings.get("wall_line_count_bottom") != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) + if (settings.get(setting_name) != settings.get("wall_line_count") && !settings.get("magic_spiralize")) { result = slayers[layer_nr].polygons_; - if (layer_nr > 0) + const auto next_layer = layer_nr + direction; + if (next_layer >= 0 && next_layer < slayers.size()) { - result = result.difference(slayers[layer_nr - 1].polygons_); - } - } - return result; -} - -Shape getTop(size_t layer_nr, const std::vector& slayers, const Settings& settings) -{ - auto result = Shape(); - if (settings.get("wall_line_count_top") != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) - { - result = slayers[layer_nr].polygons_; - if (layer_nr < slayers.size() - 1) - { - result = result.difference(slayers[layer_nr + 1].polygons_); + result = result.difference(slayers[next_layer].polygons_); } } return result; @@ -139,8 +126,8 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) mesh.settings, layer_storage, &slice_layer, - getBottom(layer_nr, slicer->layers, mesh.settings), - getTop(layer_nr, slicer->layers, mesh.settings) + getTopOrBottom(-1, "wall_line_count_bottom", layer_nr, slicer->layers, mesh.settings), + getTopOrBottom(+1, "wall_line_count_top", layer_nr, slicer->layers, mesh.settings) ); }); From fd1c22474deab4d349c61e429dc04e11f4abd25e Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 30 Apr 2025 16:12:07 +0200 Subject: [PATCH 03/22] Use the previously re-split parts to get the correct wall-count. Adding the possibility for separated top-/bottom-most wall line counts vs. 'normal' wall counts, which was the point of the ticket. should finish off the engine part of CURA-12446 --- src/WallsComputation.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index f75c89defa..cd6b4a9734 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -37,7 +37,18 @@ WallsComputation::WallsComputation(const Settings& settings, const LayerIndex la */ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_type) { - size_t wall_count = settings_.get("wall_line_count"); + constexpr auto get_wall_count_setting_name = + [](SliceLayerPart* part) + { + switch (part->wall_exposed) + { + case SliceLayerPart::WallExposedType::BOTTOM: return "wall_line_count_bottom"; + case SliceLayerPart::WallExposedType::TOP: return "wall_line_count_top"; + case SliceLayerPart::WallExposedType::SIDE_ONLY: return "wall_line_count"; + } + }; + + size_t wall_count = settings_.get(get_wall_count_setting_name(part)); if (wall_count == 0) // Early out if no walls are to be generated { part->print_outline = part->outline; From 10d773d4354be9319527ad6f05a94af405221976 Mon Sep 17 00:00:00 2001 From: rburema <41987080+rburema@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:17:12 +0000 Subject: [PATCH 04/22] Apply clang-format --- src/WallsComputation.cpp | 20 +++++++++++--------- src/layerPart.cpp | 5 ++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index cd6b4a9734..eea4813df5 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -37,16 +37,18 @@ WallsComputation::WallsComputation(const Settings& settings, const LayerIndex la */ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_type) { - constexpr auto get_wall_count_setting_name = - [](SliceLayerPart* part) + constexpr auto get_wall_count_setting_name = [](SliceLayerPart* part) + { + switch (part->wall_exposed) { - switch (part->wall_exposed) - { - case SliceLayerPart::WallExposedType::BOTTOM: return "wall_line_count_bottom"; - case SliceLayerPart::WallExposedType::TOP: return "wall_line_count_top"; - case SliceLayerPart::WallExposedType::SIDE_ONLY: return "wall_line_count"; - } - }; + case SliceLayerPart::WallExposedType::BOTTOM: + return "wall_line_count_bottom"; + case SliceLayerPart::WallExposedType::TOP: + return "wall_line_count_top"; + case SliceLayerPart::WallExposedType::SIDE_ONLY: + return "wall_line_count"; + } + }; size_t wall_count = settings_.get(get_wall_count_setting_name(part)); if (wall_count == 0) // Early out if no walls are to be generated diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 5e50d59f47..51cb597a65 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -98,7 +98,7 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl Shape getTopOrBottom(int direction, const std::string& setting_name, size_t layer_nr, const std::vector& slayers, const Settings& settings) { auto result = Shape(); - if (settings.get(setting_name) != settings.get("wall_line_count") && !settings.get("magic_spiralize")) + if (settings.get(setting_name) != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) { result = slayers[layer_nr].polygons_; const auto next_layer = layer_nr + direction; @@ -127,8 +127,7 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) layer_storage, &slice_layer, getTopOrBottom(-1, "wall_line_count_bottom", layer_nr, slicer->layers, mesh.settings), - getTopOrBottom(+1, "wall_line_count_top", layer_nr, slicer->layers, mesh.settings) - ); + getTopOrBottom(+1, "wall_line_count_top", layer_nr, slicer->layers, mesh.settings)); }); for (LayerIndex layer_nr = total_layers - 1; layer_nr >= 0; layer_nr--) From b21f96c74fa7ca804554e0a071772b83ab222682 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 6 May 2025 11:12:01 +0200 Subject: [PATCH 05/22] Make the previously 'bottom' wall line count only apply to initial. Otherwise the walls will adhere less well to the model, and while this is a nice visual change, structural integrity (especially when briding) is more important. Also this is what it already says in the ticket is needed, nothing really specified about non-top, non-initial layers. part of CURA-12446 --- include/sliceDataStorage.h | 2 +- src/WallsComputation.cpp | 4 ++-- src/layerPart.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index bcb43bff1b..4198fd06a7 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -58,7 +58,7 @@ class SliceLayerPart public: enum class WallExposedType { - BOTTOM, + BOTTOM_0, TOP, SIDE_ONLY, }; diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index eea4813df5..9cf51ae15d 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -41,8 +41,8 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t { switch (part->wall_exposed) { - case SliceLayerPart::WallExposedType::BOTTOM: - return "wall_line_count_bottom"; + case SliceLayerPart::WallExposedType::BOTTOM_0: + return "wall_line_count_0"; case SliceLayerPart::WallExposedType::TOP: return "wall_line_count_top"; case SliceLayerPart::WallExposedType::SIDE_ONLY: diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 51cb597a65..20833182e1 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -68,7 +68,7 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl for (auto& main_part : result) { std::vector>> parts_by_type = { - { SliceLayerPart::WallExposedType::BOTTOM, bottom_parts.splitIntoParts() }, + { SliceLayerPart::WallExposedType::BOTTOM_0, bottom_parts.splitIntoParts() }, { SliceLayerPart::WallExposedType::TOP, top_parts.difference(bottom_parts).splitIntoParts() }, { SliceLayerPart::WallExposedType::SIDE_ONLY, main_part.difference(bottom_parts).difference(top_parts).splitIntoParts() }, }; @@ -126,7 +126,7 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) mesh.settings, layer_storage, &slice_layer, - getTopOrBottom(-1, "wall_line_count_bottom", layer_nr, slicer->layers, mesh.settings), + layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_0", layer_nr, slicer->layers, mesh.settings) : Shape(), getTopOrBottom(+1, "wall_line_count_top", layer_nr, slicer->layers, mesh.settings)); }); From d06800f0da86a5cca537e4c582d6fd4d133e11f1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 6 May 2025 11:47:56 +0200 Subject: [PATCH 06/22] Clean up function that was only used class-internally. done as part of CURA-12446 --- include/layerPart.h | 14 +------------- src/layerPart.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/include/layerPart.h b/include/layerPart.h index 3eed7e7b8d..db6f4cb38e 100644 --- a/include/layerPart.h +++ b/include/layerPart.h @@ -1,4 +1,4 @@ -//Copyright (c) 2018 Ultimaker B.V. +//Copyright (c) 2025 UltiMaker //CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef LAYERPART_H @@ -19,21 +19,9 @@ It's also the first step that stores the result in the "data storage" so all oth namespace cura { -class Settings; -class SliceLayer; class Slicer; -class SlicerLayer; class SliceMeshStorage; -/*! - * \brief Split a layer into parts. - * \param settings The settings to get the settings from (whether to union or - * not). - * \param storageLayer Where to store the parts. - * \param layer The layer to split. - */ -void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, SlicerLayer* layer); - /*! * \brief Split all layers into parts. * \param mesh The mesh of which to split the layers into parts. diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 20833182e1..cc0b7adcee 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2023 UltiMaker +// Copyright (c) 2025 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #include "layerPart.h" @@ -110,6 +110,13 @@ Shape getTopOrBottom(int direction, const std::string& setting_name, size_t laye return result; } +/*! + * \brief Split a layer into parts. + * \param settings The settings to get the settings from (whether to union or + * not). + * \param storageLayer Where to store the parts. + * \param layer The layer to split. + */ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) { const auto total_layers = slicer->layers.size(); From 0229ab2fc43717097cf444e04e86b0ef8c63db03 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 6 May 2025 11:51:39 +0200 Subject: [PATCH 07/22] Fix top/initial wall count issues w.r.t. sloped walls. part of CURA-12446 --- src/layerPart.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layerPart.cpp b/src/layerPart.cpp index cc0b7adcee..0918edf6fb 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -104,7 +104,9 @@ Shape getTopOrBottom(int direction, const std::string& setting_name, size_t laye const auto next_layer = layer_nr + direction; if (next_layer >= 0 && next_layer < slayers.size()) { - result = result.difference(slayers[next_layer].polygons_); + constexpr coord_t EPSILON = 5; + const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width") - 5; + result = result.offset(-wall_line_width).difference(slayers[next_layer].polygons_).offset(wall_line_width); } } return result; From 39faff4f1229ab49f685c3d96070e77ae18d609f Mon Sep 17 00:00:00 2001 From: rburema <41987080+rburema@users.noreply.github.com> Date: Tue, 6 May 2025 09:52:58 +0000 Subject: [PATCH 08/22] Apply clang-format --- include/layerPart.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/layerPart.h b/include/layerPart.h index db6f4cb38e..2234f9fa18 100644 --- a/include/layerPart.h +++ b/include/layerPart.h @@ -1,5 +1,5 @@ -//Copyright (c) 2025 UltiMaker -//CuraEngine is released under the terms of the AGPLv3 or higher. +// Copyright (c) 2025 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher. #ifndef LAYERPART_H #define LAYERPART_H @@ -29,6 +29,6 @@ class SliceMeshStorage; */ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer); -}//namespace cura +} // namespace cura -#endif//LAYERPART_H +#endif // LAYERPART_H From 65d65e42228192e5b7bd6df4e2cafabe9c1c70d2 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 7 May 2025 13:12:35 +0200 Subject: [PATCH 09/22] Better variable/enum names (code review comments). part of CURA-12446 --- include/sliceDataStorage.h | 4 ++-- src/WallsComputation.cpp | 8 ++++---- src/layerPart.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 4198fd06a7..d721ddee3b 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -58,8 +58,8 @@ class SliceLayerPart public: enum class WallExposedType { - BOTTOM_0, - TOP, + LAYER_0, + ROOFING, SIDE_ONLY, }; WallExposedType wall_exposed = WallExposedType::SIDE_ONLY; diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index 9cf51ae15d..609ee7afb5 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -41,10 +41,10 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t { switch (part->wall_exposed) { - case SliceLayerPart::WallExposedType::BOTTOM_0: - return "wall_line_count_0"; - case SliceLayerPart::WallExposedType::TOP: - return "wall_line_count_top"; + case SliceLayerPart::WallExposedType::LAYER_0: + return "wall_line_count_layer_0"; + case SliceLayerPart::WallExposedType::ROOFING: + return "wall_line_count_roofing"; case SliceLayerPart::WallExposedType::SIDE_ONLY: return "wall_line_count"; } diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 0918edf6fb..bf216750b3 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -68,8 +68,8 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl for (auto& main_part : result) { std::vector>> parts_by_type = { - { SliceLayerPart::WallExposedType::BOTTOM_0, bottom_parts.splitIntoParts() }, - { SliceLayerPart::WallExposedType::TOP, top_parts.difference(bottom_parts).splitIntoParts() }, + { SliceLayerPart::WallExposedType::LAYER_0, bottom_parts.splitIntoParts() }, + { SliceLayerPart::WallExposedType::ROOFING, top_parts.difference(bottom_parts).splitIntoParts() }, { SliceLayerPart::WallExposedType::SIDE_ONLY, main_part.difference(bottom_parts).difference(top_parts).splitIntoParts() }, }; @@ -135,8 +135,8 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) mesh.settings, layer_storage, &slice_layer, - layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_0", layer_nr, slicer->layers, mesh.settings) : Shape(), - getTopOrBottom(+1, "wall_line_count_top", layer_nr, slicer->layers, mesh.settings)); + layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_layer_0", layer_nr, slicer->layers, mesh.settings) : Shape(), + getTopOrBottom(+1, "wall_line_count_roofing", layer_nr, slicer->layers, mesh.settings)); }); for (LayerIndex layer_nr = total_layers - 1; layer_nr >= 0; layer_nr--) From 0d0c936167aaf36b9b4dfd9298c13b376d3cf7f0 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 7 May 2025 13:32:14 +0200 Subject: [PATCH 10/22] Apply code-review suggestions. part of CURA-12446 --- src/WallsComputation.cpp | 20 ++++++-------------- src/layerPart.cpp | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index 609ee7afb5..740b9c0135 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -37,20 +37,12 @@ WallsComputation::WallsComputation(const Settings& settings, const LayerIndex la */ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_type) { - constexpr auto get_wall_count_setting_name = [](SliceLayerPart* part) - { - switch (part->wall_exposed) - { - case SliceLayerPart::WallExposedType::LAYER_0: - return "wall_line_count_layer_0"; - case SliceLayerPart::WallExposedType::ROOFING: - return "wall_line_count_roofing"; - case SliceLayerPart::WallExposedType::SIDE_ONLY: - return "wall_line_count"; - } - }; - - size_t wall_count = settings_.get(get_wall_count_setting_name(part)); + const std::map wall_count_setting_names({ + { SliceLayerPart::WallExposedType::LAYER_0, "wall_line_count_layer_0" }, + { SliceLayerPart::WallExposedType::ROOFING, "wall_line_count_roofing" }, + { SliceLayerPart::WallExposedType::SIDE_ONLY, "wall_line_count" } + }); + size_t wall_count = settings_.get(wall_count_setting_names.at(part->wall_exposed)); if (wall_count == 0) // Early out if no walls are to be generated { part->print_outline = part->outline; diff --git a/src/layerPart.cpp b/src/layerPart.cpp index bf216750b3..cdf425c372 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -28,6 +28,15 @@ It's also the first step that stores the result in the "data storage" so all oth namespace cura { +/*! + * \brief Split a layer into parts. + * \param settings The settings to get the settings from (whether to union or + * not). + * \param storageLayer Where to store the parts. + * \param layer The layer to split. + * \param bottom_parts The bottom parts of the layer. + * \param top_parts The top parts of the layer. + */ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, SlicerLayer* layer, const Shape& bottom_parts, const Shape& top_parts) { OpenPolylineStitcher::stitch(layer->open_polylines_, storageLayer.open_polylines, layer->polygons_, settings.get("wall_line_width_0")); @@ -67,7 +76,7 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl for (auto& main_part : result) { - std::vector>> parts_by_type = { + std::map> parts_by_type = { { SliceLayerPart::WallExposedType::LAYER_0, bottom_parts.splitIntoParts() }, { SliceLayerPart::WallExposedType::ROOFING, top_parts.difference(bottom_parts).splitIntoParts() }, { SliceLayerPart::WallExposedType::SIDE_ONLY, main_part.difference(bottom_parts).difference(top_parts).splitIntoParts() }, @@ -97,7 +106,7 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl Shape getTopOrBottom(int direction, const std::string& setting_name, size_t layer_nr, const std::vector& slayers, const Settings& settings) { - auto result = Shape(); + Shape result; if (settings.get(setting_name) != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) { result = slayers[layer_nr].polygons_; @@ -112,13 +121,6 @@ Shape getTopOrBottom(int direction, const std::string& setting_name, size_t laye return result; } -/*! - * \brief Split a layer into parts. - * \param settings The settings to get the settings from (whether to union or - * not). - * \param storageLayer Where to store the parts. - * \param layer The layer to split. - */ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) { const auto total_layers = slicer->layers.size(); From 4dd0c87eb8b39a247a1770da9b9319c60488b079 Mon Sep 17 00:00:00 2001 From: rburema <41987080+rburema@users.noreply.github.com> Date: Wed, 7 May 2025 11:36:40 +0000 Subject: [PATCH 11/22] Apply clang-format --- src/WallsComputation.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/WallsComputation.cpp b/src/WallsComputation.cpp index 740b9c0135..81dc5aa918 100644 --- a/src/WallsComputation.cpp +++ b/src/WallsComputation.cpp @@ -37,11 +37,9 @@ WallsComputation::WallsComputation(const Settings& settings, const LayerIndex la */ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_type) { - const std::map wall_count_setting_names({ - { SliceLayerPart::WallExposedType::LAYER_0, "wall_line_count_layer_0" }, - { SliceLayerPart::WallExposedType::ROOFING, "wall_line_count_roofing" }, - { SliceLayerPart::WallExposedType::SIDE_ONLY, "wall_line_count" } - }); + const std::map wall_count_setting_names({ { SliceLayerPart::WallExposedType::LAYER_0, "wall_line_count_layer_0" }, + { SliceLayerPart::WallExposedType::ROOFING, "wall_line_count_roofing" }, + { SliceLayerPart::WallExposedType::SIDE_ONLY, "wall_line_count" } }); size_t wall_count = settings_.get(wall_count_setting_names.at(part->wall_exposed)); if (wall_count == 0) // Early out if no walls are to be generated { From 86e8778840cccc00296e9cc24f3bb137ad97efa7 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Jun 2025 14:47:04 +0200 Subject: [PATCH 12/22] Apply roofing area extension CURA-12446 --- src/layerPart.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/layerPart.cpp b/src/layerPart.cpp index cdf425c372..9da8c33131 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -104,7 +104,13 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl } } -Shape getTopOrBottom(int direction, const std::string& setting_name, size_t layer_nr, const std::vector& slayers, const Settings& settings) +Shape getTopOrBottom( + int direction, + const std::string& setting_name, + size_t layer_nr, + const std::vector& slayers, + const Settings& settings, + const std::optional extension) { Shape result; if (settings.get(setting_name) != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) @@ -114,8 +120,8 @@ Shape getTopOrBottom(int direction, const std::string& setting_name, size_t laye if (next_layer >= 0 && next_layer < slayers.size()) { constexpr coord_t EPSILON = 5; - const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width") - 5; - result = result.offset(-wall_line_width).difference(slayers[next_layer].polygons_).offset(wall_line_width); + const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width"); + result = result.offset(-wall_line_width - EPSILON).difference(slayers[next_layer].polygons_).offset(extension.value_or(wall_line_width) + EPSILON); } } return result; @@ -125,11 +131,12 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) { const auto total_layers = slicer->layers.size(); assert(mesh.layers.size() == total_layers); + const coord_t roofing_extension = mesh.settings.get("roofing_extension"); cura::parallel_for( 0, total_layers, - [slicer, &mesh](size_t layer_nr) + [slicer, &mesh, &roofing_extension](size_t layer_nr) { SliceLayer& layer_storage = mesh.layers[layer_nr]; SlicerLayer& slice_layer = slicer->layers[layer_nr]; @@ -137,8 +144,8 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) mesh.settings, layer_storage, &slice_layer, - layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_layer_0", layer_nr, slicer->layers, mesh.settings) : Shape(), - getTopOrBottom(+1, "wall_line_count_roofing", layer_nr, slicer->layers, mesh.settings)); + layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_layer_0", layer_nr, slicer->layers, mesh.settings, std::nullopt) : Shape(), + getTopOrBottom(+1, "wall_line_count_roofing", layer_nr, slicer->layers, mesh.settings, roofing_extension)); }); for (LayerIndex layer_nr = total_layers - 1; layer_nr >= 0; layer_nr--) From 08633e2ed67bd9c9f5075e2da0ba659122d31458 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 10:37:45 +0200 Subject: [PATCH 13/22] Revert "Apply roofing area extension" This reverts commit 86e8778840cccc00296e9cc24f3bb137ad97efa7. --- src/layerPart.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/layerPart.cpp b/src/layerPart.cpp index 9da8c33131..cdf425c372 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -104,13 +104,7 @@ void createLayerWithParts(const Settings& settings, SliceLayer& storageLayer, Sl } } -Shape getTopOrBottom( - int direction, - const std::string& setting_name, - size_t layer_nr, - const std::vector& slayers, - const Settings& settings, - const std::optional extension) +Shape getTopOrBottom(int direction, const std::string& setting_name, size_t layer_nr, const std::vector& slayers, const Settings& settings) { Shape result; if (settings.get(setting_name) != settings.get("wall_line_count") && ! settings.get("magic_spiralize")) @@ -120,8 +114,8 @@ Shape getTopOrBottom( if (next_layer >= 0 && next_layer < slayers.size()) { constexpr coord_t EPSILON = 5; - const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width"); - result = result.offset(-wall_line_width - EPSILON).difference(slayers[next_layer].polygons_).offset(extension.value_or(wall_line_width) + EPSILON); + const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width") - 5; + result = result.offset(-wall_line_width).difference(slayers[next_layer].polygons_).offset(wall_line_width); } } return result; @@ -131,12 +125,11 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) { const auto total_layers = slicer->layers.size(); assert(mesh.layers.size() == total_layers); - const coord_t roofing_extension = mesh.settings.get("roofing_extension"); cura::parallel_for( 0, total_layers, - [slicer, &mesh, &roofing_extension](size_t layer_nr) + [slicer, &mesh](size_t layer_nr) { SliceLayer& layer_storage = mesh.layers[layer_nr]; SlicerLayer& slice_layer = slicer->layers[layer_nr]; @@ -144,8 +137,8 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer) mesh.settings, layer_storage, &slice_layer, - layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_layer_0", layer_nr, slicer->layers, mesh.settings, std::nullopt) : Shape(), - getTopOrBottom(+1, "wall_line_count_roofing", layer_nr, slicer->layers, mesh.settings, roofing_extension)); + layer_nr == 0 ? getTopOrBottom(-1, "wall_line_count_layer_0", layer_nr, slicer->layers, mesh.settings) : Shape(), + getTopOrBottom(+1, "wall_line_count_roofing", layer_nr, slicer->layers, mesh.settings)); }); for (LayerIndex layer_nr = total_layers - 1; layer_nr >= 0; layer_nr--) From 5b3d98966db442347557b2b6b2ae1e4099142cad Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 10:52:41 +0200 Subject: [PATCH 14/22] Apply roofing extension differently CURA-12446 --- src/layerPart.cpp | 2 +- src/skin.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/layerPart.cpp b/src/layerPart.cpp index cdf425c372..bc1448775c 100644 --- a/src/layerPart.cpp +++ b/src/layerPart.cpp @@ -114,7 +114,7 @@ Shape getTopOrBottom(int direction, const std::string& setting_name, size_t laye if (next_layer >= 0 && next_layer < slayers.size()) { constexpr coord_t EPSILON = 5; - const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width") - 5; + const auto wall_line_width = settings.get(layer_nr == 0 ? "wall_line_width_0" : "wall_line_width") - EPSILON; result = result.offset(-wall_line_width).difference(slayers[next_layer].polygons_).offset(wall_line_width); } } diff --git a/src/skin.cpp b/src/skin.cpp index def6af7a28..21643b839e 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -333,6 +333,7 @@ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& const size_t roofing_layer_count = std::min(mesh_.settings.get("roofing_layer_count"), mesh_.settings.get("top_layers")); const size_t flooring_layer_count = std::min(mesh_.settings.get("flooring_layer_count"), mesh_.settings.get("bottom_layers")); const coord_t skin_overlap = mesh_.settings.get("skin_overlap_mm"); + const coord_t roofing_extension = mesh_.settings.get("roofing_extension"); const Shape filled_area_above = generateFilledAreaAbove(part, roofing_layer_count); const std::optional filled_area_below = generateFilledAreaBelow(part, flooring_layer_count); @@ -351,6 +352,12 @@ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& skin_part.skin_fill = skin_part.outline.intersection(filled_area_above); } + // Allow roof areas to grow inside the skin by roofing_extension amount + constexpr coord_t epsilon = 5; // Roofing and skin edges overlap, so make sure they properly merge + Shape roof_and_skin = skin_part.roofing_fill.offset(epsilon).unionPolygons(skin_part.skin_fill); + Shape extended_roof = skin_part.roofing_fill.offset(roofing_extension); + skin_part.roofing_fill = roof_and_skin.intersection(extended_roof); + // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. // Otherwise, adjacent skin_fill and roofing/flooring would have doubled offset areas. Since they both offset into each other. skin_part.skin_fill = skin_part.skin_fill.offset(skin_overlap).difference(skin_part.roofing_fill).difference(skin_part.flooring_fill); From d93c09ce924eaacad5091b57304a971a7c15e6e5 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 14:46:46 +0200 Subject: [PATCH 15/22] Apply roofing extension only when enabled CURA-12446 --- src/skin.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 21643b839e..4db7136c95 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -352,11 +352,14 @@ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& skin_part.skin_fill = skin_part.outline.intersection(filled_area_above); } - // Allow roof areas to grow inside the skin by roofing_extension amount - constexpr coord_t epsilon = 5; // Roofing and skin edges overlap, so make sure they properly merge - Shape roof_and_skin = skin_part.roofing_fill.offset(epsilon).unionPolygons(skin_part.skin_fill); - Shape extended_roof = skin_part.roofing_fill.offset(roofing_extension); - skin_part.roofing_fill = roof_and_skin.intersection(extended_roof); + if (roofing_extension > 0) + { + // Allow roof areas to grow inside the skin by roofing_extension amount + constexpr coord_t epsilon = 5; // Roofing and skin edges overlap, so make sure they properly merge + Shape roof_and_skin = skin_part.roofing_fill.offset(epsilon).unionPolygons(skin_part.skin_fill); + Shape extended_roof = skin_part.roofing_fill.offset(roofing_extension); + skin_part.roofing_fill = roof_and_skin.intersection(extended_roof); + } // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. // Otherwise, adjacent skin_fill and roofing/flooring would have doubled offset areas. Since they both offset into each other. From a461db3b72cb8f96fe1d9e06ac93f0b62c9e514c Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 17:45:18 +0200 Subject: [PATCH 16/22] Apply roofing extension in a safer way CURA-12446 --- src/skin.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 4db7136c95..82561a02c9 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -339,26 +339,17 @@ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& const std::optional filled_area_below = generateFilledAreaBelow(part, flooring_layer_count); // An area that would have nothing below nor above is considered a roof - skin_part.roofing_fill = skin_part.outline.difference(filled_area_above); + skin_part.roofing_fill = skin_part.outline.difference(filled_area_above.offset(-roofing_extension)); if (filled_area_below.has_value()) { skin_part.flooring_fill = skin_part.outline.intersection(filled_area_above).difference(*filled_area_below); - skin_part.skin_fill = skin_part.outline.intersection(filled_area_above).intersection(*filled_area_below); + skin_part.skin_fill = skin_part.outline.difference(skin_part.roofing_fill).intersection(*filled_area_below); } else { // Mesh part is just above build plate, so it is completely supported // skin_part.flooring_fill = Shape(); - skin_part.skin_fill = skin_part.outline.intersection(filled_area_above); - } - - if (roofing_extension > 0) - { - // Allow roof areas to grow inside the skin by roofing_extension amount - constexpr coord_t epsilon = 5; // Roofing and skin edges overlap, so make sure they properly merge - Shape roof_and_skin = skin_part.roofing_fill.offset(epsilon).unionPolygons(skin_part.skin_fill); - Shape extended_roof = skin_part.roofing_fill.offset(roofing_extension); - skin_part.roofing_fill = roof_and_skin.intersection(extended_roof); + skin_part.skin_fill = skin_part.outline.intersection(skin_part.roofing_fill); } // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. From 8cefcfc4cb3f22936af41dba607ffc5a2c6ed130 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 18:04:19 +0200 Subject: [PATCH 17/22] Really disable small skin on surface when it is disabled CURA-12446 It caused interactions with the covered top/bottom surface --- src/FffGcodeWriter.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index f24d916a21..d631355119 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3473,7 +3473,21 @@ void FffGcodeWriter::processRoofingFlooring( const Ratio skin_density = 1.0; const coord_t skin_overlap = 0; // skinfill already expanded over the roofing areas; don't overlap with perimeters const bool monotonic = mesh.settings.get(settings_names.monotonic); - processSkinPrintFeature(storage, gcode_layer, mesh, extruder_nr, fill, config, pattern, roofing_angle, skin_overlap, skin_density, monotonic, added_something); + constexpr bool is_roofing_flooring = true; + processSkinPrintFeature( + storage, + gcode_layer, + mesh, + extruder_nr, + fill, + config, + pattern, + roofing_angle, + skin_overlap, + skin_density, + monotonic, + is_roofing_flooring, + added_something); } void FffGcodeWriter::processTopBottom( @@ -3641,6 +3655,7 @@ void FffGcodeWriter::processTopBottom( } } const bool monotonic = mesh.settings.get("skin_monotonic"); + constexpr bool is_roofing_flooring = false; processSkinPrintFeature( storage, gcode_layer, @@ -3653,6 +3668,7 @@ void FffGcodeWriter::processTopBottom( skin_overlap, skin_density, monotonic, + is_roofing_flooring, added_something, fan_speed); } @@ -3669,6 +3685,7 @@ void FffGcodeWriter::processSkinPrintFeature( const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, + const bool is_roofing_flooring, bool& added_something, double fan_speed) const { @@ -3679,7 +3696,6 @@ void FffGcodeWriter::processSkinPrintFeature( constexpr int infill_multiplier = 1; constexpr int extra_infill_shift = 0; const size_t wall_line_count = mesh.settings.get("skin_outline_count"); - const coord_t small_area_width = mesh.settings.get("small_skin_width"); const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG; const bool connect_polygons = mesh.settings.get("connect_skin_polygons"); coord_t max_resolution = mesh.settings.get("meshfix_maximum_resolution"); @@ -3693,6 +3709,7 @@ void FffGcodeWriter::processSkinPrintFeature( constexpr int zag_skip_count = 0; constexpr coord_t pocket_size = 0; const bool small_areas_on_surface = mesh.settings.get("small_skin_on_surface"); + const coord_t small_area_width = (small_areas_on_surface || ! is_roofing_flooring) ? mesh.settings.get("small_skin_width") : 0; const auto& current_layer = mesh.layers[gcode_layer.getLayerNr()]; const auto& exposed_to_air = current_layer.top_surface.areas.unionPolygons(current_layer.bottom_surface); From 97d783ffbf2e8b4e03a898115924619814a046c7 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 10 Oct 2025 18:09:05 +0200 Subject: [PATCH 18/22] Set proper method definition CURA-12446 --- include/FffGcodeWriter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 50483a58ba..3d1b9af378 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -575,6 +575,7 @@ class FffGcodeWriter : public NoCopy * \param skin_overlap The amount by which to expand the \p area * \param skin density Sets the density of the the skin lines by adjusting the distance between them (normal skin is 1.0) * \param monotonic Whether to order lines monotonically (``true``) or to + * \param is_roofing_flooring Indicates whether we are currently processing a top/bottom layer, or a skin layer * minimise travel moves (``false``). * \param[out] added_something Whether this function added anything to the layer plan * \param fan_speed fan speed override for this skin area @@ -591,6 +592,7 @@ class FffGcodeWriter : public NoCopy const coord_t skin_overlap, const Ratio skin_density, const bool monotonic, + const bool is_roofing_flooring, bool& added_something, double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; From 458cd00352687393ce04aa2ee0334141d1953798 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 13 Oct 2025 17:13:52 +0200 Subject: [PATCH 19/22] Avoid top surfaces to grow from the outside of the model CURA-12446 --- include/sliceDataStorage.h | 5 ++++ src/skin.cpp | 39 +++++++++++++-------------- src/sliceDataStorage.cpp | 55 ++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index 824f418182..21eff6566e 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -469,6 +469,11 @@ class SliceDataStorage : public NoCopy */ Shape getMachineBorder(int extruder_nr = -1) const; + /*! + * @return The raw outer build plate shape without any disallowed area + */ + Shape getRawMachineBorder() const; + void initializePrimeTower(); private: diff --git a/src/skin.cpp b/src/skin.cpp index 82561a02c9..9fff578861 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -328,29 +328,28 @@ void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part) */ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& part) { + const size_t roofing_layer_count = std::min(mesh_.settings.get("roofing_layer_count"), mesh_.settings.get("top_layers")); + const size_t flooring_layer_count = std::min(mesh_.settings.get("flooring_layer_count"), mesh_.settings.get("bottom_layers")); + const coord_t skin_overlap = mesh_.settings.get("skin_overlap_mm"); + const coord_t roofing_extension = mesh_.settings.get("roofing_extension"); + + constexpr coord_t epsilon = 5; + const SliceDataStorage slice_data; + const Shape build_plate = slice_data.getRawMachineBorder().offset(epsilon); + const Shape build_plate_for_roofing = build_plate.offset(roofing_extension * 2).difference(part.outline); + + const Shape filled_area_above = generateFilledAreaAbove(part, roofing_layer_count); + const Shape filled_area_below = generateFilledAreaBelow(part, flooring_layer_count).value_or(build_plate); + for (SkinPart& skin_part : part.skin_parts) { - const size_t roofing_layer_count = std::min(mesh_.settings.get("roofing_layer_count"), mesh_.settings.get("top_layers")); - const size_t flooring_layer_count = std::min(mesh_.settings.get("flooring_layer_count"), mesh_.settings.get("bottom_layers")); - const coord_t skin_overlap = mesh_.settings.get("skin_overlap_mm"); - const coord_t roofing_extension = mesh_.settings.get("roofing_extension"); - - const Shape filled_area_above = generateFilledAreaAbove(part, roofing_layer_count); - const std::optional filled_area_below = generateFilledAreaBelow(part, flooring_layer_count); + // In order to avoid edge cases, it is safer to create the extended roofing area by reducing the area above. However, we want to avoid reducing the borders, so at this + // point we extend the area above with the build plate area, so that when reducing, the border will still be far away + const Shape bordered_area_above = build_plate_for_roofing.unionPolygons(filled_area_above.offset(epsilon)); + skin_part.roofing_fill = skin_part.outline.difference(bordered_area_above.offset(-roofing_extension)); - // An area that would have nothing below nor above is considered a roof - skin_part.roofing_fill = skin_part.outline.difference(filled_area_above.offset(-roofing_extension)); - if (filled_area_below.has_value()) - { - skin_part.flooring_fill = skin_part.outline.intersection(filled_area_above).difference(*filled_area_below); - skin_part.skin_fill = skin_part.outline.difference(skin_part.roofing_fill).intersection(*filled_area_below); - } - else - { - // Mesh part is just above build plate, so it is completely supported - // skin_part.flooring_fill = Shape(); - skin_part.skin_fill = skin_part.outline.intersection(skin_part.roofing_fill); - } + skin_part.flooring_fill = skin_part.outline.intersection(filled_area_above).difference(filled_area_below); + skin_part.skin_fill = skin_part.outline.difference(skin_part.roofing_fill).intersection(filled_area_below); // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. // Otherwise, adjacent skin_fill and roofing/flooring would have doubled offset areas. Since they both offset into each other. diff --git a/src/sliceDataStorage.cpp b/src/sliceDataStorage.cpp index d26e2aaa8a..6f2e0981ee 100644 --- a/src/sliceDataStorage.cpp +++ b/src/sliceDataStorage.cpp @@ -611,29 +611,7 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const { const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; - Shape border; - border.emplace_back(); - Polygon& outline = border.back(); - switch (mesh_group_settings.get("machine_shape")) - { - case BuildPlateShape::ELLIPTIC: - { - // Construct an ellipse to approximate the build volume. - const coord_t width = machine_size.max_.x_ - machine_size.min_.x_; - const coord_t depth = machine_size.max_.y_ - machine_size.min_.y_; - constexpr unsigned int circle_resolution = 50; - for (unsigned int i = 0; i < circle_resolution; i++) - { - const double angle = std::numbers::pi * 2 * i / circle_resolution; - outline.emplace_back(machine_size.getMiddle().x_ + std::cos(angle) * width / 2, machine_size.getMiddle().y_ + std::sin(angle) * depth / 2); - } - break; - } - case BuildPlateShape::RECTANGULAR: - default: - outline = machine_size.flatten().toPolygon(); - break; - } + Shape border = getRawMachineBorder(); Shape disallowed_areas = mesh_group_settings.get("machine_disallowed_areas"); disallowed_areas = disallowed_areas.unionPolygons(); // union overlapping disallowed areas @@ -731,6 +709,37 @@ Shape SliceDataStorage::getMachineBorder(int checking_extruder_nr) const return border; } +Shape SliceDataStorage::getRawMachineBorder() const +{ + const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; + + Shape border; + border.emplace_back(); + Polygon& outline = border.back(); + switch (mesh_group_settings.get("machine_shape")) + { + case BuildPlateShape::ELLIPTIC: + { + // Construct an ellipse to approximate the build volume. + const coord_t width = machine_size.max_.x_ - machine_size.min_.x_; + const coord_t depth = machine_size.max_.y_ - machine_size.min_.y_; + constexpr unsigned int circle_resolution = 50; + for (unsigned int i = 0; i < circle_resolution; i++) + { + const double angle = std::numbers::pi * 2 * i / circle_resolution; + outline.emplace_back(machine_size.getMiddle().x_ + std::cos(angle) * width / 2, machine_size.getMiddle().y_ + std::sin(angle) * depth / 2); + } + break; + } + case BuildPlateShape::RECTANGULAR: + default: + outline = machine_size.flatten().toPolygon(); + break; + } + + return border; +} + void SliceDataStorage::initializePrimeTower() { prime_tower_ = PrimeTower::createPrimeTower(*this); From c04a9241d10affe322f3aa20040c689fd828302b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 13 Oct 2025 17:21:56 +0200 Subject: [PATCH 20/22] Optimize function by moving fixed calculations out of the loop CURA-12446 --- src/skin.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 9fff578861..83bc54b090 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -335,19 +335,18 @@ void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& constexpr coord_t epsilon = 5; const SliceDataStorage slice_data; - const Shape build_plate = slice_data.getRawMachineBorder().offset(epsilon); - const Shape build_plate_for_roofing = build_plate.offset(roofing_extension * 2).difference(part.outline); + const Shape build_plate = slice_data.getRawMachineBorder(); const Shape filled_area_above = generateFilledAreaAbove(part, roofing_layer_count); - const Shape filled_area_below = generateFilledAreaBelow(part, flooring_layer_count).value_or(build_plate); + const Shape filled_area_below = generateFilledAreaBelow(part, flooring_layer_count).value_or(build_plate.offset(epsilon)); + + // In order to avoid edge cases, it is safer to create the extended roofing area by reducing the area above. However, we want to avoid reducing the borders, so at this + // point we extend the area above with the build plate area, so that when reducing, the border will still be far away. + const Shape reduced_area_above = build_plate.offset(roofing_extension * 2).difference(part.outline).unionPolygons(filled_area_above.offset(epsilon)).offset(-roofing_extension); for (SkinPart& skin_part : part.skin_parts) { - // In order to avoid edge cases, it is safer to create the extended roofing area by reducing the area above. However, we want to avoid reducing the borders, so at this - // point we extend the area above with the build plate area, so that when reducing, the border will still be far away - const Shape bordered_area_above = build_plate_for_roofing.unionPolygons(filled_area_above.offset(epsilon)); - skin_part.roofing_fill = skin_part.outline.difference(bordered_area_above.offset(-roofing_extension)); - + skin_part.roofing_fill = skin_part.outline.difference(reduced_area_above); skin_part.flooring_fill = skin_part.outline.intersection(filled_area_above).difference(filled_area_below); skin_part.skin_fill = skin_part.outline.difference(skin_part.roofing_fill).intersection(filled_area_below); From 2c66ce49497ab0fc3eb462af9a791d20599746f1 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 13 Oct 2025 17:22:55 +0200 Subject: [PATCH 21/22] Add documentation image CURA-12446 --- doc/skin_parts.svg | 211 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 doc/skin_parts.svg diff --git a/doc/skin_parts.svg b/doc/skin_parts.svg new file mode 100644 index 0000000000..5def341e88 --- /dev/null +++ b/doc/skin_parts.svg @@ -0,0 +1,211 @@ + + + + + + + + + outline + + below + + above + + + Roof (by default) + Roof + Floor + Skin + + below_inside + + above_inside + + Roofingextensionarea + + + From 37594a16b23a5f23c3bbe630abdab34b138f77ec Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 23 Jan 2026 15:47:22 +0100 Subject: [PATCH 22/22] (WIP) different approach to customizable flooring walls count CURA-12446 --- CMakeLists.txt | 2 +- include/FffPolygonGenerator.h | 2 + .../{skin.h => SkinInfillAreaComputation.h} | 34 ++++- src/FffPolygonGenerator.cpp | 62 ++++++--- ...skin.cpp => SkinInfillAreaComputation.cpp} | 127 ++++++++++++++++-- 5 files changed, 187 insertions(+), 40 deletions(-) rename include/{skin.h => SkinInfillAreaComputation.h} (92%) rename src/{skin.cpp => SkinInfillAreaComputation.cpp} (83%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28ed81aa78..317491f60c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ set(engine_SRCS # Except main.cpp. src/Scene.cpp src/SkeletalTrapezoidation.cpp src/SkeletalTrapezoidationGraph.cpp - src/skin.cpp + src/SkinInfillAreaComputation.cpp src/SkirtBrim.cpp src/SupportInfillPart.cpp src/Slice.cpp diff --git a/include/FffPolygonGenerator.h b/include/FffPolygonGenerator.h index 49342b57fe..6118c7d528 100644 --- a/include/FffPolygonGenerator.h +++ b/include/FffPolygonGenerator.h @@ -148,6 +148,8 @@ class FffPolygonGenerator : public NoCopy */ void processSkinsAndInfill(SliceMeshStorage& mesh, const LayerIndex layer_nr, bool process_infill); + void processSkinsInfillWalls(SliceMeshStorage& mesh, const LayerIndex layer_nr, bool process_infill); + /*! * Generate the polygons where the draft screen should be. * diff --git a/include/skin.h b/include/SkinInfillAreaComputation.h similarity index 92% rename from include/skin.h rename to include/SkinInfillAreaComputation.h index db1140fa09..ddad277ce6 100644 --- a/include/skin.h +++ b/include/SkinInfillAreaComputation.h @@ -1,22 +1,40 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2026 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher. -#ifndef SKIN_H -#define SKIN_H +#ifndef SKININFILLAREACOMPUTATION_H +#define SKININFILLAREACOMPUTATION_H #include +#include "geometry/Shape.h" #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" namespace cura { -class Shape; class SkinPart; class SliceLayerPart; class SliceMeshStorage; +enum class SliceAreaType +{ + Roofing, + Flooring, + Skin, + Infill +}; + +struct SliceArea +{ + using Ptr = std::shared_ptr; + + SliceAreaType type{ SliceAreaType::Infill }; + Shape outline; + size_t walls{ 0 }; + SliceArea::Ptr inner_area; // Inner area that should be also be printed with walls +}; + /*! * Class containing all skin and infill area computation functions */ @@ -40,6 +58,8 @@ class SkinInfillAreaComputation */ void generateSkinsAndInfill(); + SliceArea::Ptr generateRawAreas(SliceLayerPart& part); + /*! * \brief Combines the infill of multiple layers for a specified mesh. * @@ -98,7 +118,7 @@ class SkinInfillAreaComputation * above. The input is the area within the inner walls (or an empty Polygons * object). */ - void calculateTopSkin(const SliceLayerPart& part, Shape& upskin); + Shape calculateTopSkin(const SliceLayerPart& part, Shape& upskin, const size_t layer_count); /*! * \brief Calculate the basic areas which have air below. @@ -107,7 +127,7 @@ class SkinInfillAreaComputation * layers above. The input is the area within the inner walls (or an empty * Polygons object). */ - void calculateBottomSkin(const SliceLayerPart& part, Shape& downskin); + void calculateBottomSkin(const SliceLayerPart& part, Shape& downskin, const size_t layer_count); /*! * Apply skin expansion: @@ -194,4 +214,4 @@ class SkinInfillAreaComputation } // namespace cura -#endif // SKIN_H +#endif diff --git a/src/FffPolygonGenerator.cpp b/src/FffPolygonGenerator.cpp index ad0fb75fed..05b09b9ce5 100644 --- a/src/FffPolygonGenerator.cpp +++ b/src/FffPolygonGenerator.cpp @@ -24,7 +24,7 @@ #include "multiVolumes.h" #include "PrintFeature.h" #include "raft.h" -#include "skin.h" +#include "SkinInfillAreaComputation.h" #include "SkirtBrim.h" #include "Slice.h" #include "TextureDataProvider.h" @@ -452,9 +452,9 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( const std::vector& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate) { - size_t mesh_idx = mesh_order[mesh_order_idx]; + const size_t mesh_idx = mesh_order[mesh_order_idx]; SliceMeshStorage& mesh = *storage.meshes[mesh_idx]; - size_t mesh_layer_count = mesh.layers.size(); + const size_t mesh_layer_count = mesh.layers.size(); if (mesh.settings.get("infill_mesh")) { processInfillMesh(storage, mesh_order_idx, mesh_order); @@ -496,20 +496,6 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( } } guarded_progress = { inset_skin_progress_estimate }; - // walls - cura::parallel_for( - 0, - mesh_layer_count, - [&](size_t layer_number) - { - spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); - processWalls(mesh, layer_number); - guarded_progress++; - }); - - ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count); - mesh_inset_skin_progress_estimator->nextStage(skin_estimator); - bool process_infill = mesh.settings.get("infill_line_distance") > 0; if (! process_infill) { // do process infill anyway if it's modified by modifier meshes @@ -530,6 +516,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( } } + // skin & infill const Settings& mesh_group_settings = Application::getInstance().current_slice_->scene.current_mesh_group->settings; bool magic_spiralize = mesh_group_settings.get("magic_spiralize"); @@ -539,6 +526,19 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( mesh_max_initial_bottom_layer_count = std::max(mesh_max_initial_bottom_layer_count, mesh.settings.get("initial_bottom_layers")); } + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); + if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/down skin and infill for the first X layers when spiralize is chosen. + { + processSkinsInfillWalls(mesh, layer_number, process_infill); + } + guarded_progress++; + }); + guarded_progress.reset(); cura::parallel_for( 0, @@ -546,12 +546,27 @@ void FffPolygonGenerator::processBasicWallsSkinInfill( [&](size_t layer_number) { spdlog::debug("Processing skins and infill layer {} of {}", layer_number, mesh.layers.size()); - if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/downskin and infill for the first X layers when spiralize is choosen. + if (! magic_spiralize || layer_number < mesh_max_initial_bottom_layer_count) // Only generate up/down skin and infill for the first X layers when spiralize is chosen. { processSkinsAndInfill(mesh, layer_number, process_infill); } guarded_progress++; }); + + // walls + guarded_progress.reset(); + cura::parallel_for( + 0, + mesh_layer_count, + [&](size_t layer_number) + { + spdlog::debug("Processing insets for layer {} of {}", layer_number, mesh.layers.size()); + processWalls(mesh, layer_number); + guarded_progress++; + }); + + ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count); + mesh_inset_skin_progress_estimator->nextStage(skin_estimator); } void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, const size_t mesh_order_idx, const std::vector& mesh_order) @@ -822,6 +837,16 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, size } } +void FffPolygonGenerator::processSkinsInfillWalls(SliceMeshStorage& mesh, const LayerIndex layer_nr, bool process_infill) +{ + SkinInfillAreaComputation skin_infill_area_computation(layer_nr, mesh, process_infill); + + for (SliceLayerPart& part : mesh.layers[layer_nr].parts) + { + skin_infill_area_computation.generateRawAreas(part); + } +} + /* * This function is executed in a parallel region based on layer_nr. * When modifying make sure any changes does not introduce data races. @@ -1200,5 +1225,4 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) } } - } // namespace cura diff --git a/src/skin.cpp b/src/SkinInfillAreaComputation.cpp similarity index 83% rename from src/skin.cpp rename to src/SkinInfillAreaComputation.cpp index 4daa5421e5..be91a6dd96 100644 --- a/src/skin.cpp +++ b/src/SkinInfillAreaComputation.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher -#include "skin.h" +#include "SkinInfillAreaComputation.h" #include // std::ceil @@ -91,12 +91,113 @@ void SkinInfillAreaComputation::generateSkinsAndInfill() SliceLayer* layer = &mesh_.layers[layer_nr_]; + const Shape outline = layer->getOutlines(); + // SVG svg(fmt::format("/tmp/areas_{}.svg", layer_nr_), AABB(outline), 0.001); + for (SliceLayerPart& part : layer->parts) { + // svg.write(part.outline, { .line = { 0.1 } }); + generateSkinRoofingFlooringFill(part); generateTopAndBottomMostSurfaces(part); + + for (const SkinPart& skin_part : part.skin_parts) + { + // svg.write(skin_part.outline, { .line = { 0.05 } }); + // svg.write(skin_part.roofing_fill, { .surface = { SVG::Color::GREEN, 0.5 } }); + // svg.write(skin_part.flooring_fill, { .surface = { SVG::Color::RED, 0.5 } }); + // svg.write(skin_part.skin_fill, { .surface = { SVG::Color::YELLOW, 0.5 } }); + } + } +} + +SliceArea::Ptr SkinInfillAreaComputation::generateRawAreas(SliceLayerPart& part) +{ + // Make a copy of the outline which we later intersect and union with the resized skins to ensure the resized skin isn't too large or removed completely. + Shape top_skin; + if (top_layer_count_ > 0) + { + top_skin = Shape(part.outline); + } + calculateTopSkin(part, top_skin); + + Shape bottom_skin; + if (bottom_layer_count_ > 0 || layer_nr_ < LayerIndex(initial_bottom_layer_count_)) + { + bottom_skin = Shape(part.outline); + } + calculateBottomSkin(part, bottom_skin); + + applySkinExpansion(part.outline, top_skin, bottom_skin); + + // Now combine the resized top skin and bottom skin. + // Shape skin = top_skin.unionPolygons(bottom_skin); + + skin.removeSmallAreas(MIN_AREA_SIZE); + + const size_t roofing_layer_count = std::min(mesh_.settings.get("roofing_layer_count"), mesh_.settings.get("top_layers")); + const size_t flooring_layer_count = std::min(mesh_.settings.get("flooring_layer_count"), mesh_.settings.get("bottom_layers")); + const coord_t skin_overlap = mesh_.settings.get("skin_overlap_mm"); + const coord_t roofing_expansion = mesh_.settings.get("roofing_expansion"); + + const SliceDataStorage slice_data; + const Shape build_plate = slice_data.getRawMachineBorder(); + + const Shape filled_area_just_above = generateFilledAreaAbove(part, roofing_layer_count); + const Shape filled_area_just_below = generateFilledAreaBelow(part, flooring_layer_count).value_or(build_plate.offset(EPSILON)); + const Shape filled_area_above = generateFilledAreaAbove(part, top_layer_count_); + const Shape filled_area_below = generateFilledAreaBelow(part, bottom_layer_count_).value_or(build_plate.offset(EPSILON)); + + // In order to avoid edge cases, it is safer to create the extended roofing area by reducing the area above. However, we want to avoid reducing the borders, so at this + // point we extend the area above with the build plate area, so that when reducing, the border will still be far away. + const Shape reduced_area_above + = build_plate.offset(roofing_expansion * 2).difference(part.outline).unionPolygons(filled_area_just_above.offset(EPSILON)).offset(-roofing_expansion - 2 * EPSILON); + + Shape roofing_area = part.outline.difference(reduced_area_above); + Shape flooring_area = part.outline.intersection(filled_area_just_above).difference(filled_area_just_below); + // Shape skin_area = part.outline.difference(roofing_area).intersection(filled_area_below); + + // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. + // Otherwise, adjacent skin_fill and roofing/flooring would have doubled offset areas. Since they both offset into each other. + skin = skin.offset(skin_overlap).difference(roofing_area).difference(flooring_area); + roofing_area = roofing_area.offset(skin_overlap); + flooring_area = flooring_area.offset(skin_overlap).difference(roofing_area); + + // Create infill area irrespective if the infill is to be generated or not(would be used for bridging). + const Shape infill = part.outline.difference(skin.offset(EPSILON)).difference(roofing_area.offset(EPSILON)).difference(flooring_area.offset(EPSILON)).offset(-EPSILON); + // part.infill_area = part.inner_area.difference(skin); + // if (process_infill_) + { // process infill when infill density > 0 + // or when other infill meshes want to modify this infill + // generateInfill(part); } + // for (const SingleShape& skin_area_part : skin.splitIntoParts()) + // { + // if (skin_area_part.empty()) + // { + // continue; + // } + // part.skin_parts.emplace_back(); + // part.skin_parts.back().outline = skin_area_part; + // } + + SVG svg(fmt::format("/tmp/areas_{}.svg", layer_nr_), AABB(part.outline), 0.001); + + svg.write(part.outline, { .line = { 0.1 } }); + + // generateSkinRoofingFlooringFill(part); + + // generateTopAndBottomMostSurfaces(part); + + svg.write(skin, { .surface = { SVG::Color::YELLOW, 0.5 } }); + svg.write(top_skin, { .surface = { SVG::Color::GREEN, 0.5 } }); + svg.write(bottom_skin, { .surface = { SVG::Color::RED, 0.5 } }); + svg.write(roofing_area, { .surface = { SVG::Color::MAGENTA, 0.5 } }); + svg.write(flooring_area, { .surface = { SVG::Color::ORANGE, 0.5 } }); + svg.write(infill, { .surface = { SVG::Color::YELLOW, 0.5 } }); + + return {}; } /* @@ -174,17 +275,17 @@ void SkinInfillAreaComputation::generateSkinAndInfillAreas(SliceLayerPart& part) * * this function may only read/write the skin and infill from the *current* layer. */ -void SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, Shape& downskin) +Shape SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, const size_t layer_count) { - if (bottom_layer_count_ == 0 && initial_bottom_layer_count_ == 0) + if (layer_count == 0) { return; // downskin remains empty } - if (layer_nr_ < LayerIndex(initial_bottom_layer_count_)) + if (layer_nr_ < LayerIndex(layer_count)) { return; // don't subtract anything form the downskin } - LayerIndex bottom_check_start_layer_idx{ std::max(LayerIndex{ 0 }, LayerIndex{ layer_nr_ - bottom_layer_count_ }) }; + LayerIndex bottom_check_start_layer_idx{ std::max(LayerIndex{ 0 }, LayerIndex{ layer_nr_ - layer_count }) }; Shape not_air = getOutlineOnLayer(part, bottom_check_start_layer_idx); if (! no_small_gaps_heuristic_) { @@ -198,22 +299,22 @@ void SkinInfillAreaComputation::calculateBottomSkin(const SliceLayerPart& part, { not_air.removeSmallAreas(min_infill_area); } - downskin = downskin.difference(not_air); // skin overlaps with the walls + return Shape(part.outline).difference(not_air); // skin overlaps with the walls } -void SkinInfillAreaComputation::calculateTopSkin(const SliceLayerPart& part, Shape& upskin) +Shape SkinInfillAreaComputation::calculateTopSkin(const SliceLayerPart& part, const size_t layer_count) { - if (layer_nr_ > LayerIndex(mesh_.layers.size()) - top_layer_count_ || top_layer_count_ <= 0) + if (layer_nr_ > LayerIndex(mesh_.layers.size()) - layer_count || layer_count == 0) { - // If we're in the very top layers (less than top_layer_count from the top of the mesh) everything will be top skin anyway, so no need to generate infill. Just take the - // original inner contour. If top_layer_count is 0, no need to calculate anything either. + // If we're in the very top layers (less than layer_count from the top of the mesh) everything will be top skin anyway, so no need to generate infill. Just take the + // original inner contour. If layer_count is 0, no need to calculate anything either. return; } - Shape not_air = getOutlineOnLayer(part, layer_nr_ + top_layer_count_); + Shape not_air = getOutlineOnLayer(part, layer_nr_ + layer_count); if (! no_small_gaps_heuristic_) { - for (int upskin_layer_nr = layer_nr_ + 1; upskin_layer_nr < layer_nr_ + top_layer_count_; upskin_layer_nr++) + for (int upskin_layer_nr = layer_nr_ + 1; upskin_layer_nr < layer_nr_ + layer_count; upskin_layer_nr++) { not_air = not_air.intersection(getOutlineOnLayer(part, upskin_layer_nr)); } @@ -225,7 +326,7 @@ void SkinInfillAreaComputation::calculateTopSkin(const SliceLayerPart& part, Sha not_air.removeSmallAreas(min_infill_area); } - upskin = upskin.difference(not_air); // skin overlaps with the walls + return Shape(part.outline).difference(not_air); // skin overlaps with the walls } /*