1 #include "Exception.hpp"
2 #include "Model.hpp"
3 #include "ModelArrange.hpp"
4 #include "Geometry.hpp"
5 #include "MTUtils.hpp"
6 #include "TriangleSelector.hpp"
7 
8 #include "Format/AMF.hpp"
9 #include "Format/OBJ.hpp"
10 #include "Format/PRUS.hpp"
11 #include "Format/STL.hpp"
12 #include "Format/3mf.hpp"
13 
14 #include <float.h>
15 
16 #include <boost/algorithm/string/predicate.hpp>
17 #include <boost/algorithm/string/replace.hpp>
18 #include <boost/filesystem.hpp>
19 #include <boost/log/trivial.hpp>
20 #include <boost/nowide/iostream.hpp>
21 
22 #include "SVG.hpp"
23 #include <Eigen/Dense>
24 #include "GCodeWriter.hpp"
25 
26 namespace Slic3r {
27 
assign_copy(const Model & rhs)28 Model& Model::assign_copy(const Model &rhs)
29 {
30     this->copy_id(rhs);
31     // copy materials
32     this->clear_materials();
33     this->materials = rhs.materials;
34     for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials) {
35         // Copy including the ID and m_model.
36         m.second = new ModelMaterial(*m.second);
37         m.second->set_model(this);
38     }
39     // copy objects
40     this->clear_objects();
41     this->objects.reserve(rhs.objects.size());
42 	for (const ModelObject *model_object : rhs.objects) {
43         // Copy including the ID, leave ID set to invalid (zero).
44         auto mo = ModelObject::new_copy(*model_object);
45         mo->set_model(this);
46 		this->objects.emplace_back(mo);
47     }
48 
49     // copy custom code per height
50     this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
51     return *this;
52 }
53 
assign_copy(Model && rhs)54 Model& Model::assign_copy(Model &&rhs)
55 {
56     this->copy_id(rhs);
57 	// Move materials, adjust the parent pointer.
58     this->clear_materials();
59     this->materials = std::move(rhs.materials);
60     for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
61         m.second->set_model(this);
62     rhs.materials.clear();
63     // Move objects, adjust the parent pointer.
64     this->clear_objects();
65 	this->objects = std::move(rhs.objects);
66     for (ModelObject *model_object : this->objects)
67         model_object->set_model(this);
68     rhs.objects.clear();
69 
70     // copy custom code per height
71     this->custom_gcode_per_print_z = std::move(rhs.custom_gcode_per_print_z);
72     return *this;
73 }
74 
assign_new_unique_ids_recursive()75 void Model::assign_new_unique_ids_recursive()
76 {
77     this->set_new_unique_id();
78     for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
79         m.second->assign_new_unique_ids_recursive();
80     for (ModelObject *model_object : this->objects)
81         model_object->assign_new_unique_ids_recursive();
82 }
83 
update_links_bottom_up_recursive()84 void Model::update_links_bottom_up_recursive()
85 {
86 	for (std::pair<const t_model_material_id, ModelMaterial*> &kvp : this->materials)
87 		kvp.second->set_model(this);
88 	for (ModelObject *model_object : this->objects) {
89 		model_object->set_model(this);
90 		for (ModelInstance *model_instance : model_object->instances)
91 			model_instance->set_model_object(model_object);
92 		for (ModelVolume *model_volume : model_object->volumes)
93 			model_volume->set_model_object(model_object);
94 	}
95 }
96 
97 // Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
read_from_file(const std::string & input_file,DynamicPrintConfig * config,ConfigSubstitutionContext * config_substitutions,LoadAttributes options)98 Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadAttributes options)
99 {
100     Model model;
101 
102     DynamicPrintConfig temp_config;
103     ConfigSubstitutionContext temp_config_substitutions_context(ForwardCompatibilitySubstitutionRule::EnableSilent);
104     if (config == nullptr)
105         config = &temp_config;
106     if (config_substitutions == nullptr)
107         config_substitutions = &temp_config_substitutions_context;
108 
109     bool result = false;
110     if (boost::algorithm::iends_with(input_file, ".stl"))
111         result = load_stl(input_file.c_str(), &model);
112     else if (boost::algorithm::iends_with(input_file, ".obj"))
113         result = load_obj(input_file.c_str(), &model);
114     else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
115         result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
116     else if (boost::algorithm::iends_with(input_file, ".3mf"))
117         //FIXME options & LoadAttribute::CheckVersion ?
118         result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
119     else if (boost::algorithm::iends_with(input_file, ".prusa"))
120         result = load_prus(input_file.c_str(), &model);
121     else
122         throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
123 
124     if (! result)
125         throw Slic3r::RuntimeError("Loading of a model file failed.");
126 
127     if (model.objects.empty())
128         throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
129 
130     for (ModelObject *o : model.objects)
131         o->input_file = input_file;
132 
133     if (options & LoadAttribute::AddDefaultInstances)
134         model.add_default_instances();
135 
136     CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
137     CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
138 
139     sort_remove_duplicates(config_substitutions->substitutions);
140     return model;
141 }
142 
143 // Loading model from a file (3MF or AMF), not from a simple geometry file (STL or OBJ).
read_from_archive(const std::string & input_file,DynamicPrintConfig * config,ConfigSubstitutionContext * config_substitutions,LoadAttributes options)144 Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadAttributes options)
145 {
146     assert(config != nullptr);
147     assert(config_substitutions != nullptr);
148 
149     Model model;
150 
151     bool result = false;
152     if (boost::algorithm::iends_with(input_file, ".3mf"))
153         result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, options & LoadAttribute::CheckVersion);
154     else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
155         result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
156     else
157         throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
158 
159     if (!result)
160         throw Slic3r::RuntimeError("Loading of a model file failed.");
161 
162     if (model.objects.empty())
163         throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
164 
165     for (ModelObject *o : model.objects)
166     {
167 //        if (boost::algorithm::iends_with(input_file, ".zip.amf"))
168 //        {
169 //            // we remove the .zip part of the extension to avoid it be added to filenames when exporting
170 //            o->input_file = boost::ireplace_last_copy(input_file, ".zip.", ".");
171 //        }
172 //        else
173             o->input_file = input_file;
174     }
175 
176     if (options & LoadAttribute::AddDefaultInstances)
177         model.add_default_instances();
178 
179     CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
180     CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
181 
182     return model;
183 }
184 
add_object()185 ModelObject* Model::add_object()
186 {
187     this->objects.emplace_back(new ModelObject(this));
188     return this->objects.back();
189 }
190 
add_object(const char * name,const char * path,const TriangleMesh & mesh)191 ModelObject* Model::add_object(const char *name, const char *path, const TriangleMesh &mesh)
192 {
193     ModelObject* new_object = new ModelObject(this);
194     this->objects.push_back(new_object);
195     new_object->name = name;
196     new_object->input_file = path;
197     ModelVolume *new_volume = new_object->add_volume(mesh);
198     new_volume->name = name;
199     new_volume->source.input_file = path;
200     new_volume->source.object_idx = (int)this->objects.size() - 1;
201     new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
202     new_object->invalidate_bounding_box();
203     return new_object;
204 }
205 
add_object(const char * name,const char * path,TriangleMesh && mesh)206 ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh &&mesh)
207 {
208     ModelObject* new_object = new ModelObject(this);
209     this->objects.push_back(new_object);
210     new_object->name = name;
211     new_object->input_file = path;
212     ModelVolume *new_volume = new_object->add_volume(std::move(mesh));
213     new_volume->name = name;
214     new_volume->source.input_file = path;
215     new_volume->source.object_idx = (int)this->objects.size() - 1;
216     new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
217     new_object->invalidate_bounding_box();
218     return new_object;
219 }
220 
add_object(const ModelObject & other)221 ModelObject* Model::add_object(const ModelObject &other)
222 {
223 	ModelObject* new_object = ModelObject::new_clone(other);
224     new_object->set_model(this);
225     this->objects.push_back(new_object);
226     return new_object;
227 }
228 
delete_object(size_t idx)229 void Model::delete_object(size_t idx)
230 {
231     ModelObjectPtrs::iterator i = this->objects.begin() + idx;
232     delete *i;
233     this->objects.erase(i);
234 }
235 
delete_object(ModelObject * object)236 bool Model::delete_object(ModelObject* object)
237 {
238     if (object != nullptr) {
239         size_t idx = 0;
240         for (ModelObject *model_object : objects) {
241             if (model_object == object) {
242                 delete model_object;
243                 objects.erase(objects.begin() + idx);
244                 return true;
245             }
246             ++ idx;
247         }
248     }
249     return false;
250 }
251 
delete_object(ObjectID id)252 bool Model::delete_object(ObjectID id)
253 {
254     if (id.id != 0) {
255         size_t idx = 0;
256         for (ModelObject *model_object : objects) {
257             if (model_object->id() == id) {
258                 delete model_object;
259                 objects.erase(objects.begin() + idx);
260                 return true;
261             }
262             ++ idx;
263         }
264     }
265     return false;
266 }
267 
clear_objects()268 void Model::clear_objects()
269 {
270     for (ModelObject *o : this->objects)
271         delete o;
272     this->objects.clear();
273 }
274 
delete_material(t_model_material_id material_id)275 void Model::delete_material(t_model_material_id material_id)
276 {
277     ModelMaterialMap::iterator i = this->materials.find(material_id);
278     if (i != this->materials.end()) {
279         delete i->second;
280         this->materials.erase(i);
281     }
282 }
283 
clear_materials()284 void Model::clear_materials()
285 {
286     for (auto &m : this->materials)
287         delete m.second;
288     this->materials.clear();
289 }
290 
add_material(t_model_material_id material_id)291 ModelMaterial* Model::add_material(t_model_material_id material_id)
292 {
293     assert(! material_id.empty());
294     ModelMaterial* material = this->get_material(material_id);
295     if (material == nullptr)
296         material = this->materials[material_id] = new ModelMaterial(this);
297     return material;
298 }
299 
add_material(t_model_material_id material_id,const ModelMaterial & other)300 ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other)
301 {
302     assert(! material_id.empty());
303     // delete existing material if any
304     ModelMaterial* material = this->get_material(material_id);
305     delete material;
306     // set new material
307 	material = new ModelMaterial(other);
308 	material->set_model(this);
309     this->materials[material_id] = material;
310     return material;
311 }
312 
313 // makes sure all objects have at least one instance
add_default_instances()314 bool Model::add_default_instances()
315 {
316     // apply a default position to all objects not having one
317     for (ModelObject *o : this->objects)
318         if (o->instances.empty())
319             o->add_instance();
320     return true;
321 }
322 
323 // this returns the bounding box of the *transformed* instances
bounding_box() const324 BoundingBoxf3 Model::bounding_box() const
325 {
326     BoundingBoxf3 bb;
327     for (ModelObject *o : this->objects)
328         bb.merge(o->bounding_box());
329     return bb;
330 }
331 
update_print_volume_state(const BoundingBoxf3 & print_volume)332 unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
333 {
334     unsigned int num_printable = 0;
335     for (ModelObject *model_object : this->objects)
336         num_printable += model_object->check_instances_print_volume_state(print_volume);
337     return num_printable;
338 }
339 
center_instances_around_point(const Vec2d & point)340 bool Model::center_instances_around_point(const Vec2d &point)
341 {
342     BoundingBoxf3 bb;
343     for (ModelObject *o : this->objects)
344         for (size_t i = 0; i < o->instances.size(); ++ i)
345             bb.merge(o->instance_bounding_box(i, false));
346 
347     Vec2d shift2 = point - to_2d(bb.center());
348 	if (std::abs(shift2(0)) < EPSILON && std::abs(shift2(1)) < EPSILON)
349 		// No significant shift, don't do anything.
350 		return false;
351 
352 	Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0);
353 	for (ModelObject *o : this->objects) {
354 		for (ModelInstance *i : o->instances)
355 			i->set_offset(i->get_offset() + shift3);
356 		o->invalidate_bounding_box();
357 	}
358 	return true;
359 }
360 
361 // flattens everything to a single mesh
mesh() const362 TriangleMesh Model::mesh() const
363 {
364     TriangleMesh mesh;
365     for (const ModelObject *o : this->objects)
366         mesh.merge(o->mesh());
367     return mesh;
368 }
369 
duplicate_objects_grid(size_t x,size_t y,coordf_t dist)370 void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
371 {
372     if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects";
373     if (this->objects.empty()) throw "No objects!";
374 
375     ModelObject* object = this->objects.front();
376     object->clear_instances();
377 
378     Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones();
379 
380     for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
381         for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
382             ModelInstance* instance = object->add_instance();
383             instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0));
384         }
385     }
386 }
387 
looks_like_multipart_object() const388 bool Model::looks_like_multipart_object() const
389 {
390     if (this->objects.size() <= 1)
391         return false;
392     double zmin = std::numeric_limits<double>::max();
393     for (const ModelObject *obj : this->objects) {
394         if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
395             return false;
396         for (const ModelVolume *vol : obj->volumes) {
397             double zmin_this = vol->mesh().bounding_box().min(2);
398             if (zmin == std::numeric_limits<double>::max())
399                 zmin = zmin_this;
400             else if (std::abs(zmin - zmin_this) > EPSILON)
401                 // The volumes don't share zmin.
402                 return true;
403         }
404     }
405     return false;
406 }
407 
408 // Generate next extruder ID string, in the range of (1, max_extruders).
auto_extruder_id(unsigned int max_extruders,unsigned int & cntr)409 static inline int auto_extruder_id(unsigned int max_extruders, unsigned int &cntr)
410 {
411     int out = ++ cntr;
412     if (cntr == max_extruders)
413     	cntr = 0;
414     return out;
415 }
416 
convert_multipart_object(unsigned int max_extruders)417 void Model::convert_multipart_object(unsigned int max_extruders)
418 {
419 	assert(this->objects.size() >= 2);
420     if (this->objects.size() < 2)
421         return;
422 
423     ModelObject* object = new ModelObject(this);
424     object->input_file = this->objects.front()->input_file;
425     object->name = this->objects.front()->name;
426     //FIXME copy the config etc?
427 
428     unsigned int extruder_counter = 0;
429 	for (const ModelObject* o : this->objects)
430     	for (const ModelVolume* v : o->volumes) {
431             // If there are more than one object, put all volumes together
432             // Each object may contain any number of volumes and instances
433             // The volumes transformations are relative to the object containing them...
434             Geometry::Transformation trafo_volume = v->get_transformation();
435             // Revert the centering operation.
436             trafo_volume.set_offset(trafo_volume.get_offset() - o->origin_translation);
437             int counter = 1;
438             auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
439                 assert(new_v != nullptr);
440                 new_v->name = o->name + "_" + std::to_string(counter++);
441                 new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
442                 return new_v;
443             };
444             if (o->instances.empty()) {
445             	copy_volume(object->add_volume(*v))->set_transformation(trafo_volume);
446             } else {
447             	for (const ModelInstance* i : o->instances)
448                     // ...so, transform everything to a common reference system (world)
449                 	copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
450             }
451         }
452 
453     // commented-out to fix #2868
454 //    object->add_instance();
455 //    object->instances[0]->set_offset(object->raw_mesh_bounding_box().center());
456 
457     this->clear_objects();
458     this->objects.push_back(object);
459 }
460 
looks_like_imperial_units() const461 bool Model::looks_like_imperial_units() const
462 {
463     if (this->objects.size() == 0)
464         return false;
465 
466     for (ModelObject* obj : this->objects)
467         if (obj->get_object_stl_stats().volume < 9.0) // 9 = 3*3*3;
468             return true;
469 
470     return false;
471 }
472 
convert_from_imperial_units(bool only_small_volumes)473 void Model::convert_from_imperial_units(bool only_small_volumes)
474 {
475     double in_to_mm = 25.4;
476     for (ModelObject* obj : this->objects)
477         if (! only_small_volumes || obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
478             obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
479             for (ModelVolume* v : obj->volumes)
480                 v->source.is_converted_from_inches = true;
481         }
482 }
483 
adjust_min_z()484 void Model::adjust_min_z()
485 {
486     if (objects.empty())
487         return;
488 
489     if (bounding_box().min(2) < 0.0)
490     {
491         for (ModelObject* obj : objects)
492         {
493             if (obj != nullptr)
494             {
495                 coordf_t obj_min_z = obj->bounding_box().min(2);
496                 if (obj_min_z < 0.0)
497                     obj->translate_instances(Vec3d(0.0, 0.0, -obj_min_z));
498             }
499         }
500     }
501 }
502 
503 // Propose a filename including path derived from the ModelObject's input path.
504 // If object's name is filled in, use the object name, otherwise use the input name.
propose_export_file_name_and_path() const505 std::string Model::propose_export_file_name_and_path() const
506 {
507     std::string input_file;
508     for (const ModelObject *model_object : this->objects)
509         for (ModelInstance *model_instance : model_object->instances)
510             if (model_instance->is_printable()) {
511                 input_file = model_object->get_export_filename();
512 
513                 if (!input_file.empty())
514                     goto end;
515                 // Other instances will produce the same name, skip them.
516                 break;
517             }
518 end:
519     return input_file;
520 }
521 
propose_export_file_name_and_path(const std::string & new_extension) const522 std::string Model::propose_export_file_name_and_path(const std::string &new_extension) const
523 {
524     return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
525 }
526 
~ModelObject()527 ModelObject::~ModelObject()
528 {
529     this->clear_volumes();
530     this->clear_instances();
531 }
532 
533 // maintains the m_model pointer
assign_copy(const ModelObject & rhs)534 ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
535 {
536 	assert(this->id().invalid() || this->id() == rhs.id());
537 	assert(this->config.id().invalid() || this->config.id() == rhs.config.id());
538 	this->copy_id(rhs);
539 
540     this->name                        = rhs.name;
541     this->input_file                  = rhs.input_file;
542     // Copies the config's ID
543     this->config                      = rhs.config;
544     assert(this->config.id() == rhs.config.id());
545     this->sla_support_points          = rhs.sla_support_points;
546     this->sla_points_status           = rhs.sla_points_status;
547     this->sla_drain_holes             = rhs.sla_drain_holes;
548     this->layer_config_ranges         = rhs.layer_config_ranges;    // #ys_FIXME_experiment
549     this->layer_height_profile        = rhs.layer_height_profile;
550     this->printable                   = rhs.printable;
551     this->origin_translation          = rhs.origin_translation;
552     m_bounding_box                    = rhs.m_bounding_box;
553     m_bounding_box_valid              = rhs.m_bounding_box_valid;
554     m_raw_bounding_box                = rhs.m_raw_bounding_box;
555     m_raw_bounding_box_valid          = rhs.m_raw_bounding_box_valid;
556     m_raw_mesh_bounding_box           = rhs.m_raw_mesh_bounding_box;
557     m_raw_mesh_bounding_box_valid     = rhs.m_raw_mesh_bounding_box_valid;
558 
559     this->clear_volumes();
560     this->volumes.reserve(rhs.volumes.size());
561     for (ModelVolume *model_volume : rhs.volumes) {
562         this->volumes.emplace_back(new ModelVolume(*model_volume));
563         this->volumes.back()->set_model_object(this);
564     }
565     this->clear_instances();
566 	this->instances.reserve(rhs.instances.size());
567     for (const ModelInstance *model_instance : rhs.instances) {
568         this->instances.emplace_back(new ModelInstance(*model_instance));
569         this->instances.back()->set_model_object(this);
570     }
571 
572     return *this;
573 }
574 
575 // maintains the m_model pointer
assign_copy(ModelObject && rhs)576 ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
577 {
578 	assert(this->id().invalid());
579     this->copy_id(rhs);
580 
581     this->name                        = std::move(rhs.name);
582     this->input_file                  = std::move(rhs.input_file);
583     // Moves the config's ID
584     this->config                      = std::move(rhs.config);
585     assert(this->config.id() == rhs.config.id());
586     this->sla_support_points          = std::move(rhs.sla_support_points);
587     this->sla_points_status           = std::move(rhs.sla_points_status);
588     this->sla_drain_holes             = std::move(rhs.sla_drain_holes);
589     this->layer_config_ranges         = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
590     this->layer_height_profile        = std::move(rhs.layer_height_profile);
591     this->origin_translation          = std::move(rhs.origin_translation);
592     m_bounding_box                    = std::move(rhs.m_bounding_box);
593     m_bounding_box_valid              = std::move(rhs.m_bounding_box_valid);
594     m_raw_bounding_box                = rhs.m_raw_bounding_box;
595     m_raw_bounding_box_valid          = rhs.m_raw_bounding_box_valid;
596     m_raw_mesh_bounding_box           = rhs.m_raw_mesh_bounding_box;
597     m_raw_mesh_bounding_box_valid     = rhs.m_raw_mesh_bounding_box_valid;
598 
599     this->clear_volumes();
600 	this->volumes = std::move(rhs.volumes);
601 	rhs.volumes.clear();
602     for (ModelVolume *model_volume : this->volumes)
603         model_volume->set_model_object(this);
604     this->clear_instances();
605 	this->instances = std::move(rhs.instances);
606 	rhs.instances.clear();
607     for (ModelInstance *model_instance : this->instances)
608         model_instance->set_model_object(this);
609 
610     return *this;
611 }
612 
assign_new_unique_ids_recursive()613 void ModelObject::assign_new_unique_ids_recursive()
614 {
615     this->set_new_unique_id();
616     for (ModelVolume *model_volume : this->volumes)
617         model_volume->assign_new_unique_ids_recursive();
618     for (ModelInstance *model_instance : this->instances)
619         model_instance->assign_new_unique_ids_recursive();
620     this->layer_height_profile.set_new_unique_id();
621 }
622 
623 // Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
624 // Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
625 //ModelObject* ModelObject::clone(Model *parent)
626 //{
627 //    return new ModelObject(parent, *this, true);
628 //}
629 
add_volume(const TriangleMesh & mesh)630 ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
631 {
632     ModelVolume* v = new ModelVolume(this, mesh);
633     this->volumes.push_back(v);
634     v->center_geometry_after_creation();
635     this->invalidate_bounding_box();
636     return v;
637 }
638 
add_volume(TriangleMesh && mesh)639 ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
640 {
641     ModelVolume* v = new ModelVolume(this, std::move(mesh));
642     this->volumes.push_back(v);
643     v->center_geometry_after_creation();
644     this->invalidate_bounding_box();
645     return v;
646 }
647 
add_volume(const ModelVolume & other)648 ModelVolume* ModelObject::add_volume(const ModelVolume &other)
649 {
650     ModelVolume* v = new ModelVolume(this, other);
651     this->volumes.push_back(v);
652 	// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
653 //	v->center_geometry_after_creation();
654 //    this->invalidate_bounding_box();
655     return v;
656 }
657 
add_volume(const ModelVolume & other,TriangleMesh && mesh)658 ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh)
659 {
660     ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
661     this->volumes.push_back(v);
662     v->center_geometry_after_creation();
663     this->invalidate_bounding_box();
664     return v;
665 }
666 
delete_volume(size_t idx)667 void ModelObject::delete_volume(size_t idx)
668 {
669     ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
670     delete *i;
671     this->volumes.erase(i);
672 
673     if (this->volumes.size() == 1)
674     {
675         // only one volume left
676         // we need to collapse the volume transform into the instances transforms because now when selecting this volume
677         // it will be seen as a single full instance ans so its volume transform may be ignored
678         ModelVolume* v = this->volumes.front();
679         Transform3d v_t = v->get_transformation().get_matrix();
680         for (ModelInstance* inst : this->instances)
681         {
682             inst->set_transformation(Geometry::Transformation(inst->get_transformation().get_matrix() * v_t));
683         }
684         Geometry::Transformation t;
685         v->set_transformation(t);
686         v->set_new_unique_id();
687     }
688 
689     this->invalidate_bounding_box();
690 }
691 
clear_volumes()692 void ModelObject::clear_volumes()
693 {
694     for (ModelVolume *v : this->volumes)
695         delete v;
696     this->volumes.clear();
697     this->invalidate_bounding_box();
698 }
699 
add_instance()700 ModelInstance* ModelObject::add_instance()
701 {
702     ModelInstance* i = new ModelInstance(this);
703     this->instances.push_back(i);
704     this->invalidate_bounding_box();
705     return i;
706 }
707 
add_instance(const ModelInstance & other)708 ModelInstance* ModelObject::add_instance(const ModelInstance &other)
709 {
710     ModelInstance* i = new ModelInstance(this, other);
711     this->instances.push_back(i);
712     this->invalidate_bounding_box();
713     return i;
714 }
715 
add_instance(const Vec3d & offset,const Vec3d & scaling_factor,const Vec3d & rotation,const Vec3d & mirror)716 ModelInstance* ModelObject::add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror)
717 {
718     auto *instance = add_instance();
719     instance->set_offset(offset);
720     instance->set_scaling_factor(scaling_factor);
721     instance->set_rotation(rotation);
722     instance->set_mirror(mirror);
723     return instance;
724 }
725 
delete_instance(size_t idx)726 void ModelObject::delete_instance(size_t idx)
727 {
728     ModelInstancePtrs::iterator i = this->instances.begin() + idx;
729     delete *i;
730     this->instances.erase(i);
731     this->invalidate_bounding_box();
732 }
733 
delete_last_instance()734 void ModelObject::delete_last_instance()
735 {
736     this->delete_instance(this->instances.size() - 1);
737 }
738 
clear_instances()739 void ModelObject::clear_instances()
740 {
741     for (ModelInstance *i : this->instances)
742         delete i;
743     this->instances.clear();
744     this->invalidate_bounding_box();
745 }
746 
747 // Returns the bounding box of the transformed instances.
748 // This bounding box is approximate and not snug.
bounding_box() const749 const BoundingBoxf3& ModelObject::bounding_box() const
750 {
751     if (! m_bounding_box_valid) {
752         m_bounding_box_valid = true;
753         BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
754         m_bounding_box.reset();
755         for (const ModelInstance *i : this->instances)
756             m_bounding_box.merge(i->transform_bounding_box(raw_bbox));
757     }
758     return m_bounding_box;
759 }
760 
761 // A mesh containing all transformed instances of this object.
mesh() const762 TriangleMesh ModelObject::mesh() const
763 {
764     TriangleMesh mesh;
765     TriangleMesh raw_mesh = this->raw_mesh();
766     for (const ModelInstance *i : this->instances) {
767         TriangleMesh m = raw_mesh;
768         i->transform_mesh(&m);
769         mesh.merge(m);
770     }
771     return mesh;
772 }
773 
774 // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
775 // Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
776 // and to display the object statistics at ModelObject::print_info().
raw_mesh() const777 TriangleMesh ModelObject::raw_mesh() const
778 {
779     TriangleMesh mesh;
780     for (const ModelVolume *v : this->volumes)
781         if (v->is_model_part())
782         {
783             TriangleMesh vol_mesh(v->mesh());
784             vol_mesh.transform(v->get_matrix());
785             mesh.merge(vol_mesh);
786         }
787     return mesh;
788 }
789 
790 // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
791 // Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
792 // and to display the object statistics at ModelObject::print_info().
raw_indexed_triangle_set() const793 indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
794 {
795     size_t num_vertices = 0;
796     size_t num_faces    = 0;
797     for (const ModelVolume *v : this->volumes)
798         if (v->is_model_part()) {
799             num_vertices += v->mesh().its.vertices.size();
800             num_faces    += v->mesh().its.indices.size();
801         }
802     indexed_triangle_set out;
803     out.vertices.reserve(num_vertices);
804     out.indices.reserve(num_faces);
805     for (const ModelVolume *v : this->volumes)
806         if (v->is_model_part()) {
807             size_t i = out.vertices.size();
808             size_t j = out.indices.size();
809             append(out.vertices, v->mesh().its.vertices);
810             append(out.indices,  v->mesh().its.indices);
811             auto m = v->get_matrix();
812             for (; i < out.vertices.size(); ++ i)
813                 out.vertices[i] = (m * out.vertices[i].cast<double>()).cast<float>().eval();
814             if (v->is_left_handed()) {
815                 for (; j < out.indices.size(); ++ j)
816                     std::swap(out.indices[j][0], out.indices[j][1]);
817             }
818         }
819     return out;
820 }
821 
822 // Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
full_raw_mesh() const823 TriangleMesh ModelObject::full_raw_mesh() const
824 {
825     TriangleMesh mesh;
826     for (const ModelVolume *v : this->volumes)
827     {
828         TriangleMesh vol_mesh(v->mesh());
829         vol_mesh.transform(v->get_matrix());
830         mesh.merge(vol_mesh);
831     }
832     return mesh;
833 }
834 
raw_mesh_bounding_box() const835 const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
836 {
837     if (! m_raw_mesh_bounding_box_valid) {
838         m_raw_mesh_bounding_box_valid = true;
839         m_raw_mesh_bounding_box.reset();
840         for (const ModelVolume *v : this->volumes)
841             if (v->is_model_part())
842                 m_raw_mesh_bounding_box.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
843     }
844     return m_raw_mesh_bounding_box;
845 }
846 
full_raw_mesh_bounding_box() const847 BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
848 {
849 	BoundingBoxf3 bb;
850 	for (const ModelVolume *v : this->volumes)
851 		bb.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
852 	return bb;
853 }
854 
855 // A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
856 // This bounding box is only used for the actual slicing and for layer editing UI to calculate the layers.
raw_bounding_box() const857 const BoundingBoxf3& ModelObject::raw_bounding_box() const
858 {
859     if (! m_raw_bounding_box_valid) {
860         m_raw_bounding_box_valid = true;
861         m_raw_bounding_box.reset();
862         if (this->instances.empty())
863             throw Slic3r::InvalidArgument("Can't call raw_bounding_box() with no instances");
864 
865         const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
866         for (const ModelVolume *v : this->volumes)
867             if (v->is_model_part())
868                 m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
869     }
870 	return m_raw_bounding_box;
871 }
872 
873 // This returns an accurate snug bounding box of the transformed object instance, without the translation applied.
instance_bounding_box(size_t instance_idx,bool dont_translate) const874 BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const
875 {
876     BoundingBoxf3 bb;
877     const Transform3d& inst_matrix = this->instances[instance_idx]->get_transformation().get_matrix(dont_translate);
878     for (ModelVolume *v : this->volumes)
879     {
880         if (v->is_model_part())
881             bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
882     }
883     return bb;
884 }
885 
886 // Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
887 // This method is cheap in that it does not make any unnecessary copy of the volume meshes.
888 // This method is used by the auto arrange function.
convex_hull_2d(const Transform3d & trafo_instance) const889 Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
890 {
891     Points pts;
892     for (const ModelVolume *v : this->volumes)
893         if (v->is_model_part()) {
894             Transform3d trafo = trafo_instance * v->get_matrix();
895 			const indexed_triangle_set &its = v->mesh().its;
896 			if (its.vertices.empty()) {
897                 // Using the STL faces.
898 				const stl_file& stl = v->mesh().stl;
899 				for (const stl_facet &facet : stl.facet_start)
900                     for (size_t j = 0; j < 3; ++ j) {
901                         Vec3d p = trafo * facet.vertex[j].cast<double>();
902                         pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
903                     }
904             } else {
905                 // Using the shared vertices should be a bit quicker than using the STL faces.
906                 for (size_t i = 0; i < its.vertices.size(); ++ i) {
907                     Vec3d p = trafo * its.vertices[i].cast<double>();
908                     pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
909                 }
910             }
911         }
912     std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
913     pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
914 
915     Polygon hull;
916     int n = (int)pts.size();
917     if (n >= 3) {
918         int k = 0;
919         hull.points.resize(2 * n);
920         // Build lower hull
921         for (int i = 0; i < n; ++ i) {
922             while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
923                 -- k;
924             hull[k ++] = pts[i];
925         }
926         // Build upper hull
927         for (int i = n-2, t = k+1; i >= 0; i--) {
928             while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
929                 -- k;
930             hull[k ++] = pts[i];
931         }
932         hull.points.resize(k);
933         assert(hull.points.front() == hull.points.back());
934         hull.points.pop_back();
935     }
936     return hull;
937 }
938 
center_around_origin(bool include_modifiers)939 void ModelObject::center_around_origin(bool include_modifiers)
940 {
941     // calculate the displacements needed to
942     // center this object around the origin
943     BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
944 
945     // Shift is the vector from the center of the bounding box to the origin
946     Vec3d shift = -bb.center();
947 
948     this->translate(shift);
949     this->origin_translation += shift;
950 }
951 
ensure_on_bed()952 void ModelObject::ensure_on_bed()
953 {
954     translate_instances(Vec3d(0.0, 0.0, -get_min_z()));
955 }
956 
translate_instances(const Vec3d & vector)957 void ModelObject::translate_instances(const Vec3d& vector)
958 {
959     for (size_t i = 0; i < instances.size(); ++i)
960     {
961         translate_instance(i, vector);
962     }
963 }
964 
translate_instance(size_t instance_idx,const Vec3d & vector)965 void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
966 {
967     ModelInstance* i = instances[instance_idx];
968     i->set_offset(i->get_offset() + vector);
969     invalidate_bounding_box();
970 }
971 
translate(double x,double y,double z)972 void ModelObject::translate(double x, double y, double z)
973 {
974     for (ModelVolume *v : this->volumes)
975     {
976         v->translate(x, y, z);
977     }
978 
979     if (m_bounding_box_valid)
980         m_bounding_box.translate(x, y, z);
981 }
982 
scale(const Vec3d & versor)983 void ModelObject::scale(const Vec3d &versor)
984 {
985     for (ModelVolume *v : this->volumes)
986     {
987         v->scale(versor);
988     }
989     this->invalidate_bounding_box();
990 }
991 
rotate(double angle,Axis axis)992 void ModelObject::rotate(double angle, Axis axis)
993 {
994     for (ModelVolume *v : this->volumes)
995     {
996         v->rotate(angle, axis);
997     }
998 
999     center_around_origin();
1000     this->invalidate_bounding_box();
1001 }
1002 
rotate(double angle,const Vec3d & axis)1003 void ModelObject::rotate(double angle, const Vec3d& axis)
1004 {
1005     for (ModelVolume *v : this->volumes)
1006     {
1007         v->rotate(angle, axis);
1008     }
1009 
1010     center_around_origin();
1011     this->invalidate_bounding_box();
1012 }
1013 
mirror(Axis axis)1014 void ModelObject::mirror(Axis axis)
1015 {
1016     for (ModelVolume *v : this->volumes)
1017     {
1018         v->mirror(axis);
1019     }
1020 
1021     this->invalidate_bounding_box();
1022 }
1023 
1024 // This method could only be called before the meshes of this ModelVolumes are not shared!
scale_mesh_after_creation(const Vec3d & versor)1025 void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
1026 {
1027     for (ModelVolume *v : this->volumes)
1028     {
1029         v->scale_geometry_after_creation(versor);
1030         v->set_offset(versor.cwiseProduct(v->get_offset()));
1031     }
1032     this->invalidate_bounding_box();
1033 }
1034 
convert_units(ModelObjectPtrs & new_objects,bool from_imperial,std::vector<int> volume_idxs)1035 void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial, std::vector<int> volume_idxs)
1036 {
1037     BOOST_LOG_TRIVIAL(trace) << "ModelObject::convert_units - start";
1038 
1039     ModelObject* new_object = new_clone(*this);
1040 
1041     double koef = from_imperial ? 25.4 : 0.0393700787;
1042     const Vec3d versor = Vec3d(koef, koef, koef);
1043 
1044     new_object->set_model(nullptr);
1045     new_object->sla_support_points.clear();
1046     new_object->sla_drain_holes.clear();
1047     new_object->sla_points_status = sla::PointsStatus::NoPoints;
1048     new_object->clear_volumes();
1049     new_object->input_file.clear();
1050 
1051     int vol_idx = 0;
1052     for (ModelVolume* volume : volumes)
1053     {
1054         if (!volume->mesh().empty()) {
1055             TriangleMesh mesh(volume->mesh());
1056             mesh.require_shared_vertices();
1057 
1058             ModelVolume* vol = new_object->add_volume(mesh);
1059             vol->name = volume->name;
1060             vol->set_type(volume->type());
1061             // Don't copy the config's ID.
1062             vol->config.assign_config(volume->config);
1063             assert(vol->config.id().valid());
1064             assert(vol->config.id() != volume->config.id());
1065             vol->set_material(volume->material_id(), *volume->material());
1066             vol->source.input_file = volume->source.input_file;
1067             vol->source.object_idx = (int)new_objects.size();
1068             vol->source.volume_idx = vol_idx;
1069 
1070             vol->supported_facets.assign(volume->supported_facets);
1071             vol->seam_facets.assign(volume->seam_facets);
1072 
1073             // Perform conversion only if the target "imperial" state is different from the current one.
1074             // This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
1075             if (//vol->source.is_converted_from_inches != from_imperial &&
1076                 (volume_idxs.empty() ||
1077                  std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
1078                 vol->scale_geometry_after_creation(versor);
1079                 vol->set_offset(versor.cwiseProduct(volume->get_offset()));
1080                 vol->source.is_converted_from_inches = from_imperial;
1081             }
1082             else
1083                 vol->set_offset(volume->get_offset());
1084         }
1085         vol_idx ++;
1086     }
1087     new_object->invalidate_bounding_box();
1088 
1089     new_objects.push_back(new_object);
1090 
1091     BOOST_LOG_TRIVIAL(trace) << "ModelObject::convert_units - end";
1092 }
1093 
materials_count() const1094 size_t ModelObject::materials_count() const
1095 {
1096     std::set<t_model_material_id> material_ids;
1097     for (const ModelVolume *v : this->volumes)
1098         material_ids.insert(v->material_id());
1099     return material_ids.size();
1100 }
1101 
facets_count() const1102 size_t ModelObject::facets_count() const
1103 {
1104     size_t num = 0;
1105     for (const ModelVolume *v : this->volumes)
1106         if (v->is_model_part())
1107             num += v->mesh().stl.stats.number_of_facets;
1108     return num;
1109 }
1110 
needed_repair() const1111 bool ModelObject::needed_repair() const
1112 {
1113     for (const ModelVolume *v : this->volumes)
1114         if (v->is_model_part() && v->mesh().needed_repair())
1115             return true;
1116     return false;
1117 }
1118 
cut(size_t instance,coordf_t z,bool keep_upper,bool keep_lower,bool rotate_lower)1119 ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
1120 {
1121     if (!keep_upper && !keep_lower) { return {}; }
1122 
1123     BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
1124 
1125     // Clone the object to duplicate instances, materials etc.
1126     ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr;
1127     ModelObject* lower = keep_lower ? ModelObject::new_clone(*this) : nullptr;
1128 
1129     if (keep_upper) {
1130         upper->set_model(nullptr);
1131         upper->sla_support_points.clear();
1132         upper->sla_drain_holes.clear();
1133         upper->sla_points_status = sla::PointsStatus::NoPoints;
1134         upper->clear_volumes();
1135         upper->input_file.clear();
1136     }
1137 
1138     if (keep_lower) {
1139         lower->set_model(nullptr);
1140         lower->sla_support_points.clear();
1141         lower->sla_drain_holes.clear();
1142         lower->sla_points_status = sla::PointsStatus::NoPoints;
1143         lower->clear_volumes();
1144         lower->input_file.clear();
1145     }
1146 
1147     // Because transformations are going to be applied to meshes directly,
1148     // we reset transformation of all instances and volumes,
1149     // except for translation and Z-rotation on instances, which are preserved
1150     // in the transformation matrix and not applied to the mesh transform.
1151 
1152     // const auto instance_matrix = instances[instance]->get_matrix(true);
1153     const auto instance_matrix = Geometry::assemble_transform(
1154         Vec3d::Zero(),  // don't apply offset
1155         instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 0.0)),   // don't apply Z-rotation
1156         instances[instance]->get_scaling_factor(),
1157         instances[instance]->get_mirror()
1158     );
1159 
1160     z -= instances[instance]->get_offset()(2);
1161 
1162     // Lower part per-instance bounding boxes
1163     std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
1164 
1165     for (ModelVolume *volume : volumes) {
1166         const auto volume_matrix = volume->get_matrix();
1167 
1168         volume->supported_facets.clear();
1169         volume->seam_facets.clear();
1170 
1171         if (! volume->is_model_part()) {
1172             // Modifiers are not cut, but we still need to add the instance transformation
1173             // to the modifier volume transformation to preserve their shape properly.
1174 
1175             volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
1176 
1177             if (keep_upper) { upper->add_volume(*volume); }
1178             if (keep_lower) { lower->add_volume(*volume); }
1179         }
1180         else if (! volume->mesh().empty()) {
1181 
1182             TriangleMesh upper_mesh, lower_mesh;
1183 
1184             // Transform the mesh by the combined transformation matrix.
1185             // Flip the triangles in case the composite transformation is left handed.
1186 			TriangleMesh mesh(volume->mesh());
1187 			mesh.transform(instance_matrix * volume_matrix, true);
1188 			volume->reset_mesh();
1189 
1190             mesh.require_shared_vertices();
1191 
1192             // Perform cut
1193             TriangleMeshSlicer tms(&mesh);
1194             tms.cut(float(z), &upper_mesh, &lower_mesh);
1195 
1196             // Reset volume transformation except for offset
1197             const Vec3d offset = volume->get_offset();
1198             volume->set_transformation(Geometry::Transformation());
1199             volume->set_offset(offset);
1200 
1201             if (keep_upper) {
1202                 upper_mesh.repair();
1203                 upper_mesh.reset_repair_stats();
1204             }
1205             if (keep_lower) {
1206                 lower_mesh.repair();
1207                 lower_mesh.reset_repair_stats();
1208             }
1209 
1210             if (keep_upper && upper_mesh.facets_count() > 0) {
1211                 ModelVolume* vol = upper->add_volume(upper_mesh);
1212                 vol->name	= volume->name;
1213                 // Don't copy the config's ID.
1214                 vol->config.assign_config(volume->config);
1215     			assert(vol->config.id().valid());
1216 	    		assert(vol->config.id() != volume->config.id());
1217                 vol->set_material(volume->material_id(), *volume->material());
1218             }
1219             if (keep_lower && lower_mesh.facets_count() > 0) {
1220                 ModelVolume* vol = lower->add_volume(lower_mesh);
1221                 vol->name	= volume->name;
1222                 // Don't copy the config's ID.
1223                 vol->config.assign_config(volume->config);
1224                 assert(vol->config.id().valid());
1225 	    		assert(vol->config.id() != volume->config.id());
1226                 vol->set_material(volume->material_id(), *volume->material());
1227 
1228                 // Compute the lower part instances' bounding boxes to figure out where to place
1229                 // the upper part
1230                 if (keep_upper) {
1231                     for (size_t i = 0; i < instances.size(); i++) {
1232                         lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true));
1233                     }
1234                 }
1235             }
1236         }
1237     }
1238 
1239     ModelObjectPtrs res;
1240 
1241     if (keep_upper && upper->volumes.size() > 0) {
1242         upper->invalidate_bounding_box();
1243         upper->center_around_origin();
1244 
1245         // Reset instance transformation except offset and Z-rotation
1246         for (size_t i = 0; i < instances.size(); i++) {
1247             auto &instance = upper->instances[i];
1248             const Vec3d offset = instance->get_offset();
1249             const double rot_z = instance->get_rotation()(2);
1250             // The upper part displacement is set to half of the lower part bounding box
1251             // this is done in hope at least a part of the upper part will always be visible and draggable
1252             const Vec3d displace = lower_bboxes[i].size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
1253 
1254             instance->set_transformation(Geometry::Transformation());
1255             instance->set_offset(offset + displace);
1256             instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
1257         }
1258 
1259         res.push_back(upper);
1260     }
1261     if (keep_lower && lower->volumes.size() > 0) {
1262         lower->invalidate_bounding_box();
1263         lower->center_around_origin();
1264 
1265         // Reset instance transformation except offset and Z-rotation
1266         for (auto *instance : lower->instances) {
1267             const Vec3d offset = instance->get_offset();
1268             const double rot_z = instance->get_rotation()(2);
1269 
1270             instance->set_transformation(Geometry::Transformation());
1271             instance->set_offset(offset);
1272             instance->set_rotation(Vec3d(rotate_lower ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
1273         }
1274 
1275         res.push_back(lower);
1276     }
1277 
1278     BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
1279 
1280     return res;
1281 }
1282 
split(ModelObjectPtrs * new_objects)1283 void ModelObject::split(ModelObjectPtrs* new_objects)
1284 {
1285     if (this->volumes.size() > 1) {
1286         // We can't split meshes if there's more than one volume, because
1287         // we can't group the resulting meshes by object afterwards
1288         new_objects->emplace_back(this);
1289         return;
1290     }
1291 
1292     ModelVolume* volume = this->volumes.front();
1293     TriangleMeshPtrs meshptrs = volume->mesh().split();
1294     for (TriangleMesh *mesh : meshptrs) {
1295 
1296         // FIXME: crashes if not satisfied
1297         if (mesh->facets_count() < 3) continue;
1298 
1299         mesh->repair();
1300 
1301         // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
1302         ModelObject* new_object = m_model->add_object();
1303         new_object->name   = this->name;
1304         // Don't copy the config's ID.
1305 		new_object->config.assign_config(this->config);
1306 		assert(new_object->config.id().valid());
1307 		assert(new_object->config.id() != this->config.id());
1308         new_object->instances.reserve(this->instances.size());
1309         for (const ModelInstance *model_instance : this->instances)
1310             new_object->add_instance(*model_instance);
1311         ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
1312 
1313         for (ModelInstance* model_instance : new_object->instances)
1314         {
1315             Vec3d shift = model_instance->get_transformation().get_matrix(true) * new_vol->get_offset();
1316             model_instance->set_offset(model_instance->get_offset() + shift);
1317         }
1318 
1319         new_vol->set_offset(Vec3d::Zero());
1320         // reset the source to disable reload from disk
1321         new_vol->source = ModelVolume::Source();
1322         new_objects->emplace_back(new_object);
1323         delete mesh;
1324     }
1325 
1326     return;
1327 }
1328 
merge()1329 void ModelObject::merge()
1330 {
1331     if (this->volumes.size() == 1) {
1332         // We can't merge meshes if there's just one volume
1333         return;
1334     }
1335 
1336     TriangleMesh mesh;
1337 
1338     for (ModelVolume* volume : volumes)
1339         if (!volume->mesh().empty())
1340             mesh.merge(volume->mesh());
1341     mesh.repair();
1342 
1343     this->clear_volumes();
1344     ModelVolume* vol = this->add_volume(mesh);
1345 
1346     if (!vol)
1347         return;
1348 }
1349 
1350 // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
1351 // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
1352 // This situation is solved by baking in the instance transformation into the mesh vertices.
1353 // Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well.
bake_xy_rotation_into_meshes(size_t instance_idx)1354 void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
1355 {
1356     assert(instance_idx < this->instances.size());
1357 
1358 	const Geometry::Transformation reference_trafo = this->instances[instance_idx]->get_transformation();
1359     if (Geometry::is_rotation_ninety_degrees(reference_trafo.get_rotation()))
1360         // nothing to do, scaling in the world coordinate space is possible in the representation of Geometry::Transformation.
1361         return;
1362 
1363     bool   left_handed        = reference_trafo.is_left_handed();
1364     bool   has_mirrorring     = ! reference_trafo.get_mirror().isApprox(Vec3d(1., 1., 1.));
1365     bool   uniform_scaling    = std::abs(reference_trafo.get_scaling_factor().x() - reference_trafo.get_scaling_factor().y()) < EPSILON &&
1366                                 std::abs(reference_trafo.get_scaling_factor().x() - reference_trafo.get_scaling_factor().z()) < EPSILON;
1367     double new_scaling_factor = uniform_scaling ? reference_trafo.get_scaling_factor().x() : 1.;
1368 
1369     // Adjust the instances.
1370     for (size_t i = 0; i < this->instances.size(); ++ i) {
1371         ModelInstance &model_instance = *this->instances[i];
1372         model_instance.set_rotation(Vec3d(0., 0., Geometry::rotation_diff_z(reference_trafo.get_rotation(), model_instance.get_rotation())));
1373         model_instance.set_scaling_factor(Vec3d(new_scaling_factor, new_scaling_factor, new_scaling_factor));
1374         model_instance.set_mirror(Vec3d(1., 1., 1.));
1375     }
1376 
1377     // Adjust the meshes.
1378     // Transformation to be applied to the meshes.
1379     Eigen::Matrix3d mesh_trafo_3x3           = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
1380 	Transform3d     volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
1381     for (ModelVolume *model_volume : this->volumes) {
1382         const Geometry::Transformation volume_trafo = model_volume->get_transformation();
1383         bool   volume_left_handed        = volume_trafo.is_left_handed();
1384         bool   volume_has_mirrorring     = ! volume_trafo.get_mirror().isApprox(Vec3d(1., 1., 1.));
1385         bool   volume_uniform_scaling    = std::abs(volume_trafo.get_scaling_factor().x() - volume_trafo.get_scaling_factor().y()) < EPSILON &&
1386                                            std::abs(volume_trafo.get_scaling_factor().x() - volume_trafo.get_scaling_factor().z()) < EPSILON;
1387         double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.;
1388         // Transform the mesh.
1389 		Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0);
1390         // Following method creates a new shared_ptr<TriangleMesh>
1391 		model_volume->transform_this_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
1392         // Reset the rotation, scaling and mirroring.
1393         model_volume->set_rotation(Vec3d(0., 0., 0.));
1394         model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor));
1395         model_volume->set_mirror(Vec3d(1., 1., 1.));
1396         // Move the reference point of the volume to compensate for the change of the instance trafo.
1397         model_volume->set_offset(volume_offset_correction * volume_trafo.get_offset());
1398         // reset the source to disable reload from disk
1399         model_volume->source = ModelVolume::Source();
1400     }
1401 
1402     this->invalidate_bounding_box();
1403 }
1404 
get_min_z() const1405 double ModelObject::get_min_z() const
1406 {
1407     if (instances.empty())
1408         return 0.0;
1409     else
1410     {
1411         double min_z = DBL_MAX;
1412         for (size_t i = 0; i < instances.size(); ++i)
1413         {
1414             min_z = std::min(min_z, get_instance_min_z(i));
1415         }
1416         return min_z;
1417     }
1418 }
1419 
get_instance_min_z(size_t instance_idx) const1420 double ModelObject::get_instance_min_z(size_t instance_idx) const
1421 {
1422     double min_z = DBL_MAX;
1423 
1424     ModelInstance* inst = instances[instance_idx];
1425     const Transform3d& mi = inst->get_matrix(true);
1426 
1427     for (const ModelVolume* v : volumes)
1428     {
1429         if (!v->is_model_part())
1430             continue;
1431 
1432         Transform3d mv = mi * v->get_matrix();
1433         const TriangleMesh& hull = v->get_convex_hull();
1434 		for (const stl_facet &facet : hull.stl.facet_start)
1435 			for (int i = 0; i < 3; ++ i)
1436 				min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
1437     }
1438 
1439     return min_z + inst->get_offset(Z);
1440 }
1441 
check_instances_print_volume_state(const BoundingBoxf3 & print_volume)1442 unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
1443 {
1444     unsigned int num_printable = 0;
1445     enum {
1446         INSIDE  = 1,
1447         OUTSIDE = 2
1448     };
1449     for (ModelInstance *model_instance : this->instances) {
1450         unsigned int inside_outside = 0;
1451         for (const ModelVolume *vol : this->volumes)
1452             if (vol->is_model_part()) {
1453                 BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix());
1454                 if (print_volume.contains(bb))
1455                     inside_outside |= INSIDE;
1456                 else if (print_volume.intersects(bb))
1457                     inside_outside |= INSIDE | OUTSIDE;
1458                 else
1459                     inside_outside |= OUTSIDE;
1460             }
1461         model_instance->print_volume_state =
1462             (inside_outside == (INSIDE | OUTSIDE)) ? ModelInstancePVS_Partly_Outside :
1463             (inside_outside == INSIDE) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;
1464         if (inside_outside == INSIDE)
1465             ++ num_printable;
1466     }
1467     return num_printable;
1468 }
1469 
print_info() const1470 void ModelObject::print_info() const
1471 {
1472     using namespace std;
1473     cout << fixed;
1474     boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
1475 
1476     TriangleMesh mesh = this->raw_mesh();
1477     mesh.check_topology();
1478     BoundingBoxf3 bb = mesh.bounding_box();
1479     Vec3d size = bb.size();
1480     cout << "size_x = " << size(0) << endl;
1481     cout << "size_y = " << size(1) << endl;
1482     cout << "size_z = " << size(2) << endl;
1483     cout << "min_x = " << bb.min(0) << endl;
1484     cout << "min_y = " << bb.min(1) << endl;
1485     cout << "min_z = " << bb.min(2) << endl;
1486     cout << "max_x = " << bb.max(0) << endl;
1487     cout << "max_y = " << bb.max(1) << endl;
1488     cout << "max_z = " << bb.max(2) << endl;
1489     cout << "number_of_facets = " << mesh.stl.stats.number_of_facets  << endl;
1490     cout << "manifold = "   << (mesh.is_manifold() ? "yes" : "no") << endl;
1491 
1492     mesh.repair();  // this calculates number_of_parts
1493     if (mesh.needed_repair()) {
1494         mesh.repair();
1495         if (mesh.stl.stats.degenerate_facets > 0)
1496             cout << "degenerate_facets = "  << mesh.stl.stats.degenerate_facets << endl;
1497         if (mesh.stl.stats.edges_fixed > 0)
1498             cout << "edges_fixed = "        << mesh.stl.stats.edges_fixed       << endl;
1499         if (mesh.stl.stats.facets_removed > 0)
1500             cout << "facets_removed = "     << mesh.stl.stats.facets_removed    << endl;
1501         if (mesh.stl.stats.facets_added > 0)
1502             cout << "facets_added = "       << mesh.stl.stats.facets_added      << endl;
1503         if (mesh.stl.stats.facets_reversed > 0)
1504             cout << "facets_reversed = "    << mesh.stl.stats.facets_reversed   << endl;
1505         if (mesh.stl.stats.backwards_edges > 0)
1506             cout << "backwards_edges = "    << mesh.stl.stats.backwards_edges   << endl;
1507     }
1508     cout << "number_of_parts =  " << mesh.stl.stats.number_of_parts << endl;
1509     cout << "volume = "           << mesh.volume()                  << endl;
1510 }
1511 
get_export_filename() const1512 std::string ModelObject::get_export_filename() const
1513 {
1514     std::string ret = input_file;
1515 
1516     if (!name.empty())
1517     {
1518         if (ret.empty())
1519             // input_file was empty, just use name
1520             ret = name;
1521         else
1522         {
1523             // Replace file name in input_file with name, but keep the path and file extension.
1524             ret = (boost::filesystem::path(name).parent_path().empty()) ?
1525                 (boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name;
1526         }
1527     }
1528 
1529     return ret;
1530 }
1531 
get_object_stl_stats() const1532 stl_stats ModelObject::get_object_stl_stats() const
1533 {
1534     if (this->volumes.size() == 1)
1535         return this->volumes[0]->mesh().stl.stats;
1536 
1537     stl_stats full_stats;
1538     full_stats.volume = 0.f;
1539 
1540     // fill full_stats from all objet's meshes
1541     for (ModelVolume* volume : this->volumes)
1542     {
1543         const stl_stats& stats = volume->mesh().stl.stats;
1544 
1545         // initialize full_stats (for repaired errors)
1546         full_stats.degenerate_facets    += stats.degenerate_facets;
1547         full_stats.edges_fixed          += stats.edges_fixed;
1548         full_stats.facets_removed       += stats.facets_removed;
1549         full_stats.facets_added         += stats.facets_added;
1550         full_stats.facets_reversed      += stats.facets_reversed;
1551         full_stats.backwards_edges      += stats.backwards_edges;
1552 
1553         // another used satistics value
1554         if (volume->is_model_part()) {
1555             full_stats.volume           += stats.volume;
1556             full_stats.number_of_parts  += stats.number_of_parts;
1557         }
1558     }
1559 
1560     return full_stats;
1561 }
1562 
get_mesh_errors_count(const int vol_idx) const1563 int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
1564 {
1565     if (vol_idx >= 0)
1566         return this->volumes[vol_idx]->get_mesh_errors_count();
1567 
1568     const stl_stats& stats = get_object_stl_stats();
1569 
1570     return  stats.degenerate_facets + stats.edges_fixed     + stats.facets_removed +
1571             stats.facets_added      + stats.facets_reversed + stats.backwards_edges;
1572 }
1573 
set_material_id(t_model_material_id material_id)1574 void ModelVolume::set_material_id(t_model_material_id material_id)
1575 {
1576     m_material_id = material_id;
1577     // ensure m_material_id references an existing material
1578     if (! material_id.empty())
1579         this->object->get_model()->add_material(material_id);
1580 }
1581 
material() const1582 ModelMaterial* ModelVolume::material() const
1583 {
1584     return this->object->get_model()->get_material(m_material_id);
1585 }
1586 
set_material(t_model_material_id material_id,const ModelMaterial & material)1587 void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
1588 {
1589     m_material_id = material_id;
1590     if (! material_id.empty())
1591         this->object->get_model()->add_material(material_id, material);
1592 }
1593 
1594 // Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.
extruder_id() const1595 int ModelVolume::extruder_id() const
1596 {
1597     int extruder_id = -1;
1598     if (this->is_model_part()) {
1599         const ConfigOption *opt = this->config.option("extruder");
1600         if ((opt == nullptr) || (opt->getInt() == 0))
1601             opt = this->object->config.option("extruder");
1602         extruder_id = (opt == nullptr) ? 0 : opt->getInt();
1603     }
1604     return extruder_id;
1605 }
1606 
is_splittable() const1607 bool ModelVolume::is_splittable() const
1608 {
1609     // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
1610     if (m_is_splittable == -1)
1611         m_is_splittable = (int)this->mesh().is_splittable();
1612 
1613     return m_is_splittable == 1;
1614 }
1615 
center_geometry_after_creation(bool update_source_offset)1616 void ModelVolume::center_geometry_after_creation(bool update_source_offset)
1617 {
1618     Vec3d shift = this->mesh().bounding_box().center();
1619     if (!shift.isApprox(Vec3d::Zero()))
1620     {
1621     	if (m_mesh)
1622         	const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
1623         if (m_convex_hull)
1624 			const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
1625         translate(shift);
1626     }
1627 
1628     if (update_source_offset)
1629         source.mesh_offset = shift;
1630 }
1631 
calculate_convex_hull()1632 void ModelVolume::calculate_convex_hull()
1633 {
1634     m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
1635 }
1636 
get_mesh_errors_count() const1637 int ModelVolume::get_mesh_errors_count() const
1638 {
1639     const stl_stats& stats = this->mesh().stl.stats;
1640 
1641     return  stats.degenerate_facets + stats.edges_fixed     + stats.facets_removed +
1642             stats.facets_added      + stats.facets_reversed + stats.backwards_edges;
1643 }
1644 
get_convex_hull() const1645 const TriangleMesh& ModelVolume::get_convex_hull() const
1646 {
1647     return *m_convex_hull.get();
1648 }
1649 
type_from_string(const std::string & s)1650 ModelVolumeType ModelVolume::type_from_string(const std::string &s)
1651 {
1652     // Legacy support
1653     if (s == "1")
1654 		return ModelVolumeType::PARAMETER_MODIFIER;
1655     // New type (supporting the support enforcers & blockers)
1656     if (s == "ModelPart")
1657 		return ModelVolumeType::MODEL_PART;
1658     if (s == "ParameterModifier")
1659 		return ModelVolumeType::PARAMETER_MODIFIER;
1660     if (s == "SupportEnforcer")
1661 		return ModelVolumeType::SUPPORT_ENFORCER;
1662     if (s == "SupportBlocker")
1663 		return ModelVolumeType::SUPPORT_BLOCKER;
1664     assert(s == "0");
1665     // Default value if invalud type string received.
1666 	return ModelVolumeType::MODEL_PART;
1667 }
1668 
type_to_string(const ModelVolumeType t)1669 std::string ModelVolume::type_to_string(const ModelVolumeType t)
1670 {
1671     switch (t) {
1672 	case ModelVolumeType::MODEL_PART:         return "ModelPart";
1673 	case ModelVolumeType::PARAMETER_MODIFIER: return "ParameterModifier";
1674 	case ModelVolumeType::SUPPORT_ENFORCER:   return "SupportEnforcer";
1675 	case ModelVolumeType::SUPPORT_BLOCKER:    return "SupportBlocker";
1676     default:
1677         assert(false);
1678         return "ModelPart";
1679     }
1680 }
1681 
1682 // Split this volume, append the result to the object owning this volume.
1683 // Return the number of volumes created from this one.
1684 // This is useful to assign different materials to different volumes of an object.
split(unsigned int max_extruders)1685 size_t ModelVolume::split(unsigned int max_extruders)
1686 {
1687     TriangleMeshPtrs meshptrs = this->mesh().split();
1688     if (meshptrs.size() <= 1) {
1689         delete meshptrs.front();
1690         return 1;
1691     }
1692 
1693     size_t idx = 0;
1694     size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
1695     std::string name = this->name;
1696 
1697     unsigned int extruder_counter = 0;
1698     Vec3d offset = this->get_offset();
1699 
1700     for (TriangleMesh *mesh : meshptrs) {
1701         mesh->repair();
1702         if (idx == 0)
1703         {
1704             this->set_mesh(std::move(*mesh));
1705             this->calculate_convex_hull();
1706             // Assign a new unique ID, so that a new GLVolume will be generated.
1707             this->set_new_unique_id();
1708             // reset the source to disable reload from disk
1709             this->source = ModelVolume::Source();
1710         }
1711         else
1712             this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
1713 
1714         this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
1715         this->object->volumes[ivolume]->center_geometry_after_creation();
1716         this->object->volumes[ivolume]->translate(offset);
1717         this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
1718         this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
1719         delete mesh;
1720         ++ idx;
1721     }
1722 
1723     return idx;
1724 }
1725 
translate(const Vec3d & displacement)1726 void ModelVolume::translate(const Vec3d& displacement)
1727 {
1728     set_offset(get_offset() + displacement);
1729 }
1730 
scale(const Vec3d & scaling_factors)1731 void ModelVolume::scale(const Vec3d& scaling_factors)
1732 {
1733     set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors));
1734 }
1735 
scale_to_fit(const Vec3d & size)1736 void ModelObject::scale_to_fit(const Vec3d &size)
1737 {
1738 /*
1739     BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
1740     Vec3d orig_size = this->bounding_box().size();
1741     float factor = fminf(
1742         size.x / orig_size.x,
1743         fminf(
1744             size.y / orig_size.y,
1745             size.z / orig_size.z
1746         )
1747     );
1748     this->scale(factor);
1749 */
1750 }
1751 
assign_new_unique_ids_recursive()1752 void ModelVolume::assign_new_unique_ids_recursive()
1753 {
1754     ObjectBase::set_new_unique_id();
1755     config.set_new_unique_id();
1756     supported_facets.set_new_unique_id();
1757     seam_facets.set_new_unique_id();
1758 }
1759 
rotate(double angle,Axis axis)1760 void ModelVolume::rotate(double angle, Axis axis)
1761 {
1762     switch (axis)
1763     {
1764     case X: { rotate(angle, Vec3d::UnitX()); break; }
1765     case Y: { rotate(angle, Vec3d::UnitY()); break; }
1766     case Z: { rotate(angle, Vec3d::UnitZ()); break; }
1767     default: break;
1768     }
1769 }
1770 
rotate(double angle,const Vec3d & axis)1771 void ModelVolume::rotate(double angle, const Vec3d& axis)
1772 {
1773     set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix()));
1774 }
1775 
mirror(Axis axis)1776 void ModelVolume::mirror(Axis axis)
1777 {
1778     Vec3d mirror = get_mirror();
1779     switch (axis)
1780     {
1781     case X: { mirror(0) *= -1.0; break; }
1782     case Y: { mirror(1) *= -1.0; break; }
1783     case Z: { mirror(2) *= -1.0; break; }
1784     default: break;
1785     }
1786     set_mirror(mirror);
1787 }
1788 
1789 // This method could only be called before the meshes of this ModelVolumes are not shared!
scale_geometry_after_creation(const Vec3d & versor)1790 void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
1791 {
1792 	const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
1793 	const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
1794 }
1795 
transform_this_mesh(const Transform3d & mesh_trafo,bool fix_left_handed)1796 void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
1797 {
1798 	TriangleMesh mesh = this->mesh();
1799 	mesh.transform(mesh_trafo, fix_left_handed);
1800 	this->set_mesh(std::move(mesh));
1801     TriangleMesh convex_hull = this->get_convex_hull();
1802     convex_hull.transform(mesh_trafo, fix_left_handed);
1803     this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
1804     // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
1805     this->set_new_unique_id();
1806 }
1807 
transform_this_mesh(const Matrix3d & matrix,bool fix_left_handed)1808 void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_handed)
1809 {
1810 	TriangleMesh mesh = this->mesh();
1811 	mesh.transform(matrix, fix_left_handed);
1812 	this->set_mesh(std::move(mesh));
1813     TriangleMesh convex_hull = this->get_convex_hull();
1814     convex_hull.transform(matrix, fix_left_handed);
1815     this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
1816     // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
1817     this->set_new_unique_id();
1818 }
1819 
convert_from_imperial_units()1820 void ModelVolume::convert_from_imperial_units()
1821 {
1822     double in_to_mm = 25.4;
1823     this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
1824     this->set_offset(Vec3d(0, 0, 0));
1825     this->source.is_converted_from_inches = true;
1826 }
1827 
transform_mesh(TriangleMesh * mesh,bool dont_translate) const1828 void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
1829 {
1830     mesh->transform(get_matrix(dont_translate));
1831 }
1832 
transform_mesh_bounding_box(const TriangleMesh & mesh,bool dont_translate) const1833 BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate) const
1834 {
1835     // Rotate around mesh origin.
1836     TriangleMesh copy(mesh);
1837     copy.transform(get_matrix(true, false, true, true));
1838     BoundingBoxf3 bbox = copy.bounding_box();
1839 
1840     if (!empty(bbox)) {
1841         // Scale the bounding box along the three axes.
1842         for (unsigned int i = 0; i < 3; ++i)
1843         {
1844             if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON)
1845             {
1846                 bbox.min(i) *= get_scaling_factor((Axis)i);
1847                 bbox.max(i) *= get_scaling_factor((Axis)i);
1848             }
1849         }
1850 
1851         // Translate the bounding box.
1852         if (! dont_translate) {
1853             bbox.min += get_offset();
1854             bbox.max += get_offset();
1855         }
1856     }
1857     return bbox;
1858 }
1859 
transform_bounding_box(const BoundingBoxf3 & bbox,bool dont_translate) const1860 BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
1861 {
1862     return bbox.transformed(get_matrix(dont_translate));
1863 }
1864 
transform_vector(const Vec3d & v,bool dont_translate) const1865 Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
1866 {
1867     return get_matrix(dont_translate) * v;
1868 }
1869 
transform_polygon(Polygon * polygon) const1870 void ModelInstance::transform_polygon(Polygon* polygon) const
1871 {
1872     // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
1873     polygon->rotate(get_rotation(Z)); // rotate around polygon origin
1874     // CHECK_ME -> Is the following correct ?
1875     polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
1876 }
1877 
get_arrange_polygon() const1878 arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
1879 {
1880 //    static const double SIMPLIFY_TOLERANCE_MM = 0.1;
1881 
1882     Vec3d rotation = get_rotation();
1883     rotation.z()   = 0.;
1884     Transform3d trafo_instance =
1885         Geometry::assemble_transform(Vec3d::Zero(), rotation,
1886                                      get_scaling_factor(), get_mirror());
1887 
1888     Polygon p = get_object()->convex_hull_2d(trafo_instance);
1889 
1890     assert(!p.points.empty());
1891 
1892 //    if (!p.points.empty()) {
1893 //        Polygons pp{p};
1894 //        pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
1895 //        if (!pp.empty()) p = pp.front();
1896 //    }
1897 
1898     arrangement::ArrangePolygon ret;
1899     ret.poly.contour = std::move(p);
1900     ret.translation  = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
1901     ret.rotation     = get_rotation(Z);
1902 
1903     return ret;
1904 }
1905 
get_facets(const ModelVolume & mv,EnforcerBlockerType type) const1906 indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
1907 {
1908     TriangleSelector selector(mv.mesh());
1909     selector.deserialize(m_data);
1910     indexed_triangle_set out = selector.get_facets(type);
1911     return out;
1912 }
1913 
set(const TriangleSelector & selector)1914 bool FacetsAnnotation::set(const TriangleSelector& selector)
1915 {
1916     std::map<int, std::vector<bool>> sel_map = selector.serialize();
1917     if (sel_map != m_data) {
1918         m_data = sel_map;
1919         this->touch();
1920         return true;
1921     }
1922     return false;
1923 }
1924 
clear()1925 void FacetsAnnotation::clear()
1926 {
1927     m_data.clear();
1928     this->reset_timestamp();
1929 }
1930 
1931 // Following function takes data from a triangle and encodes it as string
1932 // of hexadecimal numbers (one digit per triangle). Used for 3MF export,
1933 // changing it may break backwards compatibility !!!!!
get_triangle_as_string(int triangle_idx) const1934 std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
1935 {
1936     std::string out;
1937 
1938     auto triangle_it = m_data.find(triangle_idx);
1939     if (triangle_it != m_data.end()) {
1940         const std::vector<bool>& code = triangle_it->second;
1941         int offset = 0;
1942         while (offset < int(code.size())) {
1943             int next_code = 0;
1944             for (int i=3; i>=0; --i) {
1945                 next_code = next_code << 1;
1946                 next_code |= int(code[offset + i]);
1947             }
1948             offset += 4;
1949 
1950             assert(next_code >=0 && next_code <= 15);
1951             char digit = next_code < 10 ? next_code + '0' : (next_code-10)+'A';
1952             out.insert(out.begin(), digit);
1953         }
1954     }
1955     return out;
1956 }
1957 
1958 // Recover triangle splitting & state from string of hexadecimal values previously
1959 // generated by get_triangle_as_string. Used to load from 3MF.
set_triangle_from_string(int triangle_id,const std::string & str)1960 void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
1961 {
1962     assert(! str.empty());
1963     m_data[triangle_id] = std::vector<bool>(); // zero current state or create new
1964     std::vector<bool>& code = m_data[triangle_id];
1965 
1966     for (auto it = str.crbegin(); it != str.crend(); ++it) {
1967         const char ch = *it;
1968         int dec = 0;
1969         if (ch >= '0' && ch<='9')
1970             dec = int(ch - '0');
1971         else if (ch >='A' && ch <= 'F')
1972             dec = 10 + int(ch - 'A');
1973         else
1974             assert(false);
1975 
1976         // Convert to binary and append into code.
1977         for (int i=0; i<4; ++i) {
1978             code.insert(code.end(), bool(dec & (1 << i)));
1979         }
1980     }
1981 }
1982 
1983 // Test whether the two models contain the same number of ModelObjects with the same set of IDs
1984 // ordered in the same order. In that case it is not necessary to kill the background processing.
model_object_list_equal(const Model & model_old,const Model & model_new)1985 bool model_object_list_equal(const Model &model_old, const Model &model_new)
1986 {
1987     if (model_old.objects.size() != model_new.objects.size())
1988         return false;
1989     for (size_t i = 0; i < model_old.objects.size(); ++ i)
1990         if (model_old.objects[i]->id() != model_new.objects[i]->id())
1991             return false;
1992     return true;
1993 }
1994 
1995 // Test whether the new model is just an extension of the old model (new objects were added
1996 // to the end of the original list. In that case it is not necessary to kill the background processing.
model_object_list_extended(const Model & model_old,const Model & model_new)1997 bool model_object_list_extended(const Model &model_old, const Model &model_new)
1998 {
1999     if (model_old.objects.size() >= model_new.objects.size())
2000         return false;
2001     for (size_t i = 0; i < model_old.objects.size(); ++ i)
2002         if (model_old.objects[i]->id() != model_new.objects[i]->id())
2003             return false;
2004     return true;
2005 }
2006 
model_volume_list_changed(const ModelObject & model_object_old,const ModelObject & model_object_new,const ModelVolumeType type)2007 bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type)
2008 {
2009     size_t i_old, i_new;
2010     for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
2011         const ModelVolume &mv_old = *model_object_old.volumes[i_old];
2012         const ModelVolume &mv_new = *model_object_new.volumes[i_new];
2013         if (mv_old.type() != type) {
2014             ++ i_old;
2015             continue;
2016         }
2017         if (mv_new.type() != type) {
2018             ++ i_new;
2019             continue;
2020         }
2021         if (mv_old.id() != mv_new.id())
2022             return true;
2023         //FIXME test for the content of the mesh!
2024 
2025         if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
2026             return true;
2027 
2028         ++ i_old;
2029         ++ i_new;
2030     }
2031     for (; i_old < model_object_old.volumes.size(); ++ i_old) {
2032         const ModelVolume &mv_old = *model_object_old.volumes[i_old];
2033         if (mv_old.type() == type)
2034             // ModelVolume was deleted.
2035             return true;
2036     }
2037     for (; i_new < model_object_new.volumes.size(); ++ i_new) {
2038         const ModelVolume &mv_new = *model_object_new.volumes[i_new];
2039         if (mv_new.type() == type)
2040             // ModelVolume was added.
2041             return true;
2042     }
2043     return false;
2044 }
2045 
model_custom_supports_data_changed(const ModelObject & mo,const ModelObject & mo_new)2046 bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) {
2047     assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
2048     assert(mo.volumes.size() == mo_new.volumes.size());
2049     for (size_t i=0; i<mo.volumes.size(); ++i) {
2050         if (! mo_new.volumes[i]->supported_facets.timestamp_matches(mo.volumes[i]->supported_facets))
2051             return true;
2052     }
2053     return false;
2054 }
2055 
model_custom_seam_data_changed(const ModelObject & mo,const ModelObject & mo_new)2056 bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) {
2057     assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
2058     assert(mo.volumes.size() == mo_new.volumes.size());
2059     for (size_t i=0; i<mo.volumes.size(); ++i) {
2060         if (! mo_new.volumes[i]->seam_facets.timestamp_matches(mo.volumes[i]->seam_facets))
2061             return true;
2062     }
2063     return false;
2064 }
2065 
model_has_multi_part_objects(const Model & model)2066 extern bool model_has_multi_part_objects(const Model &model)
2067 {
2068     for (const ModelObject *model_object : model.objects)
2069     	if (model_object->volumes.size() != 1 || ! model_object->volumes.front()->is_model_part())
2070     		return true;
2071     return false;
2072 }
2073 
model_has_advanced_features(const Model & model)2074 extern bool model_has_advanced_features(const Model &model)
2075 {
2076 	auto config_is_advanced = [](const ModelConfig &config) {
2077         return ! (config.empty() || (config.size() == 1 && config.cbegin()->first == "extruder"));
2078 	};
2079     for (const ModelObject *model_object : model.objects) {
2080         // Is there more than one instance or advanced config data?
2081         if (model_object->instances.size() > 1 || config_is_advanced(model_object->config))
2082         	return true;
2083         // Is there any modifier or advanced config data?
2084         for (const ModelVolume* model_volume : model_object->volumes)
2085             if (! model_volume->is_model_part() || config_is_advanced(model_volume->config))
2086             	return true;
2087     }
2088     return false;
2089 }
2090 
2091 #ifndef NDEBUG
2092 // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
check_model_ids_validity(const Model & model)2093 void check_model_ids_validity(const Model &model)
2094 {
2095     std::set<ObjectID> ids;
2096     auto check = [&ids](ObjectID id) {
2097         assert(id.valid());
2098         assert(ids.find(id) == ids.end());
2099         ids.insert(id);
2100     };
2101     for (const ModelObject *model_object : model.objects) {
2102         check(model_object->id());
2103         check(model_object->config.id());
2104         for (const ModelVolume *model_volume : model_object->volumes) {
2105             check(model_volume->id());
2106 	        check(model_volume->config.id());
2107         }
2108         for (const ModelInstance *model_instance : model_object->instances)
2109             check(model_instance->id());
2110     }
2111     for (const auto mm : model.materials) {
2112         check(mm.second->id());
2113         check(mm.second->config.id());
2114     }
2115 }
2116 
check_model_ids_equal(const Model & model1,const Model & model2)2117 void check_model_ids_equal(const Model &model1, const Model &model2)
2118 {
2119     // Verify whether the IDs of model1 and model match.
2120     assert(model1.objects.size() == model2.objects.size());
2121     for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) {
2122         const ModelObject &model_object1 = *model1.objects[idx_model];
2123         const ModelObject &model_object2 = *  model2.objects[idx_model];
2124         assert(model_object1.id() == model_object2.id());
2125         assert(model_object1.config.id() == model_object2.config.id());
2126         assert(model_object1.volumes.size() == model_object2.volumes.size());
2127         assert(model_object1.instances.size() == model_object2.instances.size());
2128         for (size_t i = 0; i < model_object1.volumes.size(); ++ i) {
2129             assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
2130         	assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id());
2131         }
2132         for (size_t i = 0; i < model_object1.instances.size(); ++ i)
2133             assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
2134     }
2135     assert(model1.materials.size() == model2.materials.size());
2136     {
2137         auto it1 = model1.materials.begin();
2138         auto it2 = model2.materials.begin();
2139         for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
2140             assert(it1->first == it2->first); // compare keys
2141             assert(it1->second->id() == it2->second->id());
2142         	assert(it1->second->config.id() == it2->second->config.id());
2143         }
2144     }
2145 }
2146 
2147 #endif /* NDEBUG */
2148 
2149 }
2150 
2151 #if 0
2152 CEREAL_REGISTER_TYPE(Slic3r::ModelObject)
2153 CEREAL_REGISTER_TYPE(Slic3r::ModelVolume)
2154 CEREAL_REGISTER_TYPE(Slic3r::ModelInstance)
2155 CEREAL_REGISTER_TYPE(Slic3r::Model)
2156 
2157 CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject)
2158 CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume)
2159 CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance)
2160 CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model)
2161 #endif
2162