Merge branch 'ms_infill_above_bridges'

This commit is contained in:
Lukas Matena 2025-02-06 23:14:16 +01:00
commit 5430596a7e
18 changed files with 224 additions and 12 deletions

View File

@ -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

View File

@ -23,6 +23,7 @@ GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role)
}
if (role == ExtrusionRole::InternalInfill) return GCodeExtrusionRole::InternalInfill;
if (role == ExtrusionRole::SolidInfill) return GCodeExtrusionRole::SolidInfill;
if (role == ExtrusionRole::InfillOverBridge) return GCodeExtrusionRole::SolidInfill;
if (role == ExtrusionRole::TopSolidInfill) return GCodeExtrusionRole::TopSolidInfill;
if (role == ExtrusionRole::Ironing) return GCodeExtrusionRole::Ironing;
if (role == ExtrusionRole::BridgeInfill) return GCodeExtrusionRole::BridgeInfill;

View File

@ -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 };

View File

@ -183,12 +183,21 @@ std::vector<SurfaceFill> 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));
@ -342,7 +351,9 @@ std::vector<SurfaceFill> 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;
}
}

View File

@ -704,7 +704,8 @@ namespace DoExport {
if (region.config().get_abs_value("infill_speed") == 0 ||
region.config().get_abs_value("solid_infill_speed") == 0 ||
region.config().get_abs_value("top_solid_infill_speed") == 0 ||
region.config().get_abs_value("bridge_speed") == 0)
region.config().get_abs_value("bridge_speed") == 0 ||
region.config().get_abs_value("over_bridge_speed") == 0)
{
// Minimal volumetric flow should not be calculated over ironing extrusions.
// Use following lambda instead of the built-it method.
@ -3387,6 +3388,14 @@ 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) {
const double solid_infill_speed = m_config.get_abs_value("solid_infill_speed");
const double over_bridge_speed{m_config.get_abs_value("over_bridge_speed", solid_infill_speed)};
if (over_bridge_speed > 0) {
speed = over_bridge_speed;
} else {
speed = solid_infill_speed;
}
} else if (path_attr.role == ExtrusionRole::TopSolidInfill) {
speed = m_config.get_abs_value("top_solid_infill_speed");
} else if (path_attr.role == ExtrusionRole::Ironing) {

View File

@ -0,0 +1,39 @@
#include <tcbspan/span.hpp>
#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 &region : 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, const double expand_offset) {
if (surfaces.empty()) {
return;
}
const SurfaceRefsByRegion *previous_layer{&surfaces.front()};
for (const SurfaceRefsByRegion &layer : tcb::span{surfaces}.subspan(1)) {
ExPolygons bridges;
for (const SurfaceCollectionRef &region : *previous_layer) {
for (const Surface *bridge : region.get().filter_by_type(stBottomBridge)) {
bridges.push_back(bridge->expolygon);
}
}
if (expand_offset > 0) {
bridges = offset_ex(bridges, scale_(expand_offset));
}
mark_as_infill_above_bridge(bridges, layer);
previous_layer = &layer;
}
}
}

View File

@ -0,0 +1,16 @@
#ifndef slic3r_InfillAboveBridges_hpp_
#define slic3r_InfillAboveBridges_hpp_
#include <iostream>
#include <vector>
#include "libslic3r/SurfaceCollection.hpp"
namespace Slic3r::PrepareInfill {
using SurfaceCollectionRef = std::reference_wrapper<SurfaceCollection>;
using SurfaceRefsByRegion = std::vector<SurfaceCollectionRef>;
using SurfaceRefs = std::vector<SurfaceRefsByRegion>;
void separate_infill_above_bridges(const SurfaceRefs &surfaces, const double expand_offset);
}
#endif // slic3r_InfillAboveBridges_hpp_

View File

