diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 9ec6f987f6..5f85df1402 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -120,7 +120,7 @@ bool BranchingTreeBuilder::add_bridge(const branchingtree::Node &from, double fromR = get_radius(from), toR = get_radius(to); Beam beam{Ball{fromd, fromR}, Ball{tod, toR}}; auto hit = beam_mesh_hit(ex_tbb, m_sm.emesh, beam, - m_sm.cfg.safety_distance_mm); + 2 * m_sm.cfg.safety_distance_mm); bool ret = hit.distance() > (tod - fromd).norm(); @@ -140,7 +140,8 @@ bool BranchingTreeBuilder::add_merger(const branchingtree::Node &node, double closestR = get_radius(closest); Beam beam1{Ball{from1d, nodeR}, Ball{tod, mergeR}}; Beam beam2{Ball{from2d, closestR}, Ball{tod, mergeR}}; - auto sd = m_sm.cfg.safety_distance_mm; + + auto sd = 2 * m_sm.cfg.safety_distance_mm; auto hit1 = beam_mesh_hit(ex_tbb, m_sm.emesh, beam1, sd); auto hit2 = beam_mesh_hit(ex_tbb, m_sm.emesh, beam2, sd); @@ -166,7 +167,7 @@ bool BranchingTreeBuilder::add_ground_bridge(const branchingtree::Node &from, } bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, - const branchingtree::Node &to) + const branchingtree::Node &to) { sla::Junction fromj = {from.pos.cast(), get_radius(from)}; @@ -178,7 +179,9 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, m_builder.add_diffbridge(fromj.pos, anchor->junction_point(), fromj.r, anchor->r_back_mm); - m_builder.add_anchor(*anchor); + if (!m_sm.cfg.ground_facing_only) { // Easter egg, to omit the anchors + m_builder.add_anchor(*anchor); + } build_subtree(from.id); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d9b8e33df0..4f58c3aa13 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -57,7 +57,11 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); scfg.pillar_connection_mode = c.support_pillar_connection_mode.value; - scfg.ground_facing_only = c.support_buildplate_only.getBool(); + if (scfg.tree_type != sla::SupportTreeType::Branching) { + // Branching tree is all about routing to model body, it doesn't support + // this option. + scfg.ground_facing_only = c.support_buildplate_only.getBool(); + } scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); scfg.base_height_mm = c.support_base_height.getFloat(); diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index a33a23c882..b9ad696ed4 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -6,54 +6,58 @@ #include -void test_support_model_collision(const std::string &obj_filename, - const sla::SupportTreeConfig &input_supportcfg, - const sla::HollowingConfig &hollowingcfg, - const sla::DrainHoles &drainholes) +void test_support_model_collision( + const std::string &obj_filename, + const sla::SupportTreeConfig &input_supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes) { SupportByproducts byproducts; - + sla::SupportTreeConfig supportcfg = input_supportcfg; - + + // Ensure that there are no anchors which would pierce the model. + supportcfg.ground_facing_only = true; + // Set head penetration to a small negative value which should ensure that // the supports will not touch the model body. supportcfg.head_penetration_mm = -0.2; - + test_supports(obj_filename, supportcfg, hollowingcfg, drainholes, byproducts); - + // Slice the support mesh given the slice grid of the model. std::vector support_slices = - sla::slice(byproducts.supporttree.retrieve_mesh(sla::MeshType::Support), - byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad), + sla::slice(byproducts.suptree_builder.retrieve_mesh(sla::MeshType::Support), + byproducts.suptree_builder.retrieve_mesh(sla::MeshType::Pad), byproducts.slicegrid, CLOSING_RADIUS, {}); // The slices originate from the same slice grid so the numbers must match - + bool support_mesh_is_empty = - byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && - byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); - + byproducts.suptree_builder.retrieve_mesh(sla::MeshType::Pad).empty() && + byproducts.suptree_builder.retrieve_mesh(sla::MeshType::Support).empty(); + if (support_mesh_is_empty) REQUIRE(support_slices.empty()); else REQUIRE(support_slices.size() == byproducts.model_slices.size()); - + bool notouch = true; for (size_t n = 0; notouch && n < support_slices.size(); ++n) { const ExPolygons &sup_slice = support_slices[n]; const ExPolygons &mod_slice = byproducts.model_slices[n]; - + Polygons intersections = intersection(sup_slice, mod_slice); - + double pinhead_r = scaled(input_supportcfg.head_front_radius_mm); // TODO:: make it strict without a threshold of PI * pihead_radius ^ 2 notouch = notouch && area(intersections) < PI * pinhead_r * pinhead_r; } - + if (!notouch) export_failed_case(support_slices, byproducts); - + REQUIRE(notouch); } @@ -80,7 +84,7 @@ void export_failed_case(const std::vector &support_slices, const Sup if (do_export_stl) { indexed_triangle_set its; - byproducts.supporttree.retrieve_full_mesh(its); + byproducts.suptree_builder.retrieve_full_mesh(its); TriangleMesh m{its}; m.merge(byproducts.input_mesh); m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" + @@ -96,49 +100,49 @@ void test_supports(const std::string &obj_filename, { using namespace Slic3r; TriangleMesh mesh = load_model(obj_filename); - + REQUIRE_FALSE(mesh.empty()); - + if (hollowingcfg.enabled) { sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg); REQUIRE(interior); mesh.merge(TriangleMesh{sla::get_mesh(*interior)}); } - + auto bb = mesh.bounding_box(); double zmin = bb.min.z(); double zmax = bb.max.z(); double gnd = zmin - supportcfg.object_elevation_mm; auto layer_h = 0.05f; - + out.slicegrid = grid(float(gnd), float(zmax), layer_h); out.model_slices = slice_mesh_ex(mesh.its, out.slicegrid, CLOSING_RADIUS); sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{}); - + // Create the special index-triangle mesh with spatial indexing which // is the input of the support point and support mesh generators AABBMesh emesh{mesh}; -#ifdef SLIC3R_HOLE_RAYCASTER - if (hollowingcfg.enabled) + #ifdef SLIC3R_HOLE_RAYCASTER + if (hollowingcfg.enabled) emesh.load_holes(drainholes); -#endif + #endif // TODO: do the cgal hole cutting... - + // Create the support point generator sla::SupportPointGenerator::Config autogencfg; autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; - + point_gen.seed(0); // Make the test repeatable point_gen.execute(out.model_slices, out.slicegrid); - + // Get the calculated support points. std::vector support_points = point_gen.output(); - + int validityflags = ASSUME_NO_REPAIR; - + // If there is no elevation, support points shall be removed from the // bottom of the object. if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { @@ -146,11 +150,11 @@ void test_supports(const std::string &obj_filename, } else { // Should be support points at least on the bottom of the model REQUIRE_FALSE(support_points.empty()); - + // Also the support mesh should not be empty. validityflags |= ASSUME_NO_EMPTY; } - + // Generate the actual support tree sla::SupportTreeBuilder treebuilder; sla::SupportableMesh sm{emesh, support_points, supportcfg}; @@ -170,25 +174,25 @@ void test_supports(const std::string &obj_filename, } TriangleMesh output_mesh{treebuilder.retrieve_mesh(sla::MeshType::Support)}; - + check_validity(output_mesh, validityflags); - + // Quick check if the dimensions and placement of supports are correct auto obb = output_mesh.bounding_box(); - + double allowed_zmin = zmin - supportcfg.object_elevation_mm; - + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; - + REQUIRE(obb.min.z() >= Approx(allowed_zmin)); REQUIRE(obb.max.z() <= Approx(zmax)); - + // Move out the support tree into the byproducts, we can examine it further // in various tests. - out.obj_fname = std::move(obj_filename); - out.supporttree = std::move(treebuilder); - out.input_mesh = std::move(mesh); + out.obj_fname = std::move(obj_filename); + out.suptree_builder = std::move(treebuilder); + out.input_mesh = std::move(mesh); } void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp index c5943bd5c3..187a72d545 100644 --- a/tests/sla_print/sla_test_utils.hpp +++ b/tests/sla_print/sla_test_utils.hpp @@ -60,7 +60,7 @@ struct SupportByproducts std::string obj_fname; std::vector slicegrid; std::vector model_slices; - sla::SupportTreeBuilder supporttree; + sla::SupportTreeBuilder suptree_builder; TriangleMesh input_mesh; };