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