1 #ifndef SLA_SUPPORTTREEBUILDER_HPP
2 #define SLA_SUPPORTTREEBUILDER_HPP
3 
4 #include <libslic3r/SLA/Concurrency.hpp>
5 #include <libslic3r/SLA/SupportTree.hpp>
6 #include <libslic3r/SLA/Contour3D.hpp>
7 #include <libslic3r/SLA/Pad.hpp>
8 #include <libslic3r/MTUtils.hpp>
9 
10 namespace Slic3r {
11 namespace sla {
12 
13 /**
14  * Terminology:
15  *
16  * Support point:
17  * The point on the model surface that needs support.
18  *
19  * Pillar:
20  * A thick column that spans from a support point to the ground and has
21  * a thick cone shaped base where it touches the ground.
22  *
23  * Ground facing support point:
24  * A support point that can be directly connected with the ground with a pillar
25  * that does not collide or cut through the model.
26  *
27  * Non ground facing support point:
28  * A support point that cannot be directly connected with the ground (only with
29  * the model surface).
30  *
31  * Head:
32  * The pinhead that connects to the model surface with the sharp end end
33  * to a pillar or bridge stick with the dull end.
34  *
35  * Headless support point:
36  * A support point on the model surface for which there is not enough place for
37  * the head. It is either in a hole or there is some barrier that would collide
38  * with the head geometry. The headless support point can be ground facing and
39  * non ground facing as well.
40  *
41  * Bridge:
42  * A stick that connects two pillars or a head with a pillar.
43  *
44  * Junction:
45  * A small ball in the intersection of two or more sticks (pillar, bridge, ...)
46  *
47  * CompactBridge:
48  * A bridge that connects a headless support point with the model surface or a
49  * nearby pillar.
50  */
51 
distance(const Vec & p)52 template<class Vec> double distance(const Vec& p) {
53     return std::sqrt(p.transpose() * p);
54 }
55 
distance(const Vec & pp1,const Vec & pp2)56 template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
57     auto p = pp2 - pp1;
58     return distance(p);
59 }
60 
61 const Vec3d DOWN = {0.0, 0.0, -1.0};
62 
63 struct SupportTreeNode
64 {
65     static const constexpr long ID_UNSET = -1;
66 
67     long id = ID_UNSET; // For identification withing a tree.
68 };
69 
70 // A pinhead originating from a support point
71 struct Head: public SupportTreeNode {
72     Vec3d dir = DOWN;
73     Vec3d pos = {0, 0, 0};
74 
75     double r_back_mm = 1;
76     double r_pin_mm = 0.5;
77     double width_mm = 2;
78     double penetration_mm = 0.5;
79 
80 
81     // If there is a pillar connecting to this head, then the id will be set.
82     long pillar_id = ID_UNSET;
83 
84     long bridge_id = ID_UNSET;
85 
invalidateSlic3r::sla::Head86     inline void invalidate() { id = ID_UNSET; }
is_validSlic3r::sla::Head87     inline bool is_valid() const { return id >= 0; }
88 
89     Head(double r_big_mm,
90          double r_small_mm,
91          double length_mm,
92          double penetration,
93          const Vec3d &direction = DOWN,  // direction (normal to the dull end)
94          const Vec3d &offset = {0, 0, 0}      // displacement
95          );
96 
real_widthSlic3r::sla::Head97     inline double real_width() const
98     {
99         return 2 * r_pin_mm + width_mm + 2 * r_back_mm ;
100     }
101 
fullwidthSlic3r::sla::Head102     inline double fullwidth() const
103     {
104         return real_width() - penetration_mm;
105     }
106 
junction_pointSlic3r::sla::Head107     inline Vec3d junction_point() const
108     {
109         return pos + (fullwidth() - r_back_mm) * dir;
110     }
111 
request_pillar_radiusSlic3r::sla::Head112     inline double request_pillar_radius(double radius) const
113     {
114         const double rmax = r_back_mm;
115         return radius > 0 && radius < rmax ? radius : rmax;
116     }
117 };
118 
119 // A junction connecting bridges and pillars
120 struct Junction: public SupportTreeNode {
121     double r = 1;
122     Vec3d pos;
123 
JunctionSlic3r::sla::Junction124     Junction(const Vec3d &tr, double r_mm) : r(r_mm), pos(tr) {}
125 };
126 
127 struct Pillar: public SupportTreeNode {
128     double height, r;
129     Vec3d endpt;
130 
131     // If the pillar connects to a head, this is the id of that head
132     bool starts_from_head = true; // Could start from a junction as well
133     long start_junction_id = ID_UNSET;
134 
135     // How many bridges are connected to this pillar
136     unsigned bridges = 0;
137 
138     // How many pillars are cascaded with this one
139     unsigned links = 0;
140 
PillarSlic3r::sla::Pillar141     Pillar(const Vec3d &endp, double h, double radius = 1.):
142         height{h}, r(radius), endpt(endp), starts_from_head(false) {}
143 
startpointSlic3r::sla::Pillar144     Vec3d startpoint() const
145     {
146         return {endpt.x(), endpt.y(), endpt.z() + height};
147     }
148 
endpointSlic3r::sla::Pillar149     const Vec3d& endpoint() const { return endpt; }
150 };
151 
152 // A base for pillars or bridges that end on the ground
153 struct Pedestal: public SupportTreeNode {
154     Vec3d pos;
155     double height, r_bottom, r_top;
156 
PedestalSlic3r::sla::Pedestal157     Pedestal(const Vec3d &p, double h, double rbottom, double rtop)
158         : pos{p}, height{h}, r_bottom{rbottom}, r_top{rtop}
159     {}
160 };
161 
162 // This is the thing that anchors a pillar or bridge to the model body.
163 // It is actually a reverse pinhead.
164 struct Anchor: public Head { using Head::Head; };
165 
166 // A Bridge between two pillars (with junction endpoints)
167 struct Bridge: public SupportTreeNode {
168     double r = 0.8;
169     Vec3d startp = Vec3d::Zero(), endp = Vec3d::Zero();
170 
BridgeSlic3r::sla::Bridge171     Bridge(const Vec3d &j1,
172            const Vec3d &j2,
173            double       r_mm  = 0.8): r{r_mm}, startp{j1}, endp{j2}
174     {}
175 
get_lengthSlic3r::sla::Bridge176     double get_length() const { return (endp - startp).norm(); }
get_dirSlic3r::sla::Bridge177     Vec3d  get_dir() const { return (endp - startp).normalized(); }
178 };
179 
180 struct DiffBridge: public Bridge {
181     double end_r;
182 
DiffBridgeSlic3r::sla::DiffBridge183     DiffBridge(const Vec3d &p_s, const Vec3d &p_e, double r_s, double r_e)
184         : Bridge{p_s, p_e, r_s}, end_r{r_e}
185     {}
186 };
187 
188 // A wrapper struct around the pad
189 struct Pad {
190     TriangleMesh tmesh;
191     PadConfig cfg;
192     double zlevel = 0;
193 
194     Pad() = default;
195 
196     Pad(const TriangleMesh &support_mesh,
197         const ExPolygons &  model_contours,
198         double              ground_level,
199         const PadConfig &   pcfg,
200         ThrowOnCancel       thr);
201 
emptySlic3r::sla::Pad202     bool empty() const { return tmesh.facets_count() == 0; }
203 };
204 
205 // This class will hold the support tree meshes with some additional
206 // bookkeeping as well. Various parts of the support geometry are stored
207 // separately and are merged when the caller queries the merged mesh. The
208 // merged result is cached for fast subsequent delivery of the merged mesh
209 // which can be quite complex. The support tree creation algorithm can use an
210 // instance of this class as a somewhat higher level tool for crafting the 3D
211 // support mesh. Parts can be added with the appropriate methods such as
212 // add_head or add_pillar which forwards the constructor arguments and fills
213 // the IDs of these substructures. The IDs are basically indices into the
214 // arrays of the appropriate type (heads, pillars, etc...). One can later query
215 // e.g. a pillar for a specific head...
216 //
217 // The support pad is considered an auxiliary geometry and is not part of the
218 // merged mesh. It can be retrieved using a dedicated method (pad())
219 class SupportTreeBuilder: public SupportTree {
220     // For heads it is beneficial to use the same IDs as for the support points.
221     std::vector<Head>       m_heads;
222     std::vector<size_t>     m_head_indices;
223     std::vector<Pillar>     m_pillars;
224     std::vector<Junction>   m_junctions;
225     std::vector<Bridge>     m_bridges;
226     std::vector<Bridge>     m_crossbridges;
227     std::vector<DiffBridge> m_diffbridges;
228     std::vector<Pedestal>   m_pedestals;
229     std::vector<Anchor>     m_anchors;
230 
231     Pad m_pad;
232 
233     using Mutex = ccr::SpinningMutex;
234 
235     mutable TriangleMesh m_meshcache;
236     mutable Mutex m_mutex;
237     mutable bool m_meshcache_valid = false;
238     mutable double m_model_height = 0; // the full height of the model
239 
240     template<class BridgeT, class...Args>
_add_bridge(std::vector<BridgeT> & br,Args &&...args)241     const BridgeT& _add_bridge(std::vector<BridgeT> &br, Args&&... args)
242     {
243         std::lock_guard<Mutex> lk(m_mutex);
244         br.emplace_back(std::forward<Args>(args)...);
245         br.back().id = long(br.size() - 1);
246         m_meshcache_valid = false;
247         return br.back();
248     }
249 
250 public:
251     double ground_level = 0;
252 
253     SupportTreeBuilder() = default;
254     SupportTreeBuilder(SupportTreeBuilder &&o);
255     SupportTreeBuilder(const SupportTreeBuilder &o);
256     SupportTreeBuilder& operator=(SupportTreeBuilder &&o);
257     SupportTreeBuilder& operator=(const SupportTreeBuilder &o);
258 
add_head(unsigned id,Args &&...args)259     template<class...Args> Head& add_head(unsigned id, Args&&... args)
260     {
261         std::lock_guard<Mutex> lk(m_mutex);
262         m_heads.emplace_back(std::forward<Args>(args)...);
263         m_heads.back().id = id;
264 
265         if (id >= m_head_indices.size()) m_head_indices.resize(id + 1);
266         m_head_indices[id] = m_heads.size() - 1;
267 
268         m_meshcache_valid = false;
269         return m_heads.back();
270     }
271 
add_pillar(long headid,double length)272     template<class...Args> long add_pillar(long headid, double length)
273     {
274         std::lock_guard<Mutex> lk(m_mutex);
275         if (m_pillars.capacity() < m_heads.size())
276             m_pillars.reserve(m_heads.size() * 10);
277 
278         assert(headid >= 0 && size_t(headid) < m_head_indices.size());
279         Head &head = m_heads[m_head_indices[size_t(headid)]];
280 
281         Vec3d hjp = head.junction_point() - Vec3d{0, 0, length};
282         m_pillars.emplace_back(hjp, length, head.r_back_mm);
283 
284         Pillar& pillar = m_pillars.back();
285         pillar.id = long(m_pillars.size() - 1);
286         head.pillar_id = pillar.id;
287         pillar.start_junction_id = head.id;
288         pillar.starts_from_head = true;
289 
290         m_meshcache_valid = false;
291         return pillar.id;
292     }
293 
294     void add_pillar_base(long pid, double baseheight = 3, double radius = 2);
295 
add_anchor(Args &&...args)296     template<class...Args> const Anchor& add_anchor(Args&&...args)
297     {
298         std::lock_guard<Mutex> lk(m_mutex);
299         m_anchors.emplace_back(std::forward<Args>(args)...);
300         m_anchors.back().id = long(m_junctions.size() - 1);
301         m_meshcache_valid = false;
302         return m_anchors.back();
303     }
304 
increment_bridges(const Pillar & pillar)305     void increment_bridges(const Pillar& pillar)
306     {
307         std::lock_guard<Mutex> lk(m_mutex);
308         assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
309 
310         if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
311             m_pillars[size_t(pillar.id)].bridges++;
312     }
313 
increment_links(const Pillar & pillar)314     void increment_links(const Pillar& pillar)
315     {
316         std::lock_guard<Mutex> lk(m_mutex);
317         assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
318 
319         if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
320             m_pillars[size_t(pillar.id)].links++;
321     }
322 
bridgecount(const Pillar & pillar) const323     unsigned bridgecount(const Pillar &pillar) const {
324         std::lock_guard<Mutex> lk(m_mutex);
325         assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
326         return pillar.bridges;
327     }
328 
add_pillar(Args &&...args)329     template<class...Args> long add_pillar(Args&&...args)
330     {
331         std::lock_guard<Mutex> lk(m_mutex);
332         if (m_pillars.capacity() < m_heads.size())
333             m_pillars.reserve(m_heads.size() * 10);
334 
335         m_pillars.emplace_back(std::forward<Args>(args)...);
336         Pillar& pillar = m_pillars.back();
337         pillar.id = long(m_pillars.size() - 1);
338         pillar.starts_from_head = false;
339         m_meshcache_valid = false;
340         return pillar.id;
341     }
342 
add_junction(Args &&...args)343     template<class...Args> const Junction& add_junction(Args&&... args)
344     {
345         std::lock_guard<Mutex> lk(m_mutex);
346         m_junctions.emplace_back(std::forward<Args>(args)...);
347         m_junctions.back().id = long(m_junctions.size() - 1);
348         m_meshcache_valid = false;
349         return m_junctions.back();
350     }
351 
add_bridge(const Vec3d & s,const Vec3d & e,double r)352     const Bridge& add_bridge(const Vec3d &s, const Vec3d &e, double r)
353     {
354         return _add_bridge(m_bridges, s, e, r);
355     }
356 
add_bridge(long headid,const Vec3d & endp)357     const Bridge& add_bridge(long headid, const Vec3d &endp)
358     {
359         std::lock_guard<Mutex> lk(m_mutex);
360         assert(headid >= 0 && size_t(headid) < m_head_indices.size());
361 
362         Head &h = m_heads[m_head_indices[size_t(headid)]];
363         m_bridges.emplace_back(h.junction_point(), endp, h.r_back_mm);
364         m_bridges.back().id = long(m_bridges.size() - 1);
365 
366         h.bridge_id = m_bridges.back().id;
367         m_meshcache_valid = false;
368         return m_bridges.back();
369     }
370 
add_crossbridge(Args &&...args)371     template<class...Args> const Bridge& add_crossbridge(Args&&... args)
372     {
373         return _add_bridge(m_crossbridges, std::forward<Args>(args)...);
374     }
375 
add_diffbridge(Args &&...args)376     template<class...Args> const DiffBridge& add_diffbridge(Args&&... args)
377     {
378         return _add_bridge(m_diffbridges, std::forward<Args>(args)...);
379     }
380 
head(unsigned id)381     Head &head(unsigned id)
382     {
383         std::lock_guard<Mutex> lk(m_mutex);
384         assert(id < m_head_indices.size());
385 
386         m_meshcache_valid = false;
387         return m_heads[m_head_indices[id]];
388     }
389 
pillarcount() const390     inline size_t pillarcount() const {
391         std::lock_guard<Mutex> lk(m_mutex);
392         return m_pillars.size();
393     }
394 
pillars() const395     inline const std::vector<Pillar> &pillars() const { return m_pillars; }
heads() const396     inline const std::vector<Head>   &heads() const { return m_heads; }
bridges() const397     inline const std::vector<Bridge> &bridges() const { return m_bridges; }
crossbridges() const398     inline const std::vector<Bridge> &crossbridges() const { return m_crossbridges; }
399 
pillar(T id) const400     template<class T> inline IntegerOnly<T, const Pillar&> pillar(T id) const
401     {
402         std::lock_guard<Mutex> lk(m_mutex);
403         assert(id >= 0 && size_t(id) < m_pillars.size() &&
404                size_t(id) < std::numeric_limits<size_t>::max());
405 
406         return m_pillars[size_t(id)];
407     }
408 
pillar(T id)409     template<class T> inline IntegerOnly<T, Pillar&> pillar(T id)
410     {
411         std::lock_guard<Mutex> lk(m_mutex);
412         assert(id >= 0 && size_t(id) < m_pillars.size() &&
413                size_t(id) < std::numeric_limits<size_t>::max());
414 
415         return m_pillars[size_t(id)];
416     }
417 
pad() const418     const Pad& pad() const { return m_pad; }
419 
420     // WITHOUT THE PAD!!!
421     const TriangleMesh &merged_mesh(size_t steps = 45) const;
422 
423     // WITH THE PAD
424     double full_height() const;
425 
426     // WITHOUT THE PAD!!!
mesh_height() const427     inline double mesh_height() const
428     {
429         if (!m_meshcache_valid) merged_mesh();
430         return m_model_height;
431     }
432 
433     // Intended to be called after the generation is fully complete
434     const TriangleMesh & merge_and_cleanup();
435 
436     // Implement SupportTree interface:
437 
438     const TriangleMesh &add_pad(const ExPolygons &modelbase,
439                                 const PadConfig & pcfg) override;
440 
remove_pad()441     void remove_pad() override { m_pad = Pad(); }
442 
443     virtual const TriangleMesh &retrieve_mesh(
444         MeshType meshtype = MeshType::Support) const override;
445 };
446 
447 }} // namespace Slic3r::sla
448 
449 #endif // SUPPORTTREEBUILDER_HPP
450