Welcome to mirror list, hosted at ThFree Co, Russian Federation.

SupportTreeBuilder.hpp « SLA « libslic3r « src - github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f29263ca3f7bb3f1de881636317f1efc2d266b01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
#ifndef SLA_SUPPORTTREEBUILDER_HPP
#define SLA_SUPPORTTREEBUILDER_HPP

#include <libslic3r/SLA/Concurrency.hpp>
#include <libslic3r/SLA/SupportTree.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/Pad.hpp>
#include <libslic3r/MTUtils.hpp>

namespace Slic3r {
namespace sla {

/**
 * Terminology:
 *
 * Support point:
 * The point on the model surface that needs support.
 *
 * Pillar:
 * A thick column that spans from a support point to the ground and has
 * a thick cone shaped base where it touches the ground.
 *
 * Ground facing support point:
 * A support point that can be directly connected with the ground with a pillar
 * that does not collide or cut through the model.
 *
 * Non ground facing support point:
 * A support point that cannot be directly connected with the ground (only with
 * the model surface).
 *
 * Head:
 * The pinhead that connects to the model surface with the sharp end end
 * to a pillar or bridge stick with the dull end.
 *
 * Headless support point:
 * A support point on the model surface for which there is not enough place for
 * the head. It is either in a hole or there is some barrier that would collide
 * with the head geometry. The headless support point can be ground facing and
 * non ground facing as well.
 *
 * Bridge:
 * A stick that connects two pillars or a head with a pillar.
 *
 * Junction:
 * A small ball in the intersection of two or more sticks (pillar, bridge, ...)
 *
 * CompactBridge:
 * A bridge that connects a headless support point with the model surface or a
 * nearby pillar.
 */

template<class Vec> double distance(const Vec& p) {
    return std::sqrt(p.transpose() * p);
}

template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
    auto p = pp2 - pp1;
    return distance(p);
}

const Vec3d DOWN = {0.0, 0.0, -1.0};

struct SupportTreeNode
{
    static const constexpr long ID_UNSET = -1;

    long id = ID_UNSET; // For identification withing a tree.
};

// A pinhead originating from a support point
struct Head: public SupportTreeNode {
    Vec3d dir = DOWN;
    Vec3d pos = {0, 0, 0};
    
    double r_back_mm = 1;
    double r_pin_mm = 0.5;
    double width_mm = 2;
    double penetration_mm = 0.5;

    
    // If there is a pillar connecting to this head, then the id will be set.
    long pillar_id = ID_UNSET;
    
    long bridge_id = ID_UNSET;
    
    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,
         const Vec3d &direction = DOWN,  // direction (normal to the dull end)
         const Vec3d &offset = {0, 0, 0}      // displacement
         );

    inline double real_width() const
    {
        return 2 * r_pin_mm + width_mm + 2 * r_back_mm ;
    }

    inline double fullwidth() const
    {
        return real_width() - penetration_mm;
    }
    
    inline Vec3d junction_point() const
    {
        return pos + (fullwidth() - r_back_mm) * dir;
    }
    
    inline double request_pillar_radius(double radius) const
    {
        const double rmax = r_back_mm;
        return radius > 0 && radius < rmax ? radius : rmax;
    }
};

// A junction connecting bridges and pillars
struct Junction: public SupportTreeNode {
    double r = 1;
    Vec3d pos;

    Junction(const Vec3d &tr, double r_mm) : r(r_mm), pos(tr) {}
};

struct Pillar: public SupportTreeNode {
    double height, r;
    Vec3d endpt;
    
    // 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 = ID_UNSET;
    
    // How many bridges are connected to this pillar
    unsigned bridges = 0;
    
    // How many pillars are cascaded with this one
    unsigned links = 0;

    Pillar(const Vec3d &endp, double h, double radius = 1.):
        height{h}, r(radius), endpt(endp), starts_from_head(false) {}

    Vec3d startpoint() const
    {
        return {endpt.x(), endpt.y(), endpt.z() + height};
    }
    
    const Vec3d& endpoint() const { return endpt; }
};

// A base for pillars or bridges that end on the ground
struct Pedestal: public SupportTreeNode {
    Vec3d pos;
    double height, r_bottom, r_top;

    Pedestal(const Vec3d &p, double h, double rbottom, double rtop)
        : pos{p}, height{h}, r_bottom{rbottom}, r_top{rtop}
    {}
};

// This is the thing that anchors a pillar or bridge to the model body.
// It is actually a reverse pinhead.
struct Anchor: public Head { using Head::Head; };

