2018-11-02 11:57:57 +01:00
|
|
|
/**
|
|
|
|
|
* In this file we will implement the automatic SLA support tree generation.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <numeric>
|
|
|
|
|
#include "SLASupportTree.hpp"
|
2019-11-05 14:48:00 +01:00
|
|
|
#include "SLACommon.hpp"
|
2018-11-02 11:57:57 +01:00
|
|
|
#include "SLASpatIndex.hpp"
|
2019-09-26 09:42:08 +02:00
|
|
|
#include "SLASupportTreeBuilder.hpp"
|
2018-11-02 11:57:57 +01:00
|
|
|
|
2019-06-18 11:24:50 +02:00
|
|
|
#include <libslic3r/MTUtils.hpp>
|
2019-01-28 11:58:25 +01:00
|
|
|
#include <libslic3r/ClipperUtils.hpp>
|
|
|
|
|
#include <libslic3r/Model.hpp>
|
2018-11-02 11:57:57 +01:00
|
|
|
|
2019-03-07 12:01:21 +01:00
|
|
|
#include <libnest2d/optimizers/nlopt/genetic.hpp>
|
2019-06-11 12:40:07 +02:00
|
|
|
#include <libnest2d/optimizers/nlopt/subplex.hpp>
|
2019-01-02 14:26:25 +01:00
|
|
|
#include <boost/log/trivial.hpp>
|
2019-01-30 13:51:34 +01:00
|
|
|
#include <tbb/parallel_for.h>
|
2019-09-24 15:15:49 +02:00
|
|
|
#include <tbb/mutex.h>
|
|
|
|
|
#include <tbb/spin_mutex.h>
|
2019-02-28 19:05:11 +01:00
|
|
|
#include <libslic3r/I18N.hpp>
|
|
|
|
|
|
|
|
|
|
//! macro used to mark string used at localization,
|
|
|
|
|
//! return same string
|
|
|
|
|
#define L(s) Slic3r::I18N::translate(s)
|
2019-01-02 14:26:25 +01:00
|
|
|
|
2018-11-02 11:57:57 +01:00
|
|
|
namespace Slic3r {
|
|
|
|
|
namespace sla {
|
|
|
|
|
|
2019-03-07 12:01:21 +01:00
|
|
|
// 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
|
2019-03-11 14:55:28 +01:00
|
|
|
const double SupportConfig::safety_distance_mm = 0.5;
|
2019-03-07 12:01:21 +01:00
|
|
|
|
2019-03-08 11:39:34 +01:00
|
|
|
const double SupportConfig::max_solo_pillar_height_mm = 15.0;
|
2019-03-07 12:01:21 +01:00
|
|
|
const double SupportConfig::max_dual_pillar_height_mm = 35.0;
|
|
|
|
|
const double SupportConfig::optimizer_rel_score_diff = 1e-6;
|
2019-03-08 16:06:21 +01:00
|
|
|
const unsigned SupportConfig::optimizer_max_iterations = 1000;
|
2019-03-13 17:29:47 +01:00
|
|
|
const unsigned SupportConfig::pillar_cascade_neighbors = 3;
|
2019-03-07 17:17:47 +01:00
|
|
|
const unsigned SupportConfig::max_bridges_on_pillar = 3;
|
2019-03-07 12:01:21 +01:00
|
|
|
|
2019-09-26 09:42:08 +02:00
|
|
|
void SupportTree::retrieve_full_mesh(TriangleMesh &outmesh) const {
|
|
|
|
|
outmesh.merge(retrieve_mesh(MeshType::Support));
|
|
|
|
|
outmesh.merge(retrieve_mesh(MeshType::Pad));
|
2018-11-02 11:57:57 +01:00
|
|
|
}
|
|
|
|
|
|
2019-09-26 09:42:08 +02:00
|
|
|
std::vector<ExPolygons> SupportTree::slice(
|
2019-08-08 19:10:22 +02:00
|
|
|
const std::vector<float> &grid, float cr) const
|
2018-11-07 17:57:02 +01:00
|
|
|
{
|
2019-09-26 09:42:08 +02:00
|
|
|
const TriangleMesh &sup_mesh = retrieve_mesh(MeshType::Support);
|
|
|
|
|
const TriangleMesh &pad_mesh = retrieve_mesh(MeshType::Pad);
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-08-08 19:10:22 +02:00
|
|
|
using Slices = std::vector<ExPolygons>;
|
|
|
|
|
auto slices = reserve_vector<Slices>(2);
|
|
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
if (!sup_mesh.empty()) {
|
2019-08-08 19:10:22 +02:00
|
|
|
slices.emplace_back();
|
|
|
|
|
|
2019-07-30 17:57:07 +02:00
|
|
|
TriangleMeshSlicer sup_slicer(&sup_mesh);
|
2019-09-26 09:42:08 +02:00
|
|
|
sup_slicer.slice(grid, cr, &slices.back(), ctl().cancelfn);
|
2018-11-07 17:57:02 +01:00
|
|
|
}
|
2019-08-16 16:17:37 +02:00
|
|
|
|
|
|
|
|
if (!pad_mesh.empty()) {
|
2019-08-08 19:10:22 +02:00
|
|
|
slices.emplace_back();
|
|
|
|
|
|
|
|
|
|
auto bb = pad_mesh.bounding_box();
|
|
|
|
|
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
|
2019-09-24 15:15:49 +02:00
|
|
|
|
2019-10-02 14:55:02 +02:00
|
|
|
auto cap = grid.end() - maxzit;
|
2019-09-24 15:15:49 +02:00
|
|
|
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
|
2019-08-08 19:10:22 +02:00
|
|
|
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
|
|
|
|
|
|
2019-07-30 17:57:07 +02:00
|
|
|
TriangleMeshSlicer pad_slicer(&pad_mesh);
|
2019-09-26 09:42:08 +02:00
|
|
|
pad_slicer.slice(padgrid, cr, &slices.back(), ctl().cancelfn);
|
2019-07-30 17:57:07 +02:00
|
|
|
}
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-08-08 19:10:22 +02:00
|
|
|
size_t len = grid.size();
|
2019-08-09 17:13:18 +02:00
|
|
|
for (const Slices &slv : slices) { len = std::min(len, slv.size()); }
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-08-08 19:10:22 +02:00
|
|
|
// Either the support or the pad or both has to be non empty
|
2019-08-09 17:13:18 +02:00
|
|
|
if (slices.empty()) return {};
|
2019-08-08 19:10:22 +02:00
|
|
|
|
|
|
|
|
Slices &mrg = slices.front();
|
|
|
|
|
|
|
|
|
|
for (auto it = std::next(slices.begin()); it != slices.end(); ++it) {
|
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
|
|
|
Slices &slv = *it;
|
|
|
|
|
std::copy(slv[i].begin(), slv[i].end(), std::back_inserter(mrg[i]));
|
|
|
|
|
slv[i] = {}; // clear and delete
|
|
|
|
|
}
|
2019-07-30 17:57:07 +02:00
|
|
|
}
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-08-08 19:10:22 +02:00
|
|
|
return mrg;
|
2019-03-21 16:14:26 +01:00
|
|
|
}
|
|
|
|
|
|
2019-09-26 09:42:08 +02:00
|
|
|
SupportTree::UPtr SupportTree::create(const SupportableMesh &sm,
|
|
|
|
|
const JobController & ctl)
|
2018-11-08 10:21:13 +01:00
|
|
|
{
|
2019-09-26 09:42:08 +02:00
|
|
|
auto builder = make_unique<SupportTreeBuilder>();
|
|
|
|
|
builder->m_ctl = ctl;
|
|
|
|
|
|
|
|
|
|
if (sm.cfg.enabled) {
|
|
|
|
|
builder->build(sm);
|
|
|
|
|
builder->merge_and_cleanup(); // clean metadata, leave only the meshes.
|
|
|
|
|
} else {
|
|
|
|
|
builder->ground_level = sm.emesh.ground_level();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::move(builder);
|
2018-11-14 18:04:43 +01:00
|
|
|
}
|
2018-11-08 10:21:13 +01:00
|
|
|
|
2019-09-26 09:42:08 +02:00
|
|
|
}} // namespace Slic3r::sla
|