Fix zero elevation support maneuvers and comment to clarify the alg.
This commit is contained in:
parent
d0d73e6109
commit
45220e26c0
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user