Improvements on mini pillars

This commit is contained in:
tamasmeszaros 2020-06-05 20:19:19 +02:00
parent 67b61c23f7
commit 7b6565abeb
6 changed files with 124 additions and 91 deletions

View File

@ -60,10 +60,13 @@ inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline Vec2i32 to_2d(const Vec2i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
template<class T, int N> Eigen::Matrix<T, 2, 1, Eigen::DontAlign>
to_2d(const Eigen::Matrix<T, N, 1, Eigen::DontAlign> &ptN) { return {ptN(0), ptN(1)}; }
//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }

View File

@ -125,6 +125,8 @@ public:
}
Vec3d normal_by_face_id(int face_id) const;
const TriangleMesh * get_triangle_mesh() const { return m_tm; }
};
// Calculate the normals for the selected points (from 'points' set) on the

View File

@ -314,6 +314,22 @@ Bridge::Bridge(const Vec3d &j1, const Vec3d &j2, double r_mm, size_t steps):
for(auto& p : mesh.points) p = quater * p + j1;
}
Bridge::Bridge(const Vec3d &j1,
const Vec3d &j2,
double r1_mm,
double r2_mm,
size_t steps)
{
Vec3d dir = (j2 - j1);
mesh = pinhead(r1_mm, r2_mm, dir.norm(), steps);
dir.normalize();
using Quaternion = Eigen::Quaternion<double>;
auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir);
for(auto& p : mesh.points) p = quater * p + j1;
}
Pad::Pad(const TriangleMesh &support_mesh,
const ExPolygons & model_contours,
double ground_level,

View File

@ -216,6 +216,12 @@ struct Bridge {
const Vec3d &j2,
double r_mm = 0.8,
size_t steps = 45);
Bridge(const Vec3d &j1,
const Vec3d &j2,
double r1_mm,
double r2_mm,
size_t steps = 45);
};
// A wrapper struct around the pad

View File

