1 #ifndef slic3r_SLAPrint_hpp_
2 #define slic3r_SLAPrint_hpp_
3 
4 #include <cstdint>
5 #include <mutex>
6 #include "PrintBase.hpp"
7 #include "SLA/RasterBase.hpp"
8 #include "SLA/SupportTree.hpp"
9 #include "Point.hpp"
10 #include "MTUtils.hpp"
11 #include "Zipper.hpp"
12 #include <libnest2d/backends/clipper/clipper_polygon.hpp>
13 
14 namespace Slic3r {
15 
16 enum SLAPrintStep : unsigned int {
17     slapsMergeSlicesAndEval,
18     slapsRasterize,
19 	slapsCount
20 };
21 
22 enum SLAPrintObjectStep : unsigned int {
23     slaposHollowing,
24     slaposDrillHoles,
25 	slaposObjectSlice,
26 	slaposSupportPoints,
27 	slaposSupportTree,
28 	slaposPad,
29     slaposSliceSupports,
30 	slaposCount
31 };
32 
33 class SLAPrint;
34 class GLCanvas;
35 
36 using _SLAPrintObjectBase =
37     PrintObjectBaseWithState<SLAPrint, SLAPrintObjectStep, slaposCount>;
38 
39 // Layers according to quantized height levels. This will be consumed by
40 // the printer (rasterizer) in the SLAPrint class.
41 // using coord_t = int64_t;
42 
43 enum SliceOrigin { soSupport, soModel };
44 
45 class SLAPrintObject : public _SLAPrintObjectBase
46 {
47 private: // Prevents erroneous use by other classes.
48     using Inherited = _SLAPrintObjectBase;
49 
50 public:
51 
52     // I refuse to grantee copying (Tamas)
53     SLAPrintObject(const SLAPrintObject&) = delete;
54     SLAPrintObject& operator=(const SLAPrintObject&) = delete;
55 
config() const56     const SLAPrintObjectConfig& config() const { return m_config; }
trafo() const57     const Transform3d&          trafo()  const { return m_trafo; }
is_left_handed() const58     bool                        is_left_handed() const { return m_left_handed; }
59 
60     struct Instance {
InstanceSlic3r::SLAPrintObject::Instance61         Instance(ObjectID inst_id, const Point &shft, float rot) : instance_id(inst_id), shift(shft), rotation(rot) {}
operator ==Slic3r::SLAPrintObject::Instance62         bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
63         // ID of the corresponding ModelInstance.
64         ObjectID instance_id;
65         // Slic3r::Point objects in scaled G-code coordinates
66         Point 	shift;
67         // Rotation along the Z axis, in radians.
68         float 	rotation;
69     };
instances() const70     const std::vector<Instance>& instances() const { return m_instances; }
71 
72     bool                    has_mesh(SLAPrintObjectStep step) const;
73     TriangleMesh            get_mesh(SLAPrintObjectStep step) const;
74 
75     // Get a support mesh centered around origin in XY, and with zero rotation around Z applied.
76     // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true.
77     const TriangleMesh&     support_mesh() const;
78     // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied.
79     // Support mesh is only valid if this->is_step_done(slaposPad) is true.
80     const TriangleMesh&     pad_mesh() const;
81 
82     // Ready after this->is_step_done(slaposDrillHoles) is true
83     const TriangleMesh&     hollowed_interior_mesh() const;
84 
85     // Get the mesh that is going to be printed with all the modifications
86     // like hollowing and drilled holes.
get_mesh_to_print() const87     const TriangleMesh & get_mesh_to_print() const {
88         return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh();
89     }
90 
91     // This will return the transformed mesh which is cached
92     const TriangleMesh&     transformed_mesh() const;
93 
94     sla::SupportPoints      transformed_support_points() const;
95     sla::DrainHoles         transformed_drainhole_points() const;
96 
97     // Get the needed Z elevation for the model geometry if supports should be
98     // displayed. This Z offset should also be applied to the support
99     // geometries. Note that this is not the same as the value stored in config
100     // as the pad height also needs to be considered.
101     double get_elevation() const;
102 
103     // This method returns the needed elevation according to the processing
104     // status. If the supports are not ready, it is zero, if they are and the
105     // pad is not, then without the pad, otherwise the full value is returned.
106     double get_current_elevation() const;
107 
108     // This method returns the support points of this SLAPrintObject.
109     const std::vector<sla::SupportPoint>& get_support_points() const;
110 
111     // The public Slice record structure. It corresponds to one printable layer.
112     class SliceRecord {
113     public:
114         // this will be the max limit of size_t
115         static const size_t NONE = size_t(-1);
116 
117         static const SliceRecord EMPTY;
118 
119     private:
120         coord_t   m_print_z = 0;      // Top of the layer
121         float     m_slice_z = 0.f;    // Exact level of the slice
122         float     m_height  = 0.f;     // Height of the sliced layer
123 
124         size_t m_model_slices_idx = NONE;
125         size_t m_support_slices_idx = NONE;
126         const SLAPrintObject *m_po = nullptr;
127 
128     public:
129 
SliceRecord(coord_t key,float slicez,float height)130         SliceRecord(coord_t key, float slicez, float height):
131             m_print_z(key), m_slice_z(slicez), m_height(height) {}
132 
133         // The key will be the integer height level of the top of the layer.
print_level() const134         coord_t print_level() const { return m_print_z; }
135 
136         // Returns the exact floating point Z coordinate of the slice
slice_level() const137         float slice_level() const { return m_slice_z; }
138 
139         // Returns the current layer height
layer_height() const140         float layer_height() const { return m_height; }
141 
is_valid() const142         bool is_valid() const { return m_po && ! std::isnan(m_slice_z); }
143 
print_obj() const144         const SLAPrintObject* print_obj() const { return m_po; }
145 
146         // Methods for setting the indices into the slice vectors.
set_model_slice_idx(const SLAPrintObject & po,size_t id)147         void set_model_slice_idx(const SLAPrintObject &po, size_t id) {
148             m_po = &po; m_model_slices_idx = id;
149         }
150 
set_support_slice_idx(const SLAPrintObject & po,size_t id)151         void set_support_slice_idx(const SLAPrintObject& po, size_t id) {
152             m_po = &po; m_support_slices_idx = id;
153         }
154 
155         const ExPolygons& get_slice(SliceOrigin o) const;
get_slice_idx(SliceOrigin o) const156         size_t            get_slice_idx(SliceOrigin o) const
157         {
158             return o == soModel ? m_model_slices_idx : m_support_slices_idx;
159         }
160     };
161 
162 private:
level(const SliceRecord & sr)163     template<class T> inline static T level(const SliceRecord &sr)
164     {
165         static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
166         return std::is_integral<T>::value ? T(sr.print_level())
167                                           : T(sr.slice_level());
168     }
169 
create_slice_record(T val)170     template<class T> inline static SliceRecord create_slice_record(T val)
171     {
172         static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
173         return std::is_integral<T>::value
174                    ? SliceRecord{coord_t(val), 0.f, 0.f}
175                    : SliceRecord{0, float(val), 0.f};
176     }
177 
178     // This is a template method for searching the slice index either by
179     // an integer key: print_level or a floating point key: slice_level.
180     // The eps parameter gives the max deviation in + or - direction.
181     //
182     // This method can be used in const or non-const contexts as well.
183     template<class Container, class T>
closest_slice_record(Container & cont,T lvl,T eps=std::numeric_limits<T>::max ())184     static auto closest_slice_record(
185             Container& cont,
186             T lvl,
187             T eps = std::numeric_limits<T>::max()) -> decltype (cont.begin())
188     {
189         if(cont.empty()) return cont.end();
190         if(cont.size() == 1 && std::abs(level<T>(cont.front()) - lvl) > eps)
191             return cont.end();
192 
193         SliceRecord query = create_slice_record(lvl);
194 
195         auto it = std::lower_bound(cont.begin(), cont.end(), query,
196                                    [](const SliceRecord& r1,
197                                       const SliceRecord& r2)
198         {
199             return level<T>(r1) < level<T>(r2);
200         });
201 
202         if(it == cont.end()) return it;
203 
204         T diff = std::abs(level<T>(*it) - lvl);
205 
206         if(it != cont.begin()) {
207             auto it_prev = std::prev(it);
208             T diff_prev = std::abs(level<T>(*it_prev) - lvl);
209             if(diff_prev < diff) { diff = diff_prev; it = it_prev; }
210         }
211 
212         if(diff > eps) it = cont.end();
213 
214         return it;
215     }
216 
get_model_slices() const217     const std::vector<ExPolygons>& get_model_slices() const { return m_model_slices; }
218     const std::vector<ExPolygons>& get_support_slices() const;
219 
220 public:
221 
222     // /////////////////////////////////////////////////////////////////////////
223     //
224     // These methods should be callable on the client side (e.g. UI thread)
225     // when the appropriate steps slaposObjectSlice and slaposSliceSupports
226     // are ready. All the print objects are processed before slapsRasterize so
227     // it is safe to call them during and/or after slapsRasterize.
228     //
229     // /////////////////////////////////////////////////////////////////////////
230 
231     // Retrieve the slice index.
get_slice_index() const232     const std::vector<SliceRecord>& get_slice_index() const {
233         return m_slice_index;
234     }
235 
236     // Search slice index for the closest slice to given print_level.
237     // max_epsilon gives the allowable deviation of the returned slice record's
238     // level.
closest_slice_to_print_level(coord_t print_level,coord_t max_epsilon=std::numeric_limits<coord_t>::max ()) const239     const SliceRecord& closest_slice_to_print_level(
240             coord_t print_level,
241             coord_t max_epsilon = std::numeric_limits<coord_t>::max()) const
242     {
243         auto it = closest_slice_record(m_slice_index, print_level, max_epsilon);
244         return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
245     }
246 
247     // Search slice index for the closest slice to given slice_level.
248     // max_epsilon gives the allowable deviation of the returned slice record's
249     // level. Use SliceRecord::is_valid() to check the result.
closest_slice_to_slice_level(float slice_level,float max_epsilon=std::numeric_limits<float>::max ()) const250     const SliceRecord& closest_slice_to_slice_level(
251             float slice_level,
252             float max_epsilon = std::numeric_limits<float>::max()) const
253     {
254         auto it = closest_slice_record(m_slice_index, slice_level, max_epsilon);
255         return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
256     }
257 
258 protected:
259     // to be called from SLAPrint only.
260     friend class SLAPrint;
261 
262 	SLAPrintObject(SLAPrint* print, ModelObject* model_object);
263     ~SLAPrintObject();
264 
config_apply(const ConfigBase & other,bool ignore_nonexistent=false)265     void                    config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
config_apply_only(const ConfigBase & other,const t_config_option_keys & keys,bool ignore_nonexistent=false)266     void                    config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
267         { this->m_config.apply_only(other, keys, ignore_nonexistent); }
268 
set_trafo(const Transform3d & trafo,bool left_handed)269     void                    set_trafo(const Transform3d& trafo, bool left_handed) {
270         m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
271     }
272 
set_instances(InstVec && instances)273     template<class InstVec> inline void set_instances(InstVec&& instances) { m_instances = std::forward<InstVec>(instances); }
274 
275     // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
276     bool                    invalidate_step(SLAPrintObjectStep step);
277     bool                    invalidate_all_steps();
278     // Invalidate steps based on a set of parameters changed.
279     bool                    invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
280 
281     // Which steps have to be performed. Implicitly: all
282     // to be accessible from SLAPrint
283     std::vector<bool>                       m_stepmask;
284 
285 private:
286     // Object specific configuration, pulled from the configuration layer.
287     SLAPrintObjectConfig                    m_config;
288 
289     // Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
290     Transform3d                             m_trafo = Transform3d::Identity();
291     // m_trafo is left handed -> 3x3 affine transformation has negative determinant.
292     bool                                    m_left_handed = false;
293 
294     std::vector<Instance> 					m_instances;
295 
296     // Individual 2d slice polygons from lower z to higher z levels
297     std::vector<ExPolygons>                 m_model_slices;
298 
299     // Exact (float) height levels mapped to the slices. Each record contains
300     // the index to the model and the support slice vectors.
301     std::vector<SliceRecord>                m_slice_index;
302 
303     std::vector<float>                      m_model_height_levels;
304 
305     // Caching the transformed (m_trafo) raw mesh of the object
306     mutable CachedObject<TriangleMesh>      m_transformed_rmesh;
307 
308     class SupportData : public sla::SupportableMesh
309     {
310     public:
311         sla::SupportTree::UPtr  support_tree_ptr; // the supports
312         std::vector<ExPolygons> support_slices;   // sliced supports
313 
SupportData(const TriangleMesh & t)314         inline SupportData(const TriangleMesh &t)
315             : sla::SupportableMesh{t, {}, {}}
316         {}
317 
create_support_tree(const sla::JobController & ctl)318         sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl)
319         {
320             support_tree_ptr = sla::SupportTree::create(*this, ctl);
321             return support_tree_ptr;
322         }
323     };
324 
325     std::unique_ptr<SupportData> m_supportdata;
326 
327     class HollowingData
328     {
329     public:
330 
331         TriangleMesh interior;
332         mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
333     };
334 
335     std::unique_ptr<HollowingData> m_hollowing_data;
336 };
337 
338 using PrintObjects = std::vector<SLAPrintObject*>;
339 
340 using SliceRecord  = SLAPrintObject::SliceRecord;
341 
342 class TriangleMesh;
343 
344 struct SLAPrintStatistics
345 {
SLAPrintStatisticsSlic3r::SLAPrintStatistics346     SLAPrintStatistics() { clear(); }
347     double                          estimated_print_time;
348     double                          objects_used_material;
349     double                          support_used_material;
350     size_t                          slow_layers_count;
351     size_t                          fast_layers_count;
352     double                          total_cost;
353     double                          total_weight;
354     std::vector<double>             layers_times;
355 
356     // Config with the filled in print statistics.
357     DynamicConfig           config() const;
358     // Config with the statistics keys populated with placeholder strings.
359     static DynamicConfig    placeholders();
360     // Replace the print statistics placeholders in the path.
361     std::string             finalize_output_path(const std::string &path_in) const;
362 
clearSlic3r::SLAPrintStatistics363     void clear() {
364         estimated_print_time = 0.;
365         objects_used_material = 0.;
366         support_used_material = 0.;
367         slow_layers_count = 0;
368         fast_layers_count = 0;
369         total_cost = 0.;
370         total_weight = 0.;
371         layers_times.clear();
372     }
373 };
374 
375 class SLAPrinter {
376 protected:
377     std::vector<sla::EncodedRaster> m_layers;
378 
379     virtual uqptr<sla::RasterBase> create_raster() const = 0;
380     virtual sla::RasterEncoder get_encoder() const = 0;
381 
382 public:
383     virtual ~SLAPrinter() = default;
384 
385     virtual void apply(const SLAPrinterConfig &cfg) = 0;
386 
387     // Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid);
draw_layers(size_t layer_num,Fn && drawfn)388     template<class Fn> void draw_layers(size_t layer_num, Fn &&drawfn)
389     {
390         m_layers.resize(layer_num);
391         sla::ccr::for_each(size_t(0), m_layers.size(),
392                            [this, &drawfn] (size_t idx) {
393                                sla::EncodedRaster& enc = m_layers[idx];
394                                auto rst = create_raster();
395                                drawfn(*rst, idx);
396                                enc = rst->encode(get_encoder());
397                            });
398     }
399 };
400 
401 /**
402  * @brief This class is the high level FSM for the SLA printing process.
403  *
404  * It should support the background processing framework and contain the
405  * metadata for the support geometries and their slicing. It should also
406  * dispatch the SLA printing configuration values to the appropriate calculation
407  * steps.
408  */
409 class SLAPrint : public PrintBaseWithState<SLAPrintStep, slapsCount>
410 {
411 private: // Prevents erroneous use by other classes.
412     typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
413 
414     class Steps; // See SLAPrintSteps.cpp
415 
416 public:
417 
SLAPrint()418     SLAPrint(): m_stepmask(slapsCount, true) {}
419 
~SLAPrint()420     virtual ~SLAPrint() override { this->clear(); }
421 
technology() const422     PrinterTechnology	technology() const noexcept override { return ptSLA; }
423 
424     void                clear() override;
empty() const425     bool                empty() const override { return m_objects.empty(); }
426     // List of existing PrintObject IDs, to remove notifications for non-existent IDs.
427     std::vector<ObjectID> print_object_ids() const;
428     ApplyStatus         apply(const Model &model, DynamicPrintConfig config) override;
429     void                set_task(const TaskParams &params) override;
430     void                process() override;
431     void                finalize() override;
432     // Returns true if an object step is done on all objects and there's at least one object.
433     bool                is_step_done(SLAPrintObjectStep step) const;
434     // Returns true if the last step was finished with success.
finished() const435     bool                finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
436 
objects() const437     const PrintObjects& objects() const { return m_objects; }
438     // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
439     // in the notification center.
get_object(ObjectID object_id) const440     const SLAPrintObject* get_object(ObjectID object_id) const {
441         auto it = std::find_if(m_objects.begin(), m_objects.end(),
442             [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; });
443         return (it == m_objects.end()) ? nullptr : *it;
444     }
445 
print_config() const446     const SLAPrintConfig&       print_config() const { return m_print_config; }
printer_config() const447     const SLAPrinterConfig&     printer_config() const { return m_printer_config; }
material_config() const448     const SLAMaterialConfig&    material_config() const { return m_material_config; }
default_object_config() const449     const SLAPrintObjectConfig& default_object_config() const { return m_default_object_config; }
450 
451     // Extracted value from the configuration objects
452     Vec3d                       relative_correction() const;
453 
454     // Return sla tansformation for a given model_object
455     Transform3d sla_trafo(const ModelObject &model_object) const;
456 
457 	std::string                 output_filename(const std::string &filename_base = std::string()) const override;
458 
print_statistics() const459     const SLAPrintStatistics&   print_statistics() const { return m_print_statistics; }
460 
461     std::string validate() const override;
462 
463     // An aggregation of SliceRecord-s from all the print objects for each
464     // occupied layer. Slice record levels dont have to match exactly.
465     // They are unified if the level difference is within +/- SCALED_EPSILON
466     class PrintLayer {
467         coord_t m_level;
468 
469         // The collection of slice records for the current level.
470         std::vector<std::reference_wrapper<const SliceRecord>> m_slices;
471 
472         std::vector<ClipperLib::Polygon> m_transformed_slices;
473 
transformed_slices(Container && c)474         template<class Container> void transformed_slices(Container&& c)
475         {
476             m_transformed_slices = std::forward<Container>(c);
477         }
478 
479         friend class SLAPrint::Steps;
480 
481     public:
482 
PrintLayer(coord_t lvl)483         explicit PrintLayer(coord_t lvl) : m_level(lvl) {}
484 
485         // for being sorted in their container (see m_printer_input)
operator <(const PrintLayer & other) const486         bool operator<(const PrintLayer& other) const {
487             return m_level < other.m_level;
488         }
489 
add(const SliceRecord & sr)490         void add(const SliceRecord& sr) { m_slices.emplace_back(sr); }
491 
level() const492         coord_t level() const { return m_level; }
493 
slices() const494         auto slices() const -> const decltype (m_slices)& { return m_slices; }
495 
transformed_slices() const496         const std::vector<ClipperLib::Polygon> & transformed_slices() const {
497             return m_transformed_slices;
498         }
499     };
500 
501     // The aggregated and leveled print records from various objects.
502     // TODO: use this structure for the preview in the future.
print_layers() const503     const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
504 
505     void set_printer(SLAPrinter *archiver);
506 
507 private:
508 
509     // Implement same logic as in SLAPrintObject
510     bool invalidate_step(SLAPrintStep st);
511 
512     // Invalidate steps based on a set of parameters changed.
513     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys, bool &invalidate_all_model_objects);
514 
515     SLAPrintConfig                  m_print_config;
516     SLAPrinterConfig                m_printer_config;
517     SLAMaterialConfig               m_material_config;
518     SLAPrintObjectConfig            m_default_object_config;
519 
520     PrintObjects                    m_objects;
521     std::vector<bool>               m_stepmask;
522 
523     // Ready-made data for rasterization.
524     std::vector<PrintLayer>         m_printer_input;
525 
526     // The archive object which collects the raster images after slicing
527     SLAPrinter                     *m_printer = nullptr;
528 
529     // Estimated print time, material consumed.
530     SLAPrintStatistics              m_print_statistics;
531 
532     class StatusReporter
533     {
534         double m_st = 0;
535 
536     public:
537         void operator()(SLAPrint &         p,
538                         double             st,
539                         const std::string &msg,
540                         unsigned           flags = SlicingStatus::DEFAULT,
541                         const std::string &logmsg = "");
542 
status() const543         double status() const { return m_st; }
544     } m_report_status;
545 
546 	friend SLAPrintObject;
547 };
548 
549 // Helper functions:
550 
551 bool is_zero_elevation(const SLAPrintObjectConfig &c);
552 
553 sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c);
554 
555 sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c);
556 
557 sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c);
558 
559 bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg);
560 
561 
562 } // namespace Slic3r
563 
564 #endif /* slic3r_SLAPrint_hpp_ */
565