Upgrade support tree route search functions, add tests
This commit is contained in:
parent
4dc0741766
commit
1e9bd28714
@ -20,6 +20,9 @@ class Properties
|
||||
ExPolygons m_bed_shape;
|
||||
|
||||
public:
|
||||
|
||||
constexpr bool group_pillars() const noexcept { return false; }
|
||||
|
||||
// Maximum slope for bridges of the tree
|
||||
Properties &max_slope(double val) noexcept
|
||||
{
|
||||
|
@ -356,6 +356,8 @@ set(SLIC3R_SOURCES
|
||||
BranchingTree/BranchingTree.hpp
|
||||
BranchingTree/PointCloud.cpp
|
||||
BranchingTree/PointCloud.hpp
|
||||
OrganicTree/OrganicTree.hpp
|
||||
OrganicTree/OrganicTreeImpl.hpp
|
||||
|
||||
Arachne/BeadingStrategy/BeadingStrategy.hpp
|
||||
Arachne/BeadingStrategy/BeadingStrategy.cpp
|
||||
|
@ -13,7 +13,9 @@ template<size_t N>
|
||||
long num_iter(const std::array<size_t, N> &idx, size_t gridsz)
|
||||
{
|
||||
long ret = 0;
|
||||
for (size_t i = 0; i < N; ++i) ret += idx[i] * std::pow(gridsz, i);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
ret += idx[i] * std::pow(gridsz, i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ public:
|
||||
return optimize(nl, std::forward<Func>(func), initvals);
|
||||
}
|
||||
|
||||
explicit NLoptOpt(StopCriteria stopcr = {}) : m_stopcr(stopcr) {}
|
||||
explicit NLoptOpt(const StopCriteria &stopcr = {}) : m_stopcr(stopcr) {}
|
||||
|
||||
void set_criteria(const StopCriteria &cr) { m_stopcr = cr; }
|
||||
const StopCriteria &get_criteria() const noexcept { return m_stopcr; }
|
||||
@ -226,7 +226,7 @@ using AlgNLoptGenetic = detail::NLoptAlgComb<NLOPT_GN_ESCH>;
|
||||
using AlgNLoptSubplex = detail::NLoptAlg<NLOPT_LN_SBPLX>;
|
||||
using AlgNLoptSimplex = detail::NLoptAlg<NLOPT_LN_NELDERMEAD>;
|
||||
using AlgNLoptDIRECT = detail::NLoptAlg<NLOPT_GN_DIRECT>;
|
||||
using AlgNLoptMLSL = detail::NLoptAlg<NLOPT_GN_MLSL>;
|
||||
using AlgNLoptMLSL = detail::NLoptAlgComb<NLOPT_GN_MLSL, NLOPT_LN_SBPLX>;
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
|
95
src/libslic3r/OrganicTree/OrganicTree.hpp
Normal file
95
src/libslic3r/OrganicTree/OrganicTree.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
#ifndef ORGANICTREE_HPP
|
||||
#define ORGANICTREE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r { namespace organictree {
|
||||
|
||||
enum class NodeType { Bed, Mesh, Junction };
|
||||
|
||||
template <class T> struct DomainTraits_ {
|
||||
using Node = typename T::Node;
|
||||
|
||||
static void push(const T &dom, const Node &n)
|
||||
{
|
||||
dom.push_junction(n);
|
||||
}
|
||||
|
||||
static Node pop(T &dom) { return dom.pop(); }
|
||||
|
||||
static bool empty(const T &dom) { return dom.empty(); }
|
||||
|
||||
static std::optional<std::pair<Node, NodeType>>
|
||||
closest(const T &dom, const Node &n)
|
||||
{
|
||||
return dom.closest(n);
|
||||
}
|
||||
|
||||
static Node merge_node(const T &dom, const Node &a, const Node &b)
|
||||
{
|
||||
return dom.merge_node(a, b);
|
||||
}
|
||||
|
||||
static void bridge(T &dom, const Node &from, const Node &to)
|
||||
{
|
||||
dom.bridge(from, to);
|
||||
}
|
||||
|
||||
static void anchor(T &dom, const Node &from, const Node &to)
|
||||
{
|
||||
dom.anchor(from, to);
|
||||
}
|
||||
|
||||
static void pillar(T &dom, const Node &from, const Node &to)
|
||||
{
|
||||
dom.pillar(from, to);
|
||||
}
|
||||
|
||||
static void merge (T &dom, const Node &n1, const Node &n2, const Node &mrg)
|
||||
{
|
||||
dom.merge(n1, n2, mrg);
|
||||
}
|
||||
|
||||
static void report_fail(T &dom, const Node &n) { dom.report_fail(n); }
|
||||
};
|
||||
|
||||
template<class Domain>
|
||||
void build_tree(Domain &&D)
|
||||
{
|
||||
using Dom = DomainTraits_<std::remove_cv_t<std::remove_reference_t<Domain>>>;
|
||||
using Node = typename Dom::Node;
|
||||
|
||||
while (! Dom::empty(D)) {
|
||||
Node n = Dom::pop(D);
|
||||
|
||||
std::optional<std::pair<Node, NodeType>> C = Dom::closest(D, n);
|
||||
|
||||
if (!C) {
|
||||
Dom::report_fail(D, n);
|
||||
} else switch (C->second) {
|
||||
case NodeType::Bed:
|
||||
Dom::pillar(D, n, C->first);
|
||||
break;
|
||||
case NodeType::Mesh:
|
||||
Dom::anchor(D, n, C->first);
|
||||
break;
|
||||
case NodeType::Junction: {
|
||||
Node M = Dom::merge_node(D, n, C->first);
|
||||
|
||||
if (M == C->first) {
|
||||
Dom::bridge(D, n, C->first);
|
||||
} else {
|
||||
Dom::push(D, M);
|
||||
Dom::merge(D, n, M, C->first);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::organictree
|
||||
|
||||
#endif // ORGANICTREE_HPP
|
11
src/libslic3r/OrganicTree/OrganicTreeImpl.hpp
Normal file
11
src/libslic3r/OrganicTree/OrganicTreeImpl.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef ORGANICTREEIMPL_HPP
|
||||
#define ORGANICTREEIMPL_HPP
|
||||
|
||||
namespace Slic3r { namespace organictree {
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#endif // ORGANICTREEIMPL_HPP
|
@ -253,13 +253,11 @@ bool BranchingTreeBuilder::add_ground_bridge(const branchingtree::Node &from,
|
||||
// if (!result) {
|
||||
auto conn = optimize_ground_connection(
|
||||
ex_tbb,
|
||||
m_builder,
|
||||
m_sm,
|
||||
j,
|
||||
get_radius(to));
|
||||
|
||||
if (conn) {
|
||||
// build_ground_connection(m_builder, m_sm, conn);
|
||||
// Junction connlast = conn.path.back();
|
||||
// branchingtree::Node n{connlast.pos.cast<float>(), float(connlast.r)};
|
||||
// n.left = from.id;
|
||||
@ -321,6 +319,17 @@ bool BranchingTreeBuilder::add_mesh_bridge(const branchingtree::Node &from,
|
||||
return bool(anchor);
|
||||
}
|
||||
|
||||
inline void build_pillars(SupportTreeBuilder &builder,
|
||||
BranchingTreeBuilder &vbuilder,
|
||||
const SupportableMesh &sm)
|
||||
{
|
||||
for (size_t pill_id = 0; pill_id < vbuilder.pillars().size(); ++pill_id) {
|
||||
auto * conn = vbuilder.ground_conn(pill_id);
|
||||
if (conn)
|
||||
build_ground_connection(builder, sm, *conn);
|
||||
}
|
||||
}
|
||||
|
||||
void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &sm)
|
||||
{
|
||||
auto coordfn = [&sm](size_t id, size_t dim) { return sm.pts[id].pos(dim); };
|
||||
@ -373,6 +382,8 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s
|
||||
BranchingTreeBuilder vbuilder{builder, sm, nodes};
|
||||
branchingtree::build_tree(nodes, vbuilder);
|
||||
|
||||
if constexpr (props.group_pillars()) {
|
||||
|
||||
std::vector<branchingtree::Node> bedleafs;
|
||||
for (auto n : vbuilder.pillars()) {
|
||||
n.left = branchingtree::Node::ID_NONE;
|
||||
@ -380,27 +391,13 @@ void create_branching_tree(SupportTreeBuilder &builder, const SupportableMesh &s
|
||||
bedleafs.emplace_back(n);
|
||||
}
|
||||
|
||||
props.max_branch_length(50.f);
|
||||
auto gndsm = sm;
|
||||
branchingtree::PointCloud gndnodes{{}, nodes.get_bedpoints(), bedleafs, props};
|
||||
BranchingTreeBuilder gndbuilder{builder, sm, gndnodes};
|
||||
branchingtree::build_tree(gndnodes, gndbuilder);
|
||||
|
||||
// All leafs of gndbuilder are nodes that already proved to be routable
|
||||
// to the ground. gndbuilder should not encounter any unroutable nodes
|
||||
// assert(gndbuilder.unroutable_pinheads().empty());
|
||||
|
||||
|
||||
// for (size_t pill_id = 0; pill_id < vbuilder.pillars().size(); ++pill_id) {
|
||||
// auto * conn = vbuilder.ground_conn(pill_id);
|
||||
// if (conn)
|
||||
// build_ground_connection(builder, sm, *conn);
|
||||
// }
|
||||
|
||||
for (size_t pill_id = 0; pill_id < gndbuilder.pillars().size(); ++pill_id) {
|
||||
auto * conn = gndbuilder.ground_conn(pill_id);
|
||||
if (conn)
|
||||
build_ground_connection(builder, sm, *conn);
|
||||
build_pillars(builder, gndbuilder, sm);
|
||||
} else {
|
||||
build_pillars(builder, vbuilder, sm);
|
||||
}
|
||||
|
||||
for (size_t id : vbuilder.unroutable_pinheads())
|
||||
|
@ -96,8 +96,8 @@ struct SupportTreeConfig
|
||||
|
||||
static const double constexpr max_solo_pillar_height_mm = 15.0;
|
||||
static const double constexpr max_dual_pillar_height_mm = 35.0;
|
||||
static const double constexpr optimizer_rel_score_diff = 1e-16;
|
||||
static const unsigned constexpr optimizer_max_iterations = 20000;
|
||||
static const double constexpr optimizer_rel_score_diff = 1e-10;
|
||||
static const unsigned constexpr optimizer_max_iterations = 2000;
|
||||
static const unsigned constexpr pillar_cascade_neighbors = 3;
|
||||
|
||||
};
|
||||
|
@ -165,7 +165,8 @@ indexed_triangle_set halfcone(double baseheight,
|
||||
{
|
||||
assert(steps > 0);
|
||||
|
||||
if (baseheight <= 0 || steps <= 0) return {};
|
||||
if (baseheight <= 0 || steps <= 0 || (r_bottom <= 0. && r_top <= 0.))
|
||||
return {};
|
||||
|
||||
indexed_triangle_set base;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <libslic3r/Execution/Execution.hpp>
|
||||
#include <libslic3r/Optimize/NLoptOptimizer.hpp>
|
||||
#include <libslic3r/Optimize/BruteforceOptimizer.hpp>
|
||||
#include <libslic3r/MeshNormals.hpp>
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
#include <libslic3r/SLA/SupportTreeBuilder.hpp>
|
||||
@ -644,10 +645,9 @@ struct GroundConnection {
|
||||
static constexpr size_t MaxExpectedJunctions = 3;
|
||||
|
||||
boost::container::small_vector<Junction, MaxExpectedJunctions> path;
|
||||
double end_radius;
|
||||
std::optional<Pedestal> pillar_base;
|
||||
|
||||
operator bool() const { return !path.empty(); }
|
||||
operator bool() const { return pillar_base.has_value() && !path.empty(); }
|
||||
};
|
||||
|
||||
template<class Ex>
|
||||
@ -659,6 +659,8 @@ GroundConnection find_pillar_route(Ex policy,
|
||||
{
|
||||
GroundConnection ret;
|
||||
|
||||
ret.path.emplace_back(source);
|
||||
|
||||
Vec3d jp = source.pos, endp = jp, dir = sourcedir;
|
||||
bool can_add_base = false/*, non_head = false*/;
|
||||
|
||||
@ -666,6 +668,7 @@ GroundConnection find_pillar_route(Ex policy,
|
||||
double jp_gnd = 0.; // The lowest Z where a junction center can be
|
||||
double gap_dist = 0.; // The gap distance between the model and the pad
|
||||
double radius = source.r;
|
||||
double sd = sm.cfg.safety_distance_mm;
|
||||
|
||||
double r2 = radius + (end_radius - radius) / (jp.z() - ground_level(sm));
|
||||
|
||||
@ -692,7 +695,6 @@ GroundConnection find_pillar_route(Ex policy,
|
||||
sm.cfg.head_back_radius_mm);
|
||||
|
||||
if (diffbr && diffbr->endp.z() > jp_gnd) {
|
||||
ret.path.emplace_back(source);
|
||||
endp = diffbr->endp;
|
||||
radius = diffbr->end_r;
|
||||
ret.path.emplace_back(endp, radius);
|
||||
@ -709,7 +711,6 @@ GroundConnection find_pillar_route(Ex policy,
|
||||
auto [polar, azimuth] = dir_to_spheric(dir);
|
||||
polar = PI - sm.cfg.bridge_slope;
|
||||
Vec3d d = spheric_to_dir(polar, azimuth).normalized();
|
||||
auto sd = sm.cfg.safety_distance_mm; //radius * sm.cfg.safety_distance_mm / sm.cfg.head_back_radius_mm;
|
||||
double t = beam_mesh_hit(policy, sm.emesh, Beam{endp, d, radius, r2}, sd).distance();
|
||||
double tmax = std::min(sm.cfg.max_bridge_length_mm, t);
|
||||
t = 0.;
|
||||
@ -759,15 +760,19 @@ GroundConnection find_pillar_route(Ex policy,
|
||||
}
|
||||
|
||||
Vec3d gp = to_floor(endp);
|
||||
auto hit = beam_mesh_hit(policy, sm.emesh,
|
||||
Beam{{endp, radius}, {gp, end_radius}}, sd);
|
||||
|
||||
ret.end_radius = end_radius;
|
||||
if (std::isinf(hit.distance())) {
|
||||
double base_radius = can_add_base ?
|
||||
std::max(sm.cfg.base_radius_mm, end_radius) : end_radius;
|
||||
|
||||
if (can_add_base) {
|
||||
Vec3d gp = to_floor(endp);
|
||||
ret.pillar_base =
|
||||
Pedestal{gp, sm.cfg.base_height_mm, sm.cfg.base_radius_mm, end_radius};
|
||||
Pedestal{gp, sm.cfg.base_height_mm, base_radius, end_radius};
|
||||
}
|
||||
|
||||
return ret; //{true, pillar_id};
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline long build_ground_connection(SupportTreeBuilder &builder,
|
||||
@ -798,9 +803,8 @@ inline long build_ground_connection(SupportTreeBuilder &builder,
|
||||
// long head_id = std::abs(conn.path.back().id);
|
||||
// ret = builder.add_pillar(head_id, h);
|
||||
// } else
|
||||
ret = builder.add_pillar(gp, h, conn.path.back().r, conn.end_radius);
|
||||
ret = builder.add_pillar(gp, h, conn.path.back().r, conn.pillar_base->r_top);
|
||||
|
||||
if (conn.pillar_base)
|
||||
builder.add_pillar_base(ret, conn.pillar_base->height, conn.pillar_base->r_bottom);
|
||||
|
||||
return ret;
|
||||
@ -809,7 +813,6 @@ inline long build_ground_connection(SupportTreeBuilder &builder,
|
||||
template<class Ex>
|
||||
GroundConnection find_ground_connection(
|
||||
Ex policy,
|
||||
SupportTreeBuilder &builder,
|
||||
const SupportableMesh &sm,
|
||||
const Junction &j,
|
||||
const Vec3d &dir,
|
||||
@ -817,39 +820,36 @@ GroundConnection find_ground_connection(
|
||||
{
|
||||
auto hjp = j.pos;
|
||||
double r = j.r;
|
||||
auto sd = sm.cfg.safety_distance_mm; //r * sm.cfg.safety_distance_mm / sm.cfg.head_back_radius_mm;
|
||||
auto sd = sm.cfg.safety_distance_mm;
|
||||
double r2 = j.r + (end_r - j.r) / (j.pos.z() - ground_level(sm));
|
||||
|
||||
double t = beam_mesh_hit(policy, sm.emesh, Beam{hjp, dir, r, r2}, sd)
|
||||
.distance();
|
||||
double d = 0, tdown = 0;
|
||||
t = std::min(t,
|
||||
sm.cfg.max_bridge_length_mm * r / sm.cfg.head_back_radius_mm);
|
||||
double t = beam_mesh_hit(policy, sm.emesh, Beam{hjp, dir, r, r2}, sd).distance();
|
||||
t = std::min(t, sm.cfg.max_bridge_length_mm);
|
||||
double d = 0.;
|
||||
|
||||
GroundConnection gnd_route;
|
||||
|
||||
while (!gnd_route && d < t) {
|
||||
Vec3d endp = hjp + d * dir;
|
||||
double bridge_ratio = d / (d + (endp.z() - sm.emesh.ground_level()));
|
||||
double pill_r = r + bridge_ratio * (end_r - r);
|
||||
gnd_route = find_pillar_route(policy, sm, {endp, pill_r}, dir, end_r);
|
||||
|
||||
while (
|
||||
d < t &&
|
||||
!std::isinf(tdown = beam_mesh_hit(policy, sm.emesh,
|
||||
Beam{hjp + d * dir, DOWN, r, r2}, sd)
|
||||
.distance())) {
|
||||
d += r;
|
||||
}
|
||||
|
||||
GroundConnection ret;
|
||||
ret.end_radius = end_r;
|
||||
|
||||
if (std::isinf(tdown)) {
|
||||
ret.path.emplace_back(j);
|
||||
Vec3d endp = hjp + d * dir;
|
||||
double bridge_ratio = d / (d + (endp.z() - sm.emesh.ground_level()));
|
||||
double pill_r = r + bridge_ratio * (end_r - r);
|
||||
|
||||
auto route = find_pillar_route(policy, sm, {endp, pill_r}, dir, end_r);
|
||||
for (auto &j : route.path)
|
||||
if (d > 0.)
|
||||
ret.path.emplace_back(j);
|
||||
|
||||
ret.pillar_base = route.pillar_base;
|
||||
ret.end_radius = end_r;
|
||||
}
|
||||
for (auto &p : gnd_route.path)
|
||||
ret.path.emplace_back(p);
|
||||
|
||||
// This will ultimately determine if the route is valid or not
|
||||
// but the path junctions will be provided anyways, so invalid paths
|
||||
// can be debugged
|
||||
ret.pillar_base = gnd_route.pillar_base;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -857,7 +857,6 @@ GroundConnection find_ground_connection(
|
||||
template<class Ex>
|
||||
GroundConnection optimize_ground_connection(
|
||||
Ex policy,
|
||||
SupportTreeBuilder &builder,
|
||||
const SupportableMesh &sm,
|
||||
const Junction &j,
|
||||
double end_radius,
|
||||
@ -865,8 +864,8 @@ GroundConnection optimize_ground_connection(
|
||||
{
|
||||
double downdst = j.pos.z() - ground_level(sm);
|
||||
|
||||
auto res = find_ground_connection(policy, builder, sm, j, init_dir, end_radius);
|
||||
if (!res)
|
||||
auto res = find_ground_connection(policy, sm, j, init_dir, end_radius);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
// Optimize bridge direction:
|
||||
@ -874,7 +873,7 @@ GroundConnection optimize_ground_connection(
|
||||
// direction out of the cavity.
|
||||
auto [polar, azimuth] = dir_to_spheric(init_dir);
|
||||
|
||||
Optimizer<AlgNLoptGenetic> solver(get_criteria(sm.cfg).stop_score(1e6));
|
||||
Optimizer<opt::AlgNLoptMLSL> solver(get_criteria(sm.cfg).stop_score(1e6));
|
||||
solver.seed(0); // we want deterministic behavior
|
||||
|
||||
auto sd = /*j.r **/ sm.cfg.safety_distance_mm /*/ sm.cfg.head_back_radius_mm*/;
|
||||
@ -891,7 +890,7 @@ GroundConnection optimize_ground_connection(
|
||||
|
||||
Vec3d bridgedir = spheric_to_dir(oresult.optimum).normalized();
|
||||
|
||||
return find_ground_connection(policy, builder, sm, j, bridgedir, end_radius);
|
||||
return find_ground_connection(policy, sm, j, bridgedir, end_radius);
|
||||
}
|
||||
|
||||
template<class Ex>
|
||||
@ -904,7 +903,7 @@ std::pair<bool, long> connect_to_ground(Ex policy,
|
||||
{
|
||||
std::pair<bool, long> ret = {false, SupportTreeNode::ID_UNSET};
|
||||
|
||||
auto conn = find_ground_connection(policy, builder, sm, j, dir, end_r);
|
||||
auto conn = find_ground_connection(policy, sm, j, dir, end_r);
|
||||
ret.first = bool(conn);
|
||||
ret.second = build_ground_connection(builder, sm, conn);
|
||||
|
||||
@ -921,8 +920,7 @@ std::pair<bool, long> search_ground_route(Ex policy,
|
||||
{
|
||||
std::pair<bool, long> ret = {false, SupportTreeNode::ID_UNSET};
|
||||
|
||||
auto conn = optimize_ground_connection(policy, builder, sm, j,
|
||||
end_r, init_dir);
|
||||
auto conn = optimize_ground_connection(policy, sm, j, end_r, init_dir);
|
||||
|
||||
ret.first = bool(conn);
|
||||
ret.second = build_ground_connection(builder, sm, conn);
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <wx/settings.h>
|
||||
#include <wx/stattext.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
|
||||
@ -1019,6 +1021,8 @@ void GLGizmoSlaSupports::select_point(int i)
|
||||
m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f;
|
||||
}
|
||||
else {
|
||||
if (!m_editing_cache[i].selected)
|
||||
BOOST_LOG_TRIVIAL(debug) << "Support point selected [" << i << "]: " << m_editing_cache[i].support_point.pos.transpose() << " \tnormal: " << m_editing_cache[i].normal.transpose();
|
||||
m_editing_cache[i].selected = true;
|
||||
m_selection_empty = false;
|
||||
m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f;
|
||||
|
76
tests/data/U_overhang.obj
Normal file
76
tests/data/U_overhang.obj
Normal file
@ -0,0 +1,76 @@
|
||||
####
|
||||
#
|
||||
# OBJ File Generated by Meshlab
|
||||
#
|
||||
####
|
||||
# Object U_overhang.obj
|
||||
#
|
||||
# Vertices: 16
|
||||
# Faces: 28
|
||||
#
|
||||
####
|
||||
vn 1.570797 1.570796 1.570796
|
||||
v 10.000000 10.000000 11.000000
|
||||
vn 4.712389 1.570796 -1.570796
|
||||
v 10.000000 1.000000 10.000000
|
||||
vn 1.570796 1.570796 -1.570796
|
||||
v 10.000000 10.000000 10.000000
|
||||
vn 1.570796 -1.570796 1.570796
|
||||
v 10.000000 0.000000 11.000000
|
||||
vn 4.712389 1.570796 1.570796
|
||||
v 10.000000 1.000000 1.000000
|
||||
vn 1.570797 1.570796 -1.570796
|
||||
v 10.000000 10.000000 0.000000
|
||||
vn 1.570796 1.570796 1.570796
|
||||
v 10.000000 10.000000 1.000000
|
||||
vn 1.570796 -1.570796 -1.570796
|
||||
v 10.000000 0.000000 0.000000
|
||||
vn -1.570796 1.570796 1.570796
|
||||
v 0.000000 10.000000 1.000000
|
||||
vn -4.712389 1.570796 1.570796
|
||||
v 0.000000 1.000000 1.000000
|
||||
vn -1.570796 -1.570796 -1.570796
|
||||
v 0.000000 0.000000 0.000000
|
||||
vn -1.570797 1.570796 -1.570796
|
||||
v 0.000000 10.000000 0.000000
|
||||
vn -4.712389 1.570796 -1.570796
|
||||
v 0.000000 1.000000 10.000000
|
||||
vn -1.570797 1.570796 1.570796
|
||||
v 0.000000 10.000000 11.000000
|
||||
vn -1.570796 1.570796 -1.570796
|
||||
v 0.000000 10.000000 10.000000
|
||||
vn -1.570796 -1.570796 1.570796
|
||||
v 0.000000 0.000000 11.000000
|
||||
# 16 vertices, 0 vertices normals
|
||||
|
||||
f 1//1 2//2 3//3
|
||||
f 2//2 4//4 5//5
|
||||
f 4//4 2//2 1//1
|
||||
f 5//5 6//6 7//7
|
||||
f 5//5 8//8 6//6
|
||||
f 8//8 5//5 4//4
|
||||
f 9//9 5//5 7//7
|
||||
f 5//5 9//9 10//10
|
||||
f 11//11 6//6 8//8
|
||||
f 6//6 11//11 12//12
|
||||
f 12//12 10//10 9//9
|
||||
f 10//10 11//11 13//13
|
||||
f 11//11 10//10 12//12
|
||||
f 13//13 14//14 15//15
|
||||
f 13//13 16//16 14//14
|
||||
f 16//16 13//13 11//11
|
||||
f 6//6 9//9 7//7
|
||||
f 9//9 6//6 12//12
|
||||
f 11//11 4//4 16//16
|
||||
f 4//4 11//11 8//8
|
||||
f 13//13 3//3 2//2
|
||||
f 3//3 13//13 15//15
|
||||
f 5//5 13//13 2//2
|
||||
f 13//13 5//5 10//10
|
||||
f 14//14 4//4 1//1
|
||||
f 4//4 14//14 16//16
|
||||
f 3//3 14//14 1//1
|
||||
f 14//14 3//3 15//15
|
||||
# 28 faces, 0 coords texture
|
||||
|
||||
# End of File
|
@ -4,6 +4,7 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
|
||||
sla_test_utils.hpp sla_test_utils.cpp
|
||||
sla_supptgen_tests.cpp
|
||||
sla_raycast_tests.cpp
|
||||
sla_supptreeutils_tests.cpp
|
||||
sla_archive_readwrite_tests.cpp)
|
||||
|
||||
# mold linker for successful linking needs also to link TBB library and link it before libslic3r.
|
||||
|
119
tests/sla_print/sla_supptreeutils_tests.cpp
Normal file
119
tests/sla_print/sla_supptreeutils_tests.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include "libslic3r/Execution/ExecutionSeq.hpp"
|
||||
#include "libslic3r/SLA/SupportTreeUtils.hpp"
|
||||
|
||||
TEST_CASE("Avoid disk below junction", "[suptreeutils]")
|
||||
{
|
||||
// In this test there will be a disk mesh with some radius, centered at
|
||||
// (0, 0, 0) and above the disk, a junction from which the support pillar
|
||||
// should be routed. The algorithm needs to find an avoidance route.
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
constexpr double FromRadius = .5;
|
||||
constexpr double EndRadius = 1.;
|
||||
constexpr double CylRadius = 4.;
|
||||
constexpr double CylHeight = 1.;
|
||||
|
||||
sla::SupportTreeConfig cfg;
|
||||
|
||||
indexed_triangle_set disk = its_make_cylinder(CylRadius, CylHeight);
|
||||
|
||||
// 2.5 * CyRadius height should be enough to be able to insert a bridge
|
||||
// with 45 degree tilt above the disk.
|
||||
sla::Junction j{Vec3d{0., 0., 2.5 * CylRadius}, FromRadius};
|
||||
|
||||
sla::SupportableMesh sm{disk, sla::SupportPoints{}, cfg};
|
||||
|
||||
sla::GroundConnection conn =
|
||||
sla::optimize_ground_connection(ex_seq, sm, j, EndRadius, sla::DOWN);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
sla::SupportTreeBuilder builder;
|
||||
|
||||
if (!conn)
|
||||
builder.add_junction(j);
|
||||
|
||||
sla::build_ground_connection(builder, sm, conn);
|
||||
|
||||
its_merge(disk, builder.merged_mesh());
|
||||
|
||||
its_write_stl_ascii("output_disk.stl", "disk", disk);
|
||||
#endif
|
||||
|
||||
REQUIRE(bool(conn));
|
||||
|
||||
// The route should include the source and one avoidance junction.
|
||||
REQUIRE(conn.path.size() == 2);
|
||||
|
||||
// The end radius end the pillar base's upper radius should match
|
||||
REQUIRE(conn.pillar_base->r_top == Approx(EndRadius));
|
||||
|
||||
// Check if the avoidance junction is indeed outside of the disk barrier's
|
||||
// edge.
|
||||
auto p = conn.path.back().pos;
|
||||
double pR = std::sqrt(p.x() * p.x()) + std::sqrt(p.y() * p.y());
|
||||
REQUIRE(pR + FromRadius > CylRadius);
|
||||
}
|
||||
|
||||
TEST_CASE("Avoid disk below junction with barrier on the side", "[suptreeutils]")
|
||||
{
|
||||
// In this test there will be a disk mesh with some radius, centered at
|
||||
// (0, 0, 0) and above the disk, a junction from which the support pillar
|
||||
// should be routed. The algorithm needs to find an avoidance route.
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
constexpr double FromRadius = .5;
|
||||
constexpr double EndRadius = 1.;
|
||||
constexpr double CylRadius = 4.;
|
||||
constexpr double CylHeight = 1.;
|
||||
constexpr double JElevX = 2.5;
|
||||
|
||||
sla::SupportTreeConfig cfg;
|
||||
|
||||
indexed_triangle_set disk = its_make_cylinder(CylRadius, CylHeight);
|
||||
indexed_triangle_set wall = its_make_cube(1., 2 * CylRadius, JElevX * CylRadius);
|
||||
its_translate(wall, Vec3f{float(FromRadius), -float(CylRadius), 0.f});
|
||||
its_merge(disk, wall);
|
||||
|
||||
// 2.5 * CyRadius height should be enough to be able to insert a bridge
|
||||
// with 45 degree tilt above the disk.
|
||||
sla::Junction j{Vec3d{0., 0., JElevX * CylRadius}, FromRadius};
|
||||
|
||||
sla::SupportableMesh sm{disk, sla::SupportPoints{}, cfg};
|
||||
|
||||
sla::GroundConnection conn =
|
||||
sla::optimize_ground_connection(ex_seq, sm, j, EndRadius, sla::DOWN);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
sla::SupportTreeBuilder builder;
|
||||
|
||||
if (!conn)
|
||||
builder.add_junction(j);
|
||||
|
||||
sla::build_ground_connection(builder, sm, conn);
|
||||
|
||||
its_merge(disk, builder.merged_mesh());
|
||||
|
||||
its_write_stl_ascii("output_disk_wall.stl", "disk_wall", disk);
|
||||
#endif
|
||||
|
||||
REQUIRE(bool(conn));
|
||||
|
||||
// The route should include the source and one avoidance junction.
|
||||
REQUIRE(conn.path.size() == 2);
|
||||
|
||||
// The end radius end the pillar base's upper radius should match
|
||||
REQUIRE(conn.pillar_base->r_top == Approx(EndRadius));
|
||||
|
||||
// Check if the avoidance junction is indeed outside of the disk barrier's
|
||||
// edge.
|
||||
auto p = conn.path.back().pos;
|
||||
double pR = std::sqrt(p.x() * p.x()) + std::sqrt(p.y() * p.y());
|
||||
REQUIRE(pR + FromRadius > CylRadius);
|
||||
}
|
Loading…
Reference in New Issue
Block a user