@ -467,94 +467,102 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
return true;
}
void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir,
double radius,
long head_id)
{
const double SLOPE = 1. / std::cos(m_cfg.bridge_slope);
double gndlvl = m_builder.ground_level;
Vec3d endp = {jp(X), jp(Y), gndlvl};
double sd = m_cfg.pillar_base_safety_distance_mm;
long pillar_id = ID_UNSET;
bool can_add_base = radius >= m_cfg.head_back_radius_mm;
double base_r = can_add_base ? m_cfg.base_radius_mm : 0.;
double gndlvl = m_builder.ground_level;
if (!can_add_base) gndlvl -= m_mesh.ground_level_offset();
Vec3d endp = {jp(X), jp(Y), gndlvl};
double min_dist = sd + base_r + EPSILON;
double dist = 0;
bool normal_mode = true;
Vec3d dir = sourcedir;
// 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
&& (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
// Get the distance from the mesh. This can be later optimized
// to get the distance in 2D plane because we are dealing with
// the ground level only.
normal_mode = false;
// The min distance needed to move away from the model in XY plane.
double current_d = min_dist - dist;
double current_bride_d = SLOPE * current_d;
auto to_floor = [gndlvl](const Vec3d &p) { return Vec3d{p.x(), p.y(), gndlvl}; };
if (m_cfg.object_elevation_mm < EPSILON)
{
// 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);
auto [polar, azimuth] = dir_to_spheric(dir);
polar = PI - m_cfg.bridge_slope;
auto dir = spheric_to_dir(polar, azimuth).normalized();
Vec3d dir = spheric_to_dir(polar, azimuth).normalized();
StopCriteria scr;
scr.stop_score = min_dist;
SubplexOptimizer solver(scr);
// Check the distance of the endpoint and the closest point on model
// body. It should be greater than the min_dist which is
// the safety distance from the model. It includes the pad gap if in
// zero elevation mode.
//
// Try to move along the established bridge direction to dodge the
// forbidden region for the endpoint.
double t = -radius;
while (std::sqrt(m_mesh.squared_distance(to_floor(endp))) < min_dist ||
!std::isinf(bridge_mesh_distance(endp, DOWN, radius))) {
t += radius;
endp = jp + t * dir;
normal_mode = false;
// 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(
[this, dir, jp, gndlvl](double mv) {
Vec3d endpt = jp + mv * dir;
endpt(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endpt));
},
initvals(current_bride_d),
bound(0.0, m_cfg.max_bridge_length_mm - current_bride_d));
if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) {
m_builder.add_pillar(head_id, jp, radius);
return false;
}
}
}
endp = jp + std::get<0>(result.optimum) * dir;
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
can_add_base = can_add_base && result.score > min_dist;
// Check if the deduced route is sane and exit with error if not.
if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm()) {
m_builder.add_pillar(head_id, jp, radius);
return false;
}
double gnd_offs = m_mesh.ground_level_offset();
auto abort_in_shame =
[gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
// If this is a mini pillar, do not let it grow too long, but change the
// radius to the normal pillar as soon as it is possible.
if (radius < m_cfg.head_back_radius_mm) {
double t = 0.;
double new_radius = m_cfg.head_back_radius_mm;
Vec3d new_endp = endp;
double d = 0.;
while (!std::isinf(d = bridge_mesh_distance(new_endp, DOWN, new_radius))
&& new_endp.z() > gndlvl)
{
normal_mode = true;
can_add_base = false; // Nothing left to do, hope for the best
endp = {jp(X), jp(Y), gndlvl - gnd_offs };
};
t += m_cfg.head_fullwidth();
new_endp = endp + t * DOWN;
}
// We have to check if the bridge is feasible.
if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm())
abort_in_shame();
else {
// If the new endpoint is below ground, do not make a pillar
if (endp(Z) < gndlvl)
endp = endp - SLOPE * (gndlvl - endp(Z)) * dir; // back off
else {
if (std::isinf(d) && new_endp.z() > gndlvl) {
if (t > 0.) {
m_builder.add_bridge(endp, new_endp, radius, new_radius);
endp = new_endp;
} else {
m_builder.add_junction(endp, new_radius);
}
radius = new_radius;
}
}
auto hit = bridge_mesh_intersect(endp, DOWN, radius);
if (!std::isinf(hit.distance())) abort_in_shame();
// Straigh path down, no area to dodge
if (normal_mode) {
pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) :
m_builder.add_pillar(jp, endp, radius);
if (can_add_base)
m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm,
m_cfg.base_radius_mm);
} else {
// Insert the bridge to get around the forbidden area
Vec3d pgnd{endp.x(), endp.y(), gndlvl};
pillar_id = m_builder.add_pillar(endp, pgnd, radius);
if (can_add_base)
m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm,
m_cfg.base_radius_mm);
}
m_builder.add_bridge(jp, endp, radius);
m_builder.add_junction(endp, radius);
@ -566,19 +574,11 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
if (head_id >= 0)
m_builder.add_pillar(head_id, jp, radius);
}
}
if (normal_mode) {
pillar_id = head_id >= 0 ? m_builder.add_pillar(head_id, endp, radius) :
m_builder.add_pillar(jp, endp, radius);
if (can_add_base)
m_builder.add_pillar_base(pillar_id, m_cfg.base_height_mm,
m_cfg.base_radius_mm);
}
if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
m_pillar_index.guarded_insert(endp, unsigned(pillar_id));
return true;
}
void SupportTreeBuildsteps::filter()
@ -835,7 +835,11 @@ void SupportTreeBuildsteps::routing_to_ground()
Head &h = m_builder.head(hid);
h.transform();
create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id);
if (!create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id)) {
BOOST_LOG_TRIVIAL(warning)
<< "Pillar cannot be created for support point id: " << hid;
h.invalidate();
}
}
// now we will go through the clusters ones again and connect the
@ -999,8 +1003,9 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &source)
nearest_id = ne.second;
if(nearest_id >= 0) {
if(size_t(nearest_id) < m_builder.pillarcount()) {
if(!connect_to_nearpillar(source, nearest_id)) {
if (size_t(nearest_id) < m_builder.pillarcount()) {
if(!connect_to_nearpillar(source, nearest_id) ||
m_builder.pillar(nearest_id).r < source.r_back_mm) {
nearest_id = ID_UNSET; // continue searching
spindex.remove(ne); // without the current pillar
}
@ -1104,7 +1109,8 @@ void SupportTreeBuildsteps::interconnect_pillars()
const Pillar& neighborpillar = m_builder.pillar(re.second);
// this neighbor is occupied, skip
if(neighborpillar.links >= neighbors) continue;
if (neighborpillar.links >= neighbors) continue;
if (neighborpillar.r < pillar.r) continue;
if(interconnect(pillar, neighborpillar)) {
pairs.insert(hashval);

View File

@ -271,7 +271,7 @@ class SupportTreeBuildsteps {
// 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,
bool create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir,
double radius,
long head_id = ID_UNSET);