Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "support_tree_base_layers",
"dont_support_bridges", "thick_bridges", "notes", "custom_parameters_print", "complete_objects",
"gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
Expand Down
10 changes: 10 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3614,6 +3614,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercent(15));

def = this->add("support_tree_base_layers", coInt);
def->label = L("Branch Base Layers");
def->category = L("Support material");
// TRN PrintSettings: "Organic supports" > "Support Base Layers"
def->tooltip = L("Number of base layers to generate for Organic supports. Increasing this can make the support base sturdier and easier to remove.");
def->min = 1;
def->max = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));

def = this->add("temperature", coInts);
def->label = L("Other layers");
def->tooltip = L("Nozzle temperature for layers after the first one. Set this to zero to disable "
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionPercent, support_tree_top_rate))
((ConfigOptionFloat, support_tree_branch_distance))
((ConfigOptionFloat, support_tree_tip_diameter))
((ConfigOptionInt, support_tree_base_layers))
// The rest
((ConfigOptionBool, thick_bridges))
((ConfigOptionFloat, xy_size_compensation))
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_tree_top_rate"
|| opt_key == "support_tree_branch_distance"
|| opt_key == "support_tree_tip_diameter"
|| opt_key == "support_tree_base_layers"
|| opt_key == "raft_expansion"
|| opt_key == "raft_first_layer_density"
|| opt_key == "raft_first_layer_expansion"
Expand Down
12 changes: 12 additions & 0 deletions src/libslic3r/Support/OrganicSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,18 @@ void organic_draw_branches(
}
}

if (int base_layers = std::clamp(config.support_tree_base_layers, 1, 10);
base_layers > 1 && ! slices.empty()) {
Polygons base_polygons = slices[0].num_branches > 1 ? union_(slices[0].polygons) : slices[0].polygons;
if (! base_polygons.empty()) {
size_t limit = std::min(size_t(base_layers), slices.size());
for (size_t i = 1; i < limit; ++i) {
append(slices[i].polygons, base_polygons);
slices[i].num_branches++;
}
}
}

tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
[&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
Expand Down
59 changes: 47 additions & 12 deletions src/libslic3r/Support/SupportCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <oneapi/tbb/parallel_for.h>
#include <boost/log/trivial.hpp>
#include <cmath>
#include <algorithm>
#include <initializer_list>
#include <limits>
#include <memory>
Expand Down Expand Up @@ -458,24 +459,45 @@ SupportGeneratorLayersPtr generate_raft_base(
new_layer.contact_polygons = std::make_unique<Polygons>(columns);
}
} else {
if (columns_base != nullptr) {
// Expand the bases of the support columns in the 1st layer.
Polygons &raft = columns_base->polygons;
Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS);
int expansion_layers = std::clamp(object.config().support_tree_base_layers.value, 1, 10);
for (int i = 0; i < expansion_layers; ++i) {
if (i >= int(base_layers.size())) break;
SupportGeneratorLayer *layer = base_layers[i];
if (layer == nullptr) continue;

// Ensure we have a corresponding object layer for trimming.
if (i >= int(object.layers().size())) break;

Polygons &raft = layer->polygons;
Polygons trimming = offset(object.layers()[i]->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS);

if (inflate_factor_1st_layer > SCALED_EPSILON) {
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width())));
float step = inflate_factor_1st_layer / nsteps;
for (int i = 0; i < nsteps; ++ i)
for (int k = 0; k < nsteps; ++ k)
raft = diff(expand(raft, step), trimming);
} else
raft = diff(raft, trimming);
if (! interface_polygons.empty())
columns_base->polygons = diff(columns_base->polygons, interface_polygons);

// Trim the expanded base layer against other support types at the same layer to avoid overlap.
auto trim_against = [&layer](const SupportGeneratorLayersPtr &layers, int idx) {
if (idx < int(layers.size()) && layers[idx] != nullptr && !layers[idx]->polygons.empty())
layer->polygons = diff(layer->polygons, layers[idx]->polygons);
};
trim_against(interface_layers, i);
trim_against(base_interface_layers, i);
trim_against(top_contacts, i);