// A Bridge between two pillars (with junction endpoints)
struct Bridge: public SupportTreeNode {
    double r = 0.8;
    Vec3d startp = Vec3d::Zero(), endp = Vec3d::Zero();
    
    Bridge(const Vec3d &j1,
           const Vec3d &j2,
           double       r_mm  = 0.8): r{r_mm}, startp{j1}, endp{j2}
    {}

    double get_length() const { return (endp - startp).norm(); }
    Vec3d  get_dir() const { return (endp - startp).normalized(); }
};

struct DiffBridge: public Bridge {
    double end_r;

    DiffBridge(const Vec3d &p_s, const Vec3d &p_e, double r_s, double r_e)
        : Bridge{p_s, p_e, r_s}, end_r{r_e}
    {}
};

// A wrapper struct around the pad
struct Pad {
    TriangleMesh tmesh;
    PadConfig cfg;
    double zlevel = 0;
    
    Pad() = default;
    
    Pad(const TriangleMesh &support_mesh,
        const ExPolygons &  model_contours,
        double              ground_level,
        const PadConfig &   pcfg,
        ThrowOnCancel       thr);
    
    bool empty() const { return tmesh.facets_count() == 0; }
};

// 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 merged when the caller queries the merged mesh. The
// merged result is cached for fast subsequent delivery of the merged mesh
// which can be quite complex. The support tree creation algorithm can use an
// instance of this class as a somewhat higher level tool for crafting the 3D
// support mesh. Parts can be added with the appropriate methods such as
// add_head or add_pillar which forwards the constructor arguments and fills
// the IDs of these substructures. The IDs are basically indices into the
// arrays of the appropriate type (heads, pillars, etc...). One can later query
// e.g. a pillar for a specific head...
//
// The support pad is considered an auxiliary geometry and is not part of the
// merged mesh. It can be retrieved using a dedicated method (pad())
class SupportTreeBuilder: public SupportTree {
    // 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<Bridge>     m_crossbridges;
    std::vector<DiffBridge> m_diffbridges;
    std::vector<Pedestal>   m_pedestals;
    std::vector<Anchor>     m_anchors;

    Pad m_pad;
    
    using Mutex = ccr::SpinningMutex;
    
    mutable TriangleMesh m_meshcache;
    mutable Mutex m_mutex;
    mutable bool m_meshcache_valid = false;
    mutable double m_model_height = 0; // the full height of the model
    
    template<class BridgeT, class...Args>
    const BridgeT& _add_bridge(std::vector<BridgeT> &br, Args&&... args)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        br.emplace_back(std::forward<Args>(args)...);
        br.back().id = long(br.size() - 1);
        m_meshcache_valid = false;
        return br.back();
    }
    
