Replacing simplex optimizers with more intelligent genetic ones.
This commit is contained in:
parent
efd3d27425
commit
9131b1658a
@ -12,7 +12,7 @@
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/Model.hpp>
|
||||
|
||||
#include <libnest2d/optimizers/nlopt/simplex.hpp>
|
||||
#include <libnest2d/optimizers/nlopt/genetic.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <libslic3r/I18N.hpp>
|
||||
@ -63,6 +63,19 @@
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
||||
// Compile time configuration value definitions:
|
||||
|
||||
// The max Z angle for a normal at which it will get completely ignored.
|
||||
const double SupportConfig::normal_cutoff_angle = 150.0 * M_PI / 180.0;
|
||||
|
||||
// The shortest distance of any support structure from the model surface
|
||||
const double SupportConfig::safety_distance_mm = 0.1;
|
||||
|
||||
const double SupportConfig::max_solo_pillar_height_mm = 5.0;
|
||||
const double SupportConfig::max_dual_pillar_height_mm = 35.0;
|
||||
const double SupportConfig::optimizer_rel_score_diff = 1e-6;
|
||||
const unsigned SupportConfig::optimizer_max_iterations = 500;
|
||||
|
||||
using Coordf = double;
|
||||
using Portion = std::tuple<double, double>;
|
||||
|
||||
@ -1309,105 +1322,6 @@ class SLASupportTree::Algorithm {
|
||||
return nearest_id >= 0;
|
||||
}
|
||||
|
||||
// Interconnection strategy. Pillars with height exceeding H1 will require
|
||||
// at least one neighbor to connect with. Height exceeding H2 require two
|
||||
// neighbors. A connection only counts if the height ratio is bigger
|
||||
// than 20%
|
||||
void connect_pillars_nearest(unsigned neighbors = 1,
|
||||
double H1 = 4.0, // min 1 neighbor required
|
||||
double H2 = 35.0, // min 2 neighbor required
|
||||
double min_height_ratio = 0.2)
|
||||
{
|
||||
// Now comes the algorithm that connects ground pillars with each other.
|
||||
// Ideally every pillar should be connected with at least one of its
|
||||
// neighbors if that neighbor is within sufficient distance (a bridge to
|
||||
// it would not be longer than max_bridge_distance)
|
||||
|
||||
double d = std::cos(m_cfg.bridge_slope) * m_cfg.max_bridge_length_mm;
|
||||
|
||||
std::set<unsigned long> pairs;
|
||||
|
||||
m_pillar_index.foreach(
|
||||
[this, d, &pairs, neighbors, min_height_ratio, H1, H2]
|
||||
(const SpatElement& el)
|
||||
{
|
||||
Vec3d qp = el.first;
|
||||
|
||||
// Query all remaining points within reach
|
||||
auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
|
||||
return distance(e.first, qp) < d;
|
||||
});
|
||||
|
||||
// sort the result by distance (have to check if this is needed)
|
||||
std::sort(qres.begin(), qres.end(),
|
||||
[qp](const SpatElement& e1, const SpatElement& e2){
|
||||
return distance(e1.first, qp) < distance(e2.first, qp);
|
||||
});
|
||||
|
||||
const Pillar& pillar = m_result.pillars()[el.second];
|
||||
unsigned ncount = 0;
|
||||
for(auto& re : qres) {
|
||||
if(re.second == el.second) continue;
|
||||
|
||||
auto hashval = m_pillar_index.size() * el.second + re.second;
|
||||
if(pairs.find(hashval) != pairs.end()) continue;
|
||||
|
||||
const Pillar& neighborpillar = m_result.pillars()[re.second];
|
||||
if(interconnect(pillar, neighborpillar)) {
|
||||
pairs.insert(hashval);
|
||||
|
||||
// If the interconnection length between the two pillars is
|
||||
// less than 20% of the longer pillar's height, don't count
|
||||
if(std::min(pillar.height, neighborpillar.height) /
|
||||
std::max(pillar.height, neighborpillar.height) >
|
||||
min_height_ratio) ++ncount;
|
||||
}
|
||||
|
||||
// 3 connections are enough for one pillar
|
||||
if(ncount == neighbors) break;
|
||||
}
|
||||
|
||||
unsigned needpillars = 0;
|
||||
if(ncount < 1 && pillar.height > H1) {
|
||||
// No neighbors could not be found and the pillar is too long.
|
||||
BOOST_LOG_TRIVIAL(warning) << "Pillar is too long and has no "
|
||||
"neighbors. Head ID: "
|
||||
<< pillar.start_junction_id;
|
||||
needpillars = 1;
|
||||
} else if(ncount < 2 && pillar.height > H2) {
|
||||
// Not enough neighbors to support this pillar
|
||||
BOOST_LOG_TRIVIAL(warning) << "Pillar is too long and has too "
|
||||
"few neighbors. Head ID: "
|
||||
<< pillar.start_junction_id;
|
||||
needpillars = 2 - ncount;
|
||||
}
|
||||
|
||||
// WIP:
|
||||
// note: sideheads ARE tested to reach the ground!
|
||||
|
||||
// if(needpillars > 0) {
|
||||
// if(pillar.starts_from_head) {
|
||||
// // search for a sidehead for this head. We will route that
|
||||
// // to the ground.
|
||||
// const Head& head = m_result.head(unsigned(pillar.start_junction_id));
|
||||
// for(auto cl : m_pillar_clusters) {
|
||||
// auto it = std::find(cl.begin(), cl.end(), head.id);
|
||||
// if(it != cl.end()) {
|
||||
// cl.erase(it);
|
||||
// for(size_t j = 0; j < cl.size() && j < needpillars; j++) {
|
||||
// unsigned hid = cl[j];
|
||||
|
||||
// m_result.add_pillar(hid, endpoint, )
|
||||
// .add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Algorithm(const SupportConfig& config,
|
||||
@ -1468,9 +1382,8 @@ public:
|
||||
|
||||
using libnest2d::opt::bound;
|
||||
using libnest2d::opt::initvals;
|
||||
using libnest2d::opt::SimplexOptimizer;
|
||||
using libnest2d::opt::GeneticOptimizer;
|
||||
using libnest2d::opt::StopCriteria;
|
||||
static const unsigned MAX_TRIES = 100;
|
||||
|
||||
for(unsigned i = 0, fidx = filtered_indices[0];
|
||||
i < filtered_indices.size(); ++i, fidx = filtered_indices[i])
|
||||
@ -1525,10 +1438,10 @@ public:
|
||||
// geometry and its very close to the default.
|
||||
|
||||
StopCriteria stc;
|
||||
stc.max_iterations = MAX_TRIES;
|
||||
stc.relative_score_difference = 1e-3;
|
||||
stc.max_iterations = m_cfg.optimizer_max_iterations;
|
||||
stc.relative_score_difference = m_cfg.optimizer_rel_score_diff;
|
||||
stc.stop_score = w; // space greater than w is enough
|
||||
SimplexOptimizer solver(stc);
|
||||
GeneticOptimizer solver(stc);
|
||||
|
||||
auto oresult = solver.optimize_max(
|
||||
[this, pin_r, w, hp](double plr, double azm)
|
||||
@ -1538,8 +1451,8 @@ public:
|
||||
std::cos(plr)).normalized();
|
||||
|
||||
double score = pinhead_mesh_intersect( hp, n, pin_r,
|
||||
m_cfg.head_back_radius_mm,
|
||||
w);
|
||||
m_cfg.head_back_radius_mm, w);
|
||||
|
||||
return score;
|
||||
},
|
||||
initvals(polar, azimuth), // start with what we have
|
||||
@ -1819,14 +1732,14 @@ public:
|
||||
|
||||
using libnest2d::opt::bound;
|
||||
using libnest2d::opt::initvals;
|
||||
using libnest2d::opt::SimplexOptimizer;
|
||||
using libnest2d::opt::GeneticOptimizer;
|
||||
using libnest2d::opt::StopCriteria;
|
||||
|
||||
StopCriteria stc;
|
||||
stc.max_iterations = 100;
|
||||
stc.relative_score_difference = 1e-3;
|
||||
stc.max_iterations = m_cfg.optimizer_max_iterations;
|
||||
stc.relative_score_difference = m_cfg.optimizer_rel_score_diff;
|
||||
stc.stop_score = 1e6;
|
||||
SimplexOptimizer solver(stc);
|
||||
GeneticOptimizer solver(stc);
|
||||
|
||||
double r_back = head.r_back_mm;
|
||||
|
||||
@ -1836,7 +1749,6 @@ public:
|
||||
Vec3d n = Vec3d(std::cos(azm) * std::sin(plr),
|
||||
std::sin(azm) * std::sin(plr),
|
||||
std::cos(plr)).normalized();
|
||||
|
||||
return bridge_mesh_intersect(hjp, n, r_back);
|
||||
},
|
||||
initvals(polar, azimuth), // let's start with what we have
|
||||
@ -1923,7 +1835,102 @@ public:
|
||||
}
|
||||
|
||||
void cascade_pillars() {
|
||||
connect_pillars_nearest();
|
||||
// Now comes the algorithm that connects ground pillars with each other.
|
||||
// Ideally every pillar should be connected with at least one of its
|
||||
// neighbors if that neighbor is within sufficient distance (a bridge to
|
||||
// it would not be longer than max_bridge_distance)
|
||||
|
||||
// Pillars with height exceeding H1 will require at least one neighbor
|
||||
// to connect with. Height exceeding H2 require two neighbors.
|
||||
double H1 = m_cfg.max_solo_pillar_height_mm;
|
||||
double H2 = m_cfg.max_dual_pillar_height_mm;
|
||||
unsigned neighbors = m_cfg.pillar_cascade_neighbors;
|
||||
double d = std::cos(m_cfg.bridge_slope) * m_cfg.max_bridge_length_mm;
|
||||
|
||||
//A connection between two pillars only counts if the height ratio is
|
||||
// bigger than 20%
|
||||
double min_height_ratio = 0.2;
|
||||
|
||||
std::set<unsigned long> pairs;
|
||||
|
||||
m_pillar_index.foreach(
|
||||
[this, d, &pairs, neighbors, min_height_ratio, H1, H2]
|
||||
(const SpatElement& el)
|
||||
{
|
||||
Vec3d qp = el.first;
|
||||
|
||||
// Query all remaining points within reach
|
||||
auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
|
||||
return distance(e.first, qp) < d;
|
||||
});
|
||||
|
||||
// sort the result by distance (have to check if this is needed)
|
||||
std::sort(qres.begin(), qres.end(),
|
||||
[qp](const SpatElement& e1, const SpatElement& e2){
|
||||
return distance(e1.first, qp) < distance(e2.first, qp);
|
||||
});
|
||||
|
||||
const Pillar& pillar = m_result.pillars()[el.second];
|
||||
unsigned ncount = 0;
|
||||
for(auto& re : qres) {
|
||||
if(re.second == el.second) continue;
|
||||
|
||||
auto hashval = m_pillar_index.size() * el.second + re.second;
|
||||
if(pairs.find(hashval) != pairs.end()) continue;
|
||||
|
||||
const Pillar& neighborpillar = m_result.pillars()[re.second];
|
||||
if(interconnect(pillar, neighborpillar)) {
|
||||
pairs.insert(hashval);
|
||||
|
||||
// If the interconnection length between the two pillars is
|
||||
// less than 20% of the longer pillar's height, don't count
|
||||
if(neighborpillar.height / pillar.height > min_height_ratio)
|
||||
++ncount;
|
||||
}
|
||||
|
||||
// 3 connections are enough for one pillar
|
||||
if(ncount == neighbors) break;
|
||||
}
|
||||
|
||||
unsigned needpillars = 0;
|
||||
if(ncount < 1 && pillar.height > H1) {
|
||||
// No neighbors could not be found and the pillar is too long.
|
||||
BOOST_LOG_TRIVIAL(warning) << "Pillar is too long and has no "
|
||||
"neighbors. Head ID: "
|
||||
<< pillar.start_junction_id;
|
||||
needpillars = 1;
|
||||
} else if(ncount < 2 && pillar.height > H2) {
|
||||
// Not enough neighbors to support this pillar
|
||||
BOOST_LOG_TRIVIAL(warning) << "Pillar is too long and has too "
|
||||
"few neighbors. Head ID: "
|
||||
<< pillar.start_junction_id;
|
||||
needpillars = 2 - ncount;
|
||||
}
|
||||
|
||||
// WIP:
|
||||
// note: sideheads ARE tested to reach the ground!
|
||||
|
||||
// if(needpillars > 0) {
|
||||
// if(pillar.starts_from_head) {
|
||||
// // search for a sidehead for this head. We will route that
|
||||
// // to the ground.
|
||||
// const Head& head = m_result.head(unsigned(pillar.start_junction_id));
|
||||
// for(auto cl : m_pillar_clusters) {
|
||||
// auto it = std::find(cl.begin(), cl.end(), head.id);
|
||||
// if(it != cl.end()) {
|
||||
// cl.erase(it);
|
||||
// for(size_t j = 0; j < cl.size() && j < needpillars; j++) {
|
||||
// unsigned hid = cl[j];
|
||||
|
||||
// m_result.add_pillar(hid, endpoint, )
|
||||
// .add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Step: process the support points where there is not enough space for a
|
||||
|
@ -79,14 +79,20 @@ struct SupportConfig {
|
||||
double object_elevation_mm = 10;
|
||||
|
||||
// The max Z angle for a normal at which it will get completely ignored.
|
||||
double normal_cutoff_angle = 150.0 * M_PI / 180.0;
|
||||
static const double normal_cutoff_angle;
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Compile time configuration values (candidates for runtime)
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The shortest distance of any support structure from the model surface
|
||||
double safety_distance_mm = 0.1;
|
||||
static const double safety_distance_mm;
|
||||
|
||||
double max_solo_pillar_height_mm = 5.0;
|
||||
|
||||
double max_dual_pillar_height_mm = 35.0;
|
||||
static const double max_solo_pillar_height_mm;
|
||||
static const double max_dual_pillar_height_mm;
|
||||
static const double optimizer_rel_score_diff;
|
||||
static const unsigned optimizer_max_iterations;
|
||||
static const unsigned pillar_cascade_neighbors;
|
||||
};
|
||||
|
||||
struct PoolConfig;
|
||||
|
Loading…
Reference in New Issue
Block a user