From b1317be78aae479be5696017e61e730b5d2f0b60 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 9 May 2022 15:32:57 +0200 Subject: [PATCH 01/29] Selectable support tree type --- src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 19 ++++++++++++++++++- src/libslic3r/PrintConfig.hpp | 3 +++ src/libslic3r/SLA/SupportTreeStrategies.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 2 ++ src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/Tab.cpp | 4 ++-- tests/sla_print/sla_print_tests.cpp | 16 ++++++++-------- 8 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 092c0ed9b..db6ebe62c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -493,6 +493,7 @@ static std::vector s_Preset_sla_print_options { "layer_height", "faded_layers", "supports_enable", + "support_tree_type", "support_head_front_diameter", "support_head_penetration", "support_head_width", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index bd4f98dfd..302a9eb6f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -176,6 +176,12 @@ static const t_config_enum_values s_keys_map_SLAMaterialSpeed = { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAMaterialSpeed); +static inline const t_config_enum_values s_keys_map_SLASupportTreeType = { + {"default", int(sla::SupportTreeType::Default)}, + {"branching", int(sla::SupportTreeType::Branching)} +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLASupportTreeType); + static const t_config_enum_values s_keys_map_BrimType = { {"no_brim", btNoBrim}, {"outer_only", btOuterOnly}, @@ -3564,6 +3570,17 @@ void PrintConfigDef::init_sla_params() def->mode = comSimple; def->set_default_value(new ConfigOptionBool(true)); + def = this->add("support_tree_type", coEnum); + def->label = L("Support tree type"); + def->tooltip = L("Support tree building strategy"); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values = ConfigOptionEnum::get_enum_names(); + def->enum_labels = ConfigOptionEnum::get_enum_names(); + def->enum_labels[0] = L("Default"); + def->enum_labels[1] = L("Branching"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(sla::SupportTreeType::Default)); + def = this->add("support_head_front_diameter", coFloat); def->label = L("Pinhead front diameter"); def->category = L("Supports"); @@ -3655,7 +3672,7 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->max = 1; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(0.0)); + def->set_default_value(new ConfigOptionFloat(0.1)); def = this->add("support_base_diameter", coFloat); def->label = L("Support base diameter"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 9e1d7989d..5ec163572 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -155,6 +155,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialInterfacePattern) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLASupportTreeType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat) @@ -829,6 +830,8 @@ PRINT_CONFIG_CLASS_DEFINE( // Enabling or disabling support creation ((ConfigOptionBool, supports_enable)) + ((ConfigOptionEnum, support_tree_type)) + // Diameter in mm of the pointing side of the head. ((ConfigOptionFloat, support_head_front_diameter))/*= 0.2*/ diff --git a/src/libslic3r/SLA/SupportTreeStrategies.hpp b/src/libslic3r/SLA/SupportTreeStrategies.hpp index d04ceaa2d..487f43575 100644 --- a/src/libslic3r/SLA/SupportTreeStrategies.hpp +++ b/src/libslic3r/SLA/SupportTreeStrategies.hpp @@ -5,7 +5,7 @@ namespace Slic3r { namespace sla { -enum class SupportTreeType { Default, Clever }; +enum class SupportTreeType { Default, Branching }; enum class PillarConnectionMode { zigzag, cross, dynamic }; }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 9ad545338..d9b8e33df 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -43,6 +43,7 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) sla::SupportTreeConfig scfg; scfg.enabled = c.supports_enable.getBool(); + scfg.tree_type = c.support_tree_type.value; scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); double pillar_r = 0.5 * c.support_pillar_diameter.getFloat(); scfg.head_back_radius_mm = pillar_r; @@ -824,6 +825,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectornew_optgroup(L("Supports")); optgroup->append_single_option_line("supports_enable"); + optgroup->append_single_option_line("support_tree_type"); optgroup = page->new_optgroup(L("Support head")); optgroup->append_single_option_line("support_head_front_diameter"); @@ -4650,8 +4651,7 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_pillar_connection_mode"); optgroup->append_single_option_line("support_buildplate_only"); - // TODO: This parameter is not used at the moment. - // optgroup->append_single_option_line("support_pillar_widening_factor"); + optgroup->append_single_option_line("support_pillar_widening_factor"); optgroup->append_single_option_line("support_base_diameter"); optgroup->append_single_option_line("support_base_height"); optgroup->append_single_option_line("support_base_safety_distance"); diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index a5ed0d029..8ae02c808 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -156,37 +156,37 @@ TEST_CASE("DefaultSupports::FloorSupportsDoNotPierceModel", "[SLASupportGenerati test_support_model_collision(fname, supportcfg); } -//TEST_CASE("CleverSupports::ElevatedSupportGeometryIsValid", "[SLASupportGeneration][Clever]") { +//TEST_CASE("BranchingSupports::ElevatedSupportGeometryIsValid", "[SLASupportGeneration][Branching]") { // sla::SupportTreeConfig supportcfg; // supportcfg.object_elevation_mm = 10.; -// supportcfg.tree_type = sla::SupportTreeType::Clever; +// supportcfg.tree_type = sla::SupportTreeType::Branching; // for (auto fname : SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); //} -//TEST_CASE("CleverSupports::FloorSupportGeometryIsValid", "[SLASupportGeneration][Clever]") { +//TEST_CASE("BranchingSupports::FloorSupportGeometryIsValid", "[SLASupportGeneration][Branching]") { // sla::SupportTreeConfig supportcfg; // supportcfg.object_elevation_mm = 0; -// supportcfg.tree_type = sla::SupportTreeType::Clever; +// supportcfg.tree_type = sla::SupportTreeType::Branching; // for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); //} -TEST_CASE("CleverSupports::ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration][Clever]") { +TEST_CASE("BranchingSupports::ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration][Branching]") { sla::SupportTreeConfig supportcfg; supportcfg.object_elevation_mm = 10.; - supportcfg.tree_type = sla::SupportTreeType::Clever; + supportcfg.tree_type = sla::SupportTreeType::Branching; for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); } -TEST_CASE("CleverSupports::FloorSupportsDoNotPierceModel", "[SLASupportGeneration][Clever]") { +TEST_CASE("BranchingSupports::FloorSupportsDoNotPierceModel", "[SLASupportGeneration][Branching]") { sla::SupportTreeConfig supportcfg; supportcfg.object_elevation_mm = 0; - supportcfg.tree_type = sla::SupportTreeType::Clever; + supportcfg.tree_type = sla::SupportTreeType::Branching; for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); From 8723e421b30c2e531c2f879bc1033ef04639eaf8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 9 May 2022 16:38:30 +0200 Subject: [PATCH 02/29] Add new branching tree algorithm --- src/libslic3r/BranchingTree/BranchingTree.cpp | 141 +++++++++++ src/libslic3r/BranchingTree/BranchingTree.hpp | 157 ++++++++++++ src/libslic3r/BranchingTree/PointCloud.cpp | 172 +++++++++++++ src/libslic3r/BranchingTree/PointCloud.hpp | 214 +++++++++++++++++ src/libslic3r/CMakeLists.txt | 6 + src/libslic3r/SLA/BranchingTreeSLA.cpp | 226 ++++++++++++++++++ src/libslic3r/SLA/BranchingTreeSLA.hpp | 15 ++ src/libslic3r/SLA/SupportTree.cpp | 5 + tests/sla_print/sla_test_utils.cpp | 6 + 9 files changed, 942 insertions(+) create mode 100644 src/libslic3r/BranchingTree/BranchingTree.cpp create mode 100644 src/libslic3r/BranchingTree/BranchingTree.hpp create mode 100644 src/libslic3r/BranchingTree/PointCloud.cpp create mode 100644 src/libslic3r/BranchingTree/PointCloud.hpp create mode 100644 src/libslic3r/SLA/BranchingTreeSLA.cpp create mode 100644 src/libslic3r/SLA/BranchingTreeSLA.hpp diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp new file mode 100644 index 000000000..4c9ff899c --- /dev/null +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -0,0 +1,141 @@ +#include "BranchingTree.hpp" +#include "PointCloud.hpp" + +#include +#include +#include + +#include "libslic3r/SLA/SupportTreeUtils.hpp" + +namespace Slic3r { namespace branchingtree { + +bool build_tree(PointCloud &nodes, Builder &builder) +{ + auto ptsqueue = nodes.start_queue(); + auto &properties = nodes.properties(); + + struct NodeDistance { size_t node_id; float distance; }; + auto distances = reserve_vector(nodes.reachable_count()); + + while (!ptsqueue.empty()) { + size_t node_id = ptsqueue.top(); + ptsqueue.pop(); + + Node node = nodes.get(node_id); + nodes.remove_node(node_id); + + distances.clear(); + distances.reserve(nodes.reachable_count()); + + nodes.foreach_reachable(node.pos, [&distances](size_t id, float distance) { + if (!std::isinf(distance)) + distances.emplace_back(NodeDistance{id, distance}); + }); + + std::sort(distances.begin(), distances.end(), + [](auto &a, auto &b) { return a.distance < b.distance; }); + + if (distances.empty()) { + builder.report_unroutable(node); + continue; + } + + auto closest_it = distances.begin(); + bool routed = false; + while (closest_it != distances.end() && !routed) { + size_t closest_node_id = closest_it->node_id; + Node closest_node = nodes.get(closest_node_id); + + auto type = nodes.get_type(closest_node_id); + float w = nodes.get(node_id).weight + closest_it->distance; + closest_node.Rmin = std::max(node.Rmin, closest_node.Rmin); + + switch (type) { + case BED: { + closest_node.weight = w; + if ((routed = builder.add_ground_bridge(node, closest_node))) { + closest_node.left = closest_node.right = node_id; + nodes.get(closest_node_id) = closest_node; + nodes.remove_node(closest_node_id); + } + + break; + } + case MESH: { + closest_node.weight = w; + if ((routed = builder.add_mesh_bridge(node, closest_node))) { + closest_node.left = closest_node.right = node_id; + nodes.get(closest_node_id) = closest_node; + nodes.remove_node(closest_node_id); + } + + break; + } + case SUPP: + case JUNCTION: { + auto max_slope = float(properties.max_slope()); + + if (auto mergept = find_merge_pt(node.pos, closest_node.pos, max_slope)) { + + float mergedist_closest = (*mergept - closest_node.pos).norm(); + float mergedist_node = (*mergept - node.pos).norm(); + float Wnode = nodes.get(node_id).weight; + float Wclosest = nodes.get(closest_node_id).weight; + float Wsum = std::max(Wnode, Wclosest); + float distsum = std::max(mergedist_closest, mergedist_node); + w = Wsum + distsum; + + if (mergedist_closest > EPSILON) { + Node mergenode{*mergept, closest_node.Rmin}; + mergenode.weight = w; + mergenode.id = int(nodes.next_junction_id()); + + if ((routed = builder.add_merger(node, closest_node, mergenode))) { + mergenode.left = node_id; + mergenode.right = closest_node_id; + size_t new_idx = nodes.insert_junction(mergenode); + ptsqueue.push(new_idx); + ptsqueue.remove(nodes.get_queue_idx(closest_node_id)); + nodes.remove_node(closest_node_id); + } + } else if (closest_node.left == Node::ID_NONE || + closest_node.right == Node::ID_NONE) + { + closest_node.weight = w; + if ((routed = builder.add_bridge(node, closest_node))) { + if (closest_node.left == Node::ID_NONE) + closest_node.left = node_id; + else if (closest_node.right == Node::ID_NONE) + closest_node.right = node_id; + + nodes.get(closest_node_id) = closest_node; + } + } + } + + break; + } + case NONE:; + } + + ++closest_it; + } + + if (!routed) + builder.report_unroutable(node); + } + + return true; +} + +bool build_tree(const indexed_triangle_set & its, + const std::vector &support_roots, + Builder & builder, + const Properties & properties) +{ + PointCloud nodes(its, support_roots, properties); + + return build_tree(nodes, builder); +} + +}} // namespace Slic3r::branchingtree diff --git a/src/libslic3r/BranchingTree/BranchingTree.hpp b/src/libslic3r/BranchingTree/BranchingTree.hpp new file mode 100644 index 000000000..5313e30d9 --- /dev/null +++ b/src/libslic3r/BranchingTree/BranchingTree.hpp @@ -0,0 +1,157 @@ +#ifndef SUPPORTTREEBRANCHING_HPP +#define SUPPORTTREEBRANCHING_HPP + +// For indexed_triangle_set +#include + +#include "libslic3r/ExPolygon.hpp" +#include "libslic3r/BoundingBox.hpp" + +namespace Slic3r { namespace branchingtree { + +// Branching tree input parameters. This is an in-line fillable structure with +// setters returning self references. +class Properties +{ + double m_max_slope = PI / 4.; + double m_ground_level = 0.; + double m_sampling_radius = .5; + double m_max_branch_len = 20.; + + ExPolygons m_bed_shape; + +public: + // Maximum slope for bridges of the tree + Properties &max_slope(double val) noexcept + { + m_max_slope = val; + return *this; + } + // Z level of the ground + Properties &ground_level(double val) noexcept + { + m_ground_level = val; + return *this; + } + // How far should sample points be in the mesh and the ground + Properties &sampling_radius(double val) noexcept + { + m_sampling_radius = val; + return *this; + } + // Shape of the print bed (ground) + Properties &bed_shape(ExPolygons bed) noexcept + { + m_bed_shape = std::move(bed); + return *this; + } + + Properties &max_branch_length(double val) noexcept + { + m_max_branch_len = val; + return *this; + } + + double max_slope() const noexcept { return m_max_slope; } + double ground_level() const noexcept { return m_ground_level; } + double sampling_radius() const noexcept { return m_sampling_radius; } + double max_branch_length() const noexcept { return m_max_branch_len; } + const ExPolygons &bed_shape() const noexcept { return m_bed_shape; } +}; + +// A junction of the branching tree with position and radius. +struct Node +{ + static constexpr int ID_NONE = -1; + + int id = ID_NONE, left = ID_NONE, right = ID_NONE; + + Vec3f pos; + float Rmin; + + // Tracking the weight of each junction, which is essentially the sum of + // the lenghts of all branches emanating from this junction. + float weight; + + Node(const Vec3f &p, float r_min = .0f) : pos{p}, Rmin{r_min}, weight{0.f} + {} +}; + +// An output interface for the branching tree generator function. Consider each +// method as a callback and implement the actions that need to be done. +class Builder +{ +public: + virtual ~Builder() = default; + + // A simple bridge from junction to junction. + virtual bool add_bridge(const Node &from, const Node &to) = 0; + + // An Y shaped structure with two starting points and a merge point below + // them. The angles will respect the max_slope setting. + virtual bool add_merger(const Node &node, + const Node &closest, + const Node &merge_node) = 0; + + // Add an anchor bridge to the ground (print bed) + virtual bool add_ground_bridge(const Node &from, + const Node &to) = 0; + + // Add an anchor bridge to the model body + virtual bool add_mesh_bridge(const Node &from, const Node &to) = 0; + + // Report nodes that can not be routed to an endpoint (model or ground) + virtual void report_unroutable(const Node &j) = 0; +}; + +// Build the actual tree. +// its: The input mesh +// support_leafs: The input support points +// builder: The output interface, describes how to build the tree +// properties: Parameters of the tree +// +// Notes: +// The original algorithm implicitly ensures that the generated tree avoids +// the model body. This implementation uses point sampling of the mesh thus an +// explicit check is needed if the part of the tree being inserted properly +// avoids the model. This can be done in the builder implementation. Each +// method can return a boolean indicating whether the given branch can or +// cannot be inserted. If a particular path is unavailable, the algorithm +// will try a few other paths as well. If all of them fail, one of the +// report_unroutable_* methods will be called as a last resort. +bool build_tree(const indexed_triangle_set & its, + const std::vector &support_leafs, + Builder & builder, + const Properties & properties = {}); + +inline bool build_tree(const indexed_triangle_set & its, + const std::vector &support_leafs, + Builder && builder, + const Properties & properties = {}) +{ + return build_tree(its, support_leafs, builder, properties); +} + +//class PointCloud; + +//bool build_tree(PointCloud &pcloud, Builder &builder); + +// Helper function to derive a bed polygon only from the model bounding box. +inline ExPolygon make_bed_poly(const indexed_triangle_set &its) +{ + auto bb = bounding_box(its); + + BoundingBox bbcrd{scaled(to_2d(bb.min)), scaled(to_2d(bb.max))}; + bbcrd.offset(scaled(10.)); + Point min = bbcrd.min, max = bbcrd.max; + ExPolygon ret = {{min.x(), min.y()}, + {max.x(), min.y()}, + {max.x(), max.y()}, + {min.x(), max.y()}}; + + return ret; +} + +}} // namespace Slic3r::branchingtree + +#endif // SUPPORTTREEBRANCHING_HPP diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp new file mode 100644 index 000000000..a4855ee53 --- /dev/null +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -0,0 +1,172 @@ +#include "PointCloud.hpp" + +#include "libslic3r/Geometry.hpp" +#include "libslic3r/Tesselate.hpp" + +#include + +namespace Slic3r { namespace branchingtree { + +std::optional find_merge_pt(const Vec3f &A, + const Vec3f &B, + float max_slope) +{ + Vec3f Da = (B - A).normalized(), Db = -Da; + auto [polar_da, azim_da] = Geometry::dir_to_spheric(Da); + auto [polar_db, azim_db] = Geometry::dir_to_spheric(Db); + polar_da = std::max(polar_da, float(PI) - max_slope); + polar_db = std::max(polar_db, float(PI) - max_slope); + + Da = Geometry::spheric_to_dir(polar_da, azim_da); + Db = Geometry::spheric_to_dir(polar_db, azim_db); + + double t1 = + (A.z() * Db.x() + Db.z() * B.x() - B.z() * Db.x() - Db.z() * A.x()) / + (Da.x() * Db.z() - Da.z() * Db.x()); + + double t2 = (A.x() + Da.x() * t1 - B.x()) / Db.x(); + + return t1 > 0. && t2 > 0. ? A + t1 * Da : std::optional{}; +} + +void to_eigen_mesh(const indexed_triangle_set &its, + Eigen::MatrixXd &V, + Eigen::MatrixXi &F) +{ + V.resize(its.vertices.size(), 3); + F.resize(its.indices.size(), 3); + for (unsigned int i = 0; i < its.indices.size(); ++i) + F.row(i) = its.indices[i]; + + for (unsigned int i = 0; i < its.vertices.size(); ++i) + V.row(i) = its.vertices[i].cast(); +} + +std::vector sample_mesh(const indexed_triangle_set &its, + double radius) +{ + std::vector ret; + + double surface_area = 0.; + for (const Vec3i &face : its.indices) { + std::array tri = {its.vertices[face(0)], + its.vertices[face(1)], + its.vertices[face(2)]}; + + auto U = tri[1] - tri[0], V = tri[2] - tri[0]; + surface_area += 0.5 * U.cross(V).norm(); + } + + int N = surface_area / (PI * radius * radius); + + Eigen::MatrixXd B; + Eigen::MatrixXi FI; + Eigen::MatrixXd V; + Eigen::MatrixXi F; + to_eigen_mesh(its, V, F); + igl::random_points_on_mesh(N, V, F, B, FI); + + ret.reserve(size_t(N)); + for (int i = 0; i < FI.size(); i++) { + Vec3i face = its.indices[FI(i)]; + + Vec3f c = B.row(i)(0) * its.vertices[face(0)] + + B.row(i)(1) * its.vertices[face(1)] + + B.row(i)(2) * its.vertices[face(2)]; + + ret.emplace_back(c); + } + + return ret; +} + +std::vector sample_bed(const ExPolygons &bed, float z, double radius) +{ + std::vector ret; + + auto triangles = triangulate_expolygons_3d(bed, z); + indexed_triangle_set its; + its.vertices.reserve(triangles.size()); + + for (size_t i = 0; i < triangles.size(); i += 3) { + its.vertices.emplace_back(triangles[i].cast()); + its.vertices.emplace_back(triangles[i + 1].cast()); + its.vertices.emplace_back(triangles[i + 2].cast()); + + its.indices.emplace_back(i, i + 1, i + 2); + } + + return sample_mesh(its, radius); +} + +PointCloud::PointCloud(const indexed_triangle_set &M, + std::vector support_leafs, + const Properties &props) + : m_leafs{std::move(support_leafs)} + , m_meshpoints{sample_mesh(M, props.sampling_radius())} + , m_bedpoints{sample_bed(props.bed_shape(), + props.ground_level(), + props.sampling_radius())} + , m_props{props} + , cos2bridge_slope{std::cos(props.max_slope()) * + std::abs(std::cos(props.max_slope()))} + , MESHPTS_BEGIN{m_bedpoints.size()} + , SUPP_BEGIN{MESHPTS_BEGIN + m_meshpoints.size()} + , JUNCTIONS_BEGIN{SUPP_BEGIN + m_leafs.size()} + , m_searchable_indices(JUNCTIONS_BEGIN, true) + , m_queue_indices(JUNCTIONS_BEGIN, UNQUEUED) + , m_reachable_cnt{JUNCTIONS_BEGIN} + , m_ktree{CoordFn{this}, SUPP_BEGIN} // Only for bed and mesh points +{ + for (size_t i = 0; i < m_bedpoints.size(); ++i) + m_bedpoints[i].id = int(i); + + for (size_t i = 0; i < m_meshpoints.size(); ++i) + m_meshpoints[i].id = int(MESHPTS_BEGIN + i); + + for (size_t i = 0; i < m_leafs.size(); ++i) + m_leafs[i].id = int(SUPP_BEGIN + i); +} + +float PointCloud::get_distance(const Vec3f &p, size_t node) +{ + auto t = get_type(node); + auto ret = std::numeric_limits::infinity(); + + switch (t) { + case MESH: + case BED: { + // Points of mesh or bed which are outside of the support cone of + // 'pos' must be discarded. + if (is_outside_support_cone(p, get(node).pos)) + ret = std::numeric_limits::infinity(); + else + ret = (get(node).pos - p).norm(); + + break; + } + case SUPP: + case JUNCTION:{ + auto mergept = find_merge_pt(p, get(node).pos, m_props.max_slope()); + if (!mergept || mergept->z() < (m_props.ground_level() + 2 * get(node).Rmin)) + ret = std::numeric_limits::infinity(); + else + ret = (p - *mergept).norm(); + + break; + } + case NONE: + ; + } + + // Setting the ret val to infinity will effectively discard this + // connection of nodes. max_branch_length property if used here + // to discard node=>node and node=>mesh connections longer than this + // property. + if (t != BED && ret > m_props.max_branch_length()) + ret = std::numeric_limits::infinity(); + + return ret; +} + +}} // namespace Slic3r::branchingtree diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp new file mode 100644 index 000000000..6f583759e --- /dev/null +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -0,0 +1,214 @@ +#ifndef POINTCLOUD_HPP +#define POINTCLOUD_HPP + +#include "BranchingTree.hpp" + +#include "libslic3r/KDTreeIndirect.hpp" +#include "libslic3r/MutablePriorityQueue.hpp" + +namespace Slic3r { namespace branchingtree { + +std::optional find_merge_pt(const Vec3f &A, + const Vec3f &B, + float max_slope); + +void to_eigen_mesh(const indexed_triangle_set &its, + Eigen::MatrixXd &V, + Eigen::MatrixXi &F); + +std::vector sample_mesh(const indexed_triangle_set &its, + double radius); + +std::vector sample_bed(const ExPolygons &bed, + float z, + double radius = 1.); + +enum PtType { SUPP, MESH, BED, JUNCTION, NONE }; + +// A cloud of points including support points, mesh points, junction points +// and anchor points on the bed. Junction points can be added or removed, all +// the other point types are established on creation and remain unchangeable. +class PointCloud { + std::vector m_leafs, m_junctions, m_meshpoints, m_bedpoints; + + const branchingtree::Properties &m_props; + + const double cos2bridge_slope; + const size_t MESHPTS_BEGIN, SUPP_BEGIN, JUNCTIONS_BEGIN; + +private: + + // These vectors have the same size as there are indices for nodes to keep + // access complexity constant. WARN: there might be cache non-locality costs + std::vector m_searchable_indices; // searchable flag value of a node + std::vector m_queue_indices; // queue id of a node if queued + + size_t m_reachable_cnt; + + struct CoordFn + { + const PointCloud *self; + CoordFn(const PointCloud *s) : self{s} {} + float operator()(size_t nodeid, size_t dim) const + { + return self->get(nodeid).pos(int(dim)); + } + }; + + KDTreeIndirect<3, float, CoordFn> m_ktree; + + bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt) + { + Vec3d D = (pt - supp).cast(); + double dot_sq = -D.z() * std::abs(-D.z()); + + return dot_sq < D.squaredNorm() * cos2bridge_slope; + } + + static constexpr auto UNQUEUED = size_t(-1); + + template + static auto *get_node(PC &&pc, size_t id) + { + auto *ret = decltype(pc.m_bedpoints.data())(nullptr); + + switch(pc.get_type(id)) { + case BED: ret = &pc.m_bedpoints[id]; break; + case MESH: ret = &pc.m_meshpoints[id - pc.MESHPTS_BEGIN]; break; + case SUPP: ret = &pc.m_leafs [id - pc.SUPP_BEGIN]; break; + case JUNCTION: ret = &pc.m_junctions[id - pc.JUNCTIONS_BEGIN]; break; + case NONE: assert(false); + } + + return ret; + } + +public: + + struct ZCompareFn + { + const PointCloud *self; + ZCompareFn(const PointCloud *s) : self{s} {} + bool operator()(size_t node_a, size_t node_b) const + { + return self->get(node_a).pos.z() > self->get(node_b).pos.z(); + } + }; + + PointCloud(const indexed_triangle_set & M, + std::vector support_leafs, + const Properties & props); + + PtType get_type(size_t node_id) const + { + PtType ret = NONE; + + if (node_id < MESHPTS_BEGIN && !m_bedpoints.empty()) ret = BED; + else if (node_id < SUPP_BEGIN && !m_meshpoints.empty()) ret = MESH; + else if (node_id < JUNCTIONS_BEGIN && !m_leafs.empty()) ret = SUPP; + else if (node_id >= JUNCTIONS_BEGIN && !m_junctions.empty()) ret = JUNCTION; + + return ret; + } + + const Node &get(size_t node_id) const + { + return *get_node(*this, node_id); + } + + Node &get(size_t node_id) + { + return *get_node(*this, node_id); + } + + const Node *find(size_t node_id) const { return get_node(*this, node_id); } + Node *find(size_t node_id) { return get_node(*this, node_id); } + + // Return the original index of a leaf in the input array, if the given + // node id is indeed of type SUPP + int get_leaf_id(size_t node_id) const + { + return node_id >= SUPP_BEGIN && node_id < JUNCTIONS_BEGIN ? + node_id - SUPP_BEGIN : + Node::ID_NONE; + } + + size_t get_queue_idx(size_t node_id) const { return m_queue_indices[node_id]; } + + float get_distance(const Vec3f &p, size_t node); + + size_t next_junction_id() const + { + return JUNCTIONS_BEGIN + m_junctions.size(); + } + + size_t insert_junction(const Node &p) + { + size_t new_id = next_junction_id(); + m_junctions.emplace_back(p); + m_junctions.back().id = int(new_id); + m_searchable_indices.emplace_back(true); + m_queue_indices.emplace_back(UNQUEUED); + ++m_reachable_cnt; + + return new_id; + } + + void remove_node(size_t node_id) + { + m_searchable_indices[node_id] = false; + m_queue_indices[node_id] = UNQUEUED; + --m_reachable_cnt; + } + + size_t reachable_count() const { return m_reachable_cnt; } + + template void foreach_reachable(const Vec3f &pos, Fn &&visitor) + { + auto closest_anchors = + find_closest_points<5>(m_ktree, pos, [this, &pos](size_t id) { + return m_searchable_indices[id] && + !is_outside_support_cone(pos, get(id).pos); + }); + + for (size_t anchor : closest_anchors) + if (anchor != m_ktree.npos) + visitor(anchor, get_distance(pos, anchor)); + + for (size_t i = SUPP_BEGIN; i < m_searchable_indices.size(); ++i) + if (m_searchable_indices[i]) + visitor(i, get_distance(pos, i)); + } + + auto start_queue() + { + auto ptsqueue = make_mutable_priority_queue( + [this](size_t el, size_t idx) { m_queue_indices[el] = idx; }, + ZCompareFn{this}); + + ptsqueue.reserve(m_leafs.size()); + size_t iend = SUPP_BEGIN + m_leafs.size(); + for (size_t i = SUPP_BEGIN; i < iend; ++i) + ptsqueue.push(i); + + return ptsqueue; + } + + const Properties & properties() const { return m_props; } +}; + +template void traverse(PC &&pc, size_t root, Fn &&fn) +{ + if (auto nodeptr = pc.find(root); nodeptr != nullptr) { + auto &nroot = *nodeptr; + fn(nroot); + if (nroot.left >= 0) traverse(pc, nroot.left, fn); + if (nroot.right >= 0) traverse(pc, nroot.right, fn); + } +} + +bool build_tree(PointCloud &pcloud, Builder &builder); + +}} // namespace Slic3r::branchingtree + +#endif // POINTCLOUD_HPP diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e637b1402..2c5944fef 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -319,6 +319,12 @@ set(SLIC3R_SOURCES SLA/ReprojectPointsOnMesh.hpp SLA/DefaultSupportTree.hpp SLA/DefaultSupportTree.cpp + SLA/BranchingTreeSLA.hpp + SLA/BranchingTreeSLA.cpp + BranchingTree/BranchingTree.cpp + BranchingTree/BranchingTree.hpp + BranchingTree/PointCloud.cpp + BranchingTree/PointCloud.hpp Arachne/BeadingStrategy/BeadingStrategy.hpp Arachne/BeadingStrategy/BeadingStrategy.cpp diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp new file mode 100644 index 000000000..751544fb5 --- /dev/null +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -0,0 +1,226 @@ +#include "BranchingTreeSLA.hpp" + +#include "libslic3r/Execution/ExecutionTBB.hpp" + +#include "libslic3r/KDTreeIndirect.hpp" + +#include "SupportTreeUtils.hpp" +#include "BranchingTree/PointCloud.hpp" + +#include "Pad.hpp" + +#include + +namespace Slic3r { namespace sla { + +class BranchingTreeBuilder: public branchingtree::Builder { + SupportTreeBuilder &m_builder; + const SupportableMesh &m_sm; + const branchingtree::PointCloud &m_cloud; + + // Scaling of the input value 'widening_factor:<0, 1>' to produce resonable + // widening behaviour + static constexpr double WIDENING_SCALE = 0.2; + + double get_radius(const branchingtree::Node &j) + { + double w = WIDENING_SCALE * m_sm.cfg.pillar_widening_factor * j.weight; + + return std::max(double(j.Rmin), std::min(m_sm.cfg.base_radius_mm, w)); + } + + std::vector m_unroutable_pinheads; + + void build_subtree(size_t root) + { + traverse(m_cloud, root, [this](const branchingtree::Node &node) { + if (node.left >= 0 && node.right >= 0) { + auto nparent = m_cloud.get(node.id); + auto nleft = m_cloud.get(node.left); + auto nright = m_cloud.get(node.right); + Vec3d from1d = nleft.pos.cast(); + Vec3d from2d = nright.pos.cast(); + Vec3d tod = nparent.pos.cast(); + double mergeR = get_radius(nparent); + double leftR = get_radius(nleft); + double rightR = get_radius(nright); + + m_builder.add_diffbridge(from1d, tod, leftR, mergeR); + m_builder.add_diffbridge(from2d, tod, rightR, mergeR); + m_builder.add_junction(tod, mergeR); + } else if (int child = node.left + node.right + 1; child >= 0) { + auto from = m_cloud.get(child); + auto to = m_cloud.get(node.id); + m_builder.add_diffbridge(from.pos.cast(), + to.pos.cast(), + get_radius(from), + get_radius(to)); + } + }); + } + + void discard_subtree(size_t root) + { + // Discard all the support points connecting to this branch. + traverse(m_cloud, root, [this](const branchingtree::Node &node) { + int suppid_parent = m_cloud.get_leaf_id(node.id); + int suppid_left = m_cloud.get_leaf_id(node.left); + int suppid_right = m_cloud.get_leaf_id(node.right); + if (suppid_parent >= 0) + m_unroutable_pinheads.emplace_back(suppid_parent); + if (suppid_left >= 0) + m_unroutable_pinheads.emplace_back(suppid_left); + if (suppid_right >= 0) + m_unroutable_pinheads.emplace_back(suppid_right); + }); + } + +public: + BranchingTreeBuilder(SupportTreeBuilder &builder, + const SupportableMesh &sm, + const branchingtree::PointCloud &cloud) + : m_builder{builder}, m_sm{sm}, m_cloud{cloud} + {} + + bool add_bridge(const branchingtree::Node &from, + const branchingtree::Node &to) override; + + bool add_merger(const branchingtree::Node &node, + const branchingtree::Node &closest, + const branchingtree::Node &merge_node) override; + + bool add_ground_bridge(const branchingtree::Node &from, + const branchingtree::Node &/*to*/) override; + + bool add_mesh_bridge(const branchingtree::Node &from, + const branchingtree::Node &to) override; + + void report_unroutable(const branchingtree::Node &j) override + { + BOOST_LOG_TRIVIAL(error) << "Cannot route junction at " << j.pos.x() + << " " << j.pos.y() << " " << j.pos.z(); + + // Discard all the support points connecting to this branch. + discard_subtree(j.id); + } + + const std::vector& unroutable_pinheads() const + { + return m_unroutable_pinheads; + } +}; + +bool BranchingTreeBuilder::add_bridge(const branchingtree::Node &from, + const branchingtree::Node &to) +{ + Vec3d fromd = from.pos.cast(), tod = to.pos.cast(); + 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); + + bool ret = hit.distance() > (tod - fromd).norm(); + + return ret; +} + +bool BranchingTreeBuilder::add_merger(const branchingtree::Node &node, + const branchingtree::Node &closest, + const branchingtree::Node &merge_node) +{ + Vec3d from1d = node.pos.cast(), + from2d = closest.pos.cast(), + tod = merge_node.pos.cast(); + + double mergeR = get_radius(merge_node); + double nodeR = get_radius(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 hit1 = beam_mesh_hit(ex_tbb, m_sm.emesh, beam1, sd); + auto hit2 = beam_mesh_hit(ex_tbb, m_sm.emesh, beam2, sd); + + bool ret = hit1.distance() > (tod - from1d).norm() && + hit2.distance() > (tod - from2d).norm(); + + return ret; +} + +bool BranchingTreeBuilder::add_ground_bridge(const branchingtree::Node &from, + const branchingtree::Node &to) +{ + bool ret = search_ground_route(ex_tbb, m_builder, m_sm, + sla::Junction{from.pos.cast(), + get_radius(from)}, + get_radius(to)).first; + + if (ret) { + build_subtree(from.id); + } + + return ret; +} + +bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, + const branchingtree::Node &to) +{ + sla::Junction fromj = {from.pos.cast(), get_radius(from)}; + + auto anchor = calculate_anchor_placement(ex_tbb, m_sm, + fromj, + to.pos.cast()); + + if (anchor) { + m_builder.add_diffbridge(fromj.pos, anchor->junction_point(), fromj.r, + anchor->r_back_mm); + + m_builder.add_anchor(*anchor); + + build_subtree(from.id); + } + + return bool(anchor); +} + +void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &sm) +{ + auto coordfn = [&sm](size_t id, size_t dim) { return sm.pts[id].pos(dim); }; + KDTreeIndirect<3, float, decltype (coordfn)> tree{coordfn, sm.pts.size()}; + + auto nondup_idx = non_duplicate_suppt_indices(tree, sm.pts, 0.1); + std::vector> heads(nondup_idx.size()); + auto leafs = reserve_vector(nondup_idx.size()); + + execution::for_each( + ex_tbb, size_t(0), nondup_idx.size(), + [&sm, &heads](size_t i) { + heads[i] = calculate_pinhead_placement(ex_seq, sm, i); + }, + execution::max_concurrency(ex_tbb) + ); + + for (auto &h : heads) + if (h && h->is_valid()) { + leafs.emplace_back(h->junction_point().cast(), h->r_back_mm); + builder.add_head(h->id, *h); + } + + auto &its = *sm.emesh.get_triangle_mesh(); + ExPolygons bedpolys = {branchingtree::make_bed_poly(its)}; + + auto props = branchingtree::Properties{} + .bed_shape(bedpolys) + .ground_level(sla::ground_level(sm)) + .max_slope(sm.cfg.bridge_slope); + + branchingtree::PointCloud nodes{its, std::move(leafs), props}; + BranchingTreeBuilder vbuilder{builder, sm, nodes}; + branchingtree::build_tree(nodes, vbuilder); + + for (size_t id : vbuilder.unroutable_pinheads()) + builder.head(id).invalidate(); + +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/BranchingTreeSLA.hpp b/src/libslic3r/SLA/BranchingTreeSLA.hpp new file mode 100644 index 000000000..c101029bd --- /dev/null +++ b/src/libslic3r/SLA/BranchingTreeSLA.hpp @@ -0,0 +1,15 @@ +#ifndef BRANCHINGTREESLA_HPP +#define BRANCHINGTREESLA_HPP + +#include "libslic3r/BranchingTree/BranchingTree.hpp" +#include "SupportTreeBuilder.hpp" + +#include + +namespace Slic3r { namespace sla { + +void create_branching_tree(SupportTreeBuilder& builder, const SupportableMesh &sm); + +}} // namespace Slic3r::sla + +#endif // BRANCHINGTREESLA_HPP diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 466fade88..b221c4330 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,10 @@ indexed_triangle_set create_support_tree(const SupportableMesh &sm, create_default_tree(*builder, sm); break; } + case SupportTreeType::Branching: { + create_branching_tree(*builder, sm); + break; + } default:; } diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 07f19300c..a33a23c88 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -2,6 +2,7 @@ #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/SLA/AGGRaster.hpp" #include "libslic3r/SLA/DefaultSupportTree.hpp" +#include "libslic3r/SLA/BranchingTreeSLA.hpp" #include @@ -160,6 +161,11 @@ void test_supports(const std::string &obj_filename, check_support_tree_integrity(treebuilder, supportcfg, sla::ground_level(sm)); break; } + case sla::SupportTreeType::Branching: { + create_branching_tree(treebuilder, sm); + // TODO: check_support_tree_integrity(treebuilder, supportcfg); + break; + } default:; } From ae23adff104710bd584511501c71e72ca1291dcd Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 13 May 2022 12:26:03 +0200 Subject: [PATCH 03/29] Fix crash on windows Fix crash on windows - 2nd try --- src/libslic3r/BranchingTree/PointCloud.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index a4855ee53..14ce4f63c 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -42,8 +42,7 @@ void to_eigen_mesh(const indexed_triangle_set &its, V.row(i) = its.vertices[i].cast(); } -std::vector sample_mesh(const indexed_triangle_set &its, - double radius) +std::vector sample_mesh(const indexed_triangle_set &its, double radius) { std::vector ret; @@ -68,7 +67,17 @@ std::vector sample_mesh(const indexed_triangle_set &its, ret.reserve(size_t(N)); for (int i = 0; i < FI.size(); i++) { - Vec3i face = its.indices[FI(i)]; + int face_id = FI(i); + + if (face_id < 0 || face_id >= int(its.indices.size())) + continue; + + Vec3i face = its.indices[face_id]; + + if (face(0) >= int(its.vertices.size()) || + face(1) >= int(its.vertices.size()) || + face(2) >= int(its.vertices.size())) + continue; Vec3f c = B.row(i)(0) * its.vertices[face(0)] + B.row(i)(1) * its.vertices[face(1)] + From fd8fd77077dcc2eb399f943d90248dc8685e13a3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 13 May 2022 13:56:35 +0200 Subject: [PATCH 04/29] Improvements to widening strategy and branch length limitations Also some refactoring --- src/libslic3r/BranchingTree/BranchingTree.cpp | 38 +++++++++-- src/libslic3r/BranchingTree/BranchingTree.hpp | 21 ++---- src/libslic3r/BranchingTree/PointCloud.cpp | 66 +++++++++++-------- src/libslic3r/BranchingTree/PointCloud.hpp | 45 ++++++++----- src/libslic3r/SLA/BranchingTreeSLA.cpp | 9 ++- 5 files changed, 108 insertions(+), 71 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index 4c9ff899c..5c0198c18 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -22,7 +22,7 @@ bool build_tree(PointCloud &nodes, Builder &builder) ptsqueue.pop(); Node node = nodes.get(node_id); - nodes.remove_node(node_id); + nodes.mark_unreachable(node_id); distances.clear(); distances.reserve(nodes.reachable_count()); @@ -53,10 +53,21 @@ bool build_tree(PointCloud &nodes, Builder &builder) switch (type) { case BED: { closest_node.weight = w; - if ((routed = builder.add_ground_bridge(node, closest_node))) { + if (closest_it->distance > nodes.properties().max_branch_length()) { + auto hl_br_len = float(nodes.properties().max_branch_length()) / 2.f; + Node new_node {{node.pos.x(), node.pos.y(), node.pos.z() - hl_br_len}, node.Rmin}; + new_node.id = int(nodes.next_junction_id()); + new_node.weight = nodes.get(node_id).weight + nodes.properties().max_branch_length(); + new_node.left = node.id; + if ((routed = builder.add_bridge(node, new_node))) { + size_t new_idx = nodes.insert_junction(new_node); + ptsqueue.push(new_idx); + } + } + else if ((routed = builder.add_ground_bridge(node, closest_node))) { closest_node.left = closest_node.right = node_id; nodes.get(closest_node_id) = closest_node; - nodes.remove_node(closest_node_id); + nodes.mark_unreachable(closest_node_id); } break; @@ -66,12 +77,12 @@ bool build_tree(PointCloud &nodes, Builder &builder) if ((routed = builder.add_mesh_bridge(node, closest_node))) { closest_node.left = closest_node.right = node_id; nodes.get(closest_node_id) = closest_node; - nodes.remove_node(closest_node_id); + nodes.mark_unreachable(closest_node_id); } break; } - case SUPP: + case LEAF: case JUNCTION: { auto max_slope = float(properties.max_slope()); @@ -96,7 +107,7 @@ bool build_tree(PointCloud &nodes, Builder &builder) size_t new_idx = nodes.insert_junction(mergenode); ptsqueue.push(new_idx); ptsqueue.remove(nodes.get_queue_idx(closest_node_id)); - nodes.remove_node(closest_node_id); + nodes.mark_unreachable(closest_node_id); } } else if (closest_node.left == Node::ID_NONE || closest_node.right == Node::ID_NONE) @@ -138,4 +149,19 @@ bool build_tree(const indexed_triangle_set & its, return build_tree(nodes, builder); } +ExPolygon make_bed_poly(const indexed_triangle_set &its) +{ + auto bb = bounding_box(its); + + BoundingBox bbcrd{scaled(to_2d(bb.min)), scaled(to_2d(bb.max))}; + bbcrd.offset(scaled(10.)); + Point min = bbcrd.min, max = bbcrd.max; + ExPolygon ret = {{min.x(), min.y()}, + {max.x(), min.y()}, + {max.x(), max.y()}, + {min.x(), max.y()}}; + + return ret; +} + }} // namespace Slic3r::branchingtree diff --git a/src/libslic3r/BranchingTree/BranchingTree.hpp b/src/libslic3r/BranchingTree/BranchingTree.hpp index 5313e30d9..b03ed06ec 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.hpp +++ b/src/libslic3r/BranchingTree/BranchingTree.hpp @@ -16,7 +16,7 @@ class Properties double m_max_slope = PI / 4.; double m_ground_level = 0.; double m_sampling_radius = .5; - double m_max_branch_len = 20.; + double m_max_branch_len = 10.; ExPolygons m_bed_shape; @@ -67,11 +67,11 @@ struct Node int id = ID_NONE, left = ID_NONE, right = ID_NONE; Vec3f pos; - float Rmin; + float Rmin = 0.f; // Tracking the weight of each junction, which is essentially the sum of // the lenghts of all branches emanating from this junction. - float weight; + float weight = 0.f; Node(const Vec3f &p, float r_min = .0f) : pos{p}, Rmin{r_min}, weight{0.f} {} @@ -137,20 +137,7 @@ inline bool build_tree(const indexed_triangle_set & its, //bool build_tree(PointCloud &pcloud, Builder &builder); // Helper function to derive a bed polygon only from the model bounding box. -inline ExPolygon make_bed_poly(const indexed_triangle_set &its) -{ - auto bb = bounding_box(its); - - BoundingBox bbcrd{scaled(to_2d(bb.min)), scaled(to_2d(bb.max))}; - bbcrd.offset(scaled(10.)); - Point min = bbcrd.min, max = bbcrd.max; - ExPolygon ret = {{min.x(), min.y()}, - {max.x(), min.y()}, - {max.x(), max.y()}, - {min.x(), max.y()}}; - - return ret; -} +ExPolygon make_bed_poly(const indexed_triangle_set &its); }} // namespace Slic3r::branchingtree diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index 14ce4f63c..f84b1dfc6 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -109,72 +109,82 @@ std::vector sample_bed(const ExPolygons &bed, float z, double radius) } PointCloud::PointCloud(const indexed_triangle_set &M, - std::vector support_leafs, + std::vector support_leafs, const Properties &props) + : PointCloud{sample_mesh(M, props.sampling_radius()), + sample_bed(props.bed_shape(), + props.ground_level(), + props.sampling_radius()), + std::move(support_leafs), props} +{} + +PointCloud::PointCloud(std::vector meshpts, + std::vector bedpts, + std::vector support_leafs, + const Properties &props) : m_leafs{std::move(support_leafs)} - , m_meshpoints{sample_mesh(M, props.sampling_radius())} - , m_bedpoints{sample_bed(props.bed_shape(), - props.ground_level(), - props.sampling_radius())} + , m_meshpoints{std::move(meshpts)} + , m_bedpoints{std::move(bedpts)} , m_props{props} , cos2bridge_slope{std::cos(props.max_slope()) * std::abs(std::cos(props.max_slope()))} , MESHPTS_BEGIN{m_bedpoints.size()} - , SUPP_BEGIN{MESHPTS_BEGIN + m_meshpoints.size()} - , JUNCTIONS_BEGIN{SUPP_BEGIN + m_leafs.size()} + , LEAFS_BEGIN{MESHPTS_BEGIN + m_meshpoints.size()} + , JUNCTIONS_BEGIN{LEAFS_BEGIN + m_leafs.size()} , m_searchable_indices(JUNCTIONS_BEGIN, true) , m_queue_indices(JUNCTIONS_BEGIN, UNQUEUED) , m_reachable_cnt{JUNCTIONS_BEGIN} - , m_ktree{CoordFn{this}, SUPP_BEGIN} // Only for bed and mesh points + , m_ktree{CoordFn{this}, LEAFS_BEGIN} // Only for bed and mesh points { for (size_t i = 0; i < m_bedpoints.size(); ++i) m_bedpoints[i].id = int(i); - + for (size_t i = 0; i < m_meshpoints.size(); ++i) m_meshpoints[i].id = int(MESHPTS_BEGIN + i); - + for (size_t i = 0; i < m_leafs.size(); ++i) - m_leafs[i].id = int(SUPP_BEGIN + i); + m_leafs[i].id = int(LEAFS_BEGIN + i); } -float PointCloud::get_distance(const Vec3f &p, size_t node) +float PointCloud::get_distance(const Vec3f &p, size_t node_id) const { - auto t = get_type(node); + auto t = get_type(node_id); auto ret = std::numeric_limits::infinity(); - + const auto &node = get(node_id); + switch (t) { case MESH: case BED: { // Points of mesh or bed which are outside of the support cone of // 'pos' must be discarded. - if (is_outside_support_cone(p, get(node).pos)) + if (is_outside_support_cone(p, node.pos)) ret = std::numeric_limits::infinity(); else - ret = (get(node).pos - p).norm(); - + ret = (node.pos - p).norm(); + break; } - case SUPP: + case LEAF: case JUNCTION:{ - auto mergept = find_merge_pt(p, get(node).pos, m_props.max_slope()); - if (!mergept || mergept->z() < (m_props.ground_level() + 2 * get(node).Rmin)) + auto mergept = find_merge_pt(p, node.pos, m_props.max_slope()); + if (!mergept || mergept->z() < (m_props.ground_level() + 2 * node.Rmin)) ret = std::numeric_limits::infinity(); - else + else if ( (node.pos - *mergept).norm() < m_props.max_branch_length()) ret = (p - *mergept).norm(); - + break; } case NONE: ; } - - // Setting the ret val to infinity will effectively discard this - // connection of nodes. max_branch_length property if used here - // to discard node=>node and node=>mesh connections longer than this - // property. + + // Setting the ret val to infinity will effectively discard this + // connection of nodes. max_branch_length property if used here + // to discard node=>node and node=>mesh connections longer than this + // property. if (t != BED && ret > m_props.max_branch_length()) ret = std::numeric_limits::infinity(); - + return ret; } diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index 6f583759e..d80f23946 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -1,8 +1,11 @@ #ifndef POINTCLOUD_HPP #define POINTCLOUD_HPP +#include + #include "BranchingTree.hpp" +#include "libslic3r/Execution/Execution.hpp" #include "libslic3r/KDTreeIndirect.hpp" #include "libslic3r/MutablePriorityQueue.hpp" @@ -16,14 +19,13 @@ void to_eigen_mesh(const indexed_triangle_set &its, Eigen::MatrixXd &V, Eigen::MatrixXi &F); -std::vector sample_mesh(const indexed_triangle_set &its, - double radius); +std::vector sample_mesh(const indexed_triangle_set &its, double radius); std::vector sample_bed(const ExPolygons &bed, float z, double radius = 1.); -enum PtType { SUPP, MESH, BED, JUNCTION, NONE }; +enum PtType { LEAF, MESH, BED, JUNCTION, NONE }; // A cloud of points including support points, mesh points, junction points // and anchor points on the bed. Junction points can be added or removed, all @@ -34,7 +36,7 @@ class PointCloud { const branchingtree::Properties &m_props; const double cos2bridge_slope; - const size_t MESHPTS_BEGIN, SUPP_BEGIN, JUNCTIONS_BEGIN; + const size_t MESHPTS_BEGIN, LEAFS_BEGIN, JUNCTIONS_BEGIN; private: @@ -57,7 +59,7 @@ private: KDTreeIndirect<3, float, CoordFn> m_ktree; - bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt) + bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt) const { Vec3d D = (pt - supp).cast(); double dot_sq = -D.z() * std::abs(-D.z()); @@ -75,7 +77,7 @@ private: switch(pc.get_type(id)) { case BED: ret = &pc.m_bedpoints[id]; break; case MESH: ret = &pc.m_meshpoints[id - pc.MESHPTS_BEGIN]; break; - case SUPP: ret = &pc.m_leafs [id - pc.SUPP_BEGIN]; break; + case LEAF: ret = &pc.m_leafs [id - pc.LEAFS_BEGIN]; break; case JUNCTION: ret = &pc.m_junctions[id - pc.JUNCTIONS_BEGIN]; break; case NONE: assert(false); } @@ -99,13 +101,18 @@ public: std::vector support_leafs, const Properties & props); + PointCloud(std::vector meshpts, + std::vector bedpts, + std::vector support_leafs, + const Properties &props); + PtType get_type(size_t node_id) const { PtType ret = NONE; if (node_id < MESHPTS_BEGIN && !m_bedpoints.empty()) ret = BED; - else if (node_id < SUPP_BEGIN && !m_meshpoints.empty()) ret = MESH; - else if (node_id < JUNCTIONS_BEGIN && !m_leafs.empty()) ret = SUPP; + else if (node_id < LEAFS_BEGIN && !m_meshpoints.empty()) ret = MESH; + else if (node_id < JUNCTIONS_BEGIN && !m_leafs.empty()) ret = LEAF; else if (node_id >= JUNCTIONS_BEGIN && !m_junctions.empty()) ret = JUNCTION; return ret; @@ -128,14 +135,14 @@ public: // node id is indeed of type SUPP int get_leaf_id(size_t node_id) const { - return node_id >= SUPP_BEGIN && node_id < JUNCTIONS_BEGIN ? - node_id - SUPP_BEGIN : + return node_id >= LEAFS_BEGIN && node_id < JUNCTIONS_BEGIN ? + node_id - LEAFS_BEGIN : Node::ID_NONE; } size_t get_queue_idx(size_t node_id) const { return m_queue_indices[node_id]; } - float get_distance(const Vec3f &p, size_t node); + float get_distance(const Vec3f &p, size_t node) const; size_t next_junction_id() const { @@ -154,7 +161,13 @@ public: return new_id; } - void remove_node(size_t node_id) + const std::vector &get_junctions() const noexcept { return m_junctions; } + const std::vector &get_bedpoints() const noexcept { return m_bedpoints; } + const std::vector &get_meshpoints() const noexcept { return m_meshpoints; } + const std::vector &get_leafs() const noexcept { return m_leafs; } + const Properties & properties() const noexcept { return m_props; } + + void mark_unreachable(size_t node_id) { m_searchable_indices[node_id] = false; m_queue_indices[node_id] = UNQUEUED; @@ -175,7 +188,7 @@ public: if (anchor != m_ktree.npos) visitor(anchor, get_distance(pos, anchor)); - for (size_t i = SUPP_BEGIN; i < m_searchable_indices.size(); ++i) + for (size_t i = LEAFS_BEGIN; i < m_searchable_indices.size(); ++i) if (m_searchable_indices[i]) visitor(i, get_distance(pos, i)); } @@ -187,14 +200,12 @@ public: ZCompareFn{this}); ptsqueue.reserve(m_leafs.size()); - size_t iend = SUPP_BEGIN + m_leafs.size(); - for (size_t i = SUPP_BEGIN; i < iend; ++i) + size_t iend = LEAFS_BEGIN + m_leafs.size(); + for (size_t i = LEAFS_BEGIN; i < iend; ++i) ptsqueue.push(i); return ptsqueue; } - - const Properties & properties() const { return m_props; } }; template void traverse(PC &&pc, size_t root, Fn &&fn) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 751544fb5..9ec6f987f 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -26,7 +26,7 @@ class BranchingTreeBuilder: public branchingtree::Builder { { double w = WIDENING_SCALE * m_sm.cfg.pillar_widening_factor * j.weight; - return std::max(double(j.Rmin), std::min(m_sm.cfg.base_radius_mm, w)); + return std::min(m_sm.cfg.base_radius_mm, double(j.Rmin) + w); } std::vector m_unroutable_pinheads; @@ -51,10 +51,13 @@ class BranchingTreeBuilder: public branchingtree::Builder { } else if (int child = node.left + node.right + 1; child >= 0) { auto from = m_cloud.get(child); auto to = m_cloud.get(node.id); + auto tod = to.pos.cast(); + double toR = get_radius(to); m_builder.add_diffbridge(from.pos.cast(), - to.pos.cast(), + tod, get_radius(from), - get_radius(to)); + toR); + m_builder.add_junction(tod, toR); } }); } From 0a3b17f940db3a7cfea7e58b6e2d166e0f833b3e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 May 2022 10:52:52 +0200 Subject: [PATCH 05/29] Fix failing sla tree tests Try to increase number of rays in Beam to prevent colisions Put back threshold for intersections with model in sla tree tests Increase safety distance for branching tree instead of increasing rays --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 11 +-- src/libslic3r/SLAPrint.cpp | 6 +- tests/sla_print/sla_test_utils.cpp | 92 ++++++++++++++------------ tests/sla_print/sla_test_utils.hpp | 2 +- 4 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 9ec6f987f..5f85df140 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 d9b8e33df..4f58c3aa1 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 a33a23c88..b9ad696ed 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 c5943bd5c..187a72d54 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; }; From 792779f2deb81fae3373371883d4e0596420878f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 May 2022 11:58:59 +0200 Subject: [PATCH 06/29] Fix creation of unchecked diffbridge in branching tree --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 5f85df140..4bd8e23c0 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, - 2 * m_sm.cfg.safety_distance_mm); + m_sm.cfg.head_back_radius_mm); bool ret = hit.distance() > (tod - fromd).norm(); @@ -141,7 +141,7 @@ bool BranchingTreeBuilder::add_merger(const branchingtree::Node &node, Beam beam1{Ball{from1d, nodeR}, Ball{tod, mergeR}}; Beam beam2{Ball{from2d, closestR}, Ball{tod, mergeR}}; - auto sd = 2 * m_sm.cfg.safety_distance_mm; + auto sd = 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); @@ -175,15 +175,20 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, fromj, to.pos.cast()); + sla::Junction toj = {anchor->junction_point(), anchor->r_back_mm}; + if (anchor) { - m_builder.add_diffbridge(fromj.pos, anchor->junction_point(), fromj.r, - anchor->r_back_mm); + auto hit1 = beam_mesh_hit(ex_tbb, m_sm.emesh, + Beam{{fromj.pos, fromj.r}, {toj.pos, toj.r}}, + m_sm.cfg.safety_distance_mm); + if (hit1.distance() >= distance(fromj.pos, toj.pos)) { + m_builder.add_diffbridge(fromj.pos, toj.pos, fromj.r, toj.r); - if (!m_sm.cfg.ground_facing_only) { // Easter egg, to omit the anchors - 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); } - - build_subtree(from.id); } return bool(anchor); From 7705ecd5aa2575f925dfa6b458945025e073f133 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 May 2022 16:29:16 +0200 Subject: [PATCH 07/29] Fix indexing of heads after duplicate removal --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 4bd8e23c0..94cfbb1ec 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -205,8 +205,8 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s execution::for_each( ex_tbb, size_t(0), nondup_idx.size(), - [&sm, &heads](size_t i) { - heads[i] = calculate_pinhead_placement(ex_seq, sm, i); + [&sm, &heads, &nondup_idx](size_t i) { + heads[i] = calculate_pinhead_placement(ex_seq, sm, nondup_idx[i]); }, execution::max_concurrency(ex_tbb) ); @@ -214,6 +214,7 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s for (auto &h : heads) if (h && h->is_valid()) { leafs.emplace_back(h->junction_point().cast(), h->r_back_mm); + h->id = leafs.size() - 1; builder.add_head(h->id, *h); } From 4c3ba3f6bf21777a4567800c120a8749f5b62003 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 May 2022 16:30:55 +0200 Subject: [PATCH 08/29] Fix tree struts piercing model body Free path between the anchor and the last bridge was not verified --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 94cfbb1ec..1ef3d6f9a 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -175,19 +175,22 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, fromj, to.pos.cast()); - sla::Junction toj = {anchor->junction_point(), anchor->r_back_mm}; - if (anchor) { - auto hit1 = beam_mesh_hit(ex_tbb, m_sm.emesh, - Beam{{fromj.pos, fromj.r}, {toj.pos, toj.r}}, - m_sm.cfg.safety_distance_mm); - if (hit1.distance() >= distance(fromj.pos, toj.pos)) { + sla::Junction toj = {anchor->junction_point(), anchor->r_back_mm}; + + auto hit = beam_mesh_hit(ex_tbb, m_sm.emesh, + Beam{{fromj.pos, fromj.r}, {toj.pos, toj.r}}, 0.); + + if (hit.distance() > distance(fromj.pos, toj.pos)) { m_builder.add_diffbridge(fromj.pos, toj.pos, fromj.r, toj.r); if (!m_sm.cfg.ground_facing_only) { // Easter egg, to omit the anchors m_builder.add_anchor(*anchor); } + build_subtree(from.id); + } else { + anchor.reset(); } } From 5d6d1f52342c8de7978e7637351823a43b9b619e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 May 2022 09:56:16 +0200 Subject: [PATCH 09/29] Apply branch length setting from UI for branching tree --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 1ef3d6f9a..6f8994a63 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -227,7 +227,8 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s auto props = branchingtree::Properties{} .bed_shape(bedpolys) .ground_level(sla::ground_level(sm)) - .max_slope(sm.cfg.bridge_slope); + .max_slope(sm.cfg.bridge_slope) + .max_branch_length(sm.cfg.max_bridge_length_mm); branchingtree::PointCloud nodes{its, std::move(leafs), props}; BranchingTreeBuilder vbuilder{builder, sm, nodes}; From 6523bfdf99811203c67540f5479d3d4e16bae271 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 May 2022 10:15:51 +0200 Subject: [PATCH 10/29] Enable ground facing only option for branching tree Essentially discards mesh nodes --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 12 +++++------- src/libslic3r/SLAPrint.cpp | 6 +----- tests/sla_print/sla_test_utils.cpp | 3 --- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 6f8994a63..b73c5eaae 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -171,9 +171,10 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, { sla::Junction fromj = {from.pos.cast(), get_radius(from)}; - auto anchor = calculate_anchor_placement(ex_tbb, m_sm, - fromj, - to.pos.cast()); + auto anchor = m_sm.cfg.ground_facing_only ? + std::optional{} : // If no mesh connections are allowed + calculate_anchor_placement(ex_tbb, m_sm, fromj, + to.pos.cast()); if (anchor) { sla::Junction toj = {anchor->junction_point(), anchor->r_back_mm}; @@ -183,10 +184,7 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from, if (hit.distance() > distance(fromj.pos, toj.pos)) { m_builder.add_diffbridge(fromj.pos, toj.pos, fromj.r, toj.r); - - if (!m_sm.cfg.ground_facing_only) { // Easter egg, to omit the anchors - m_builder.add_anchor(*anchor); - } + m_builder.add_anchor(*anchor); build_subtree(from.id); } else { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 4f58c3aa1..d9b8e33df 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -57,11 +57,7 @@ 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; - 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.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 b9ad696ed..737659eed 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -16,9 +16,6 @@ void test_support_model_collision( 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; From 371da354a016b131a3844e6da05ff3757b78e169 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 May 2022 14:48:48 +0200 Subject: [PATCH 11/29] Adjust configuration layer to better reflect tree strategies. --- src/libslic3r/PrintConfig.cpp | 10 +++++++--- src/slic3r/GUI/ConfigManipulation.cpp | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 302a9eb6f..f9db6cc5f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3666,9 +3666,13 @@ void PrintConfigDef::init_sla_params() def = this->add("support_pillar_widening_factor", coFloat); def->label = L("Pillar widening factor"); def->category = L("Supports"); - def->tooltip = L("Merging bridges or pillars into another pillars can " - "increase the radius. Zero means no increase, one means " - "full increase."); + def->tooltip = L( + "Merging bridges or pillars into another pillars can " + "increase the radius. Zero means no increase, one means " + "full increase. The exact amount of increase is unspecified and can " + "change in the future. What is garanteed is that thickness will not " + "exceed \"support_base_diameter\""); + def->min = 0; def->max = 1; def->mode = comExpert; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 14fba19fa..dcdadebad 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -363,14 +363,17 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) { bool supports_en = config->opt_bool("supports_enable"); + sla::SupportTreeType treetype = config->opt_enum("support_tree_type"); + bool is_default_tree = treetype == sla::SupportTreeType::Default; + bool is_branching_tree = treetype == sla::SupportTreeType::Branching; toggle_field("support_head_front_diameter", supports_en); toggle_field("support_head_penetration", supports_en); toggle_field("support_head_width", supports_en); toggle_field("support_pillar_diameter", supports_en); toggle_field("support_small_pillar_diameter_percent", supports_en); - toggle_field("support_max_bridges_on_pillar", supports_en); - toggle_field("support_pillar_connection_mode", supports_en); + toggle_field("support_max_bridges_on_pillar", supports_en && is_default_tree); + toggle_field("support_pillar_connection_mode", supports_en && is_default_tree); toggle_field("support_tree_type", supports_en); toggle_field("support_buildplate_only", supports_en); toggle_field("support_base_diameter", supports_en); @@ -378,7 +381,8 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("support_base_safety_distance", supports_en); toggle_field("support_critical_angle", supports_en); toggle_field("support_max_bridge_length", supports_en); - toggle_field("support_max_pillar_link_distance", supports_en); + toggle_field("support_max_pillar_link_distance", supports_en && is_default_tree); + toggle_field("support_pillar_widening_factor", supports_en && is_branching_tree); toggle_field("support_points_density_relative", supports_en); toggle_field("support_points_minimal_distance", supports_en); From 8a78428d107d63f1ce00099c1105fb76abde7956 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 25 May 2022 15:12:22 +0200 Subject: [PATCH 12/29] Add tests to find_merge_pt and make them pass --- src/libslic3r/BranchingTree/PointCloud.cpp | 16 +++-- tests/sla_print/sla_print_tests.cpp | 74 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index f84b1dfc6..29284c1d7 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -14,19 +14,27 @@ std::optional find_merge_pt(const Vec3f &A, Vec3f Da = (B - A).normalized(), Db = -Da; auto [polar_da, azim_da] = Geometry::dir_to_spheric(Da); auto [polar_db, azim_db] = Geometry::dir_to_spheric(Db); - polar_da = std::max(polar_da, float(PI) - max_slope); - polar_db = std::max(polar_db, float(PI) - max_slope); + polar_da = std::max(polar_da, float(PI) / 2.f + max_slope); + polar_db = std::max(polar_db, float(PI) / 2.f + max_slope); Da = Geometry::spheric_to_dir(polar_da, azim_da); Db = Geometry::spheric_to_dir(polar_db, azim_db); + // This formula is based on + // https://stackoverflow.com/questions/27459080/given-two-points-and-two-direction-vectors-find-the-point-where-they-intersect double t1 = (A.z() * Db.x() + Db.z() * B.x() - B.z() * Db.x() - Db.z() * A.x()) / (Da.x() * Db.z() - Da.z() * Db.x()); - double t2 = (A.x() + Da.x() * t1 - B.x()) / Db.x(); + if (std::isnan(t1) || std::abs(t1) < EPSILON) + t1 = (A.z() * Db.y() + Db.z() * B.y() - B.z() * Db.y() - Db.z() * A.y()) / + (Da.y() * Db.z() - Da.z() * Db.y()); - return t1 > 0. && t2 > 0. ? A + t1 * Da : std::optional{}; + Vec3f m1 = A + t1 * Da; + + double t2 = (m1.z() - B.z()) / Db.z(); + + return t1 >= 0. && t2 >= 0. ? m1 : std::optional{}; } void to_eigen_mesh(const indexed_triangle_set &its, diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 8ae02c808..fa64b0c10 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace { @@ -172,6 +173,79 @@ TEST_CASE("DefaultSupports::FloorSupportsDoNotPierceModel", "[SLASupportGenerati // for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); //} +TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branching]") { + SECTION("Identical points have the same merge point") { + Vec3f a{0.f, 0.f, 0.f}, b = a; + float slope = PI / 4.f; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + REQUIRE((*mergept - b).norm() < EPSILON); + REQUIRE((*mergept - a).norm() < EPSILON); + } + + // ^ Z + // | a * + // | + // | b * <= mergept + SECTION("Points at different heights have the lower point as mergepoint") { + Vec3f a{0.f, 0.f, 0.f}, b = {0.f, 0.f, -1.f}; + float slope = PI / 4.f; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + REQUIRE((*mergept - b).squaredNorm() < EPSILON); + } + + // -|---------> X + // a b + // * * + // * <= mergept + SECTION("Points at different X have mergept in the middle at lower Z") { + Vec3f a{0.f, 0.f, 0.f}, b = {1.f, 0.f, 0.f}; + float slope = PI / 4.f; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + + // Distance of mergept should be equal from both input points + float D = std::abs((*mergept - b).squaredNorm() - (*mergept - a).squaredNorm()); + + REQUIRE(D < EPSILON); + } + + // -|---------> Y + // a b + // * * + // * <= mergept + SECTION("Points at different Y have mergept in the middle at lower Z") { + Vec3f a{0.f, 0.f, 0.f}, b = {0.f, 1.f, 0.f}; + float slope = PI / 4.f; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + + // Distance of mergept should be equal from both input points + float D = std::abs((*mergept - b).squaredNorm() - (*mergept - a).squaredNorm()); + + REQUIRE(D < EPSILON); + } + + SECTION("Points separated by less than critical angle have the lower point as mergept") { + Vec3f a{0.f, 0.f, 0.f}, b = {-0.5f, -0.5f, -1.f}; + float slope = PI / 4.f; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + REQUIRE((*mergept - b).norm() < EPSILON); + } +} + TEST_CASE("BranchingSupports::ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration][Branching]") { sla::SupportTreeConfig supportcfg; From 91c0741f61e1c8c68e5be4c73dc0deb29c3c69de Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 10:15:01 +0200 Subject: [PATCH 13/29] Minor performance optimization --- src/libslic3r/BranchingTree/PointCloud.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index 29284c1d7..96e8bfd24 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -175,11 +175,15 @@ float PointCloud::get_distance(const Vec3f &p, size_t node_id) const case LEAF: case JUNCTION:{ auto mergept = find_merge_pt(p, node.pos, m_props.max_slope()); + double maxL2 = m_props.max_branch_length() * m_props.max_branch_length(); + if (!mergept || mergept->z() < (m_props.ground_level() + 2 * node.Rmin)) ret = std::numeric_limits::infinity(); - else if ( (node.pos - *mergept).norm() < m_props.max_branch_length()) - ret = (p - *mergept).norm(); - + else if (double a = (node.pos - *mergept).squaredNorm(), + b = (p - *mergept).squaredNorm(); + a < maxL2 && b < maxL2) + ret = std::sqrt(b); + break; } case NONE: @@ -187,7 +191,7 @@ float PointCloud::get_distance(const Vec3f &p, size_t node_id) const } // Setting the ret val to infinity will effectively discard this - // connection of nodes. max_branch_length property if used here + // connection of nodes. max_branch_length property is used here // to discard node=>node and node=>mesh connections longer than this // property. if (t != BED && ret > m_props.max_branch_length()) From 688c9c644f7940156060661f707f4dbc2e1b56c0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 25 May 2022 17:11:04 +0200 Subject: [PATCH 14/29] Prevent merging with node being lower than closest point --- src/libslic3r/BranchingTree/BranchingTree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index 5c0198c18..a7d9a1b4d 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -96,7 +96,7 @@ bool build_tree(PointCloud &nodes, Builder &builder) float distsum = std::max(mergedist_closest, mergedist_node); w = Wsum + distsum; - if (mergedist_closest > EPSILON) { + if (mergedist_closest > EPSILON && mergedist_node > EPSILON) { Node mergenode{*mergept, closest_node.Rmin}; mergenode.weight = w; mergenode.id = int(nodes.next_junction_id()); @@ -109,9 +109,9 @@ bool build_tree(PointCloud &nodes, Builder &builder) ptsqueue.remove(nodes.get_queue_idx(closest_node_id)); nodes.mark_unreachable(closest_node_id); } - } else if (closest_node.left == Node::ID_NONE || - closest_node.right == Node::ID_NONE) - { + } else if (closest_node.pos.z() < node.pos.z() && + (closest_node.left == Node::ID_NONE || + closest_node.right == Node::ID_NONE)) { closest_node.weight = w; if ((routed = builder.add_bridge(node, closest_node))) { if (closest_node.left == Node::ID_NONE) From 95374f9ed4e0c0ad5739936313a5e9211acf226e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 11:18:19 +0200 Subject: [PATCH 15/29] Fix some crashes Fix minor bugs --- src/libslic3r/BranchingTree/PointCloud.cpp | 6 +++--- src/libslic3r/BranchingTree/PointCloud.hpp | 4 +++- src/libslic3r/SLA/BranchingTreeSLA.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index 96e8bfd24..35498f4c1 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -139,9 +139,9 @@ PointCloud::PointCloud(std::vector meshpts, , MESHPTS_BEGIN{m_bedpoints.size()} , LEAFS_BEGIN{MESHPTS_BEGIN + m_meshpoints.size()} , JUNCTIONS_BEGIN{LEAFS_BEGIN + m_leafs.size()} - , m_searchable_indices(JUNCTIONS_BEGIN, true) - , m_queue_indices(JUNCTIONS_BEGIN, UNQUEUED) - , m_reachable_cnt{JUNCTIONS_BEGIN} + , m_searchable_indices(JUNCTIONS_BEGIN + m_junctions.size(), true) + , m_queue_indices(JUNCTIONS_BEGIN + m_junctions.size(), UNQUEUED) + , m_reachable_cnt{JUNCTIONS_BEGIN + m_junctions.size()} , m_ktree{CoordFn{this}, LEAFS_BEGIN} // Only for bed and mesh points { for (size_t i = 0; i < m_bedpoints.size(); ++i) diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index d80f23946..d2072bee9 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -169,6 +169,8 @@ public: void mark_unreachable(size_t node_id) { + assert(node_id < m_searchable_indices.size()); + m_searchable_indices[node_id] = false; m_queue_indices[node_id] = UNQUEUED; --m_reachable_cnt; @@ -195,7 +197,7 @@ public: auto start_queue() { - auto ptsqueue = make_mutable_priority_queue( + auto ptsqueue = make_mutable_priority_queue( [this](size_t el, size_t idx) { m_queue_indices[el] = idx; }, ZCompareFn{this}); diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index b73c5eaae..3f3e99d8e 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.head_back_radius_mm); + m_sm.cfg.safety_distance_mm); bool ret = hit.distance() > (tod - fromd).norm(); From 1a30c0aaa097ede9f6466c1e309d173b3fbbf36e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 3 Jun 2022 17:01:30 +0200 Subject: [PATCH 16/29] Extend mergept tests --- tests/sla_print/sla_print_tests.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index fa64b0c10..273c3f8f6 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -173,6 +173,15 @@ TEST_CASE("DefaultSupports::FloorSupportsDoNotPierceModel", "[SLASupportGenerati // for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); //} +bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt, float angle) +{ + Vec3d D = (pt - supp).cast(); + double dot_sq = -D.z() * std::abs(-D.z()); + + return dot_sq < + D.squaredNorm() * std::cos(angle) * std::abs(std::cos(angle)); +} + TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branching]") { SECTION("Identical points have the same merge point") { Vec3f a{0.f, 0.f, 0.f}, b = a; @@ -215,6 +224,8 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi float D = std::abs((*mergept - b).squaredNorm() - (*mergept - a).squaredNorm()); REQUIRE(D < EPSILON); + REQUIRE(!is_outside_support_cone(a, *mergept, slope)); + REQUIRE(!is_outside_support_cone(b, *mergept, slope)); } // -|---------> Y @@ -233,6 +244,8 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi float D = std::abs((*mergept - b).squaredNorm() - (*mergept - a).squaredNorm()); REQUIRE(D < EPSILON); + REQUIRE(!is_outside_support_cone(a, *mergept, slope)); + REQUIRE(!is_outside_support_cone(b, *mergept, slope)); } SECTION("Points separated by less than critical angle have the lower point as mergept") { From 63a58ce1ad02ea6f2e298bb6bb1ae220c928cb91 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 11:06:59 +0200 Subject: [PATCH 17/29] Require higher values for widening factor to have the same effect --- src/libslic3r/PrintConfig.cpp | 2 +- src/libslic3r/SLA/BranchingTreeSLA.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f9db6cc5f..40a3cb3d5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3676,7 +3676,7 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->max = 1; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(0.1)); + def->set_default_value(new ConfigOptionFloat(0.15)); def = this->add("support_base_diameter", coFloat); def->label = L("Support base diameter"); diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 3f3e99d8e..50c5ad015 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -20,7 +20,7 @@ class BranchingTreeBuilder: public branchingtree::Builder { // Scaling of the input value 'widening_factor:<0, 1>' to produce resonable // widening behaviour - static constexpr double WIDENING_SCALE = 0.2; + static constexpr double WIDENING_SCALE = 0.08; double get_radius(const branchingtree::Node &j) { @@ -206,12 +206,16 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s execution::for_each( ex_tbb, size_t(0), nondup_idx.size(), - [&sm, &heads, &nondup_idx](size_t i) { - heads[i] = calculate_pinhead_placement(ex_seq, sm, nondup_idx[i]); + [&sm, &heads, &nondup_idx, &builder](size_t i) { + if (!builder.ctl().stopcondition()) + heads[i] = calculate_pinhead_placement(ex_tbb, sm, nondup_idx[i]); }, execution::max_concurrency(ex_tbb) ); + if (builder.ctl().stopcondition()) + return; + for (auto &h : heads) if (h && h->is_valid()) { leafs.emplace_back(h->junction_point().cast(), h->r_back_mm); From 1a8cf3b029ebc79d1513be3d082bb4ed2ba6189a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 11:31:22 +0200 Subject: [PATCH 18/29] Change UNQUEUED to Unqueued to keep convensions --- src/libslic3r/BranchingTree/BranchingTree.cpp | 6 +++++- src/libslic3r/BranchingTree/PointCloud.cpp | 2 +- src/libslic3r/BranchingTree/PointCloud.hpp | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index a7d9a1b4d..3102f213f 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -106,7 +106,11 @@ bool build_tree(PointCloud &nodes, Builder &builder) mergenode.right = closest_node_id; size_t new_idx = nodes.insert_junction(mergenode); ptsqueue.push(new_idx); - ptsqueue.remove(nodes.get_queue_idx(closest_node_id)); + size_t qid = nodes.get_queue_idx(closest_node_id); + + if (qid != PointCloud::Unqueued) + ptsqueue.remove(nodes.get_queue_idx(closest_node_id)); + nodes.mark_unreachable(closest_node_id); } } else if (closest_node.pos.z() < node.pos.z() && diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index 35498f4c1..11b500843 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -140,7 +140,7 @@ PointCloud::PointCloud(std::vector meshpts, , LEAFS_BEGIN{MESHPTS_BEGIN + m_meshpoints.size()} , JUNCTIONS_BEGIN{LEAFS_BEGIN + m_leafs.size()} , m_searchable_indices(JUNCTIONS_BEGIN + m_junctions.size(), true) - , m_queue_indices(JUNCTIONS_BEGIN + m_junctions.size(), UNQUEUED) + , m_queue_indices(JUNCTIONS_BEGIN + m_junctions.size(), Unqueued) , m_reachable_cnt{JUNCTIONS_BEGIN + m_junctions.size()} , m_ktree{CoordFn{this}, LEAFS_BEGIN} // Only for bed and mesh points { diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index d2072bee9..3c5e8a9a2 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -67,8 +67,6 @@ private: return dot_sq < D.squaredNorm() * cos2bridge_slope; } - static constexpr auto UNQUEUED = size_t(-1); - template static auto *get_node(PC &&pc, size_t id) { @@ -87,6 +85,8 @@ private: public: + static constexpr auto Unqueued = size_t(-1); + struct ZCompareFn { const PointCloud *self; @@ -155,7 +155,7 @@ public: m_junctions.emplace_back(p); m_junctions.back().id = int(new_id); m_searchable_indices.emplace_back(true); - m_queue_indices.emplace_back(UNQUEUED); + m_queue_indices.emplace_back(Unqueued); ++m_reachable_cnt; return new_id; @@ -172,7 +172,7 @@ public: assert(node_id < m_searchable_indices.size()); m_searchable_indices[node_id] = false; - m_queue_indices[node_id] = UNQUEUED; + m_queue_indices[node_id] = Unqueued; --m_reachable_cnt; } From d7c52433001d7c1de185f7c11410013c9862be5c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 16:14:50 +0200 Subject: [PATCH 19/29] Further increase widening scale --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 50c5ad015..f90ba58fb 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -20,7 +20,7 @@ class BranchingTreeBuilder: public branchingtree::Builder { // Scaling of the input value 'widening_factor:<0, 1>' to produce resonable // widening behaviour - static constexpr double WIDENING_SCALE = 0.08; + static constexpr double WIDENING_SCALE = 0.02; double get_radius(const branchingtree::Node &j) { @@ -128,8 +128,8 @@ bool BranchingTreeBuilder::add_bridge(const branchingtree::Node &from, } bool BranchingTreeBuilder::add_merger(const branchingtree::Node &node, - const branchingtree::Node &closest, - const branchingtree::Node &merge_node) + const branchingtree::Node &closest, + const branchingtree::Node &merge_node) { Vec3d from1d = node.pos.cast(), from2d = closest.pos.cast(), @@ -152,7 +152,7 @@ bool BranchingTreeBuilder::add_merger(const branchingtree::Node &node, } bool BranchingTreeBuilder::add_ground_bridge(const branchingtree::Node &from, - const branchingtree::Node &to) + const branchingtree::Node &to) { bool ret = search_ground_route(ex_tbb, m_builder, m_sm, sla::Junction{from.pos.cast(), @@ -232,7 +232,18 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s .max_slope(sm.cfg.bridge_slope) .max_branch_length(sm.cfg.max_bridge_length_mm); - branchingtree::PointCloud nodes{its, std::move(leafs), props}; + auto meshpts = sm.cfg.ground_facing_only ? + std::vector{} : + branchingtree::sample_mesh(its, + props.sampling_radius()); + + auto bedpts = branchingtree::sample_bed(props.bed_shape(), + props.ground_level(), + props.sampling_radius()); + + branchingtree::PointCloud nodes{std::move(meshpts), std::move(bedpts), + std::move(leafs), props}; + BranchingTreeBuilder vbuilder{builder, sm, nodes}; branchingtree::build_tree(nodes, vbuilder); From 36ec731adfb2c46780f9e707b549d757fe7afb1b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 16:26:34 +0200 Subject: [PATCH 20/29] Replace KDTreeIndirect with boost::rtree for queries of PointCloud - rtree can be populated with junction points gradually - Use repeated queries of 5 nearest reachable points in branching tree alg --- src/libslic3r/BranchingTree/BranchingTree.cpp | 28 ++++++---- src/libslic3r/BranchingTree/PointCloud.cpp | 19 ++++--- src/libslic3r/BranchingTree/PointCloud.hpp | 52 +++++++++++++------ 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index 3102f213f..4e61ebda3 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -11,35 +11,42 @@ namespace Slic3r { namespace branchingtree { bool build_tree(PointCloud &nodes, Builder &builder) { + constexpr size_t ReachablesToExamine = 5; + auto ptsqueue = nodes.start_queue(); auto &properties = nodes.properties(); - struct NodeDistance { size_t node_id; float distance; }; - auto distances = reserve_vector(nodes.reachable_count()); + struct NodeDistance { size_t node_id = Node::ID_NONE; float distance = NaNf; }; + auto distances = reserve_vector(ReachablesToExamine); + double prev_dist_max = 0.; while (!ptsqueue.empty()) { size_t node_id = ptsqueue.top(); - ptsqueue.pop(); Node node = nodes.get(node_id); nodes.mark_unreachable(node_id); distances.clear(); - distances.reserve(nodes.reachable_count()); - nodes.foreach_reachable(node.pos, [&distances](size_t id, float distance) { - if (!std::isinf(distance)) + nodes.foreach_reachable( + node.pos, + [&distances](size_t id, float distance) { distances.emplace_back(NodeDistance{id, distance}); - }); + }, + prev_dist_max); std::sort(distances.begin(), distances.end(), [](auto &a, auto &b) { return a.distance < b.distance; }); if (distances.empty()) { builder.report_unroutable(node); + ptsqueue.pop(); + prev_dist_max = 0.; continue; } + prev_dist_max = distances.back().distance; + auto closest_it = distances.begin(); bool routed = false; while (closest_it != distances.end() && !routed) { @@ -136,8 +143,11 @@ bool build_tree(PointCloud &nodes, Builder &builder) ++closest_it; } - if (!routed) - builder.report_unroutable(node); + if (routed) { + ptsqueue.pop(); + prev_dist_max = 0.; + } + } return true; diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index 11b500843..b5b6893c8 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -142,16 +142,23 @@ PointCloud::PointCloud(std::vector meshpts, , m_searchable_indices(JUNCTIONS_BEGIN + m_junctions.size(), true) , m_queue_indices(JUNCTIONS_BEGIN + m_junctions.size(), Unqueued) , m_reachable_cnt{JUNCTIONS_BEGIN + m_junctions.size()} - , m_ktree{CoordFn{this}, LEAFS_BEGIN} // Only for bed and mesh points { - for (size_t i = 0; i < m_bedpoints.size(); ++i) + for (size_t i = 0; i < m_bedpoints.size(); ++i) { m_bedpoints[i].id = int(i); + m_ktree.insert({m_bedpoints[i].pos, i}); + } - for (size_t i = 0; i < m_meshpoints.size(); ++i) - m_meshpoints[i].id = int(MESHPTS_BEGIN + i); + for (size_t i = 0; i < m_meshpoints.size(); ++i) { + Node &n = m_meshpoints[i]; + n.id = int(MESHPTS_BEGIN + i); + m_ktree.insert({n.pos, n.id}); + } - for (size_t i = 0; i < m_leafs.size(); ++i) - m_leafs[i].id = int(LEAFS_BEGIN + i); + for (size_t i = 0; i < m_leafs.size(); ++i) { + Node &n = m_leafs[i]; + n.id = int(LEAFS_BEGIN + i); + m_ktree.insert({n.pos, n.id}); + } } float PointCloud::get_distance(const Vec3f &p, size_t node_id) const diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index 3c5e8a9a2..d61013737 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -6,9 +6,11 @@ #include "BranchingTree.hpp" #include "libslic3r/Execution/Execution.hpp" -#include "libslic3r/KDTreeIndirect.hpp" #include "libslic3r/MutablePriorityQueue.hpp" +#include "libslic3r/BoostAdapter.hpp" +#include "boost/geometry/index/rtree.hpp" + namespace Slic3r { namespace branchingtree { std::optional find_merge_pt(const Vec3f &A, @@ -57,7 +59,11 @@ private: } }; - KDTreeIndirect<3, float, CoordFn> m_ktree; + using PointIndexEl = std::pair; + + boost::geometry::index:: + rtree /* ? */> + m_ktree; bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt) const { @@ -97,9 +103,9 @@ public: } }; - PointCloud(const indexed_triangle_set & M, - std::vector support_leafs, - const Properties & props); + PointCloud(const indexed_triangle_set &M, + std::vector support_leafs, + const Properties &props); PointCloud(std::vector meshpts, std::vector bedpts, @@ -154,6 +160,7 @@ public: size_t new_id = next_junction_id(); m_junctions.emplace_back(p); m_junctions.back().id = int(new_id); + m_ktree.insert({m_junctions.back().pos, new_id}); m_searchable_indices.emplace_back(true); m_queue_indices.emplace_back(Unqueued); ++m_reachable_cnt; @@ -178,21 +185,32 @@ public: size_t reachable_count() const { return m_reachable_cnt; } - template void foreach_reachable(const Vec3f &pos, Fn &&visitor) + template + void foreach_reachable(const Vec3f &pos, Fn &&visitor, double min_dist = 0.) { - auto closest_anchors = - find_closest_points<5>(m_ktree, pos, [this, &pos](size_t id) { - return m_searchable_indices[id] && - !is_outside_support_cone(pos, get(id).pos); - }); + // Fake output iterator + struct Output { + const PointCloud *self; + Vec3f p; + Fn &visitorfn; - for (size_t anchor : closest_anchors) - if (anchor != m_ktree.npos) - visitor(anchor, get_distance(pos, anchor)); + Output& operator *() { return *this; } + void operator=(const PointIndexEl &el) { + visitorfn(el.second, self->get_distance(p, el.second)); + } + Output& operator++() { return *this; } + }; - for (size_t i = LEAFS_BEGIN; i < m_searchable_indices.size(); ++i) - if (m_searchable_indices[i]) - visitor(i, get_distance(pos, i)); + namespace bgi = boost::geometry::index; + + size_t cnt = 0; + auto filter = bgi::satisfies([this, &pos, min_dist, &cnt](const PointIndexEl &e) { + double d = get_distance(pos, e.second); + cnt++; + return m_searchable_indices[e.second] && !isinf(d) && d > min_dist; + }); + + m_ktree.query(bgi::nearest(pos, K) && filter, Output{this, pos, visitor}); } auto start_queue() From 725f5c05e3d37eee76b497f80fdfe393cba7d4b2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 17:07:20 +0200 Subject: [PATCH 21/29] Change build_tree return value to void as its not used anywhere --- src/libslic3r/BranchingTree/BranchingTree.cpp | 14 +++++------- src/libslic3r/BranchingTree/BranchingTree.hpp | 22 ++++++++----------- src/libslic3r/BranchingTree/PointCloud.hpp | 2 +- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index 4e61ebda3..3026c3d75 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -9,7 +9,7 @@ namespace Slic3r { namespace branchingtree { -bool build_tree(PointCloud &nodes, Builder &builder) +void build_tree(PointCloud &nodes, Builder &builder) { constexpr size_t ReachablesToExamine = 5; @@ -149,18 +149,16 @@ bool build_tree(PointCloud &nodes, Builder &builder) } } - - return true; } -bool build_tree(const indexed_triangle_set & its, - const std::vector &support_roots, - Builder & builder, - const Properties & properties) +void build_tree(const indexed_triangle_set &its, + const std::vector &support_roots, + Builder &builder, + const Properties &properties) { PointCloud nodes(its, support_roots, properties); - return build_tree(nodes, builder); + build_tree(nodes, builder); } ExPolygon make_bed_poly(const indexed_triangle_set &its) diff --git a/src/libslic3r/BranchingTree/BranchingTree.hpp b/src/libslic3r/BranchingTree/BranchingTree.hpp index b03ed06ec..d2eabc7ee 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.hpp +++ b/src/libslic3r/BranchingTree/BranchingTree.hpp @@ -119,23 +119,19 @@ public: // cannot be inserted. If a particular path is unavailable, the algorithm // will try a few other paths as well. If all of them fail, one of the // report_unroutable_* methods will be called as a last resort. -bool build_tree(const indexed_triangle_set & its, - const std::vector &support_leafs, - Builder & builder, - const Properties & properties = {}); +void build_tree(const indexed_triangle_set &its, + const std::vector &support_leafs, + Builder &builder, + const Properties &properties = {}); -inline bool build_tree(const indexed_triangle_set & its, - const std::vector &support_leafs, - Builder && builder, - const Properties & properties = {}) +inline void build_tree(const indexed_triangle_set &its, + const std::vector &support_leafs, + Builder &&builder, + const Properties &properties = {}) { - return build_tree(its, support_leafs, builder, properties); + build_tree(its, support_leafs, builder, properties); } -//class PointCloud; - -//bool build_tree(PointCloud &pcloud, Builder &builder); - // Helper function to derive a bed polygon only from the model bounding box. ExPolygon make_bed_poly(const indexed_triangle_set &its); diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index d61013737..de4afc2a1 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -238,7 +238,7 @@ template void traverse(PC &&pc, size_t root, Fn &&fn) } } -bool build_tree(PointCloud &pcloud, Builder &builder); +void build_tree(PointCloud &pcloud, Builder &builder); }} // namespace Slic3r::branchingtree From e6d49b75dea161e88fcee8cd1451872187e9c8a2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 17:14:08 +0200 Subject: [PATCH 22/29] Make branching tree algorithm cancellable --- src/libslic3r/BranchingTree/BranchingTree.cpp | 4 ++-- src/libslic3r/BranchingTree/BranchingTree.hpp | 3 +++ src/libslic3r/SLA/BranchingTreeSLA.cpp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index 3026c3d75..b101f38aa 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -20,7 +20,7 @@ void build_tree(PointCloud &nodes, Builder &builder) auto distances = reserve_vector(ReachablesToExamine); double prev_dist_max = 0.; - while (!ptsqueue.empty()) { + while (!ptsqueue.empty() && builder.is_valid()) { size_t node_id = ptsqueue.top(); Node node = nodes.get(node_id); @@ -49,7 +49,7 @@ void build_tree(PointCloud &nodes, Builder &builder) auto closest_it = distances.begin(); bool routed = false; - while (closest_it != distances.end() && !routed) { + while (closest_it != distances.end() && !routed && builder.is_valid()) { size_t closest_node_id = closest_it->node_id; Node closest_node = nodes.get(closest_node_id); diff --git a/src/libslic3r/BranchingTree/BranchingTree.hpp b/src/libslic3r/BranchingTree/BranchingTree.hpp index d2eabc7ee..c88410b3a 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.hpp +++ b/src/libslic3r/BranchingTree/BranchingTree.hpp @@ -102,6 +102,9 @@ public: // Report nodes that can not be routed to an endpoint (model or ground) virtual void report_unroutable(const Node &j) = 0; + + // If returns false, the tree building process shall stop + virtual bool is_valid() const { return true; } }; // Build the actual tree. diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index f90ba58fb..3f5e61b87 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -111,6 +111,8 @@ public: { return m_unroutable_pinheads; } + + bool is_valid() const override { return !m_builder.ctl().stopcondition(); } }; bool BranchingTreeBuilder::add_bridge(const branchingtree::Node &from, From de3cbd483dd35ac484a488cc6e3da83ec39773b4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 17:22:57 +0200 Subject: [PATCH 23/29] Try to increase query size with each failed attempt TODO: check performance gain --- src/libslic3r/BranchingTree/BranchingTree.cpp | 14 +++++++----- src/libslic3r/BranchingTree/PointCloud.hpp | 22 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index b101f38aa..d690b2cc4 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -11,14 +11,15 @@ namespace Slic3r { namespace branchingtree { void build_tree(PointCloud &nodes, Builder &builder) { - constexpr size_t ReachablesToExamine = 5; + constexpr size_t initK = 5; auto ptsqueue = nodes.start_queue(); auto &properties = nodes.properties(); struct NodeDistance { size_t node_id = Node::ID_NONE; float distance = NaNf; }; - auto distances = reserve_vector(ReachablesToExamine); + auto distances = reserve_vector(initK); double prev_dist_max = 0.; + size_t K = initK; while (!ptsqueue.empty() && builder.is_valid()) { size_t node_id = ptsqueue.top(); @@ -27,13 +28,13 @@ void build_tree(PointCloud &nodes, Builder &builder) nodes.mark_unreachable(node_id); distances.clear(); + distances.reserve(K); - nodes.foreach_reachable( + nodes.foreach_reachable( node.pos, [&distances](size_t id, float distance) { distances.emplace_back(NodeDistance{id, distance}); - }, - prev_dist_max); + }, K, prev_dist_max); std::sort(distances.begin(), distances.end(), [](auto &a, auto &b) { return a.distance < b.distance; }); @@ -41,11 +42,13 @@ void build_tree(PointCloud &nodes, Builder &builder) if (distances.empty()) { builder.report_unroutable(node); ptsqueue.pop(); + K = initK; prev_dist_max = 0.; continue; } prev_dist_max = distances.back().distance; + K *= 2; auto closest_it = distances.begin(); bool routed = false; @@ -146,6 +149,7 @@ void build_tree(PointCloud &nodes, Builder &builder) if (routed) { ptsqueue.pop(); prev_dist_max = 0.; + K = initK; } } diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index de4afc2a1..e781aad01 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -185,8 +185,11 @@ public: size_t reachable_count() const { return m_reachable_cnt; } - template - void foreach_reachable(const Vec3f &pos, Fn &&visitor, double min_dist = 0.) + template + void foreach_reachable(const Vec3f &pos, + Fn &&visitor, + size_t k, + double min_dist = 0.) { // Fake output iterator struct Output { @@ -203,14 +206,15 @@ public: namespace bgi = boost::geometry::index; - size_t cnt = 0; - auto filter = bgi::satisfies([this, &pos, min_dist, &cnt](const PointIndexEl &e) { - double d = get_distance(pos, e.second); - cnt++; - return m_searchable_indices[e.second] && !isinf(d) && d > min_dist; - }); + size_t cnt = 0; + auto filter = bgi::satisfies( + [this, &pos, min_dist, &cnt](const PointIndexEl &e) { + cnt++; + double d = get_distance(pos, e.second); + return m_searchable_indices[e.second] && !isinf(d) && d > min_dist; + }); - m_ktree.query(bgi::nearest(pos, K) && filter, Output{this, pos, visitor}); + m_ktree.query(bgi::nearest(pos, k) && filter, Output{this, pos, visitor}); } auto start_queue() From 2cb74013be2e244115ba3c6acdc19ed4570f680e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jun 2022 17:23:55 +0200 Subject: [PATCH 24/29] Remove junk --- src/libslic3r/BranchingTree/PointCloud.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index e781aad01..e11c2f851 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -206,10 +206,8 @@ public: namespace bgi = boost::geometry::index; - size_t cnt = 0; auto filter = bgi::satisfies( - [this, &pos, min_dist, &cnt](const PointIndexEl &e) { - cnt++; + [this, &pos, min_dist](const PointIndexEl &e) { double d = get_distance(pos, e.second); return m_searchable_indices[e.second] && !isinf(d) && d > min_dist; }); From 064e9935d1d20ecee35b205feae264391bc9e36c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Jun 2022 18:20:00 +0200 Subject: [PATCH 25/29] Fix dangling pinheads --- src/libslic3r/BranchingTree/BranchingTree.cpp | 41 ++++++++++++------- src/libslic3r/BranchingTree/PointCloud.hpp | 39 ++++++++++++++---- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index d690b2cc4..a01163f48 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -16,13 +16,23 @@ void build_tree(PointCloud &nodes, Builder &builder) auto ptsqueue = nodes.start_queue(); auto &properties = nodes.properties(); - struct NodeDistance { size_t node_id = Node::ID_NONE; float distance = NaNf; }; + struct NodeDistance + { + size_t node_id = Node::ID_NONE; + float dst_branching = NaNf; + float dst_euql = NaNf; + }; auto distances = reserve_vector(initK); double prev_dist_max = 0.; size_t K = initK; + bool routed = true; + size_t node_id = Node::ID_NONE; - while (!ptsqueue.empty() && builder.is_valid()) { - size_t node_id = ptsqueue.top(); + while ((!ptsqueue.empty() && builder.is_valid()) || !routed) { + if (routed) { + node_id = ptsqueue.top(); + ptsqueue.pop(); + } Node node = nodes.get(node_id); nodes.mark_unreachable(node_id); @@ -30,44 +40,47 @@ void build_tree(PointCloud &nodes, Builder &builder) distances.clear(); distances.reserve(K); + float dmax = 0.; nodes.foreach_reachable( node.pos, - [&distances](size_t id, float distance) { - distances.emplace_back(NodeDistance{id, distance}); + [&distances, &dmax](size_t id, float dst_branching, float dst_euql) { + distances.emplace_back(NodeDistance{id, dst_branching, dst_euql}); + dmax = std::max(dmax, dst_euql); }, K, prev_dist_max); std::sort(distances.begin(), distances.end(), - [](auto &a, auto &b) { return a.distance < b.distance; }); + [](auto &a, auto &b) { return a.dst_branching < b.dst_branching; }); if (distances.empty()) { builder.report_unroutable(node); - ptsqueue.pop(); K = initK; prev_dist_max = 0.; + routed = true; + continue; } - prev_dist_max = distances.back().distance; + prev_dist_max = dmax; K *= 2; auto closest_it = distances.begin(); - bool routed = false; + routed = false; while (closest_it != distances.end() && !routed && builder.is_valid()) { size_t closest_node_id = closest_it->node_id; Node closest_node = nodes.get(closest_node_id); auto type = nodes.get_type(closest_node_id); - float w = nodes.get(node_id).weight + closest_it->distance; + float w = nodes.get(node_id).weight + closest_it->dst_branching; closest_node.Rmin = std::max(node.Rmin, closest_node.Rmin); switch (type) { case BED: { closest_node.weight = w; - if (closest_it->distance > nodes.properties().max_branch_length()) { - auto hl_br_len = float(nodes.properties().max_branch_length()) / 2.f; + if (closest_it->dst_branching > nodes.properties().max_branch_length()) { + auto hl_br_len = float(nodes.properties().max_branch_length()) / 2.; Node new_node {{node.pos.x(), node.pos.y(), node.pos.z() - hl_br_len}, node.Rmin}; new_node.id = int(nodes.next_junction_id()); - new_node.weight = nodes.get(node_id).weight + nodes.properties().max_branch_length(); + new_node.weight = nodes.get(node_id).weight + hl_br_len; new_node.left = node.id; if ((routed = builder.add_bridge(node, new_node))) { size_t new_idx = nodes.insert_junction(new_node); @@ -147,11 +160,9 @@ void build_tree(PointCloud &nodes, Builder &builder) } if (routed) { - ptsqueue.pop(); prev_dist_max = 0.; K = initK; } - } } diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index e11c2f851..1ce4739c3 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -29,6 +29,19 @@ std::vector sample_bed(const ExPolygons &bed, enum PtType { LEAF, MESH, BED, JUNCTION, NONE }; +inline BoundingBox3Base get_support_cone_bb(const Vec3f &p, const Properties &props) +{ + double gnd = props.ground_level() - EPSILON; + double h = p.z() - gnd; + double phi = PI / 2 - props.max_slope(); + double r = std::min(h * std::tan(phi), props.max_branch_length() * std::sin(phi)); + + Vec3f bb_min = {p.x() - r, p.y() - r, gnd}; + Vec3f bb_max = {p.x() + r, p.y() + r, p.z()}; + + return {bb_min, bb_max}; +} + // A cloud of points including support points, mesh points, junction points // and anchor points on the bed. Junction points can be added or removed, all // the other point types are established on creation and remain unchangeable. @@ -199,20 +212,32 @@ public: Output& operator *() { return *this; } void operator=(const PointIndexEl &el) { - visitorfn(el.second, self->get_distance(p, el.second)); + visitorfn(el.second, self->get_distance(p, el.second), + (p - el.first).squaredNorm()); } Output& operator++() { return *this; } }; namespace bgi = boost::geometry::index; + float brln = 2 * m_props.max_branch_length(); + BoundingBox3Base bb{{pos.x() - brln, pos.y() - brln, + m_props.ground_level() - EPSILON}, + {pos.x() + brln, pos.y() + brln, + m_ktree.bounds().max_corner().get()}}; - auto filter = bgi::satisfies( - [this, &pos, min_dist](const PointIndexEl &e) { - double d = get_distance(pos, e.second); - return m_searchable_indices[e.second] && !isinf(d) && d > min_dist; - }); + // Extend upwards to find mergable junctions and support points + bb.max.z() = m_ktree.bounds().max_corner().get(); - m_ktree.query(bgi::nearest(pos, k) && filter, Output{this, pos, visitor}); + auto filter = bgi::satisfies( + [this, &pos, min_dist](const PointIndexEl &e) { + double D_branching = get_distance(pos, e.second); + double D_euql = (pos - e.first).squaredNorm() ; + return m_searchable_indices[e.second] && + !std::isinf(D_branching) && D_euql > min_dist; + }); + + m_ktree.query(bgi::intersects(bb) && filter && bgi::nearest(pos, k), + Output{this, pos, visitor}); } auto start_queue() From bf1303b9cf24460ba6d5659c8a3ba442dd2d5ddd Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jun 2022 16:32:07 +0200 Subject: [PATCH 26/29] Fix double parallelization --- src/libslic3r/SLA/BranchingTreeSLA.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/BranchingTreeSLA.cpp b/src/libslic3r/SLA/BranchingTreeSLA.cpp index 3f5e61b87..72314c9d8 100644 --- a/src/libslic3r/SLA/BranchingTreeSLA.cpp +++ b/src/libslic3r/SLA/BranchingTreeSLA.cpp @@ -210,7 +210,7 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s ex_tbb, size_t(0), nondup_idx.size(), [&sm, &heads, &nondup_idx, &builder](size_t i) { if (!builder.ctl().stopcondition()) - heads[i] = calculate_pinhead_placement(ex_tbb, sm, nondup_idx[i]); + heads[i] = calculate_pinhead_placement(ex_seq, sm, nondup_idx[i]); }, execution::max_concurrency(ex_tbb) ); From f3d4a9072173e3f1323db2e544430cf0f0113a35 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jun 2022 16:52:59 +0200 Subject: [PATCH 27/29] Fixes to compile on MSVC --- src/libslic3r/BranchingTree/BranchingTree.cpp | 2 +- src/libslic3r/BranchingTree/PointCloud.hpp | 6 +++--- tests/sla_print/sla_print_tests.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/BranchingTree/BranchingTree.cpp b/src/libslic3r/BranchingTree/BranchingTree.cpp index a01163f48..6463a30de 100644 --- a/src/libslic3r/BranchingTree/BranchingTree.cpp +++ b/src/libslic3r/BranchingTree/BranchingTree.cpp @@ -77,7 +77,7 @@ void build_tree(PointCloud &nodes, Builder &builder) case BED: { closest_node.weight = w; if (closest_it->dst_branching > nodes.properties().max_branch_length()) { - auto hl_br_len = float(nodes.properties().max_branch_length()) / 2.; + auto hl_br_len = float(nodes.properties().max_branch_length()) / 2.f; Node new_node {{node.pos.x(), node.pos.y(), node.pos.z() - hl_br_len}, node.Rmin}; new_node.id = int(nodes.next_junction_id()); new_node.weight = nodes.get(node_id).weight + hl_br_len; diff --git a/src/libslic3r/BranchingTree/PointCloud.hpp b/src/libslic3r/BranchingTree/PointCloud.hpp index 1ce4739c3..f99b17990 100644 --- a/src/libslic3r/BranchingTree/PointCloud.hpp +++ b/src/libslic3r/BranchingTree/PointCloud.hpp @@ -34,9 +34,9 @@ inline BoundingBox3Base get_support_cone_bb(const Vec3f &p, const Propert double gnd = props.ground_level() - EPSILON; double h = p.z() - gnd; double phi = PI / 2 - props.max_slope(); - double r = std::min(h * std::tan(phi), props.max_branch_length() * std::sin(phi)); + auto r = float(std::min(h * std::tan(phi), props.max_branch_length() * std::sin(phi))); - Vec3f bb_min = {p.x() - r, p.y() - r, gnd}; + Vec3f bb_min = {p.x() - r, p.y() - r, float(gnd)}; Vec3f bb_max = {p.x() + r, p.y() + r, p.z()}; return {bb_min, bb_max}; @@ -221,7 +221,7 @@ public: namespace bgi = boost::geometry::index; float brln = 2 * m_props.max_branch_length(); BoundingBox3Base bb{{pos.x() - brln, pos.y() - brln, - m_props.ground_level() - EPSILON}, + float(m_props.ground_level() - EPSILON)}, {pos.x() + brln, pos.y() + brln, m_ktree.bounds().max_corner().get()}}; diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 273c3f8f6..4670bf209 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -185,7 +185,7 @@ bool is_outside_support_cone(const Vec3f &supp, const Vec3f &pt, float angle) TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branching]") { SECTION("Identical points have the same merge point") { Vec3f a{0.f, 0.f, 0.f}, b = a; - float slope = PI / 4.f; + auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); @@ -200,7 +200,7 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi // | b * <= mergept SECTION("Points at different heights have the lower point as mergepoint") { Vec3f a{0.f, 0.f, 0.f}, b = {0.f, 0.f, -1.f}; - float slope = PI / 4.f; + auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); @@ -214,7 +214,7 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi // * <= mergept SECTION("Points at different X have mergept in the middle at lower Z") { Vec3f a{0.f, 0.f, 0.f}, b = {1.f, 0.f, 0.f}; - float slope = PI / 4.f; + auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); @@ -234,7 +234,7 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi // * <= mergept SECTION("Points at different Y have mergept in the middle at lower Z") { Vec3f a{0.f, 0.f, 0.f}, b = {0.f, 1.f, 0.f}; - float slope = PI / 4.f; + auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); @@ -250,7 +250,7 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi SECTION("Points separated by less than critical angle have the lower point as mergept") { Vec3f a{0.f, 0.f, 0.f}, b = {-0.5f, -0.5f, -1.f}; - float slope = PI / 4.f; + auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); From a55be295685d899988e7ba1bdc7d9d6a6ce6cdef Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Jun 2022 12:35:43 +0200 Subject: [PATCH 28/29] Fix failing tests for merge point search Improvements and comments to find_merge_pt --- src/libslic3r/BranchingTree/PointCloud.cpp | 73 ++++++++++++++++------ src/libslic3r/Geometry.hpp | 12 ++-- tests/sla_print/sla_print_tests.cpp | 6 +- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index b5b6893c8..e6fbbf0eb 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -9,32 +9,65 @@ namespace Slic3r { namespace branchingtree { std::optional find_merge_pt(const Vec3f &A, const Vec3f &B, - float max_slope) + float critical_angle) { - Vec3f Da = (B - A).normalized(), Db = -Da; - auto [polar_da, azim_da] = Geometry::dir_to_spheric(Da); - auto [polar_db, azim_db] = Geometry::dir_to_spheric(Db); - polar_da = std::max(polar_da, float(PI) / 2.f + max_slope); - polar_db = std::max(polar_db, float(PI) / 2.f + max_slope); + // The idea is that A and B both have their support cones. But searching + // for the intersection of these support cones is difficult and its enough + // to reduce this problem to 2D and search for the intersection of two + // rays that merge somewhere between A and B. The 2D plane is a vertical + // slice of the 3D scene where the X axis is determined by the XY direction + // of the AB vector. + // + // Z^ + // | A * + // | . . B * + // | . . . . + // | . . . . + // | . x . + // -------------------> X - Da = Geometry::spheric_to_dir(polar_da, azim_da); - Db = Geometry::spheric_to_dir(polar_db, azim_db); + // Determine the transformation matrix for the 2D projection: + Vec3f diff = {B.x() - A.x(), B.y() - A.y(), 0.f}; + Vec3f dir = diff.normalized(); // TODO: avoid normalization - // This formula is based on + Eigen::Matrix tr2D; + tr2D.row(0) = Vec3f{dir.x(), dir.y(), dir.z()}; + tr2D.row(1) = Vec3f{0.f, 0.f, 1.f}; + + // Transform the 2 vectors A and B into 2D vector 'a' and 'b'. Here we can + // omit 'a', pretend that its the origin and use BA as the vector b. + Vec2f b = tr2D * (B - A); + + // Get the slope of the ray emanating from 'a' towards 'b'. This ray might + // exceed the allowed angle but that is corrected subsequently. + // if b.x() is 0, slope is (+/-) pi/2 depending on b.y() sign + float slope_a = std::atan2(b.y(), b.x()); + + // slope from 'b' to 'a' is the opposite of slope_a, the angle will also + // be corrected later. + float slope_b = -slope_a; + + // Derive the allowed angles from the given critical angle. + // critical_angle is measured from the horizontal X axis. + // The rays need to go downwards which corresponds to negative angles + + float min_angle = critical_angle - float(PI) / 2.f; + slope_a = std::min(slope_a, min_angle); + slope_b = std::min(slope_b, min_angle); + Vec2f Da = {std::cos(slope_a), std::sin(slope_a)}; + Vec2f Db = {-std::cos(slope_b), std::sin(slope_b)}; + + // Determine where two rays ([0, 0], Da), (b, Db) intersect. + // Based on // https://stackoverflow.com/questions/27459080/given-two-points-and-two-direction-vectors-find-the-point-where-they-intersect - double t1 = - (A.z() * Db.x() + Db.z() * B.x() - B.z() * Db.x() - Db.z() * A.x()) / - (Da.x() * Db.z() - Da.z() * Db.x()); + // One ray is emanating from (0, 0) so the formula is simplified + double t1 = (Db.y() * b.x() - b.y() * Db.x()) / + (Da.x() * Db.y() - Da.y() * Db.x()); - if (std::isnan(t1) || std::abs(t1) < EPSILON) - t1 = (A.z() * Db.y() + Db.z() * B.y() - B.z() * Db.y() - Db.z() * A.y()) / - (Da.y() * Db.z() - Da.z() * Db.y()); + Vec2f mp = t1 * Da; + Vec3f Mp = A + tr2D.transpose() * mp; - Vec3f m1 = A + t1 * Da; - - double t2 = (m1.z() - B.z()) / Db.z(); - - return t1 >= 0. && t2 >= 0. ? m1 : std::optional{}; + return t1 >= 0.f ? Mp : Vec3f{}; } void to_eigen_mesh(const indexed_triangle_set &its, diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c04bdf07a..d0ae84d86 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -560,13 +560,13 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); } -template -std::pair dir_to_spheric(const Vec<3, T> &n, T norm = 1.) +template +std::pair dir_to_spheric(const Vec<3, Tin> &n, Tout norm = 1.) { - T z = n.z(); - T r = norm; - T polar = std::acos(z / r); - T azimuth = std::atan2(n(1), n(0)); + Tout z = n.z(); + Tout r = norm; + Tout polar = std::acos(z / r); + Tout azimuth = std::atan2(n(1), n(0)); return {polar, azimuth}; } diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 4670bf209..79e9ce67a 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -205,7 +205,7 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi auto mergept = branchingtree::find_merge_pt(a, b, slope); REQUIRE(bool(mergept)); - REQUIRE((*mergept - b).squaredNorm() < EPSILON); + REQUIRE((*mergept - b).squaredNorm() < 2 * EPSILON); } // -|---------> X @@ -249,13 +249,13 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi } SECTION("Points separated by less than critical angle have the lower point as mergept") { - Vec3f a{0.f, 0.f, 0.f}, b = {-0.5f, -0.5f, -1.f}; + Vec3f a{-1.f, -1.f, -1.f}, b = {-1.5f, -1.5f, -2.f}; auto slope = float(PI / 4.); auto mergept = branchingtree::find_merge_pt(a, b, slope); REQUIRE(bool(mergept)); - REQUIRE((*mergept - b).norm() < EPSILON); + REQUIRE((*mergept - b).norm() < 2 * EPSILON); } } From b45bb84fa6c4c2e5aeb3322576a7973e91c506eb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Jun 2022 15:48:45 +0200 Subject: [PATCH 29/29] Another fix to find_merge_pt --- src/libslic3r/BranchingTree/PointCloud.cpp | 37 ++++++++++++++-------- tests/sla_print/sla_print_tests.cpp | 15 +++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/BranchingTree/PointCloud.cpp b/src/libslic3r/BranchingTree/PointCloud.cpp index e6fbbf0eb..f1d7ae521 100644 --- a/src/libslic3r/BranchingTree/PointCloud.cpp +++ b/src/libslic3r/BranchingTree/PointCloud.cpp @@ -15,8 +15,8 @@ std::optional find_merge_pt(const Vec3f &A, // for the intersection of these support cones is difficult and its enough // to reduce this problem to 2D and search for the intersection of two // rays that merge somewhere between A and B. The 2D plane is a vertical - // slice of the 3D scene where the X axis is determined by the XY direction - // of the AB vector. + // slice of the 3D scene where the 2D Y axis is equal to the 3D Z axis and + // the 2D X axis is determined by the XY direction of the AB vector. // // Z^ // | A * @@ -24,7 +24,7 @@ std::optional find_merge_pt(const Vec3f &A, // | . . . . // | . . . . // | . x . - // -------------------> X + // -------------------> XY // Determine the transformation matrix for the 2D projection: Vec3f diff = {B.x() - A.x(), B.y() - A.y(), 0.f}; @@ -38,24 +38,33 @@ std::optional find_merge_pt(const Vec3f &A, // omit 'a', pretend that its the origin and use BA as the vector b. Vec2f b = tr2D * (B - A); - // Get the slope of the ray emanating from 'a' towards 'b'. This ray might + // Get the square sine of the ray emanating from 'a' towards 'b'. This ray might // exceed the allowed angle but that is corrected subsequently. - // if b.x() is 0, slope is (+/-) pi/2 depending on b.y() sign - float slope_a = std::atan2(b.y(), b.x()); + // The sign of the original sine is also needed, hence b.y is multiplied by + // abs(b.y) + float b_sqn = b.squaredNorm(); + float sin2sig_a = b_sqn > EPSILON ? (b.y() * std::abs(b.y())) / b_sqn : 0.f; - // slope from 'b' to 'a' is the opposite of slope_a, the angle will also - // be corrected later. - float slope_b = -slope_a; + // sine2 from 'b' to 'a' is the opposite of sine2 from a to b + float sin2sig_b = -sin2sig_a; // Derive the allowed angles from the given critical angle. // critical_angle is measured from the horizontal X axis. // The rays need to go downwards which corresponds to negative angles - float min_angle = critical_angle - float(PI) / 2.f; - slope_a = std::min(slope_a, min_angle); - slope_b = std::min(slope_b, min_angle); - Vec2f Da = {std::cos(slope_a), std::sin(slope_a)}; - Vec2f Db = {-std::cos(slope_b), std::sin(slope_b)}; + float sincrit = std::sin(critical_angle); // sine of the critical angle + float sin2crit = -sincrit * sincrit; // signed sine squared + sin2sig_a = std::min(sin2sig_a, sin2crit); // Do the angle saturation of both rays + sin2sig_b = std::min(sin2sig_b, sin2crit); // + float sin2_a = std::abs(sin2sig_a); // Get cosine squared values + float sin2_b = std::abs(sin2sig_b); + float cos2_a = 1.f - sin2_a; + float cos2_b = 1.f - sin2_b; + + // Derive the new direction vectors. This is by square rooting the sin2 + // and cos2 values and restoring the original signs + Vec2f Da = {std::copysign(std::sqrt(cos2_a), b.x()), std::copysign(std::sqrt(sin2_a), sin2sig_a)}; + Vec2f Db = {-std::copysign(std::sqrt(cos2_b), b.x()), std::copysign(std::sqrt(sin2_b), sin2sig_b)}; // Determine where two rays ([0, 0], Da), (b, Db) intersect. // Based on diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 79e9ce67a..8ea91d57a 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -257,6 +257,21 @@ TEST_CASE("BranchingSupports::MergePointFinder", "[SLASupportGeneration][Branchi REQUIRE(bool(mergept)); REQUIRE((*mergept - b).norm() < 2 * EPSILON); } + + // -|----------------------------> Y + // a b + // * * <= mergept * + // + SECTION("Points at same height have mergepoint in the middle if critical angle is zero ") { + Vec3f a{-1.f, -1.f, -1.f}, b = {-1.5f, -1.5f, -1.f}; + auto slope = EPSILON; + + auto mergept = branchingtree::find_merge_pt(a, b, slope); + + REQUIRE(bool(mergept)); + Vec3f middle = (b + a) / 2.; + REQUIRE((*mergept - middle).norm() < 4 * EPSILON); + } } TEST_CASE("BranchingSupports::ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration][Branching]") {