Improvements on mini pillars
This commit is contained in:
parent
67b61c23f7
commit
7b6565abeb
@ -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); }
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -467,107 +467,86 @@ 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;
|
||||
|
||||
// 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.
|
||||
Vec3d dir = sourcedir;
|
||||
|
||||
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();
|
||||
|
||||
StopCriteria scr;
|
||||
scr.stop_score = min_dist;
|
||||
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(
|
||||
[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));
|
||||
|
||||
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;
|
||||
|
||||
double gnd_offs = m_mesh.ground_level_offset();
|
||||
auto abort_in_shame =
|
||||
[gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
|
||||
{
|
||||
normal_mode = true;
|
||||
can_add_base = false; // Nothing left to do, hope for the best
|
||||
endp = {jp(X), jp(Y), gndlvl - gnd_offs };
|
||||
};
|
||||
|
||||
// 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 {
|
||||
|
||||
auto hit = bridge_mesh_intersect(endp, DOWN, radius);
|
||||
if (!std::isinf(hit.distance())) abort_in_shame();
|
||||
|
||||
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);
|
||||
|
||||
// Add a degenerated pillar and the bridge.
|
||||
// The degenerate pillar will have zero length and it will
|
||||
// prevent from queries of head_pillar() to have non-existing
|
||||
// pillar when the head should have one.
|
||||
if (head_id >= 0)
|
||||
Vec3d dir = spheric_to_dir(polar, azimuth).normalized();
|
||||
|
||||
// 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;
|
||||
|
||||
if (t > m_cfg.max_bridge_length_mm || endp(Z) < gndlvl) {
|
||||
m_builder.add_pillar(head_id, jp, radius);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
t += m_cfg.head_fullwidth();
|
||||
new_endp = endp + t * DOWN;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
@ -575,10 +554,31 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
|
||||
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);
|
||||
|
||||
// Add a degenerated pillar and the bridge.
|
||||
// The degenerate pillar will have zero length and it will
|
||||
// prevent from queries of head_pillar() to have non-existing
|
||||
// pillar when the head should have one.
|
||||
if (head_id >= 0)
|
||||
m_builder.add_pillar(head_id, jp, radius);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user