#include #include "libslic3r/Layer.hpp" #include "libslic3r/TriangleMesh.hpp" #include "test_helpers.hpp" using namespace Slic3r::Test; using namespace Slic3r; namespace { // The upper plate overhangs both the lower plate and open air, so branches land on the model and on // the bed in the same slice. TriangleMesh two_tier_mesh() { TriangleMesh lower = make_cube(30, 30, 3); TriangleMesh column = make_cube(8, 8, 15); TriangleMesh upper = make_cube(50, 50, 3); // Each part overlaps the one below rather than resting on it; a coplanar join slices ambiguously. column.translate(11.f, 11.f, 2.f); upper.translate(-10.f, -10.f, 16.f); TriangleMesh mesh = lower; mesh.merge(column); mesh.merge(upper); return mesh; } TriangleMesh scaled(TestMesh id, float scale) { TriangleMesh mesh = Slic3r::Test::mesh(id); mesh.scale(scale); return mesh; } void slice_with_tree_support(const TriangleMesh &mesh, Slic3r::Print &print, const char *style, int threshold_angle = 30, int build_plate_only = 0, int raft_layers = 0) { Slic3r::Test::init_and_process_print({ mesh }, print, { { "enable_support", 1 }, { "support_type", "tree(auto)" }, { "support_style", style }, { "support_on_build_plate_only", build_plate_only }, { "support_threshold_angle", threshold_angle }, { "raft_layers", raft_layers }, { "layer_height", 0.2 }, }); } Points support_points(const Slic3r::Print &print) { Points points; for (const SupportLayer *layer : print.objects().front()->support_layers()) layer->support_fills.collect_points(points); return points; } size_t support_point_count(const TriangleMesh &mesh, const char *style, int threshold_angle = 30, int build_plate_only = 0) { Slic3r::Print print; slice_with_tree_support(mesh, print, style, threshold_angle, build_plate_only); return support_points(print).size(); } } // namespace TEST_CASE("Tree support is generated for an overhang and not for a plain cube", "[TreeSupport]") { REQUIRE(support_point_count(scaled(TestMesh::overhang, 2.f), "tree_slim") > 1000); REQUIRE(support_point_count(Slic3r::Test::cube(20), "tree_slim") == 0); } TEST_CASE("Restricting tree support to the build plate changes what is generated", "[TreeSupport]") { const TriangleMesh mesh = two_tier_mesh(); const size_t anywhere = support_point_count(mesh, "tree_slim", 30, 0); const size_t plate_only = support_point_count(mesh, "tree_slim", 30, 1); REQUIRE(anywhere > 1000); REQUIRE(plate_only > 1000); // The upper plate overhangs the lower one, so some branches would land on the model. REQUIRE(plate_only != anywhere); } TEST_CASE("Tree support layers rise monotonically within the layer height limits", "[TreeSupport]") { Slic3r::Print print; slice_with_tree_support(scaled(TestMesh::overhang, 2.f), print, "tree_slim"); const double nozzle = print.config().nozzle_diameter.values.front(); size_t checked = 0; double previous = 0; bool previous_was_adjacent = false; for (const SupportLayer *layer : print.objects().front()->support_layers()) { if (layer->print_z <= 0 || layer->height <= 0) { // Layers with no nodes are left at zero. Skipping one leaves a hole, so the next pair // spans more than one layer and its gap says nothing about the layer height limit. previous_was_adjacent = false; continue; } if (previous > 0) { CAPTURE(previous, layer->print_z); REQUIRE(layer->print_z > previous); if (previous_was_adjacent) REQUIRE(layer->print_z - previous <= nozzle + EPSILON); } previous = layer->print_z; previous_was_adjacent = true; ++checked; } REQUIRE(checked > 10); } TEST_CASE("A raft is still generated under tree support", "[TreeSupport]") { // The mesh supports itself, so a layer count alone passes with no raft at all. Slic3r::Print rafted, unrafted; slice_with_tree_support(scaled(TestMesh::overhang, 2.f), rafted, "tree_slim", 30, 0, 3); slice_with_tree_support(scaled(TestMesh::overhang, 2.f), unrafted, "tree_slim", 30, 0, 0); const PrintObject *rafted_object = rafted.objects().front(); const PrintObject *unrafted_object = unrafted.objects().front(); REQUIRE(rafted_object->support_layers().size() > unrafted_object->support_layers().size()); // The raft goes under the object. REQUIRE(rafted_object->layers().front()->print_z > unrafted_object->layers().front()->print_z); }