Reworked pad creation algorithm with new parameters:

* brim size
* force pad around object everywhere
This commit is contained in:
tamasmeszaros 2019-09-24 15:15:49 +02:00
parent 9d775d0a43
commit e675a5d5c6
27 changed files with 1410 additions and 1448 deletions

View file

@ -7,7 +7,7 @@
#include "SLASupportTree.hpp"
#include "SLABoilerPlate.hpp"
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
#include "SLAPad.hpp"
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
@ -17,6 +17,8 @@
#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <tbb/mutex.h>
#include <tbb/spin_mutex.h>
#include <libslic3r/I18N.hpp>
//! macro used to mark string used at localization,
@ -91,27 +93,34 @@ template<bool> struct _ccr {};
template<> struct _ccr<true>
{
using Mutex = SpinMutex;
using SpinningMutex = tbb::spin_mutex;
using LockingMutex = tbb::mutex;
template<class It, class Fn>
static inline void enumerate(It from, It to, Fn fn)
{
using TN = size_t;
auto iN = to - from;
TN N = iN < 0 ? 0 : TN(iN);
auto iN = to - from;
size_t N = iN < 0 ? 0 : size_t(iN);
tbb::parallel_for(TN(0), N, [from, fn](TN n) { fn(*(from + n), n); });
tbb::parallel_for(size_t(0), N, [from, fn](size_t n) {
fn(*(from + decltype(iN)(n)), n);
});
}
};
template<> struct _ccr<false>
{
struct Mutex { inline void lock() {} inline void unlock() {} };
private:
struct _Mtx { inline void lock() {} inline void unlock() {} };
public:
using SpinningMutex = _Mtx;
using LockingMutex = _Mtx;
template<class It, class Fn>
static inline void enumerate(It from, It to, Fn fn)
{
for (auto it = from; it != to; ++it) fn(*it, it - from);
for (auto it = from; it != to; ++it) fn(*it, size_t(it - from));
}
};
@ -132,6 +141,8 @@ template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
return distance(p);
}
namespace {
Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
double fa=(2*PI/360)) {
@ -175,10 +186,11 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
vertices.emplace_back(Vec3d(b(0), b(1), z));
if(sbegin == 0)
facets.emplace_back((i == 0) ? Vec3crd(coord_t(ring.size()), 0, 1) :
Vec3crd(id - 1, 0, id));
++ id;
if (sbegin == 0)
facets.emplace_back((i == 0) ?
Vec3crd(coord_t(ring.size()), 0, 1) :
Vec3crd(id - 1, 0, id));
++id;
}
// General case: insert and form facets for each step,
@ -229,7 +241,7 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
// h: Height
// ssteps: how many edges will create the base circle
// sp: starting point
Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp = {0,0,0})
{
Contour3D ret;
@ -289,6 +301,8 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
return ret;
}
const constexpr long ID_UNSET = -1;
struct Head {
Contour3D mesh;
@ -300,25 +314,25 @@ struct Head {
double r_pin_mm = 0.5;
double width_mm = 2;
double penetration_mm = 0.5;
// For identification purposes. This will be used as the index into the
// container holding the head structures. See SLASupportTree::Impl
long id = -1;
long id = ID_UNSET;
// If there is a pillar connecting to this head, then the id will be set.
long pillar_id = -1;
long pillar_id = ID_UNSET;
inline void invalidate() { id = -1; }
inline void invalidate() { id = ID_UNSET; }
inline bool is_valid() const { return id >= 0; }
Head(double r_big_mm,
double r_small_mm,
double length_mm,
double penetration,
Vec3d direction = {0, 0, -1}, // direction (normal to the dull end )
Vec3d offset = {0, 0, 0}, // displacement
const Vec3d &direction = {0, 0, -1}, // direction (normal to the dull end)
const Vec3d &offset = {0, 0, 0}, // displacement
const size_t circlesteps = 45):
steps(circlesteps), dir(direction), tr(offset),
steps(circlesteps), dir(direction), tr(offset),
r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm),
penetration_mm(penetration)
{
@ -347,7 +361,7 @@ struct Head {
auto&& s1 = sphere(r_big_mm, make_portion(0, PI/2 + phi), detail);
auto&& s2 = sphere(r_small_mm, make_portion(PI/2 + phi, PI), detail);
for(auto& p : s2.points) z(p) += h;
for(auto& p : s2.points) p.z() += h;
mesh.merge(s1);
mesh.merge(s2);
@ -373,7 +387,7 @@ struct Head {
// To simplify further processing, we translate the mesh so that the
// last vertex of the pointing sphere (the pinpoint) will be at (0,0,0)
for(auto& p : mesh.points) z(p) -= (h + r_small_mm - penetration_mm);
for(auto& p : mesh.points) p.z() -= (h + r_small_mm - penetration_mm);
}
void transform()
@ -393,11 +407,6 @@ struct Head {
return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm;
}
static double fullwidth(const SupportConfig& cfg) {
return 2 * cfg.head_front_radius_mm + cfg.head_width_mm +
2 * cfg.head_back_radius_mm - cfg.head_penetration_mm;
}
Vec3d junction_point() const {
return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir;
}
@ -414,7 +423,7 @@ struct Junction {
size_t steps = 45;
Vec3d pos;
long id = -1;
long id = ID_UNSET;
Junction(const Vec3d& tr, double r_mm, size_t stepnum = 45):
r(r_mm), steps(stepnum), pos(tr)
@ -432,11 +441,11 @@ struct Pillar {
Vec3d endpt;
double height = 0;
long id = -1;
long id = ID_UNSET;
// If the pillar connects to a head, this is the id of that head
bool starts_from_head = true; // Could start from a junction as well
long start_junction_id = -1;
long start_junction_id = ID_UNSET;
// How many bridges are connected to this pillar
unsigned bridges = 0;
@ -461,22 +470,24 @@ struct Pillar {
}
}
Pillar(const Junction& junc, const Vec3d& endp):
Pillar(junc.pos, endp, junc.r, junc.steps){}
Pillar(const Junction &junc, const Vec3d &endp)
: Pillar(junc.pos, endp, junc.r, junc.steps)
{}
Pillar(const Head& head, const Vec3d& endp, double radius = 1):
Pillar(head.junction_point(), endp, head.request_pillar_radius(radius),
head.steps)
Pillar(const Head &head, const Vec3d &endp, double radius = 1)
: Pillar(head.junction_point(), endp,
head.request_pillar_radius(radius), head.steps)
{}
inline Vec3d startpoint() const
{
}
inline Vec3d startpoint() const {
return {endpt(X), endpt(Y), endpt(Z) + height};
}
inline const Vec3d& endpoint() const { return endpt; }
Pillar& add_base(double baseheight = 3, double radius = 2) {
Pillar& add_base(double baseheight = 3, double radius = 2)
{
if(baseheight <= 0) return *this;
if(baseheight > height) baseheight = height;
@ -523,8 +534,6 @@ struct Pillar {
indices.emplace_back(offs, offs + last, lcenter);
return *this;
}
bool has_base() const { return !base.points.empty(); }
};
// A Bridge between two pillars (with junction endpoints)
@ -532,9 +541,9 @@ struct Bridge {
Contour3D mesh;
double r = 0.8;
long id = -1;
long start_jid = -1;
long end_jid = -1;
long id = ID_UNSET;
long start_jid = ID_UNSET;
long end_jid = ID_UNSET;
// We should reduce the radius a tiny bit to help the convex hull algorithm
Bridge(const Vec3d& j1, const Vec3d& j2,
@ -550,17 +559,13 @@ struct Bridge {
auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir);
for(auto& p : mesh.points) p = quater * p + j1;
}
Bridge(const Junction& j1, const Junction& j2, double r_mm = 0.8):
Bridge(j1.pos, j2.pos, r_mm, j1.steps) {}
};
// A bridge that spans from model surface to model surface with small connecting
// edges on the endpoints. Used for headless support points.
struct CompactBridge {
Contour3D mesh;
long id = -1;
long id = ID_UNSET;
CompactBridge(const Vec3d& sp,
const Vec3d& ep,
@ -594,123 +599,28 @@ struct CompactBridge {
// A wrapper struct around the base pool (pad)
struct Pad {
TriangleMesh tmesh;
PoolConfig cfg;
PadConfig cfg;
double zlevel = 0;
Pad() = default;
Pad(const TriangleMesh& support_mesh,
const ExPolygons& modelbase,
double ground_level,
const PoolConfig& pcfg) :
cfg(pcfg),
zlevel(ground_level +
sla::get_pad_fullheight(pcfg) -
sla::get_pad_elevation(pcfg))
Pad(const TriangleMesh &support_mesh,
const ExPolygons & model_contours,
double ground_level,
const PadConfig & pcfg,
ThrowOnCancel thr)
: cfg(pcfg)
, zlevel(ground_level + pcfg.full_height() - pcfg.required_elevation())
{
Polygons basep;
auto &thr = cfg.throw_on_cancel;
thr();
// Get a sample for the pad from the support mesh
{
ExPolygons platetmp;
ExPolygons sup_contours;
float zstart = float(zlevel);
float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
float zstart = float(zlevel);
float zend = zstart + float(pcfg.full_height() + EPSILON);
base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr);
// We don't need no... holes control...
for (const ExPolygon &bp : platetmp)
basep.emplace_back(std::move(bp.contour));
}
if(pcfg.embed_object) {
// If the zero elevation mode is ON, we need to process the model
// base silhouette. Create the offsetted version and punch the
// breaksticks across its perimeter.
ExPolygons modelbase_offs = modelbase;
if (pcfg.embed_object.object_gap_mm > 0.0)
modelbase_offs
= offset_ex(modelbase_offs,
float(scaled(pcfg.embed_object.object_gap_mm)));
// Create a spatial index of the support silhouette polygons.
// This will be used to check for intersections with the model
// silhouette polygons. If there is no intersection, then a certain
// part of the pad is redundant as it does not host any supports.
BoxIndex bindex;
{
unsigned idx = 0;
for(auto &bp : basep) {
auto bb = bp.bounding_box();
bb.offset(float(scaled(pcfg.min_wall_thickness_mm)));
bindex.insert(bb, idx++);
}
}
ExPolygons concaveh = offset_ex(
concave_hull(basep, pcfg.max_merge_distance_mm, thr),
scaled<float>(pcfg.min_wall_thickness_mm));
// Punching the breaksticks across the offsetted polygon perimeters
auto pad_stickholes = reserve_vector<ExPolygon>(modelbase.size());
for(auto& poly : modelbase_offs) {
bool overlap = false;
for (const ExPolygon &p : concaveh)
overlap = overlap || poly.overlaps(p);
auto bb = poly.contour.bounding_box();
bb.offset(scaled<float>(pcfg.min_wall_thickness_mm));
std::vector<BoxIndexEl> qres =
bindex.query(bb, BoxIndex::qtIntersects);
if (!qres.empty() || overlap) {
// The model silhouette polygon 'poly' HAS an intersection
// with the support silhouettes. Include this polygon
// in the pad holes with the breaksticks and merge the
// original (offsetted) version with the rest of the pad
// base plate.
basep.emplace_back(poly.contour);
// The holes of 'poly' will become positive parts of the
// pad, so they has to be checked for intersections as well
// and erased if there is no intersection with the supports
auto it = poly.holes.begin();
while(it != poly.holes.end()) {
if (bindex.query(it->bounding_box(),
BoxIndex::qtIntersects).empty())
it = poly.holes.erase(it);
else
++it;
}
// Punch the breaksticks
sla::breakstick_holes(
poly,
pcfg.embed_object.object_gap_mm, // padding
pcfg.embed_object.stick_stride_mm,
pcfg.embed_object.stick_width_mm,
pcfg.embed_object.stick_penetration_mm);
pad_stickholes.emplace_back(poly);
}
}
create_base_pool(basep, tmesh, pad_stickholes, cfg);
} else {
for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
create_base_pool(basep, tmesh, {}, cfg);
}
pad_blueprint(support_mesh, sup_contours, grid(zstart, zend, 0.1f), thr);
create_pad(sup_contours, model_contours, tmesh, pcfg);
tmesh.translate(0, 0, float(zlevel));
if (!tmesh.empty()) tmesh.require_shared_vertices();
@ -720,43 +630,18 @@ struct Pad {
};
// The minimum distance for two support points to remain valid.
static const double /*constexpr*/ D_SP = 0.1;
const double /*constexpr*/ D_SP = 0.1;
} // namespace
enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers
X, Y, Z
};
// Calculate the normals for the selected points (from 'points' set) on the
// mesh. This will call squared distance for each point.
PointSet normals(const PointSet& points,
const EigenMesh3D& mesh,
double eps = 0.05, // min distance from edges
std::function<void()> throw_on_cancel = [](){},
const std::vector<unsigned>& selected_points = {});
inline Vec2d to_vec2(const Vec3d& v3) {
return {v3(X), v3(Y)};
}
bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) {
return e1.second == e2.second;
}
// Clustering a set of points by the given distance.
ClusteredPoints cluster(const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
double dist,
unsigned max_points);
ClusteredPoints cluster(const PointSet& points,
double dist,
unsigned max_points);
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points);
// This class will hold the support tree meshes with some additional bookkeeping
// as well. Various parts of the support geometry are stored separately and are
@ -775,20 +660,20 @@ class SLASupportTree::Impl {
// For heads it is beneficial to use the same IDs as for the support points.
std::vector<Head> m_heads;
std::vector<size_t> m_head_indices;
std::vector<Pillar> m_pillars;
std::vector<Junction> m_junctions;
std::vector<Bridge> m_bridges;
std::vector<CompactBridge> m_compact_bridges;
std::vector<CompactBridge> m_compact_bridges;
Pad m_pad;
Controller m_ctl;
Pad m_pad;
using Mutex = ccr::Mutex;
using Mutex = ccr::SpinningMutex;
mutable TriangleMesh m_meshcache;
mutable Mutex m_mutex;
mutable TriangleMesh meshcache; mutable bool meshcache_valid = false;
mutable double model_height = 0; // the full height of the model
mutable bool m_meshcache_valid = false;
mutable double m_model_height = 0; // the full height of the model
public:
double ground_level = 0;
@ -807,7 +692,7 @@ public:
if (id >= m_head_indices.size()) m_head_indices.resize(id + 1);
m_head_indices[id] = m_heads.size() - 1;
meshcache_valid = false;
m_meshcache_valid = false;
return m_heads.back();
}
@ -825,7 +710,7 @@ public:
pillar.start_junction_id = head.id;
pillar.starts_from_head = true;
meshcache_valid = false;
m_meshcache_valid = false;
return m_pillars.back();
}
@ -854,22 +739,10 @@ public:
Pillar& pillar = m_pillars.back();
pillar.id = long(m_pillars.size() - 1);
pillar.starts_from_head = false;
meshcache_valid = false;
m_meshcache_valid = false;
return m_pillars.back();
}
const Head& pillar_head(long pillar_id) const
{
std::lock_guard<Mutex> lk(m_mutex);
assert(pillar_id >= 0 && pillar_id < long(m_pillars.size()));
const Pillar& p = m_pillars[size_t(pillar_id)];
assert(p.starts_from_head && p.start_junction_id >= 0);
assert(size_t(p.start_junction_id) < m_head_indices.size());
return m_heads[m_head_indices[p.start_junction_id]];
}
const Pillar& head_pillar(unsigned headid) const
{
std::lock_guard<Mutex> lk(m_mutex);
@ -886,7 +759,7 @@ public:
std::lock_guard<Mutex> lk(m_mutex);
m_junctions.emplace_back(std::forward<Args>(args)...);
m_junctions.back().id = long(m_junctions.size() - 1);
meshcache_valid = false;
m_meshcache_valid = false;
return m_junctions.back();
}
@ -895,7 +768,7 @@ public:
std::lock_guard<Mutex> lk(m_mutex);
m_bridges.emplace_back(std::forward<Args>(args)...);
m_bridges.back().id = long(m_bridges.size() - 1);
meshcache_valid = false;
m_meshcache_valid = false;
return m_bridges.back();
}
@ -904,7 +777,7 @@ public:
std::lock_guard<Mutex> lk(m_mutex);
m_compact_bridges.emplace_back(std::forward<Args>(args)...);
m_compact_bridges.back().id = long(m_compact_bridges.size() - 1);
meshcache_valid = false;
m_meshcache_valid = false;
return m_compact_bridges.back();
}
@ -913,7 +786,7 @@ public:
std::lock_guard<Mutex> lk(m_mutex);
assert(id < m_head_indices.size());
meshcache_valid = false;
m_meshcache_valid = false;
return m_heads[m_head_indices[id]];
}
@ -933,9 +806,10 @@ public:
const Pad &create_pad(const TriangleMesh &object_supports,
const ExPolygons & modelbase,
const PoolConfig & cfg)
const PadConfig & cfg)
{
m_pad = Pad(object_supports, modelbase, ground_level, cfg);
m_pad = Pad{object_supports, modelbase, ground_level, cfg, m_ctl.cancelfn};
return m_pad;
}
@ -946,7 +820,7 @@ public:
// WITHOUT THE PAD!!!
const TriangleMesh &merged_mesh() const
{
if (meshcache_valid) return meshcache;
if (m_meshcache_valid) return m_meshcache;
Contour3D merged;
@ -978,39 +852,39 @@ public:
if (m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
return meshcache;
m_meshcache = TriangleMesh();
return m_meshcache;
}
meshcache = mesh(merged);
m_meshcache = mesh(merged);
// The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this.
if (!meshcache.empty()) meshcache.require_shared_vertices();
if (!m_meshcache.empty()) m_meshcache.require_shared_vertices();
BoundingBoxf3 &&bb = meshcache.bounding_box();
model_height = bb.max(Z) - bb.min(Z);
BoundingBoxf3 &&bb = m_meshcache.bounding_box();
m_model_height = bb.max(Z) - bb.min(Z);
meshcache_valid = true;
return meshcache;
m_meshcache_valid = true;
return m_meshcache;
}
// WITH THE PAD
double full_height() const
{
if (merged_mesh().empty() && !pad().empty())
return get_pad_fullheight(pad().cfg);
return pad().cfg.full_height();
double h = mesh_height();
if (!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
if (!pad().empty()) h += pad().cfg.required_elevation();
return h;
}
// WITHOUT THE PAD!!!
double mesh_height() const
{
if (!meshcache_valid) merged_mesh();
return model_height;
if (!m_meshcache_valid) merged_mesh();
return m_model_height;
}
// Intended to be called after the generation is fully complete
@ -1032,11 +906,11 @@ public:
// vector of point indices.
template<class DistFn>
long cluster_centroid(const ClusterEl& clust,
std::function<Vec3d(size_t)> pointfn,
const std::function<Vec3d(size_t)> &pointfn,
DistFn df)
{
switch(clust.size()) {
case 0: /* empty cluster */ return -1;
case 0: /* empty cluster */ return ID_UNSET;
case 1: /* only one element */ return 0;
case 2: /* if two elements, there is no center */ return 0;
default: ;
@ -1116,8 +990,53 @@ class SLASupportTree::Algorithm {
ThrowOnCancel m_thr;
// A spatial index to easily find strong pillars to connect to.
PointIndex m_pillar_index;
class PillarIndex {
PointIndex m_index;
mutable ccr::LockingMutex m_mutex;
public:
template<class...Args> inline void guarded_insert(Args&&...args)
{
std::lock_guard<ccr::LockingMutex> lck(m_mutex);
m_index.insert(std::forward<Args>(args)...);
}
template<class...Args>
inline std::vector<PointIndexEl> guarded_query(Args&&...args) const
{
std::lock_guard<ccr::LockingMutex> lck(m_mutex);
return m_index.query(std::forward<Args>(args)...);
}
template<class...Args> inline void insert(Args&&...args)
{
m_index.insert(std::forward<Args>(args)...);
}
template<class...Args>
inline std::vector<PointIndexEl> query(Args&&...args) const
{
return m_index.query(std::forward<Args>(args)...);
}
template<class Fn> inline void foreach(Fn fn) { m_index.foreach(fn); }
template<class Fn> inline void guarded_foreach(Fn fn)
{
std::lock_guard<ccr::LockingMutex> lck(m_mutex);
m_index.foreach(fn);
}
PointIndex guarded_clone()
{
std::lock_guard<ccr::LockingMutex> lck(m_mutex);
return m_index;
}
} m_pillar_index;
inline double ray_mesh_intersect(const Vec3d& s,
const Vec3d& dir)
{
@ -1382,7 +1301,7 @@ class SLASupportTree::Algorithm {
// Align to center
double available_dist = (startz - endz);
double rounds = std::floor(available_dist / std::abs(zstep));
startz -= 0.5 * (available_dist - rounds * std::abs(zstep));;
startz -= 0.5 * (available_dist - rounds * std::abs(zstep));
}
auto pcm = m_cfg.pillar_connection_mode;
@ -1507,9 +1426,9 @@ class SLASupportTree::Algorithm {
}
bool search_pillar_and_connect(const Head& head) {
PointIndex spindex = m_pillar_index;
PointIndex spindex = m_pillar_index.guarded_clone();
long nearest_id = -1;
long nearest_id = ID_UNSET;
Vec3d querypoint = head.junction_point();
@ -1530,8 +1449,8 @@ class SLASupportTree::Algorithm {
auto nearpillarID = unsigned(nearest_id);
if(nearpillarID < m_result.pillarcount()) {
if(!connect_to_nearpillar(head, nearpillarID)) {
nearest_id = -1; // continue searching
spindex.remove(ne); // without the current pillar
nearest_id = ID_UNSET; // continue searching
spindex.remove(ne); // without the current pillar
}
}
}
@ -1545,7 +1464,7 @@ class SLASupportTree::Algorithm {
void create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir,
double radius,
int head_id = -1)
long head_id = ID_UNSET)
{
// People were killed for this number (seriously)
static const double SQR2 = std::sqrt(2.0);
@ -1554,7 +1473,7 @@ class SLASupportTree::Algorithm {
double gndlvl = m_result.ground_level;
Vec3d endp = {jp(X), jp(Y), gndlvl};
double sd = m_cfg.pillar_base_safety_distance_mm;
int pillar_id = -1;
long pillar_id = ID_UNSET;
double min_dist = sd + m_cfg.base_radius_mm + EPSILON;
double dist = 0;
bool can_add_base = true;
@ -1567,7 +1486,7 @@ class SLASupportTree::Algorithm {
// the ground level only.
normal_mode = false;
double mv = min_dist - dist;
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);
@ -1584,14 +1503,14 @@ class SLASupportTree::Algorithm {
auto result = solver.optimize_max(
[this, dir, jp, gndlvl](double mv) {
Vec3d endp = jp + SQR2 * mv * dir;
endp(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endp));
Vec3d endpt = jp + SQR2 * mv * dir;
endpt(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endpt));
},
initvals(mv), bound(0.0, 2 * min_dist));
initvals(mind), bound(0.0, 2 * min_dist));
mv = std::get<0>(result.optimum);
endp = jp + SQR2 * mv * dir;
mind = std::get<0>(result.optimum);
endp = jp + SQR2 * mind * dir;
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
can_add_base = result.score > min_dist;
@ -1651,7 +1570,7 @@ class SLASupportTree::Algorithm {
}
if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
m_pillar_index.insert(endp, pillar_id);
m_pillar_index.guarded_insert(endp, unsigned(pillar_id));
}
public:
@ -1717,9 +1636,9 @@ public:
using libnest2d::opt::GeneticOptimizer;
using libnest2d::opt::StopCriteria;
ccr::Mutex mutex;
ccr::SpinningMutex mutex;
auto addfn = [&mutex](PtIndices &container, unsigned val) {
std::lock_guard<ccr::Mutex> lk(mutex);
std::lock_guard<ccr::SpinningMutex> lk(mutex);
container.emplace_back(val);
};
@ -1727,8 +1646,8 @@ public:
[this, &nmls, addfn](unsigned fidx, size_t i)
{
m_thr();
auto n = nmls.row(i);
auto n = nmls.row(Eigen::Index(i));
// for all normals we generate the spherical coordinates and
// saturate the polar angle to 45 degrees from the bottom then
@ -1786,19 +1705,20 @@ public:
auto oresult = solver.optimize_max(
[this, pin_r, w, hp](double plr, double azm)
{
auto n = Vec3d(std::cos(azm) * std::sin(plr),
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
auto dir = Vec3d(std::cos(azm) * std::sin(plr),
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
double score = pinhead_mesh_intersect(
hp, n, pin_r, m_cfg.head_back_radius_mm, w);
hp, dir, pin_r, m_cfg.head_back_radius_mm, w);
return score;
},
initvals(polar, azimuth), // start with what we have
bound(3*PI/4, PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full search
);
bound(3 * PI / 4,
PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full search
);
if(oresult.score > w) {
polar = std::get<0>(oresult.optimum);
@ -1921,7 +1841,9 @@ public:
ClusterEl cl_centroids;
cl_centroids.reserve(m_pillar_clusters.size());
for(auto& cl : m_pillar_clusters) { m_thr();
for(auto& cl : m_pillar_clusters) {
m_thr();
// place all the centroid head positions into the index. We
// will query for alternative pillar positions. If a sidehead
// cannot connect to the cluster centroid, we have to search
@ -1957,7 +1879,8 @@ public:
// sidepoints with the cluster centroid (which is a ground pillar)
// or a nearby pillar if the centroid is unreachable.
size_t ci = 0;
for(auto cl : m_pillar_clusters) { m_thr();
for(auto cl : m_pillar_clusters) {
m_thr();
auto cidx = cl_centroids[ci++];
@ -2015,7 +1938,7 @@ public:
};
std::vector<unsigned> modelpillars;
ccr::Mutex mutex;
ccr::SpinningMutex mutex;
// TODO: connect these to the ground pillars if possible
ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(),
@ -2161,7 +2084,7 @@ public:
pill.base = tailhead.mesh;
// Experimental: add the pillar to the index for cascading
std::lock_guard<ccr::Mutex> lk(mutex);
std::lock_guard<ccr::SpinningMutex> lk(mutex);
modelpillars.emplace_back(unsigned(pill.id));
return;
}
@ -2184,13 +2107,10 @@ public:
// in O(log) or even constant time with a set or an unordered set of hash
// values uniquely representing a pair of integers. The order of numbers
// within the pair should not matter, it has the same unique hash.
template<class I> static I pairhash(I a, I b)
template<class I> static IntegerOnly<I> pairhash(I a, I b)
{
using std::ceil; using std::log2; using std::max; using std::min;
static_assert(std::is_integral<I>::value,
"This function works only for integral types.");
I g = min(a, b), l = max(a, b);
auto bits_g = g ? int(ceil(log2(g))) : 0;
@ -2414,7 +2334,8 @@ public:
// We will sink the pins into the model surface for a distance of 1/3 of
// the pin radius
for(unsigned i : m_iheadless) { m_thr();
for(unsigned i : m_iheadless) {
m_thr();
const auto R = double(m_support_pts[i].head_front_radius);
const double HWIDTH_MM = R/3;
@ -2617,8 +2538,9 @@ std::vector<ExPolygons> SLASupportTree::slice(
auto bb = pad_mesh.bounding_box();
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
auto padgrid = reserve_vector<float>(grid.end() - maxzit);
long cap = grid.end() - maxzit;
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
TriangleMeshSlicer pad_slicer(&pad_mesh);
@ -2645,7 +2567,7 @@ std::vector<ExPolygons> SLASupportTree::slice(
}
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const
const PadConfig& pcfg) const
{
return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh;
}
@ -2670,6 +2592,9 @@ SLASupportTree::SLASupportTree(const std::vector<SupportPoint> &points,
generate(points, emesh, cfg, ctl);
}
SLASupportTree::SLASupportTree(SLASupportTree &&o) = default;
SLASupportTree &SLASupportTree::operator=(SLASupportTree &&o) = default;
SLASupportTree::~SLASupportTree() {}
}