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 ¶ms) 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