public:
    double ground_level = 0;
    
    SupportTreeBuilder() = default;
    SupportTreeBuilder(SupportTreeBuilder &&o);
    SupportTreeBuilder(const SupportTreeBuilder &o);
    SupportTreeBuilder& operator=(SupportTreeBuilder &&o);
    SupportTreeBuilder& operator=(const SupportTreeBuilder &o);

    template<class...Args> Head& add_head(unsigned id, Args&&... args)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        m_heads.emplace_back(std::forward<Args>(args)...);
        m_heads.back().id = id;
        
        if (id >= m_head_indices.size()) m_head_indices.resize(id + 1);
        m_head_indices[id] = m_heads.size() - 1;
        
        m_meshcache_valid = false;
        return m_heads.back();
    }
    
    template<class...Args> long add_pillar(long headid, double length)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        if (m_pillars.capacity() < m_heads.size())
            m_pillars.reserve(m_heads.size() * 10);
        
        assert(headid >= 0 && size_t(headid) < m_head_indices.size());
        Head &head = m_heads[m_head_indices[size_t(headid)]];
        
        Vec3d hjp = head.junction_point() - Vec3d{0, 0, length};
        m_pillars.emplace_back(hjp, length, head.r_back_mm);

        Pillar& pillar = m_pillars.back();
        pillar.id = long(m_pillars.size() - 1);
        head.pillar_id = pillar.id;
        pillar.start_junction_id = head.id;
        pillar.starts_from_head = true;
        
        m_meshcache_valid = false;
        return pillar.id;
    }
    
    void add_pillar_base(long pid, double baseheight = 3, double radius = 2);

    template<class...Args> const Anchor& add_anchor(Args&&...args)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        m_anchors.emplace_back(std::forward<Args>(args)...);
        m_anchors.back().id = long(m_junctions.size() - 1);
        m_meshcache_valid = false;
        return m_anchors.back();
    }
    
    void increment_bridges(const Pillar& pillar)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
        
        if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
            m_pillars[size_t(pillar.id)].bridges++;
    }
    
    void increment_links(const Pillar& pillar)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
        
        if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size()) 
            m_pillars[size_t(pillar.id)].links++;
    }
    
    unsigned bridgecount(const Pillar &pillar) const {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
        return pillar.bridges;
    }
    
    template<class...Args> long add_pillar(Args&&...args)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        if (m_pillars.capacity() < m_heads.size())
            m_pillars.reserve(m_heads.size() * 10);
        
        m_pillars.emplace_back(std::forward<Args>(args)...);
        Pillar& pillar = m_pillars.back();
        pillar.id = long(m_pillars.size() - 1);
        pillar.starts_from_head = false;
        m_meshcache_valid = false;
        return pillar.id;
    }
    
    template<class...Args> const Junction& add_junction(Args&&... args)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        m_junctions.emplace_back(std::forward<Args>(args)...);
        m_junctions.back().id = long(m_junctions.size() - 1);
        m_meshcache_valid = false;
        return m_junctions.back();
    }
    
    const Bridge& add_bridge(const Vec3d &s, const Vec3d &e, double r)
    {
        return _add_bridge(m_bridges, s, e, r);
    }
    
    const Bridge& add_bridge(long headid, const Vec3d &endp)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(headid >= 0 && size_t(headid) < m_head_indices.size());
        
        Head &h = m_heads[m_head_indices[size_t(headid)]];
        m_bridges.emplace_back(h.junction_point(), endp, h.r_back_mm);
        m_bridges.back().id = long(m_bridges.size() - 1);
        
        h.bridge_id = m_bridges.back().id;
        m_meshcache_valid = false;
        return m_bridges.back();
    }
    
    template<class...Args> const Bridge& add_crossbridge(Args&&... args)
    {
        return _add_bridge(m_crossbridges, std::forward<Args>(args)...);
    }

    template<class...Args> const DiffBridge& add_diffbridge(Args&&... args)
    {
        return _add_bridge(m_diffbridges, std::forward<Args>(args)...);
    }
    
    Head &head(unsigned id)
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(id < m_head_indices.size());
        
        m_meshcache_valid = false;
        return m_heads[m_head_indices[id]];
    }
    
    inline size_t pillarcount() const {
        std::lock_guard<Mutex> lk(m_mutex);
        return m_pillars.size();
    }
    
    inline const std::vector<Pillar> &pillars() const { return m_pillars; }
    inline const std::vector<Head>   &heads() const { return m_heads; }
    inline const std::vector<Bridge> &bridges() const { return m_bridges; }
    inline const std::vector<Bridge> &crossbridges() const { return m_crossbridges; }
    
    template<class T> inline IntegerOnly<T, const Pillar&> pillar(T id) const
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(id >= 0 && size_t(id) < m_pillars.size() &&
               size_t(id) < std::numeric_limits<size_t>::max());
        
        return m_pillars[size_t(id)];
    }
    
    template<class T> inline IntegerOnly<T, Pillar&> pillar(T id) 
    {
        std::lock_guard<Mutex> lk(m_mutex);
        assert(id >= 0 && size_t(id) < m_pillars.size() &&
               size_t(id) < std::numeric_limits<size_t>::max());
        
        return m_pillars[size_t(id)];
    }
    
    const Pad& pad() const { return m_pad; }
    
    // WITHOUT THE PAD!!!
    const TriangleMesh &merged_mesh(size_t steps = 45) const;
    
    // WITH THE PAD
    double full_height() const;
    
    // WITHOUT THE PAD!!!
    inline double mesh_height() const
    {
        if (!m_meshcache_valid) merged_mesh();
        return m_model_height;
    }
    
    // Intended to be called after the generation is fully complete
    const TriangleMesh & merge_and_cleanup();
    
    // Implement SupportTree interface:

    const TriangleMesh &add_pad(const ExPolygons &modelbase,
                                const PadConfig & pcfg) override;
    
    void remove_pad() override { m_pad = Pad(); }
    
    virtual const TriangleMesh &retrieve_mesh(
        MeshType meshtype = MeshType::Support) const override;
};

}} // namespace Slic3r::sla

#endif // SUPPORTTREEBUILDER_HPP