From fc7ab914a1963261413d619ea4f385126062902c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0ach?= Date: Thu, 31 Oct 2024 14:43:47 +0100 Subject: [PATCH] Create method for infill above bridges identification --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ExtrusionRole.hpp | 2 + src/libslic3r/Fill/Fill.cpp | 27 +++++-- src/libslic3r/GCode.cpp | 2 + src/libslic3r/InfillAboveBridges.cpp | 36 +++++++++ src/libslic3r/InfillAboveBridges.hpp | 16 ++++ src/libslic3r/PrintObject.cpp | 14 ++++ src/libslic3r/Surface.cpp | 3 + src/libslic3r/Surface.hpp | 9 ++- tests/fff_print/CMakeLists.txt | 1 + tests/fff_print/test_infill_above_bridges.cpp | 81 +++++++++++++++++++ 11 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 src/libslic3r/InfillAboveBridges.cpp create mode 100644 src/libslic3r/InfillAboveBridges.hpp create mode 100644 tests/fff_print/test_infill_above_bridges.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 57d42e6409..3d79437076 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,6 +245,8 @@ set(SLIC3R_SOURCES Geometry/VoronoiUtils.hpp Geometry/VoronoiUtils.cpp Geometry/VoronoiVisualUtils.hpp + InfillAboveBridges.hpp + InfillAboveBridges.cpp JumpPointSearch.cpp JumpPointSearch.hpp KDTreeIndirect.hpp diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index 844c485f10..5d6b9e0cdc 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -31,6 +31,7 @@ enum class ExtrusionRoleModifier : uint16_t { Solid, Ironing, Bridge, + OverBridge, // 3) Special types // Indicator that the extrusion role was mixed from multiple differing extrusion roles, // for example from Support and SupportInterface. @@ -62,6 +63,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers static constexpr const ExtrusionRoleModifiers SolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid }; // Top solid infill (visible). //FIXME why there is no bottom solid infill type? + static constexpr const ExtrusionRoleModifiers InfillOverBridge{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::OverBridge }; static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External }; // Ironing infill at the top surfaces. static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index b49ee57a0b..7a0ffe39bc 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -183,15 +183,24 @@ std::vector group_fills(const Layer &layer) } else if (params.density <= 0) continue; - params.extrusion_role = - is_bridge ? - ExtrusionRole::BridgeInfill : - (surface.is_solid() ? - (surface.is_top() ? ExtrusionRole::TopSolidInfill : ExtrusionRole::SolidInfill) : - ExtrusionRole::InternalInfill); + if (is_bridge) { + params.extrusion_role = ExtrusionRole::BridgeInfill; + } else { + if (surface.is_solid()) { + if (surface.is_top()) { + params.extrusion_role = ExtrusionRole::TopSolidInfill; + } else if (surface.surface_type == stSolidOverBridge) { + params.extrusion_role = ExtrusionRole::InfillOverBridge; + } else { + params.extrusion_role = ExtrusionRole::SolidInfill; + } + } else { + params.extrusion_role = ExtrusionRole::InternalInfill; + } + } params.bridge_angle = float(surface.bridge_angle); params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); - + // Calculate the actual flow we'll be using for this infill. params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern); params.flow = params.bridge ? @@ -342,7 +351,9 @@ std::vector group_fills(const Layer &layer) // Use ipEnsuring pattern for all internal Solids. { for (size_t surface_fill_id = 0; surface_fill_id < surface_fills.size(); ++surface_fill_id) - if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) { + if (SurfaceFill &fill = surface_fills[surface_fill_id]; + fill.surface.surface_type == stInternalSolid + || fill.surface.surface_type == stSolidOverBridge) { fill.params.pattern = ipEnsuring; } } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b427ea82f2..7f576412db 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3387,6 +3387,8 @@ std::string GCodeGenerator::_extrude( speed = m_config.get_abs_value("infill_speed"); } else if (path_attr.role == ExtrusionRole::SolidInfill) { speed = m_config.get_abs_value("solid_infill_speed"); + } else if (path_attr.role == ExtrusionRole::InfillOverBridge) { + speed = 10; } else if (path_attr.role == ExtrusionRole::TopSolidInfill) { speed = m_config.get_abs_value("top_solid_infill_speed"); } else if (path_attr.role == ExtrusionRole::Ironing) { diff --git a/src/libslic3r/InfillAboveBridges.cpp b/src/libslic3r/InfillAboveBridges.cpp new file mode 100644 index 0000000000..dd3206215f --- /dev/null +++ b/src/libslic3r/InfillAboveBridges.cpp @@ -0,0 +1,36 @@ +#include +#include "libslic3r/InfillAboveBridges.hpp" +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r::PrepareInfill { + void mark_as_infill_above_bridge(const ExPolygons &marker, const SurfaceRefsByRegion &layer) { + for (const SurfaceCollectionRef ®ion : layer) { + const ExPolygons intersection{intersection_ex(region.get().filter_by_type(stInternalSolid), marker, ApplySafetyOffset::No)}; + if (intersection.empty()) { + continue; + } + const ExPolygons clipped{diff_ex(region.get().filter_by_type(stInternalSolid), marker, ApplySafetyOffset::Yes)}; + region.get().remove_type(stInternalSolid); + region.get().append(clipped, stInternalSolid); + region.get().append(intersection, stSolidOverBridge); + } + } + + void separate_infill_above_bridges(const SurfaceRefs &surfaces) { + if (surfaces.empty()) { + return; + } + + const SurfaceRefsByRegion *previous_layer{&surfaces.front()}; + for (const SurfaceRefsByRegion &layer : tcb::span{surfaces}.subspan(1)) { + ExPolygons bridges; + for (const SurfaceCollectionRef ®ion : *previous_layer) { + for (const Surface *bridge : region.get().filter_by_type(stBottomBridge)) { + bridges.push_back(bridge->expolygon); + } + } + mark_as_infill_above_bridge(bridges, layer); + previous_layer = &layer; + } + } +} diff --git a/src/libslic3r/InfillAboveBridges.hpp b/src/libslic3r/InfillAboveBridges.hpp new file mode 100644 index 0000000000..2e2fc2c564 --- /dev/null +++ b/src/libslic3r/InfillAboveBridges.hpp @@ -0,0 +1,16 @@ +#ifndef slic3r_InfillAboveBridges_hpp_ +#define slic3r_InfillAboveBridges_hpp_ + +#include +#include +#include "libslic3r/SurfaceCollection.hpp" + +namespace Slic3r::PrepareInfill { + using SurfaceCollectionRef = std::reference_wrapper; + using SurfaceRefsByRegion = std::vector; + using SurfaceRefs = std::vector; + + void separate_infill_above_bridges(const SurfaceRefs &surfaces); +} + +#endif // slic3r_InfillAboveBridges_hpp_ diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f9c5727b44..9f8d404763 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -64,6 +64,7 @@ #include "libslic3r/TriangleSelector.hpp" #include "tcbspan/span.hpp" #include "libslic3r/Point.hpp" +#include "libslic3r/InfillAboveBridges.hpp" using namespace std::literals; @@ -412,6 +413,19 @@ void PrintObject::prepare_infill() } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + PrepareInfill::SurfaceRefs surfaces; + for (const Layer *layer : m_layers) { + surfaces.emplace_back(); + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + LayerRegion *layerm = layer->m_regions[region_id]; + if (!layerm->fill_surfaces().empty()) { + surfaces.back().push_back(std::ref(layerm->m_fill_surfaces)); + } + } + } + PrepareInfill::separate_infill_above_bridges(surfaces); + this->set_done(posPrepareInfill); } diff --git a/src/libslic3r/Surface.cpp b/src/libslic3r/Surface.cpp index fbecc33118..65dda46ae0 100644 --- a/src/libslic3r/Surface.cpp +++ b/src/libslic3r/Surface.cpp @@ -53,6 +53,7 @@ const char* surface_type_to_color_name(const SurfaceType surface_type) case stInternalSolid: return "rgb(255,0,255)"; // magenta case stInternalBridge: return "rgb(0,255,255)"; case stInternalVoid: return "rgb(128,128,128)"; + case stSolidOverBridge: return "rgb(255,128,0)"; // orange case stPerimeter: return "rgb(128,0,0)"; // maroon default: return "rgb(64,64,64)"; }; @@ -89,6 +90,8 @@ void export_surface_type_legend_to_svg(SVG &svg, const Point &pos) svg.draw_legend(Point(pos_x, pos_y), "internal bridge", surface_type_to_color_name(stInternalBridge)); pos_x += step_x; svg.draw_legend(Point(pos_x, pos_y), "internal void" , surface_type_to_color_name(stInternalVoid)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "over bridge" , surface_type_to_color_name(stSolidOverBridge)); } bool export_to_svg(const char *path, const Surfaces &surfaces, const float transparency) diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index 8e7663afa4..557ceb8340 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -44,6 +44,8 @@ enum SurfaceType { stInternalVoid, // Inner/outer perimeters. stPerimeter, + // InternalSolid that is directly above stBottomBridge. + stSolidOverBridge, // Number of SurfaceType enums. stCount, }; @@ -111,7 +113,12 @@ public: bool is_bridge() const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; } bool is_external() const { return this->is_top() || this->is_bottom(); } bool is_internal() const { return ! this->is_external(); } - bool is_solid() const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; } + bool is_solid() const { + return this->is_external() + || this->surface_type == stInternalSolid + || this->surface_type == stSolidOverBridge + || this->surface_type == stInternalBridge; + } }; typedef std::vector Surfaces; diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt index ce04286a64..2fc1573c2b 100644 --- a/tests/fff_print/CMakeLists.txt +++ b/tests/fff_print/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${_TEST_NAME}_tests test_gaps.cpp test_gcode.cpp test_gcode_travels.cpp + test_infill_above_bridges.cpp test_seam_perimeters.cpp test_seam_shells.cpp test_seam_geometry.cpp diff --git a/tests/fff_print/test_infill_above_bridges.cpp b/tests/fff_print/test_infill_above_bridges.cpp new file mode 100644 index 0000000000..c55c230125 --- /dev/null +++ b/tests/fff_print/test_infill_above_bridges.cpp @@ -0,0 +1,81 @@ +#include +#include + +using namespace Slic3r; + +const ExPolygon square{ + Point::new_scale(0, 0), + Point::new_scale(10, 0), + Point::new_scale(10, 10), + Point::new_scale(0, 10) +}; + +ExPolygon translate(const ExPolygon &polygon, const Point &offset) { + ExPolygons result{polygon}; + translate(result, offset); + return result.front(); +} + +constexpr bool debug_files{false}; + +void draw_surfaces(const PrepareInfill::SurfaceRefsByRegion &surfaces, std::string_view file_name) { + using PrepareInfill::SurfaceCollectionRef; + + SurfaceCollection to_display; + for (const SurfaceCollectionRef &surface_collection : surfaces) { + to_display.append(surface_collection.get()); + } + to_display.export_to_svg(file_name.data(), false); +} + +TEST_CASE("Separate infill above bridges", "[PrepareInfill]") { + ExPolygons layer_0_region_0_bridge{ + square + }; + ExPolygons layer_0_region_0_internal{ + translate(square, Point::new_scale(10, 0)) + }; + ExPolygons layer_0_region_1_internal{ + translate(square, Point::new_scale(0, 10)) + }; + ExPolygons layer_0_region_1_bridge{ + translate(square, Point::new_scale(10, 10)) + }; + SurfaceCollection layer_0_region_0; + layer_0_region_0.append(layer_0_region_0_bridge, stBottomBridge); + layer_0_region_0.append(layer_0_region_0_internal, stInternal); + SurfaceCollection layer_0_region_1; + layer_0_region_1.append(layer_0_region_1_bridge, stBottomBridge); + layer_0_region_1.append(layer_0_region_1_internal, stInternal); + + PrepareInfill::SurfaceRefsByRegion layer_0{layer_0_region_0, layer_0_region_1}; + + ExPolygons layer_1_region_0_solid{ + translate(square, Point::new_scale(5, 5)) + }; + SurfaceCollection layer_1_region_0; + layer_1_region_0.append(layer_1_region_0_solid, stInternalSolid); + PrepareInfill::SurfaceRefsByRegion layer_1{layer_1_region_0}; + + if constexpr (debug_files) { + draw_surfaces(layer_0, "layer_0.svg"); + } + + PrepareInfill::separate_infill_above_bridges({layer_0, layer_1}); + + if constexpr (debug_files) { + draw_surfaces(layer_1, "layer_1.svg"); + } + + const Surfaces &result{layer_1.front().get().surfaces}; + REQUIRE(result.size() == 4); + const double expected_area{scale_(5.0) * scale_(5.0)}; + CHECK(result[0].expolygon.contour.area() == Approx(expected_area)); + CHECK(result[0].surface_type == stInternalSolid); + CHECK(result[1].expolygon.contour.area() == Approx(expected_area)); + CHECK(result[1].surface_type == stInternalSolid); + CHECK(result[2].expolygon.contour.area() == Approx(expected_area)); + CHECK(result[2].surface_type == stSolidOverBridge); + CHECK(result[3].expolygon.contour.area() == Approx(expected_area)); + CHECK(result[3].surface_type == stSolidOverBridge); +}