if (i == 0) {
if (! interface_polygons.empty())
layer->polygons = diff(layer->polygons, interface_polygons);
if (! brim.empty())
layer->polygons = diff(layer->polygons, brim);
}
}

if (! brim.empty()) {
if (columns_base)
columns_base->polygons = diff(columns_base->polygons, brim);
if (contacts)
contacts->polygons = diff(contacts->polygons, brim);
if (interfaces)
Expand Down Expand Up @@ -1505,6 +1527,7 @@ void generate_support_toolpaths(

// Insert the raft base layers.
auto n_raft_layers = std::min<size_t>(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1));
const size_t tree_support_base_layers = size_t(std::clamp(config.support_tree_base_layers.value, 1, 10));

tbb::parallel_for(tbb::blocked_range<size_t>(0, n_raft_layers),
[&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params,
Expand Down Expand Up @@ -1616,7 +1639,7 @@ void generate_support_toolpaths(

tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, support_layers.size()),
[&config, &slicing_params, &support_params, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor,
&bbox_object, &angles, n_raft_layers, link_max_length_factor]
&bbox_object, &angles, n_raft_layers, link_max_length_factor, tree_support_base_layers]
(const tbb::blocked_range<size_t>& range) {
// Indices of the 1st layer in their respective container at the support layer height.
size_t idx_layer_bottom_contact = size_t(-1);
Expand Down Expand Up @@ -1815,8 +1838,20 @@ void generate_support_toolpaths(
sheath = true;
no_sort = true;
} else if (config.support_material_style == SupportMaterialStyle::smsOrganic) {
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params);
done = true;
if (support_layer_id < n_raft_layers + tree_support_base_layers) {
// Additional solid base layers for organic support.
filler = filler_support.get();
filler->angle = angles[support_layer_id % angles.size()];
density = 1.f; // Solid
// Use the standard support flow (already calculated above)
filler->spacing = support_params.support_material_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
sheath = true;
no_sort = true;
} else {
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params);
done = true;
}
}
if (! done)
fill_expolygons_with_sheath_generate_paths(
Expand Down
4 changes: 3 additions & 1 deletion src/libslic3r/Support/TreeSupportCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
this->support_tree_top_rate = config.support_tree_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.support_tree_tip_diameter.value), 0, this->support_tree_branch_diameter);
this->support_tree_base_layers = std::clamp(config.support_tree_base_layers.value, 1, 10);
}

TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
Expand Down Expand Up @@ -119,6 +120,7 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mes
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
support_tree_base_layers(mesh_group_settings.support_tree_base_layers),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
Expand Down Expand Up @@ -200,4 +202,4 @@ void tree_supports_show_error(std::string_view message, bool critical)
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}

} // namespace Slic3r::FFFTreeSupport
} // namespace Slic3r::FFFTreeSupport
6 changes: 6 additions & 0 deletions src/libslic3r/Support/TreeSupportCommon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ struct TreeSupportMeshGroupSettings {
// The diameter of the top of the tip of the branches of tree support.
// minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width
coord_t support_tree_tip_diameter { scaled<coord_t>(0.4) };
// Number of base layers to generate for Organic supports.
int support_tree_base_layers { 1 };

// Support Interface Priority
// How support interface and support will interact when they overlap. Currently only implemented for support roof.
Expand Down Expand Up @@ -355,6 +357,10 @@ struct TreeSupportSettings
* \brief Amount of walls the support area will have.
*/
int support_wall_count;
/*
* \brief Number of base layers to generate for Organic supports.
*/
int support_tree_base_layers;
/*
* \brief Maximum allowed deviation when simplifying.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
config->opt_int("support_material_enforce_layers") > 0);
for (const std::string& key : { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter",
"support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" })
"support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate",
"support_tree_base_layers" })
toggle_field(key, has_organic_supports);

for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",
Expand Down
1 change: 1 addition & 0 deletions src/slic3r/GUI/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_tree_tip_diameter", path);
optgroup->append_single_option_line("support_tree_branch_distance", path);
optgroup->append_single_option_line("support_tree_top_rate", path);
optgroup->append_single_option_line("support_tree_base_layers", path);

page = add_options_page(L("Speed"), "time");
optgroup = page->new_optgroup(L("Speed for print moves"));
Expand Down
137 changes: 137 additions & 0 deletions tests/fff_print/test_support_material.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <catch2/catch_test_macros.hpp>
#include <algorithm>

#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Layer.hpp"
Expand Down Expand Up @@ -495,3 +496,139 @@ Old Perl tests, which were disabled by Vojtech at the time of first Support Gene
}

*/
#include <catch2/catch_test_macros.hpp>

#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp"

#include "test_data.hpp"

using namespace Slic3r;
using namespace Slic3r::Test;

static size_t count_no_sort_layers(SpanOfConstPtrs<SupportLayer> support_layers)
{
return std::count_if(support_layers.begin(), support_layers.end(),
[](const SupportLayer *layer) {
return std::any_of(layer->support_fills.entities.begin(), layer->support_fills.entities.end(),
[](const ExtrusionEntity *entity) {
if (auto collection = dynamic_cast<const ExtrusionEntityCollection*>(entity))
return collection->no_sort;
return false;
});
});
}

TEST_CASE("Organic Support: Base Layers", "[OrganicSupport]")
{
TriangleMesh mesh = Slic3r::Test::mesh(Slic3r::Test::TestMesh::cube_20x20x20);
mesh.translate(0, 0, 10); // Lift it up by 10mm.

Slic3r::Model model;
ModelObject *object = model.add_object();
object->name = "floating_cube.stl";
object->add_volume(mesh);
object->add_instance(); // Default instance at 0,0,0

Slic3r::Print print;
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "support_material", 1 },
{ "support_material_style", "organic" },
{ "support_tree_base_layers", 5 },
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "support_material_threshold", 0 }
});