@ -485,7 +485,7 @@ static std::vector<std::string> s_Preset_print_options {
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"bridge_speed", "over_bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration", "travel_acceleration", "wipe_tower_acceleration",
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",

View File

@ -727,6 +727,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60));
def = this->add("over_bridge_speed", coFloatOrPercent);
def->label = L("Over bridges");
def->category = L("Speed");
def->tooltip = L("Speed for printing solid infill above bridges. Set to 0 to use solid infill speed. "
"If set as percentage, the speed is calculated over solid infill speed. ");
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def = this->add("enable_dynamic_overhang_speeds", coBool);
def->label = L("Enable dynamic overhang speeds");
def->category = L("Speed");

View File

@ -693,6 +693,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, bridge_flow_ratio))
((ConfigOptionFloat, bridge_speed))
((ConfigOptionEnum<EnsureVerticalShellThickness>, ensure_vertical_shell_thickness))
((ConfigOptionFloatOrPercent, over_bridge_speed))
((ConfigOptionEnum<InfillPattern>, top_fill_pattern))
((ConfigOptionEnum<InfillPattern>, bottom_fill_pattern))
((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width))

View File

@ -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,20 @@ 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() && layerm->region().config().over_bridge_speed > 0) {
surfaces.back().push_back(std::ref(layerm->m_fill_surfaces));
}
}
}
constexpr double infill_over_bridges_expand{1.0};
PrepareInfill::separate_infill_above_bridges(surfaces, infill_over_bridges_expand);
this->set_done(posPrepareInfill);
}
@ -833,6 +848,18 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posInfill);
} else if (opt_key == "fill_pattern") {
steps.emplace_back(posPrepareInfill);
} else if (opt_key == "over_bridge_speed") {
const auto *old_speed = old_config.option<ConfigOptionFloat>(opt_key);
const auto *new_speed = new_config.option<ConfigOptionFloat>(opt_key);
if (
old_speed == nullptr
|| new_speed == nullptr
|| old_speed->value == 0
|| new_speed->value == 0
) {
steps.emplace_back(posPrepareInfill);
}
invalidated |= m_print->invalidate_step(psGCodeExport);
} else if (opt_key == "fill_density") {
// One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes),
// normal infill and 100% (solid) infill.

View File

@ -278,6 +278,7 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role)
if (role == ExtrusionRole::GapFill) return region->flow(FlowRole::frInfill).width();
if (role == ExtrusionRole::Perimeter) return region->flow(FlowRole::frPerimeter).width();
if (role == ExtrusionRole::SolidInfill) return region->flow(FlowRole::frSolidInfill).width();
if (role == ExtrusionRole::InfillOverBridge) return region->flow(FlowRole::frSolidInfill).width();
if (role == ExtrusionRole::InternalInfill) return region->flow(FlowRole::frInfill).width();
if (role == ExtrusionRole::TopSolidInfill) return region->flow(FlowRole::frTopSolidInfill).width();
// default

View File

@ -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)

View File

@ -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<Surface> Surfaces;

View File

@ -317,7 +317,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field(el, has_solid_infill);
for (auto el : { "fill_angle", "bridge_angle", "infill_extrusion_width",
"infill_speed", "bridge_speed" })
"infill_speed", "bridge_speed", "over_bridge_speed" })
toggle_field(el, have_infill || has_solid_infill);
const bool has_ensure_vertical_shell_thickness = config->opt_enum<EnsureVerticalShellThickness>("ensure_vertical_shell_thickness") != EnsureVerticalShellThickness::Disabled;

View File

@ -1585,6 +1585,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_material_speed");
optgroup->append_single_option_line("support_material_interface_speed");
optgroup->append_single_option_line("bridge_speed");
optgroup->append_single_option_line("over_bridge_speed");
optgroup->append_single_option_line("gap_fill_speed");
optgroup->append_single_option_line("ironing_speed");

View File

@ -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

View File

@ -0,0 +1,81 @@
#include <catch2/catch.hpp>
#include <libslic3r/InfillAboveBridges.hpp>
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}, 0);
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);
}