Fix zero elevation support maneuvers and comment to clarify the alg.

This commit is contained in:
tamasmeszaros 2020-01-10 11:31:30 +01:00
parent d0d73e6109
commit 45220e26c0
2 changed files with 49 additions and 32 deletions

View File

@ -561,8 +561,8 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
long head_id) long head_id)
{ {
// People were killed for this number (seriously) // People were killed for this number (seriously)
static const double SQR2 = std::sqrt(2.0);
static const Vec3d DOWN = {0.0, 0.0, -1.0}; static const Vec3d DOWN = {0.0, 0.0, -1.0};
const double SLOPE = 1. / std::cos(m_cfg.bridge_slope);
double gndlvl = m_builder.ground_level; double gndlvl = m_builder.ground_level;
Vec3d endp = {jp(X), jp(Y), gndlvl}; Vec3d endp = {jp(X), jp(Y), gndlvl};
@ -573,38 +573,47 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
bool can_add_base = true; bool can_add_base = true;
bool normal_mode = true; bool normal_mode = true;
// If in zero elevation mode and the pillar is too close to the model body,
// the support pillar can not be placed in the gap between the model and
// the pad, and the pillar bases must not touch the model body either.
// To solve this, a corrector bridge is inserted between the starting point
// (jp) and the new pillar.
if (m_cfg.object_elevation_mm < EPSILON if (m_cfg.object_elevation_mm < EPSILON
&& (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) { && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
// Get the distance from the mesh. This can be later optimized // Get the distance from the mesh. This can be later optimized
// to get the distance in 2D plane because we are dealing with // to get the distance in 2D plane because we are dealing with
// the ground level only. // the ground level only.
normal_mode = false; normal_mode = false;
double mind = min_dist - dist;
double azimuth = std::atan2(sourcedir(Y), sourcedir(X));
double sinpolar = std::sin(PI - m_cfg.bridge_slope);
double cospolar = std::cos(PI - m_cfg.bridge_slope);
double cosazm = std::cos(azimuth);
double sinazm = std::sin(azimuth);
auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar) // The min distance needed to move away from the model in XY plane.
.normalized(); double mind = min_dist - dist;
// get a suitable direction for the corrector bridge. It is the
// original sourcedir's azimuth but the polar angle is saturated to the
// configured bridge slope.
auto [polar, azimuth] = dir_to_spheric(sourcedir);
polar = PI - m_cfg.bridge_slope;
auto dir = spheric_to_dir(polar, azimuth).normalized();
using namespace libnest2d::opt; using namespace libnest2d::opt;
StopCriteria scr; StopCriteria scr;
scr.stop_score = min_dist; scr.stop_score = min_dist;
SubplexOptimizer solver(scr); SubplexOptimizer solver(scr);
// Search for a distance along the corrector bridge to move the endpoint
// sufficiently away form the model body. The first few optimization
// cycles should succeed here.
auto result = solver.optimize_max( auto result = solver.optimize_max(
[this, dir, jp, gndlvl](double mv) { [this, dir, jp, gndlvl](double mv) {
Vec3d endpt = jp + SQR2 * mv * dir; Vec3d endpt = jp + mv * dir;
endpt(Z) = gndlvl; endpt(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endpt)); return std::sqrt(m_mesh.squared_distance(endpt));
}, },
initvals(mind), bound(0.0, 2 * min_dist)); initvals(SLOPE * mind), bound(0.0, 2 * SLOPE * min_dist));
mind = std::get<0>(result.optimum); mind = std::get<0>(result.optimum);
endp = jp + SQR2 * mind * dir; endp = jp + SLOPE * mind * dir;
Vec3d pgnd = {endp(X), endp(Y), gndlvl}; Vec3d pgnd = {endp(X), endp(Y), gndlvl};
can_add_base = result.score > min_dist; can_add_base = result.score > min_dist;
@ -623,7 +632,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
else { else {
// If the new endpoint is below ground, do not make a pillar // If the new endpoint is below ground, do not make a pillar
if (endp(Z) < gndlvl) if (endp(Z) < gndlvl)
endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off endp = endp - SLOPE * (gndlvl - endp(Z)) * dir; // back off
else { else {
auto hit = bridge_mesh_intersect(endp, DOWN, radius); auto hit = bridge_mesh_intersect(endp, DOWN, radius);
@ -708,10 +717,7 @@ void SupportTreeBuildsteps::filter()
// (Quaternion::FromTwoVectors) and apply the rotation to the // (Quaternion::FromTwoVectors) and apply the rotation to the
// arrow head. // arrow head.
double z = n(2); auto [polar, azimuth] = dir_to_spheric(n);
double r = 1.0; // for normalized vector
double polar = std::acos(z / r);
double azimuth = std::atan2(n(1), n(0));
// skip if the tilt is not sane // skip if the tilt is not sane
if(polar >= PI - m_cfg.normal_cutoff_angle) { if(polar >= PI - m_cfg.normal_cutoff_angle) {
@ -729,9 +735,7 @@ void SupportTreeBuildsteps::filter()
double pin_r = double(m_support_pts[fidx].head_front_radius); double pin_r = double(m_support_pts[fidx].head_front_radius);
// Reassemble the now corrected normal // Reassemble the now corrected normal
auto nn = Vec3d(std::cos(azimuth) * std::sin(polar), auto nn = spheric_to_dir(polar, azimuth).normalized();
std::sin(azimuth) * std::sin(polar),
std::cos(polar)).normalized();
// check available distance // check available distance
EigenMesh3D::hit_result t EigenMesh3D::hit_result t
@ -757,9 +761,7 @@ void SupportTreeBuildsteps::filter()
auto oresult = solver.optimize_max( auto oresult = solver.optimize_max(
[this, pin_r, w, hp](double plr, double azm) [this, pin_r, w, hp](double plr, double azm)
{ {
auto dir = Vec3d(std::cos(azm) * std::sin(plr), auto dir = spheric_to_dir(plr, azm).normalized();
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
double score = pinhead_mesh_intersect( double score = pinhead_mesh_intersect(
hp, dir, pin_r, m_cfg.head_back_radius_mm, w); hp, dir, pin_r, m_cfg.head_back_radius_mm, w);
@ -767,17 +769,14 @@ void SupportTreeBuildsteps::filter()
return score; return score;
}, },
initvals(polar, azimuth), // start with what we have initvals(polar, azimuth), // start with what we have
bound(3 * PI / 4, bound(3 * PI / 4, PI), // Must not exceed the tilt limit
PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full search bound(-PI, PI) // azimuth can be a full search
); );
if(oresult.score > w) { if(oresult.score > w) {
polar = std::get<0>(oresult.optimum); polar = std::get<0>(oresult.optimum);
azimuth = std::get<1>(oresult.optimum); azimuth = std::get<1>(oresult.optimum);
nn = Vec3d(std::cos(azimuth) * std::sin(polar), nn = spheric_to_dir(polar, azimuth).normalized();
std::sin(azimuth) * std::sin(polar),
std::cos(polar)).normalized();
t = EigenMesh3D::hit_result(oresult.score); t = EigenMesh3D::hit_result(oresult.score);
} }
} }

View File

@ -20,6 +20,21 @@ inline Vec2d to_vec2(const Vec3d& v3) {
return {v3(X), v3(Y)}; return {v3(X), v3(Y)};
} }
inline std::pair<double, double> dir_to_spheric(const Vec3d &n, double norm = 1.)
{
double z = n.z();
double r = norm;
double polar = std::acos(z / r);
double azimuth = std::atan2(n(1), n(0));
return {polar, azimuth};
}
inline Vec3d spheric_to_dir(double polar, double azimuth)
{
return {std::cos(azimuth) * std::sin(polar),
std::sin(azimuth) * std::sin(polar), std::cos(polar)};
}
// This function returns the position of the centroid in the input 'clust' // This function returns the position of the centroid in the input 'clust'
// vector of point indices. // vector of point indices.
template<class DistFn> template<class DistFn>
@ -228,6 +243,9 @@ class SupportTreeBuildsteps {
// This is a proxy function for pillar creation which will mind the gap // This is a proxy function for pillar creation which will mind the gap
// between the pad and the model bottom in zero elevation mode. // between the pad and the model bottom in zero elevation mode.
// jp is the starting junction point which needs to be routed down.
// sourcedir is the allowed direction of an optional bridge between the
// jp junction and the final pillar.
void create_ground_pillar(const Vec3d &jp, void create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir, const Vec3d &sourcedir,
double radius, double radius,