print.apply(model, config);
print.validate();
print.process();

auto print_object = print.objects().front();
const auto& support_layers = print_object->support_layers();

REQUIRE(support_layers.size() > 5);

double area0 = 0;
for (const ExPolygon& expoly : support_layers[0]->support_islands) area0 += expoly.area();

REQUIRE(area0 > 0);

for (size_t i = 1; i < 5; ++i) {
double areai = 0;
for (const ExPolygon& expoly : support_layers[i]->support_islands) areai += expoly.area();

REQUIRE(areai >= area0 - 1.0);
}

REQUIRE(count_no_sort_layers(support_layers) == size_t(std::clamp(5, 1, 10)));
}

TEST_CASE("Organic Support: Negative base layers clamp to at least one", "[OrganicSupport]")
{
TriangleMesh mesh = Slic3r::Test::mesh(Slic3r::Test::TestMesh::cube_20x20x20);
mesh.translate(0, 0, 10);

Slic3r::Model model;
ModelObject *object = model.add_object();
object->name = "floating_cube.stl";
object->add_volume(mesh);
object->add_instance();

Slic3r::Print print;
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "support_material", 1 },
{ "support_material_style", "organic" },
{ "support_tree_base_layers", -1 }, // Misconfigured input should clamp to minimum.
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "support_material_threshold", 0 }
});

print.apply(model, config);
print.validate();
print.process();

const auto &support_layers = print.objects().front()->support_layers();
REQUIRE_FALSE(support_layers.empty());
REQUIRE(std::any_of(support_layers.begin(), support_layers.end(),
[](const SupportLayer *layer) { return !layer->support_fills.empty(); }));

REQUIRE(count_no_sort_layers(support_layers) == size_t(1));
}

TEST_CASE("Organic Support: Excessive base layers clamp to max", "[OrganicSupport]")
{
TriangleMesh mesh = Slic3r::Test::mesh(Slic3r::Test::TestMesh::cube_20x20x20);
mesh.translate(0, 0, 10);

Slic3r::Model model;
ModelObject *object = model.add_object();
object->name = "floating_cube.stl";
object->add_volume(mesh);
object->add_instance();

Slic3r::Print print;
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "support_material", 1 },
{ "support_material_style", "organic" },
{ "support_tree_base_layers", 60 },
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "support_material_threshold", 0 }
});

print.apply(model, config);
print.validate();
print.process();

const auto &support_layers = print.objects().front()->support_layers();
REQUIRE_FALSE(support_layers.empty());

REQUIRE(count_no_sort_layers(support_layers) == size_t(10));
}