Optimization for bad head angles.
This commit is contained in:
parent
f8e87a118c
commit
01091152be
2 changed files with 153 additions and 60 deletions
|
@ -163,7 +163,7 @@ public:
|
||||||
{
|
{
|
||||||
dir_ = OptDir::MIN;
|
dir_ = OptDir::MIN;
|
||||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||||
objectfunction, initvals, Bound<Args>()... );
|
forward<Func>(objectfunction), initvals, Bound<Args>()... );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args, class Func>
|
template<class...Args, class Func>
|
||||||
|
@ -171,7 +171,7 @@ public:
|
||||||
{
|
{
|
||||||
dir_ = OptDir::MIN;
|
dir_ = OptDir::MIN;
|
||||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||||
objectfunction,
|
forward<Func>(objectfunction),
|
||||||
Input<Args...>(),
|
Input<Args...>(),
|
||||||
Bound<Args>()... );
|
Bound<Args>()... );
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ public:
|
||||||
{
|
{
|
||||||
dir_ = OptDir::MAX;
|
dir_ = OptDir::MAX;
|
||||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||||
objectfunction, initvals, bounds... );
|
forward<Func>(objectfunction), initvals, bounds... );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Func, class...Args>
|
template<class Func, class...Args>
|
||||||
|
@ -193,7 +193,7 @@ public:
|
||||||
{
|
{
|
||||||
dir_ = OptDir::MAX;
|
dir_ = OptDir::MAX;
|
||||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||||
objectfunction, initvals, Bound<Args>()... );
|
forward<Func>(objectfunction), initvals, Bound<Args>()... );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args, class Func>
|
template<class...Args, class Func>
|
||||||
|
@ -201,7 +201,7 @@ public:
|
||||||
{
|
{
|
||||||
dir_ = OptDir::MAX;
|
dir_ = OptDir::MAX;
|
||||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||||
objectfunction,
|
forward<Func>(objectfunction),
|
||||||
Input<Args...>(),
|
Input<Args...>(),
|
||||||
Bound<Args>()... );
|
Bound<Args>()... );
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <libslic3r/ClipperUtils.hpp>
|
#include <libslic3r/ClipperUtils.hpp>
|
||||||
#include <libslic3r/Model.hpp>
|
#include <libslic3r/Model.hpp>
|
||||||
|
|
||||||
|
#include <libnest2d/optimizers/nlopt/simplex.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
|
|
||||||
|
@ -588,7 +589,7 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
||||||
double r_back,
|
double r_back,
|
||||||
double width,
|
double width,
|
||||||
const EigenMesh3D& m,
|
const EigenMesh3D& m,
|
||||||
unsigned samples = 8,
|
unsigned samples = 16,
|
||||||
double safety_distance = 0.001)
|
double safety_distance = 0.001)
|
||||||
{
|
{
|
||||||
// method based on:
|
// method based on:
|
||||||
|
@ -614,9 +615,17 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
||||||
std::vector<double> phis(samples);
|
std::vector<double> phis(samples);
|
||||||
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
|
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
|
||||||
|
|
||||||
a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z);
|
// We have to address the case when the direction vector v (same as dir)
|
||||||
|
// is coincident with one of the world axes. In this case two of its
|
||||||
|
// components will be completely zero and one is 1.0. Our method becomes
|
||||||
|
// dangerous here due to division with zero. Instead, vector a can be a
|
||||||
|
// rotated version of v
|
||||||
|
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
|
||||||
|
if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) a = {v(Z), v(X), v(Y)};
|
||||||
|
else {
|
||||||
|
a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize();
|
||||||
b = a.cross(v);
|
b = a.cross(v);
|
||||||
|
}
|
||||||
|
|
||||||
// Now a and b vectors are perpendicular to v and to each other. Together
|
// Now a and b vectors are perpendicular to v and to each other. Together
|
||||||
// they define the plane where we have to iterate with the given angles
|
// they define the plane where we have to iterate with the given angles
|
||||||
|
@ -686,8 +695,13 @@ double bridge_mesh_intersect(const Vec3d& s,
|
||||||
Vec3d a(0, 1, 0), b;
|
Vec3d a(0, 1, 0), b;
|
||||||
const double& sd = safety_distance;
|
const double& sd = safety_distance;
|
||||||
|
|
||||||
a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z);
|
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
|
||||||
|
if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z)))
|
||||||
|
a = {dir(Z), dir(X), dir(Y)};
|
||||||
|
else {
|
||||||
|
a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize();
|
||||||
b = a.cross(dir);
|
b = a.cross(dir);
|
||||||
|
}
|
||||||
|
|
||||||
// circle portions
|
// circle portions
|
||||||
std::vector<double> phis(samples);
|
std::vector<double> phis(samples);
|
||||||
|
@ -1149,16 +1163,16 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
//...
|
//...
|
||||||
};
|
};
|
||||||
|
|
||||||
// t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will
|
// throw if canceled: It will be called many times so a shorthand will
|
||||||
// come in handy.
|
// come in handy.
|
||||||
auto& tifcl = ctl.cancelfn;
|
auto& thr = ctl.cancelfn;
|
||||||
|
|
||||||
// Filtering step: here we will discard inappropriate support points and
|
// Filtering step: here we will discard inappropriate support points and
|
||||||
// decide the future of the appropriate ones. We will check if a pinhead
|
// decide the future of the appropriate ones. We will check if a pinhead
|
||||||
// is applicable and adjust its angle at each support point.
|
// is applicable and adjust its angle at each support point.
|
||||||
// We will also merge the support points that are just too close and can be
|
// We will also merge the support points that are just too close and can be
|
||||||
// considered as one.
|
// considered as one.
|
||||||
auto filterfn = [tifcl] (
|
auto filterfn = [thr] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& points,
|
const PointSet& points,
|
||||||
const EigenMesh3D& mesh,
|
const EigenMesh3D& mesh,
|
||||||
|
@ -1172,9 +1186,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// first one
|
// first one
|
||||||
auto aliases =
|
auto aliases =
|
||||||
cluster(points,
|
cluster(points,
|
||||||
[tifcl](const SpatElement& p, const SpatElement& se)
|
[thr](const SpatElement& p, const SpatElement& se)
|
||||||
{
|
{
|
||||||
tifcl();
|
thr();
|
||||||
return distance(p.first, se.first) < D_SP;
|
return distance(p.first, se.first) < D_SP;
|
||||||
}, 2);
|
}, 2);
|
||||||
|
|
||||||
|
@ -1185,10 +1199,10 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
filt_pts.row(count++) = points.row(a.front());
|
filt_pts.row(count++) = points.row(a.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
tifcl();
|
thr();
|
||||||
|
|
||||||
// calculate the normals to the triangles belonging to filtered points
|
// calculate the normals to the triangles belonging to filtered points
|
||||||
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl);
|
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, thr);
|
||||||
|
|
||||||
head_norm.resize(count, 3);
|
head_norm.resize(count, 3);
|
||||||
head_pos.resize(count, 3);
|
head_pos.resize(count, 3);
|
||||||
|
@ -1200,9 +1214,15 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// not be enough space for the pinhead. Filtering is applied for
|
// not be enough space for the pinhead. Filtering is applied for
|
||||||
// these reasons.
|
// these reasons.
|
||||||
|
|
||||||
|
using libnest2d::opt::bound;
|
||||||
|
using libnest2d::opt::initvals;
|
||||||
|
using libnest2d::opt::SimplexOptimizer;
|
||||||
|
using libnest2d::opt::StopCriteria;
|
||||||
|
static const unsigned MAX_TRIES = 100;
|
||||||
|
|
||||||
int pcount = 0, hlcount = 0;
|
int pcount = 0, hlcount = 0;
|
||||||
for(int i = 0; i < count; i++) {
|
for(int i = 0; i < count; i++) {
|
||||||
tifcl();
|
thr();
|
||||||
auto n = nmls.row(i);
|
auto n = nmls.row(i);
|
||||||
|
|
||||||
// for all normals we generate the spherical coordinates and
|
// for all normals we generate the spherical coordinates and
|
||||||
|
@ -1223,32 +1243,67 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// We saturate the polar angle to 3pi/4
|
// We saturate the polar angle to 3pi/4
|
||||||
polar = std::max(polar, 3*PI / 4);
|
polar = std::max(polar, 3*PI / 4);
|
||||||
|
|
||||||
// Reassemble the now corrected normal
|
|
||||||
Vec3d nn(std::cos(azimuth) * std::sin(polar),
|
|
||||||
std::sin(azimuth) * std::sin(polar),
|
|
||||||
std::cos(polar));
|
|
||||||
|
|
||||||
nn.normalize();
|
|
||||||
|
|
||||||
// save the head (pinpoint) position
|
// save the head (pinpoint) position
|
||||||
Vec3d hp = filt_pts.row(i);
|
Vec3d hp = filt_pts.row(i);
|
||||||
|
|
||||||
// the full width of the head
|
|
||||||
double w = cfg.head_width_mm +
|
double w = cfg.head_width_mm +
|
||||||
cfg.head_back_radius_mm +
|
cfg.head_back_radius_mm +
|
||||||
2*cfg.head_front_radius_mm;
|
2*cfg.head_front_radius_mm;
|
||||||
|
|
||||||
// We should shoot a ray in the direction of the pinhead and
|
// Reassemble the now corrected normal
|
||||||
// see if there is enough space for it
|
auto nn = Vec3d(std::cos(azimuth) * std::sin(polar),
|
||||||
|
std::sin(azimuth) * std::sin(polar),
|
||||||
|
std::cos(polar)).normalized();
|
||||||
|
|
||||||
|
// check available distance
|
||||||
double t = pinhead_mesh_intersect(
|
double t = pinhead_mesh_intersect(
|
||||||
hp, // touching point
|
hp, // touching point
|
||||||
nn,
|
nn, // normal
|
||||||
cfg.head_front_radius_mm, // approx the radius
|
cfg.head_front_radius_mm,
|
||||||
cfg.head_back_radius_mm,
|
cfg.head_back_radius_mm,
|
||||||
w,
|
w,
|
||||||
mesh);
|
mesh);
|
||||||
|
|
||||||
if(t > w || std::isinf(t)) {
|
if(t <= w) {
|
||||||
|
// Let's try to optimize this angle, there might be a viable
|
||||||
|
// normal that doesn't collide with the model geometry and
|
||||||
|
// its very close to the default.
|
||||||
|
|
||||||
|
StopCriteria stc;
|
||||||
|
stc.max_iterations = MAX_TRIES;
|
||||||
|
stc.relative_score_difference = 1e-3;
|
||||||
|
stc.stop_score = w; // space greater than w is enough
|
||||||
|
SimplexOptimizer solver(stc);
|
||||||
|
|
||||||
|
auto oresult = solver.optimize_max(
|
||||||
|
[&mesh, &cfg, w, hp](double plr, double azm)
|
||||||
|
{
|
||||||
|
auto n = Vec3d(std::cos(azm) * std::sin(plr),
|
||||||
|
std::sin(azm) * std::sin(plr),
|
||||||
|
std::cos(plr)).normalized();
|
||||||
|
|
||||||
|
double score = pinhead_mesh_intersect(
|
||||||
|
hp, n,
|
||||||
|
cfg.head_front_radius_mm,
|
||||||
|
cfg.head_back_radius_mm,
|
||||||
|
w,
|
||||||
|
mesh);
|
||||||
|
return score;
|
||||||
|
},
|
||||||
|
initvals(polar, azimuth), // let's start with what we have
|
||||||
|
bound(3*PI/4, PI), // Must not exceed the tilt limit
|
||||||
|
bound(-PI, PI) // azimuth can be a full range search
|
||||||
|
);
|
||||||
|
|
||||||
|
t = oresult.score;
|
||||||
|
polar = std::get<0>(oresult.optimum);
|
||||||
|
azimuth = std::get<1>(oresult.optimum);
|
||||||
|
nn = Vec3d(std::cos(azimuth) * std::sin(polar),
|
||||||
|
std::sin(azimuth) * std::sin(polar),
|
||||||
|
std::cos(polar)).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t > w) {
|
||||||
head_pos.row(pcount) = hp;
|
head_pos.row(pcount) = hp;
|
||||||
|
|
||||||
// save the verified and corrected normal
|
// save the verified and corrected normal
|
||||||
|
@ -1256,6 +1311,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
|
|
||||||
++pcount;
|
++pcount;
|
||||||
} else if( polar >= 3*PI/4 ) {
|
} else if( polar >= 3*PI/4 ) {
|
||||||
|
|
||||||
// Headless supports do not tilt like the headed ones so
|
// Headless supports do not tilt like the headed ones so
|
||||||
// the normal should point almost to the ground.
|
// the normal should point almost to the ground.
|
||||||
headless_norm.row(hlcount) = nn;
|
headless_norm.row(hlcount) = nn;
|
||||||
|
@ -1272,7 +1328,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
|
|
||||||
// Pinhead creation: based on the filtering results, the Head objects will
|
// Pinhead creation: based on the filtering results, the Head objects will
|
||||||
// be constructed (together with their triangle meshes).
|
// be constructed (together with their triangle meshes).
|
||||||
auto pinheadfn = [tifcl] (
|
auto pinheadfn = [thr] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
PointSet& head_pos,
|
PointSet& head_pos,
|
||||||
PointSet& nmls,
|
PointSet& nmls,
|
||||||
|
@ -1285,7 +1341,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
|
|
||||||
for (int i = 0; i < head_pos.rows(); ++i) {
|
for (int i = 0; i < head_pos.rows(); ++i) {
|
||||||
tifcl();
|
thr();
|
||||||
result.add_head(
|
result.add_head(
|
||||||
cfg.head_back_radius_mm,
|
cfg.head_back_radius_mm,
|
||||||
cfg.head_front_radius_mm,
|
cfg.head_front_radius_mm,
|
||||||
|
@ -1304,7 +1360,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// will process it. Also, the pillars will be grouped into clusters that can
|
// will process it. Also, the pillars will be grouped into clusters that can
|
||||||
// be interconnected with bridges. Elements of these groups may or may not
|
// be interconnected with bridges. Elements of these groups may or may not
|
||||||
// be interconnected. Here we only run the clustering algorithm.
|
// be interconnected. Here we only run the clustering algorithm.
|
||||||
auto classifyfn = [tifcl] (
|
auto classifyfn = [thr] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const EigenMesh3D& mesh,
|
const EigenMesh3D& mesh,
|
||||||
PointSet& head_pos,
|
PointSet& head_pos,
|
||||||
|
@ -1313,7 +1369,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
std::vector<double>& gndheight,
|
std::vector<double>& gndheight,
|
||||||
ClusteredPoints& ground_clusters,
|
ClusteredPoints& ground_clusters,
|
||||||
Result& result
|
Result& result
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
|
|
||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
/* Classification */
|
/* Classification */
|
||||||
|
@ -1328,7 +1385,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// pillars and which shall be connected to the model surface (or search
|
// pillars and which shall be connected to the model surface (or search
|
||||||
// a suitable path around the surface that leads to the ground -- TODO)
|
// a suitable path around the surface that leads to the ground -- TODO)
|
||||||
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
||||||
tifcl();
|
thr();
|
||||||
auto& head = result.head(i);
|
auto& head = result.head(i);
|
||||||
|
|
||||||
Vec3d dir(0, 0, -1);
|
Vec3d dir(0, 0, -1);
|
||||||
|
@ -1337,6 +1394,35 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
double t = std::numeric_limits<double>::infinity();
|
double t = std::numeric_limits<double>::infinity();
|
||||||
double hw = head.width_mm;
|
double hw = head.width_mm;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// using libnest2d::opt::Method;
|
||||||
|
// using libnest2d::opt::bound;
|
||||||
|
// using libnest2d::opt::Optimizer;
|
||||||
|
// using libnest2d::opt::TOptimizer;
|
||||||
|
// using libnest2d::opt::StopCriteria;
|
||||||
|
|
||||||
|
// auto stopcond = [] () { return false; };
|
||||||
|
// static const unsigned max_tries = 100;
|
||||||
|
|
||||||
|
// auto objfunc =
|
||||||
|
// [&head](double polar, double azimuth, double width)
|
||||||
|
// {
|
||||||
|
// Vec3d nn(std::cos(azimuth) * std::sin(polar),
|
||||||
|
// std::sin(azimuth) * std::sin(polar),
|
||||||
|
// std::cos(polar));
|
||||||
|
|
||||||
|
|
||||||
|
// };
|
||||||
|
|
||||||
|
// StopCriteria stc;
|
||||||
|
// stc.max_iterations = max_tries;
|
||||||
|
// stc.relative_score_difference = 1e-3;
|
||||||
|
// stc.stop_condition = stopcond;
|
||||||
|
// TOptimizer<Method::L_SIMPLEX> solver(stc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// We will try to assign a pillar to all the pinheads. If a pillar
|
// We will try to assign a pillar to all the pinheads. If a pillar
|
||||||
// would pierce the model surface, we will try to adjust slightly
|
// would pierce the model surface, we will try to adjust slightly
|
||||||
// the head with so that the pillar can be deployed.
|
// the head with so that the pillar can be deployed.
|
||||||
|
@ -1356,6 +1442,13 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// not route this to the ground nor to the model surface.
|
// not route this to the ground nor to the model surface.
|
||||||
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
|
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
|
||||||
} else {
|
} else {
|
||||||
|
if(!std::isinf(t) && !std::isinf(tprec) &&
|
||||||
|
std::abs(tprec - t) > hw)
|
||||||
|
{
|
||||||
|
// In this case the head would scratch the model body
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Head scratch detected.";
|
||||||
|
}
|
||||||
|
|
||||||
accept = true; t = tprec;
|
accept = true; t = tprec;
|
||||||
|
|
||||||
auto id = head.id;
|
auto id = head.id;
|
||||||
|
@ -1410,9 +1503,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
ground_clusters =
|
ground_clusters =
|
||||||
cluster(
|
cluster(
|
||||||
gnd,
|
gnd,
|
||||||
[d_base, tifcl](const SpatElement& p, const SpatElement& s)
|
[d_base, thr](const SpatElement& p, const SpatElement& s)
|
||||||
{
|
{
|
||||||
tifcl();
|
thr();
|
||||||
return distance(Vec2d(p.first(X), p.first(Y)),
|
return distance(Vec2d(p.first(X), p.first(Y)),
|
||||||
Vec2d(s.first(X), s.first(Y))) < d_base;
|
Vec2d(s.first(X), s.first(Y))) < d_base;
|
||||||
}, 3); // max 3 heads to connect to one centroid
|
}, 3); // max 3 heads to connect to one centroid
|
||||||
|
@ -1485,7 +1578,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// a full pillar (ground connected). Some will connect to a nearby pillar
|
// a full pillar (ground connected). Some will connect to a nearby pillar
|
||||||
// using a bridge. The max number of such side-heads for a central pillar
|
// using a bridge. The max number of such side-heads for a central pillar
|
||||||
// is limited to avoid bad weight distribution.
|
// is limited to avoid bad weight distribution.
|
||||||
auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl](
|
auto routing_ground_fn = [gnd_head_pt, interconnect, thr](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const ClusteredPoints& gnd_clusters,
|
const ClusteredPoints& gnd_clusters,
|
||||||
const IndexSet& gndidx,
|
const IndexSet& gndidx,
|
||||||
|
@ -1501,7 +1594,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
cl_centroids.reserve(gnd_clusters.size());
|
cl_centroids.reserve(gnd_clusters.size());
|
||||||
|
|
||||||
SpatIndex pheadindex; // spatial index for the junctions
|
SpatIndex pheadindex; // spatial index for the junctions
|
||||||
for(auto& cl : gnd_clusters) { tifcl();
|
for(auto& cl : gnd_clusters) { thr();
|
||||||
// place all the centroid head positions into the index. We will
|
// place all the centroid head positions into the index. We will
|
||||||
// query for alternative pillar positions. If a sidehead cannot
|
// query for alternative pillar positions. If a sidehead cannot
|
||||||
// connect to the cluster centroid, we have to search for another
|
// connect to the cluster centroid, we have to search for another
|
||||||
|
@ -1512,9 +1605,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
|
|
||||||
// get the current cluster centroid
|
// get the current cluster centroid
|
||||||
long lcid = cluster_centroid(cl, gnd_head_pt,
|
long lcid = cluster_centroid(cl, gnd_head_pt,
|
||||||
[tifcl](const Vec3d& p1, const Vec3d& p2)
|
[thr](const Vec3d& p1, const Vec3d& p2)
|
||||||
{
|
{
|
||||||
tifcl();
|
thr();
|
||||||
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
|
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1535,7 +1628,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// sidepoints with the cluster centroid (which is a ground pillar)
|
// sidepoints with the cluster centroid (which is a ground pillar)
|
||||||
// or a nearby pillar if the centroid is unreachable.
|
// or a nearby pillar if the centroid is unreachable.
|
||||||
size_t ci = 0;
|
size_t ci = 0;
|
||||||
for(auto cl : gnd_clusters) { tifcl();
|
for(auto cl : gnd_clusters) { thr();
|
||||||
|
|
||||||
auto cidx = cl_centroids[ci];
|
auto cidx = cl_centroids[ci];
|
||||||
cl_centroids[ci++] = cl[cidx];
|
cl_centroids[ci++] = cl[cidx];
|
||||||
|
@ -1559,12 +1652,12 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// is distributed more effectively on the pillar.
|
// is distributed more effectively on the pillar.
|
||||||
|
|
||||||
auto search_nearest =
|
auto search_nearest =
|
||||||
[&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
|
[&thr, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
|
||||||
(SpatIndex& spindex, const Vec3d& jsh)
|
(SpatIndex& spindex, const Vec3d& jsh)
|
||||||
{
|
{
|
||||||
long nearest_id = -1;
|
long nearest_id = -1;
|
||||||
const double max_len = maxbridgelen / 2;
|
const double max_len = maxbridgelen / 2;
|
||||||
while(nearest_id < 0 && !spindex.empty()) { tifcl();
|
while(nearest_id < 0 && !spindex.empty()) { thr();
|
||||||
// loop until a suitable head is not found
|
// loop until a suitable head is not found
|
||||||
// if there is a pillar closer than the cluster center
|
// if there is a pillar closer than the cluster center
|
||||||
// (this may happen as the clustering is not perfect)
|
// (this may happen as the clustering is not perfect)
|
||||||
|
@ -1603,7 +1696,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
return nearest_id;
|
return nearest_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
for(auto c : cl) { tifcl();
|
for(auto c : cl) { thr();
|
||||||
auto& sidehead = result.head(gndidx[c]);
|
auto& sidehead = result.head(gndidx[c]);
|
||||||
sidehead.transform();
|
sidehead.transform();
|
||||||
|
|
||||||
|
@ -1669,7 +1762,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
ClusterEl ring;
|
ClusterEl ring;
|
||||||
|
|
||||||
while(!rem.empty()) { // loop until all the points belong to some ring
|
while(!rem.empty()) { // loop until all the points belong to some ring
|
||||||
tifcl();
|
thr();
|
||||||
std::sort(rem.begin(), rem.end());
|
std::sort(rem.begin(), rem.end());
|
||||||
|
|
||||||
auto newring = pts_convex_hull(rem,
|
auto newring = pts_convex_hull(rem,
|
||||||
|
@ -1681,7 +1774,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
if(!ring.empty()) {
|
if(!ring.empty()) {
|
||||||
// inner ring is now in 'newring' and outer ring is in 'ring'
|
// inner ring is now in 'newring' and outer ring is in 'ring'
|
||||||
SpatIndex innerring;
|
SpatIndex innerring;
|
||||||
for(unsigned i : newring) { tifcl();
|
for(unsigned i : newring) { thr();
|
||||||
const Pillar& pill = result.head_pillar(gndidx[i]);
|
const Pillar& pill = result.head_pillar(gndidx[i]);
|
||||||
assert(pill.id >= 0);
|
assert(pill.id >= 0);
|
||||||
innerring.insert(pill.endpoint, unsigned(pill.id));
|
innerring.insert(pill.endpoint, unsigned(pill.id));
|
||||||
|
@ -1690,7 +1783,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// For all pillars in the outer ring find the closest in the
|
// For all pillars in the outer ring find the closest in the
|
||||||
// inner ring and connect them. This will create the spider web
|
// inner ring and connect them. This will create the spider web
|
||||||
// fashioned connections between pillars
|
// fashioned connections between pillars
|
||||||
for(unsigned i : ring) { tifcl();
|
for(unsigned i : ring) { thr();
|
||||||
const Pillar& outerpill = result.head_pillar(gndidx[i]);
|
const Pillar& outerpill = result.head_pillar(gndidx[i]);
|
||||||
auto res = innerring.nearest(outerpill.endpoint, 1);
|
auto res = innerring.nearest(outerpill.endpoint, 1);
|
||||||
if(res.empty()) continue;
|
if(res.empty()) continue;
|
||||||
|
@ -1716,7 +1809,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
next != ring.end();
|
next != ring.end();
|
||||||
++it, ++next)
|
++it, ++next)
|
||||||
{
|
{
|
||||||
tifcl();
|
thr();
|
||||||
const Pillar& pillar = result.head_pillar(gndidx[*it]);
|
const Pillar& pillar = result.head_pillar(gndidx[*it]);
|
||||||
const Pillar& nextpillar = result.head_pillar(gndidx[*next]);
|
const Pillar& nextpillar = result.head_pillar(gndidx[*next]);
|
||||||
interconnect(pillar, nextpillar, emesh, result);
|
interconnect(pillar, nextpillar, emesh, result);
|
||||||
|
@ -1736,14 +1829,14 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// the model surface with a flipped pinhead. In the future here we could use
|
// the model surface with a flipped pinhead. In the future here we could use
|
||||||
// some smart algorithms to search for a safe path to the ground or to a
|
// some smart algorithms to search for a safe path to the ground or to a
|
||||||
// nearby pillar that can hold the supported weight.
|
// nearby pillar that can hold the supported weight.
|
||||||
auto routing_nongnd_fn = [tifcl](
|
auto routing_nongnd_fn = [thr](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const std::vector<double>& gndheight,
|
const std::vector<double>& gndheight,
|
||||||
const IndexSet& nogndidx,
|
const IndexSet& nogndidx,
|
||||||
Result& result)
|
Result& result)
|
||||||
{
|
{
|
||||||
// TODO: connect these to the ground pillars if possible
|
// TODO: connect these to the ground pillars if possible
|
||||||
for(auto idx : nogndidx) { tifcl();
|
for(auto idx : nogndidx) { thr();
|
||||||
double gh = gndheight[idx];
|
double gh = gndheight[idx];
|
||||||
double base_width = cfg.head_width_mm;
|
double base_width = cfg.head_width_mm;
|
||||||
|
|
||||||
|
@ -1800,7 +1893,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// Step: process the support points where there is not enough space for a
|
// Step: process the support points where there is not enough space for a
|
||||||
// full pinhead. In this case we will use a rounded sphere as a touching
|
// full pinhead. In this case we will use a rounded sphere as a touching
|
||||||
// point and use a thinner bridge (let's call it a stick).
|
// point and use a thinner bridge (let's call it a stick).
|
||||||
auto process_headless = [tifcl](
|
auto process_headless = [thr](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& headless_pts,
|
const PointSet& headless_pts,
|
||||||
const PointSet& headless_norm,
|
const PointSet& headless_norm,
|
||||||
|
@ -1815,7 +1908,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
|
|
||||||
// We will sink the pins into the model surface for a distance of 1/3 of
|
// We will sink the pins into the model surface for a distance of 1/3 of
|
||||||
// the pin radius
|
// the pin radius
|
||||||
for(int i = 0; i < headless_pts.rows(); i++) { tifcl();
|
for(int i = 0; i < headless_pts.rows(); i++) { thr();
|
||||||
Vec3d sph = headless_pts.row(i); // Exact support position
|
Vec3d sph = headless_pts.row(i); // Exact support position
|
||||||
Vec3d n = headless_norm.row(i); // mesh outward normal
|
Vec3d n = headless_norm.row(i); // mesh outward normal
|
||||||
Vec3d sp = sph - n * HWIDTH_MM; // stick head start point
|
Vec3d sp = sph - n * HWIDTH_MM; // stick head start point
|
||||||
|
|
Loading…
Reference in a new issue