2021-05-20 09:32:17 +02:00
#include "libslic3r.h"
2020-09-14 16:27:55 +02:00
#include "Exception.hpp"
2014-04-30 02:04:49 +03:00
#include "Model.hpp"
2020-04-23 18:19:03 +02:00
#include "ModelArrange.hpp"
2015-04-16 21:22:04 +02:00
#include "Geometry.hpp"
2019-06-27 21:13:44 +02:00
#include "MTUtils.hpp"
2021-05-17 20:25:59 +02:00
#include "TriangleMeshSlicer.hpp"
2020-07-09 16:25:34 +02:00
#include "TriangleSelector.hpp"
2014-04-30 02:04:49 +03:00
2017-08-02 16:05:18 +02:00
#include "Format/AMF.hpp"
#include "Format/OBJ.hpp"
#include "Format/PRUS.hpp"
#include "Format/STL.hpp"
2018-01-30 09:27:10 +01:00
#include "Format/3mf.hpp"
2017-08-02 16:05:18 +02:00
2017-06-21 14:12:11 +02:00
#include <float.h>
2017-08-02 16:05:18 +02:00
#include <boost/algorithm/string/predicate.hpp>
2018-02-22 15:27:32 +01:00
#include <boost/algorithm/string/replace.hpp>
2019-02-05 21:05:31 +01:00
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/iostream.hpp>
2017-08-02 16:05:18 +02:00
2018-07-13 11:26:59 +02:00
#include "SVG.hpp"
2018-06-21 08:37:04 +02:00
#include <Eigen/Dense>
2019-12-17 08:37:50 +01:00
#include "GCodeWriter.hpp"
2018-06-05 12:02:45 +02:00
2014-04-30 02:04:49 +03:00
namespace Slic3r {
2018-11-02 14:47:13 +01:00
Model & Model :: assign_copy ( const Model & rhs )
2014-04-30 02:04:49 +03:00
{
2018-11-02 14:47:13 +01:00
this -> copy_id ( rhs );
2014-05-09 14:24:35 +02:00
// copy materials
2018-11-02 19:49:40 +01:00
this -> clear_materials ();
this -> materials = rhs . materials ;
for ( std :: pair < const t_model_material_id , ModelMaterial *> & m : this -> materials ) {
// Copy including the ID and m_model.
m . second = new ModelMaterial ( * m . second );
m . second -> set_model ( this );
}
2014-05-09 14:24:35 +02:00
// copy objects
2018-11-02 19:49:40 +01:00
this -> clear_objects ();
2018-10-18 18:06:40 +02:00
this -> objects . reserve ( rhs . objects . size ());
2018-11-02 19:49:40 +01:00
for ( const ModelObject * model_object : rhs . objects ) {
// Copy including the ID, leave ID set to invalid (zero).
auto mo = ModelObject :: new_copy ( * model_object );
mo -> set_model ( this );
this -> objects . emplace_back ( mo );
}
2019-11-11 16:01:34 +01:00
// copy custom code per height
2019-12-17 08:37:50 +01:00
this -> custom_gcode_per_print_z = rhs . custom_gcode_per_print_z ;
2014-05-08 13:33:43 +02:00
return * this ;
2014-04-30 02:04:49 +03:00
}
2018-11-02 14:47:13 +01:00
Model & Model :: assign_copy ( Model && rhs )
2014-05-08 13:33:43 +02:00
{
2018-11-02 14:47:13 +01:00
this -> copy_id ( rhs );
2018-11-02 19:49:40 +01:00
// Move materials, adjust the parent pointer.
this -> clear_materials ();
this -> materials = std :: move ( rhs . materials );
for ( std :: pair < const t_model_material_id , ModelMaterial *> & m : this -> materials )
m . second -> set_model ( this );
2018-11-02 14:47:13 +01:00
rhs . materials . clear ();
2018-11-02 19:49:40 +01:00
// Move objects, adjust the parent pointer.
this -> clear_objects ();
2018-11-02 14:47:13 +01:00
this -> objects = std :: move ( rhs . objects );
2018-11-02 19:49:40 +01:00
for ( ModelObject * model_object : this -> objects )
model_object -> set_model ( this );
2019-03-28 12:34:41 +01:00
rhs . objects . clear ();
2019-11-11 16:01:34 +01:00
// copy custom code per height
2020-01-06 09:32:13 +01:00
this -> custom_gcode_per_print_z = std :: move ( rhs . custom_gcode_per_print_z );
2014-05-08 13:33:43 +02:00
return * this ;
}
2018-11-02 14:47:13 +01:00
void Model :: assign_new_unique_ids_recursive ()
{
this -> set_new_unique_id ();
for ( std :: pair < const t_model_material_id , ModelMaterial *> & m : this -> materials )
m . second -> assign_new_unique_ids_recursive ();
for ( ModelObject * model_object : this -> objects )
model_object -> assign_new_unique_ids_recursive ();
}
2019-07-04 10:45:41 +02:00
void Model :: update_links_bottom_up_recursive ()
{
for ( std :: pair < const t_model_material_id , ModelMaterial *> & kvp : this -> materials )
kvp . second -> set_model ( this );
for ( ModelObject * model_object : this -> objects ) {
model_object -> set_model ( this );
for ( ModelInstance * model_instance : model_object -> instances )
model_instance -> set_model_object ( model_object );
for ( ModelVolume * model_volume : model_object -> volumes )
model_volume -> set_model_object ( model_object );
}
}
2019-08-23 13:12:31 +02:00
Model Model :: read_from_file ( const std :: string & input_file , DynamicPrintConfig * config , bool add_default_instances , bool check_version )
2014-04-30 02:04:49 +03:00
{
2017-08-02 16:05:18 +02:00
Model model ;
2018-09-25 11:53:05 +02:00
DynamicPrintConfig temp_config ;
if ( config == nullptr )
config = & temp_config ;
2017-08-02 16:05:18 +02:00
bool result = false ;
if ( boost :: algorithm :: iends_with ( input_file , ".stl" ))
result = load_stl ( input_file . c_str (), & model );
else if ( boost :: algorithm :: iends_with ( input_file , ".obj" ))
result = load_obj ( input_file . c_str (), & model );
2019-08-02 09:43:41 +02:00
else if ( boost :: algorithm :: iends_with ( input_file , ".amf" ) || boost :: algorithm :: iends_with ( input_file , ".amf.xml" ))
2019-08-23 13:12:31 +02:00
result = load_amf ( input_file . c_str (), config , & model , check_version );
2018-09-25 11:53:05 +02:00
else if ( boost :: algorithm :: iends_with ( input_file , ".3mf" ))
2019-08-23 13:12:31 +02:00
result = load_3mf ( input_file . c_str (), config , & model , false );
2017-08-02 16:05:18 +02:00
else if ( boost :: algorithm :: iends_with ( input_file , ".prusa" ))
result = load_prus ( input_file . c_str (), & model );
else
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension." );
2018-01-30 09:27:10 +01:00
2017-08-02 16:05:18 +02:00
if ( ! result )
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "Loading of a model file failed." );
2017-08-02 16:05:18 +02:00
if ( model . objects . empty ())
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "The supplied file couldn't be read because it's empty" );
2017-08-02 16:05:18 +02:00
for ( ModelObject * o : model . objects )
o -> input_file = input_file ;
if ( add_default_instances )
model . add_default_instances ();
2020-01-23 12:49:39 +01:00
CustomGCode :: update_custom_gcode_per_print_z_from_config ( model . custom_gcode_per_print_z , config );
CustomGCode :: check_mode_for_custom_gcode_per_print_z ( model . custom_gcode_per_print_z );
2019-12-17 13:16:28 +01:00
2017-08-02 16:05:18 +02:00
return model ;
2014-04-30 02:04:49 +03:00
}
2019-08-23 13:12:31 +02:00
Model Model :: read_from_archive ( const std :: string & input_file , DynamicPrintConfig * config , bool add_default_instances , bool check_version )
2018-02-13 15:19:55 +01:00
{
Model model ;
bool result = false ;
if ( boost :: algorithm :: iends_with ( input_file , ".3mf" ))
2019-08-23 13:12:31 +02:00
result = load_3mf ( input_file . c_str (), config , & model , check_version );
2018-02-14 14:30:03 +01:00
else if ( boost :: algorithm :: iends_with ( input_file , ".zip.amf" ))
2019-08-23 13:12:31 +02:00
result = load_amf ( input_file . c_str (), config , & model , check_version );
2018-02-13 15:19:55 +01:00
else
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "Unknown file format. Input file must have .3mf or .zip.amf extension." );
2018-02-13 15:19:55 +01:00
if ( ! result )
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "Loading of a model file failed." );
2018-02-13 15:19:55 +01:00
if ( model . objects . empty ())
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( "The supplied file couldn't be read because it's empty" );
2018-02-13 15:19:55 +01:00
for ( ModelObject * o : model . objects )
2018-02-22 15:27:32 +01:00
{
2019-09-23 15:35:49 +02:00
// if (boost::algorithm::iends_with(input_file, ".zip.amf"))
// {
// // we remove the .zip part of the extension to avoid it be added to filenames when exporting
// o->input_file = boost::ireplace_last_copy(input_file, ".zip.", ".");
// }
// else
2018-02-22 15:27:32 +01:00
o -> input_file = input_file ;
}
2018-02-13 15:19:55 +01:00
if ( add_default_instances )
model . add_default_instances ();
2020-01-23 12:49:39 +01:00
CustomGCode :: update_custom_gcode_per_print_z_from_config ( model . custom_gcode_per_print_z , config );
CustomGCode :: check_mode_for_custom_gcode_per_print_z ( model . custom_gcode_per_print_z );
2019-12-17 13:16:28 +01:00
2018-02-13 15:19:55 +01:00
return model ;
}
2017-06-13 11:35:24 +02:00
ModelObject * Model :: add_object ()
2014-04-30 02:04:49 +03:00
{
2017-06-13 11:35:24 +02:00
this -> objects . emplace_back ( new ModelObject ( this ));
return this -> objects . back ();
2014-05-09 14:24:35 +02:00
}
2017-06-13 11:35:24 +02:00
ModelObject * Model :: add_object ( const char * name , const char * path , const TriangleMesh & mesh )
2017-02-26 22:03:03 +01:00
{
ModelObject * new_object = new ModelObject ( this );
this -> objects . push_back ( new_object );
new_object -> name = name ;
new_object -> input_file = path ;
ModelVolume * new_volume = new_object -> add_volume ( mesh );
new_volume -> name = name ;
2019-09-19 09:09:11 +02:00
new_volume -> source . input_file = path ;
new_volume -> source . object_idx = ( int ) this -> objects . size () - 1 ;
new_volume -> source . volume_idx = ( int ) new_object -> volumes . size () - 1 ;
2017-02-26 22:03:03 +01:00
new_object -> invalidate_bounding_box ();
return new_object ;
}
2017-06-13 11:35:24 +02:00
ModelObject * Model :: add_object ( const char * name , const char * path , TriangleMesh && mesh )
2017-02-26 22:03:03 +01:00
{
ModelObject * new_object = new ModelObject ( this );
this -> objects . push_back ( new_object );
new_object -> name = name ;
new_object -> input_file = path ;
ModelVolume * new_volume = new_object -> add_volume ( std :: move ( mesh ));
new_volume -> name = name ;
2019-09-19 09:09:11 +02:00
new_volume -> source . input_file = path ;
new_volume -> source . object_idx = ( int ) this -> objects . size () - 1 ;
new_volume -> source . volume_idx = ( int ) new_object -> volumes . size () - 1 ;
2017-02-26 22:03:03 +01:00
new_object -> invalidate_bounding_box ();
return new_object ;
}
2018-11-02 14:47:13 +01:00
ModelObject * Model :: add_object ( const ModelObject & other )
2014-05-09 14:24:35 +02:00
{
2018-11-02 19:49:40 +01:00
ModelObject * new_object = ModelObject :: new_clone ( other );
new_object -> set_model ( this );
2014-05-09 14:24:35 +02:00
this -> objects . push_back ( new_object );
return new_object ;
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
void Model :: delete_object ( size_t idx )
2014-04-30 02:04:49 +03:00
{
ModelObjectPtrs :: iterator i = this -> objects . begin () + idx ;
delete * i ;
this -> objects . erase ( i );
}
2018-11-02 19:49:40 +01:00
bool Model :: delete_object ( ModelObject * object )
2018-01-30 09:27:10 +01:00
{
2018-11-02 19:49:40 +01:00
if ( object != nullptr ) {
size_t idx = 0 ;
for ( ModelObject * model_object : objects ) {
if ( model_object == object ) {
delete model_object ;
objects . erase ( objects . begin () + idx );
return true ;
}
++ idx ;
2018-01-30 09:27:10 +01:00
}
}
2018-11-02 19:49:40 +01:00
return false ;
}
2019-06-27 11:02:45 +02:00
bool Model :: delete_object ( ObjectID id )
2018-11-02 19:49:40 +01:00
{
if ( id . id != 0 ) {
size_t idx = 0 ;
for ( ModelObject * model_object : objects ) {
if ( model_object -> id () == id ) {
delete model_object ;
objects . erase ( objects . begin () + idx );
return true ;
}
++ idx ;
}
}
return false ;
2018-01-30 09:27:10 +01:00
}
2017-06-13 11:35:24 +02:00
void Model :: clear_objects ()
2014-04-30 02:04:49 +03:00
{
2017-06-13 11:35:24 +02:00
for ( ModelObject * o : this -> objects )
delete o ;
this -> objects . clear ();
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
void Model :: delete_material ( t_model_material_id material_id )
2014-04-30 02:04:49 +03:00
{
2014-05-07 00:58:29 +02:00
ModelMaterialMap :: iterator i = this -> materials . find ( material_id );
if ( i != this -> materials . end ()) {
2014-04-30 02:04:49 +03:00
delete i -> second ;
2014-05-07 00:58:29 +02:00
this -> materials . erase ( i );
2014-04-30 02:04:49 +03:00
}
2014-05-07 00:58:29 +02:00
}
2014-04-30 02:04:49 +03:00
2017-06-13 11:35:24 +02:00
void Model :: clear_materials ()
2014-05-07 00:58:29 +02:00
{
2017-06-13 11:35:24 +02:00
for ( auto & m : this -> materials )
delete m . second ;
this -> materials . clear ();
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
ModelMaterial * Model :: add_material ( t_model_material_id material_id )
2014-04-30 02:04:49 +03:00
{
2018-11-05 17:52:55 +01:00
assert ( ! material_id . empty ());
2014-05-09 14:24:35 +02:00
ModelMaterial * material = this -> get_material ( material_id );
2017-06-13 11:35:24 +02:00
if ( material == nullptr )
2014-05-09 14:24:35 +02:00
material = this -> materials [ material_id ] = new ModelMaterial ( this );
return material ;
}
2017-06-13 11:35:24 +02:00
ModelMaterial * Model :: add_material ( t_model_material_id material_id , const ModelMaterial & other )
2014-05-09 14:24:35 +02:00
{
2018-11-05 17:52:55 +01:00
assert ( ! material_id . empty ());
2014-05-09 14:24:35 +02:00
// delete existing material if any
ModelMaterial * material = this -> get_material ( material_id );
2017-06-13 11:35:24 +02:00
delete material ;
2014-05-09 14:24:35 +02:00
// set new material
2018-11-02 19:49:40 +01:00
material = new ModelMaterial ( other );
material -> set_model ( this );
2014-05-09 14:24:35 +02:00
this -> materials [ material_id ] = material ;
return material ;
}
2014-08-08 21:48:59 +02:00
// makes sure all objects have at least one instance
2017-06-13 11:35:24 +02:00
bool Model :: add_default_instances ()
2014-08-08 21:48:59 +02:00
{
// apply a default position to all objects not having one
2017-06-13 11:35:24 +02:00
for ( ModelObject * o : this -> objects )
if ( o -> instances . empty ())
o -> add_instance ();
2014-08-08 21:48:59 +02:00
return true ;
}
2014-09-21 10:51:36 +02:00
// this returns the bounding box of the *transformed* instances
2018-03-09 10:40:42 +01:00
BoundingBoxf3 Model :: bounding_box () const
2014-09-21 10:51:36 +02:00
{
2015-01-19 18:53:04 +01:00
BoundingBoxf3 bb ;
2017-06-13 11:35:24 +02:00
for ( ModelObject * o : this -> objects )
bb . merge ( o -> bounding_box ());
2015-01-19 18:53:04 +01:00
return bb ;
2014-09-21 10:51:36 +02:00
}
2018-10-23 15:27:31 +02:00
unsigned int Model :: update_print_volume_state ( const BoundingBoxf3 & print_volume )
{
unsigned int num_printable = 0 ;
for ( ModelObject * model_object : this -> objects )
num_printable += model_object -> check_instances_print_volume_state ( print_volume );
return num_printable ;
}
2018-11-08 20:18:40 +01:00
bool Model :: center_instances_around_point ( const Vec2d & point )
2014-09-21 10:51:36 +02:00
{
2017-06-13 11:35:24 +02:00
BoundingBoxf3 bb ;
for ( ModelObject * o : this -> objects )
for ( size_t i = 0 ; i < o -> instances . size (); ++ i )
bb . merge ( o -> instance_bounding_box ( i , false ));
2018-09-17 15:12:13 +02:00
Vec2d shift2 = point - to_2d ( bb . center ());
2018-11-08 20:18:40 +01:00
if ( std :: abs ( shift2 ( 0 )) < EPSILON && std :: abs ( shift2 ( 1 )) < EPSILON )
// No significant shift, don't do anything.
return false ;
Vec3d shift3 = Vec3d ( shift2 ( 0 ), shift2 ( 1 ), 0.0 );
for ( ModelObject * o : this -> objects ) {
for ( ModelInstance * i : o -> instances )
i -> set_offset ( i -> get_offset () + shift3 );
o -> invalidate_bounding_box ();
}
return true ;
2014-09-21 10:51:36 +02:00
}
// flattens everything to a single mesh
2017-06-13 11:35:24 +02:00
TriangleMesh Model :: mesh () const
2014-09-21 10:51:36 +02:00
{
2015-01-19 18:53:04 +01:00
TriangleMesh mesh ;
2017-06-13 11:35:24 +02:00
for ( const ModelObject * o : this -> objects )
mesh . merge ( o -> mesh ());
2015-01-19 18:53:04 +01:00
return mesh ;
2014-09-21 10:51:36 +02:00
}
2017-06-13 11:35:24 +02:00
void Model :: duplicate_objects_grid ( size_t x , size_t y , coordf_t dist )
2015-12-02 18:06:18 +01:00
{
if ( this -> objects . size () > 1 ) throw "Grid duplication is not supported with multiple objects" ;
if ( this -> objects . empty ()) throw "No objects!" ;
ModelObject * object = this -> objects . front ();
object -> clear_instances ();
2018-09-17 15:12:13 +02:00
Vec3d ext_size = object -> bounding_box (). size () + dist * Vec3d :: Ones ();
2015-12-02 18:06:18 +01:00
for ( size_t x_copy = 1 ; x_copy <= x ; ++ x_copy ) {
for ( size_t y_copy = 1 ; y_copy <= y ; ++ y_copy ) {
ModelInstance * instance = object -> add_instance ();
2018-09-17 15:12:13 +02:00
instance -> set_offset ( Vec3d ( ext_size ( 0 ) * ( double )( x_copy - 1 ), ext_size ( 1 ) * ( double )( y_copy - 1 ), 0.0 ));
2015-12-02 18:06:18 +01:00
}
}
}
2017-08-02 16:05:18 +02:00
bool Model :: looks_like_multipart_object () const
{
if ( this -> objects . size () <= 1 )
return false ;
double zmin = std :: numeric_limits < double >:: max ();
for ( const ModelObject * obj : this -> objects ) {
if ( obj -> volumes . size () > 1 || obj -> config . keys (). size () > 1 )
return false ;
for ( const ModelVolume * vol : obj -> volumes ) {
2019-06-11 17:08:47 +02:00
double zmin_this = vol -> mesh (). bounding_box (). min ( 2 );
2017-08-02 16:05:18 +02:00
if ( zmin == std :: numeric_limits < double >:: max ())
zmin = zmin_this ;
else if ( std :: abs ( zmin - zmin_this ) > EPSILON )
// The volumes don't share zmin.
return true ;
}
}
return false ;
}
2019-08-05 17:49:21 +02:00
// Generate next extruder ID string, in the range of (1, max_extruders).
static inline std :: string auto_extruder_id ( unsigned int max_extruders , unsigned int & cntr )
{
char str_extruder [ 64 ];
sprintf ( str_extruder , "%ud" , cntr + 1 );
if ( ++ cntr == max_extruders )
cntr = 0 ;
return str_extruder ;
}
2018-05-07 16:13:58 +02:00
void Model :: convert_multipart_object ( unsigned int max_extruders )
2017-08-02 16:05:18 +02:00
{
2019-08-05 17:49:21 +02:00
assert ( this -> objects . size () >= 2 );
if ( this -> objects . size () < 2 )
2017-08-02 16:05:18 +02:00
return ;
2017-08-28 14:54:46 +02:00
ModelObject * object = new ModelObject ( this );
2017-08-02 16:05:18 +02:00
object -> input_file = this -> objects . front () -> input_file ;
2018-10-19 11:02:50 +02:00
object -> name = this -> objects . front () -> name ;
2018-11-02 19:49:40 +01:00
//FIXME copy the config etc?
2018-04-10 12:17:55 +02:00
2019-08-05 17:49:21 +02:00
unsigned int extruder_counter = 0 ;
for ( const ModelObject * o : this -> objects )
for ( const ModelVolume * v : o -> volumes ) {
// If there are more than one object, put all volumes together
// Each object may contain any number of volumes and instances
// The volumes transformations are relative to the object containing them...
Geometry :: Transformation trafo_volume = v -> get_transformation ();
// Revert the centering operation.
trafo_volume . set_offset ( trafo_volume . get_offset () - o -> origin_translation );
int counter = 1 ;
auto copy_volume = [ o , max_extruders , & counter , & extruder_counter ]( ModelVolume * new_v ) {
assert ( new_v != nullptr );
new_v -> name = o -> name + "_" + std :: to_string ( counter ++ );
new_v -> config . set_deserialize ( "extruder" , auto_extruder_id ( max_extruders , extruder_counter ));
return new_v ;
};
if ( o -> instances . empty ()) {
copy_volume ( object -> add_volume ( * v )) -> set_transformation ( trafo_volume );
} else {
for ( const ModelInstance * i : o -> instances )
// ...so, transform everything to a common reference system (world)
copy_volume ( object -> add_volume ( * v )) -> set_transformation ( i -> get_transformation () * trafo_volume );
2018-04-09 14:57:23 +02:00
}
}
2019-08-22 13:29:21 +02:00
2019-09-06 09:44:39 +02:00
// commented-out to fix #2868
// object->add_instance();
// object->instances[0]->set_offset(object->raw_mesh_bounding_box().center());
2017-08-02 16:05:18 +02:00
2017-08-28 14:54:46 +02:00
this -> clear_objects ();
this -> objects . push_back ( object );
2017-08-02 16:05:18 +02:00
}
2020-04-29 19:10:13 +02:00
bool Model :: looks_like_imperial_units () const
{
if ( this -> objects . size () == 0 )
return false ;
2020-05-27 10:55:48 +02:00
for ( ModelObject * obj : this -> objects )
if ( obj -> get_object_stl_stats (). volume < 9.0 ) // 9 = 3*3*3;
return true ;
2020-04-29 19:10:13 +02:00
2020-05-27 10:55:48 +02:00
return false ;
2020-04-29 19:10:13 +02:00
}
2020-12-12 18:54:34 +01:00
void Model :: convert_from_imperial_units ( bool only_small_volumes )
2020-04-29 19:10:13 +02:00
{
double in_to_mm = 25.4 ;
2020-05-27 10:55:48 +02:00
for ( ModelObject * obj : this -> objects )
2020-12-12 18:54:34 +01:00
if ( ! only_small_volumes || obj -> get_object_stl_stats (). volume < 9.0 ) { // 9 = 3*3*3;
2020-05-27 10:55:48 +02:00
obj -> scale_mesh_after_creation ( Vec3d ( in_to_mm , in_to_mm , in_to_mm ));
2020-12-11 13:57:38 +01:00
for ( ModelVolume * v : obj -> volumes )
v -> source . is_converted_from_inches = true ;
}
2020-04-29 19:10:13 +02:00
}
2021-02-09 17:04:32 +01:00
bool Model :: looks_like_saved_in_meters () const
{
if ( this -> objects . size () == 0 )
return false ;
for ( ModelObject * obj : this -> objects )
if ( obj -> get_object_stl_stats (). volume < 0.001 ) // 0.001 = 0.1*0.1*0.1;
return true ;
return false ;
}
void Model :: convert_from_meters ( bool only_small_volumes )
{
double m_to_mm = 1000 ;
for ( ModelObject * obj : this -> objects )
if ( ! only_small_volumes || obj -> get_object_stl_stats (). volume < 0.001 ) { // 0.001 = 0.1*0.1*0.1;
obj -> scale_mesh_after_creation ( Vec3d ( m_to_mm , m_to_mm , m_to_mm ));
for ( ModelVolume * v : obj -> volumes )
2021-02-10 20:31:17 +01:00
v -> source . is_converted_from_meters = true ;
2021-02-09 17:04:32 +01:00
}
}
2018-03-06 10:26:39 +01:00
void Model :: adjust_min_z ()
{
if ( objects . empty ())
return ;
2018-08-17 15:53:43 +02:00
if ( bounding_box (). min ( 2 ) < 0.0 )
2018-03-06 10:26:39 +01:00
{
for ( ModelObject * obj : objects )
{
if ( obj != nullptr )
{
2018-08-17 15:53:43 +02:00
coordf_t obj_min_z = obj -> bounding_box (). min ( 2 );
2018-03-06 10:26:39 +01:00
if ( obj_min_z < 0.0 )
2018-12-14 09:52:15 +01:00
obj -> translate_instances ( Vec3d ( 0.0 , 0.0 , - obj_min_z ));
2018-03-06 10:26:39 +01:00
}
}
}
}
2019-02-03 10:41:14 +01:00
// Propose a filename including path derived from the ModelObject's input path.
// If object's name is filled in, use the object name, otherwise use the input name.
std :: string Model :: propose_export_file_name_and_path () const
2018-12-03 13:14:28 +01:00
{
2019-02-01 11:44:08 +01:00
std :: string input_file ;
2018-12-03 13:14:28 +01:00
for ( const ModelObject * model_object : this -> objects )
for ( ModelInstance * model_instance : model_object -> instances )
2019-02-01 11:44:08 +01:00
if ( model_instance -> is_printable ()) {
2019-04-04 14:00:31 +02:00
input_file = model_object -> get_export_filename ();
if ( ! input_file . empty ())
2019-02-01 11:44:08 +01:00
goto end ;
// Other instances will produce the same name, skip them.
break ;
}
end :
return input_file ;
2018-12-03 13:14:28 +01:00
}
2019-03-13 15:44:50 +01:00
std :: string Model :: propose_export_file_name_and_path ( const std :: string & new_extension ) const
{
return boost :: filesystem :: path ( this -> propose_export_file_name_and_path ()). replace_extension ( new_extension ). string ();
}
2014-04-30 02:04:49 +03:00
ModelObject ::~ ModelObject ()
{
this -> clear_volumes ();
this -> clear_instances ();
}
2018-11-02 14:47:13 +01:00
// maintains the m_model pointer
ModelObject & ModelObject :: assign_copy ( const ModelObject & rhs )
2018-10-17 11:12:38 +02:00
{
2019-07-03 13:43:54 +02:00
assert ( this -> id (). invalid () || this -> id () == rhs . id ());
assert ( this -> config . id (). invalid () || this -> config . id () == rhs . config . id ());
this -> copy_id ( rhs );
2018-10-17 11:12:38 +02:00
2018-11-02 14:47:13 +01:00
this -> name = rhs . name ;
this -> input_file = rhs . input_file ;
2019-07-03 13:43:54 +02:00
// Copies the config's ID
2018-11-02 14:47:13 +01:00
this -> config = rhs . config ;
2019-07-03 13:43:54 +02:00
assert ( this -> config . id () == rhs . config . id ());
2018-11-02 14:47:13 +01:00
this -> sla_support_points = rhs . sla_support_points ;
2019-02-27 16:23:10 +01:00
this -> sla_points_status = rhs . sla_points_status ;
2019-11-08 14:05:56 +01:00
this -> sla_drain_holes = rhs . sla_drain_holes ;
2019-06-05 11:50:59 +02:00
this -> layer_config_ranges = rhs . layer_config_ranges ; // #ys_FIXME_experiment
2018-11-02 14:47:13 +01:00
this -> layer_height_profile = rhs . layer_height_profile ;
2019-07-31 08:36:08 +02:00
this -> printable = rhs . printable ;
2018-11-02 14:47:13 +01:00
this -> origin_translation = rhs . origin_translation ;
m_bounding_box = rhs . m_bounding_box ;
m_bounding_box_valid = rhs . m_bounding_box_valid ;
2019-04-13 14:15:54 +02:00
m_raw_bounding_box = rhs . m_raw_bounding_box ;
m_raw_bounding_box_valid = rhs . m_raw_bounding_box_valid ;
2019-01-26 18:51:34 +01:00
m_raw_mesh_bounding_box = rhs . m_raw_mesh_bounding_box ;
m_raw_mesh_bounding_box_valid = rhs . m_raw_mesh_bounding_box_valid ;
2018-10-30 15:24:36 +01:00
2018-11-02 19:49:40 +01:00
this -> clear_volumes ();
2018-11-02 14:47:13 +01:00
this -> volumes . reserve ( rhs . volumes . size ());
2018-11-02 19:49:40 +01:00
for ( ModelVolume * model_volume : rhs . volumes ) {
this -> volumes . emplace_back ( new ModelVolume ( * model_volume ));
this -> volumes . back () -> set_model_object ( this );
}
this -> clear_instances ();
2018-11-02 14:47:13 +01:00
this -> instances . reserve ( rhs . instances . size ());
2018-11-02 19:49:40 +01:00
for ( const ModelInstance * model_instance : rhs . instances ) {
this -> instances . emplace_back ( new ModelInstance ( * model_instance ));
this -> instances . back () -> set_model_object ( this );
2018-10-30 15:24:36 +01:00
}
return * this ;
}
2018-11-02 14:47:13 +01:00
// maintains the m_model pointer
ModelObject & ModelObject :: assign_copy ( ModelObject && rhs )
{
2019-07-03 13:43:54 +02:00
assert ( this -> id (). invalid ());
2018-11-02 14:47:13 +01:00
this -> copy_id ( rhs );
this -> name = std :: move ( rhs . name );
this -> input_file = std :: move ( rhs . input_file );
2019-07-03 13:43:54 +02:00
// Moves the config's ID
2018-11-02 14:47:13 +01:00
this -> config = std :: move ( rhs . config );
2019-07-03 13:43:54 +02:00
assert ( this -> config . id () == rhs . config . id ());
2018-11-02 14:47:13 +01:00
this -> sla_support_points = std :: move ( rhs . sla_support_points );
2019-02-27 16:23:10 +01:00
this -> sla_points_status = std :: move ( rhs . sla_points_status );
2019-11-08 14:05:56 +01:00
this -> sla_drain_holes = std :: move ( rhs . sla_drain_holes );
2019-06-05 11:50:59 +02:00
this -> layer_config_ranges = std :: move ( rhs . layer_config_ranges ); // #ys_FIXME_experiment
2018-11-02 14:47:13 +01:00
this -> layer_height_profile = std :: move ( rhs . layer_height_profile );
this -> origin_translation = std :: move ( rhs . origin_translation );
m_bounding_box = std :: move ( rhs . m_bounding_box );
m_bounding_box_valid = std :: move ( rhs . m_bounding_box_valid );
2019-04-13 14:15:54 +02:00
m_raw_bounding_box = rhs . m_raw_bounding_box ;
m_raw_bounding_box_valid = rhs . m_raw_bounding_box_valid ;
2019-01-26 18:51:34 +01:00
m_raw_mesh_bounding_box = rhs . m_raw_mesh_bounding_box ;
m_raw_mesh_bounding_box_valid = rhs . m_raw_mesh_bounding_box_valid ;
2018-11-02 14:47:13 +01:00
2018-11-02 19:49:40 +01:00
this -> clear_volumes ();
2018-11-02 14:47:13 +01:00
this -> volumes = std :: move ( rhs . volumes );
rhs . volumes . clear ();
2018-11-02 19:49:40 +01:00
for ( ModelVolume * model_volume : this -> volumes )
model_volume -> set_model_object ( this );
this -> clear_instances ();
2018-11-02 14:47:13 +01:00
this -> instances = std :: move ( rhs . instances );
rhs . instances . clear ();
2018-11-02 19:49:40 +01:00
for ( ModelInstance * model_instance : this -> instances )
model_instance -> set_model_object ( this );
2018-11-02 14:47:13 +01:00
2019-03-28 12:34:41 +01:00
return * this ;
2018-11-02 14:47:13 +01:00
}
void ModelObject :: assign_new_unique_ids_recursive ()
{
this -> set_new_unique_id ();
for ( ModelVolume * model_volume : this -> volumes )
model_volume -> assign_new_unique_ids_recursive ();
for ( ModelInstance * model_instance : this -> instances )
model_instance -> assign_new_unique_ids_recursive ();
2020-10-09 12:25:57 +02:00
this -> layer_height_profile . set_new_unique_id ();
2018-11-02 14:47:13 +01:00
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
//ModelObject* ModelObject::clone(Model *parent)
//{
// return new ModelObject(parent, *this, true);
//}
2017-06-13 11:35:24 +02:00
ModelVolume * ModelObject :: add_volume ( const TriangleMesh & mesh )
2014-04-30 02:04:49 +03:00
{
2014-05-09 14:24:35 +02:00
ModelVolume * v = new ModelVolume ( this , mesh );
this -> volumes . push_back ( v );
2019-06-11 17:08:47 +02:00
v -> center_geometry_after_creation ();
2014-05-09 14:24:35 +02:00
this -> invalidate_bounding_box ();
return v ;
}
2017-06-13 11:35:24 +02:00
ModelVolume * ModelObject :: add_volume ( TriangleMesh && mesh )
2017-02-26 22:03:03 +01:00
{
ModelVolume * v = new ModelVolume ( this , std :: move ( mesh ));
this -> volumes . push_back ( v );
2019-06-11 17:08:47 +02:00
v -> center_geometry_after_creation ();
2017-02-26 22:03:03 +01:00
this -> invalidate_bounding_box ();
return v ;
}
2017-06-13 11:35:24 +02:00
ModelVolume * ModelObject :: add_volume ( const ModelVolume & other )
2014-05-09 14:24:35 +02:00
{
ModelVolume * v = new ModelVolume ( this , other );
2014-04-30 02:04:49 +03:00
this -> volumes . push_back ( v );
2019-06-13 17:24:37 +02:00
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
// this->invalidate_bounding_box();
2014-04-30 02:04:49 +03:00
return v ;
}
2018-11-02 14:47:13 +01:00
ModelVolume * ModelObject :: add_volume ( const ModelVolume & other , TriangleMesh && mesh )
{
ModelVolume * v = new ModelVolume ( this , other , std :: move ( mesh ));
this -> volumes . push_back ( v );
2019-06-11 17:08:47 +02:00
v -> center_geometry_after_creation ();
2018-11-02 14:47:13 +01:00
this -> invalidate_bounding_box ();
return v ;
}
2017-06-13 11:35:24 +02:00
void ModelObject :: delete_volume ( size_t idx )
2014-04-30 02:04:49 +03:00
{
ModelVolumePtrs :: iterator i = this -> volumes . begin () + idx ;
delete * i ;
this -> volumes . erase ( i );
2018-11-23 10:50:25 +01:00
2019-01-18 12:52:09 +01:00
if ( this -> volumes . size () == 1 )
{
// only one volume left
// we need to collapse the volume transform into the instances transforms because now when selecting this volume
// it will be seen as a single full instance ans so its volume transform may be ignored
ModelVolume * v = this -> volumes . front ();
Transform3d v_t = v -> get_transformation (). get_matrix ();
for ( ModelInstance * inst : this -> instances )
{
inst -> set_transformation ( Geometry :: Transformation ( inst -> get_transformation (). get_matrix () * v_t ));
}
Geometry :: Transformation t ;
v -> set_transformation ( t );
v -> set_new_unique_id ();
}
2018-11-23 10:50:25 +01:00
2014-04-30 02:04:49 +03:00
this -> invalidate_bounding_box ();
}
2017-06-13 11:35:24 +02:00
void ModelObject :: clear_volumes ()
2014-04-30 02:04:49 +03:00
{
2017-06-13 11:35:24 +02:00
for ( ModelVolume * v : this -> volumes )
delete v ;
this -> volumes . clear ();
this -> invalidate_bounding_box ();
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
ModelInstance * ModelObject :: add_instance ()
2014-04-30 02:04:49 +03:00
{
2014-05-09 14:24:35 +02:00
ModelInstance * i = new ModelInstance ( this );
this -> instances . push_back ( i );
this -> invalidate_bounding_box ();
return i ;
}
2017-06-13 11:35:24 +02:00
ModelInstance * ModelObject :: add_instance ( const ModelInstance & other )
2014-05-09 14:24:35 +02:00
{
ModelInstance * i = new ModelInstance ( this , other );
2014-04-30 02:04:49 +03:00
this -> instances . push_back ( i );
this -> invalidate_bounding_box ();
return i ;
}
2019-01-03 15:04:29 +01:00
ModelInstance * ModelObject :: add_instance ( const Vec3d & offset , const Vec3d & scaling_factor , const Vec3d & rotation , const Vec3d & mirror )
2018-10-11 10:03:38 +02:00
{
auto * instance = add_instance ();
instance -> set_offset ( offset );
instance -> set_scaling_factor ( scaling_factor );
instance -> set_rotation ( rotation );
2019-01-03 15:04:29 +01:00
instance -> set_mirror ( mirror );
2018-10-11 10:03:38 +02:00
return instance ;
}
2017-06-13 11:35:24 +02:00
void ModelObject :: delete_instance ( size_t idx )
2014-05-07 00:58:29 +02:00
{
ModelInstancePtrs :: iterator i = this -> instances . begin () + idx ;
delete * i ;
this -> instances . erase ( i );
this -> invalidate_bounding_box ();
}
2017-06-13 11:35:24 +02:00
void ModelObject :: delete_last_instance ()
2014-04-30 02:04:49 +03:00
{
2014-05-07 00:58:29 +02:00
this -> delete_instance ( this -> instances . size () - 1 );
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
void ModelObject :: clear_instances ()
2014-04-30 02:04:49 +03:00
{
2017-06-13 11:35:24 +02:00
for ( ModelInstance * i : this -> instances )
delete i ;
this -> instances . clear ();
this -> invalidate_bounding_box ();
2014-04-30 02:04:49 +03:00
}
2017-06-13 11:35:24 +02:00
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
2018-05-25 15:56:14 +02:00
const BoundingBoxf3 & ModelObject :: bounding_box () const
2014-09-21 10:51:36 +02:00
{
2017-06-13 11:35:24 +02:00
if ( ! m_bounding_box_valid ) {
m_bounding_box_valid = true ;
2019-01-26 18:51:34 +01:00
BoundingBoxf3 raw_bbox = this -> raw_mesh_bounding_box ();
m_bounding_box . reset ();
for ( const ModelInstance * i : this -> instances )
m_bounding_box . merge ( i -> transform_bounding_box ( raw_bbox ));
2016-11-16 11:53:29 +01:00
}
2017-06-13 11:35:24 +02:00
return m_bounding_box ;
2014-09-21 10:51:36 +02:00
}
2017-06-13 11:35:24 +02:00
// A mesh containing all transformed instances of this object.
TriangleMesh ModelObject :: mesh () const
2014-09-21 10:51:36 +02:00
{
2015-01-19 18:53:04 +01:00
TriangleMesh mesh ;
TriangleMesh raw_mesh = this -> raw_mesh ();
2017-06-13 11:35:24 +02:00
for ( const ModelInstance * i : this -> instances ) {
2014-09-21 10:51:36 +02:00
TriangleMesh m = raw_mesh ;
2017-06-13 11:35:24 +02:00
i -> transform_mesh ( & m );
2015-01-19 18:53:04 +01:00
mesh . merge ( m );
2014-09-21 10:51:36 +02:00
}
2015-01-19 18:53:04 +01:00
return mesh ;
2014-09-21 10:51:36 +02:00
}
2017-06-13 11:35:24 +02:00
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
2019-12-02 22:48:01 -05:00
// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
2017-06-13 11:35:24 +02:00
// and to display the object statistics at ModelObject::print_info().
TriangleMesh ModelObject :: raw_mesh () const
2014-08-03 20:33:16 +02:00
{
2015-01-19 18:53:04 +01:00
TriangleMesh mesh ;
2017-06-13 11:35:24 +02:00
for ( const ModelVolume * v : this -> volumes )
2018-09-17 15:12:13 +02:00
if ( v -> is_model_part ())
2018-11-02 12:11:28 +01:00
{
2019-06-11 17:08:47 +02:00
TriangleMesh vol_mesh ( v -> mesh ());
2018-11-02 13:47:47 +01:00
vol_mesh . transform ( v -> get_matrix ());
2018-11-02 12:11:28 +01:00
mesh . merge ( vol_mesh );
}
2015-01-19 18:53:04 +01:00
return mesh ;
2014-08-03 20:33:16 +02:00
}
2020-09-17 18:39:28 +02:00
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
// and to display the object statistics at ModelObject::print_info().
indexed_triangle_set ModelObject :: raw_indexed_triangle_set () const
{
size_t num_vertices = 0 ;
size_t num_faces = 0 ;
for ( const ModelVolume * v : this -> volumes )
if ( v -> is_model_part ()) {
num_vertices += v -> mesh (). its . vertices . size ();
num_faces += v -> mesh (). its . indices . size ();
}
indexed_triangle_set out ;
out . vertices . reserve ( num_vertices );
out . indices . reserve ( num_faces );
for ( const ModelVolume * v : this -> volumes )
if ( v -> is_model_part ()) {
size_t i = out . vertices . size ();
size_t j = out . indices . size ();
append ( out . vertices , v -> mesh (). its . vertices );
append ( out . indices , v -> mesh (). its . indices );
auto m = v -> get_matrix ();
for (; i < out . vertices . size (); ++ i )
out . vertices [ i ] = ( m * out . vertices [ i ]. cast < double > ()). cast < float > (). eval ();
if ( v -> is_left_handed ()) {
for (; j < out . indices . size (); ++ j )
std :: swap ( out . indices [ j ][ 0 ], out . indices [ j ][ 1 ]);
}
}
return out ;
}
2018-12-18 10:49:22 +01:00
2019-04-13 14:15:54 +02:00
const BoundingBoxf3 & ModelObject :: raw_mesh_bounding_box () const
2019-01-26 18:51:34 +01:00
{
if ( ! m_raw_mesh_bounding_box_valid ) {
m_raw_mesh_bounding_box_valid = true ;
m_raw_mesh_bounding_box . reset ();
for ( const ModelVolume * v : this -> volumes )
if ( v -> is_model_part ())
2019-06-11 17:08:47 +02:00
m_raw_mesh_bounding_box . merge ( v -> mesh (). transformed_bounding_box ( v -> get_matrix ()));
2019-01-26 18:51:34 +01:00
}
return m_raw_mesh_bounding_box ;
}
BoundingBoxf3 ModelObject :: full_raw_mesh_bounding_box () const
{
BoundingBoxf3 bb ;
for ( const ModelVolume * v : this -> volumes )
2019-06-11 17:08:47 +02:00
bb . merge ( v -> mesh (). transformed_bounding_box ( v -> get_matrix ()));
2019-01-26 18:51:34 +01:00
return bb ;
}
2017-06-13 11:35:24 +02:00
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
2019-04-13 14:15:54 +02:00
// This bounding box is only used for the actual slicing and for layer editing UI to calculate the layers.
const BoundingBoxf3 & ModelObject :: raw_bounding_box () const
2014-09-21 10:51:36 +02:00
{
2019-04-13 14:15:54 +02:00
if ( ! m_raw_bounding_box_valid ) {
m_raw_bounding_box_valid = true ;
m_raw_bounding_box . reset ();
if ( this -> instances . empty ())
2020-09-14 16:27:55 +02:00
throw Slic3r :: InvalidArgument ( "Can't call raw_bounding_box() with no instances" );
2019-01-14 13:29:06 +01:00
2019-04-13 14:15:54 +02:00
const Transform3d & inst_matrix = this -> instances . front () -> get_transformation (). get_matrix ( true );
for ( const ModelVolume * v : this -> volumes )
2019-05-21 10:01:04 +02:00
if ( v -> is_model_part ())
2019-06-11 17:08:47 +02:00
m_raw_bounding_box . merge ( v -> mesh (). transformed_bounding_box ( inst_matrix * v -> get_matrix ()));
2019-04-13 14:15:54 +02:00
}
return m_raw_bounding_box ;
2014-09-21 10:51:36 +02:00
}
2017-06-13 11:35:24 +02:00
// This returns an accurate snug bounding box of the transformed object instance, without the translation applied.
BoundingBoxf3 ModelObject :: instance_bounding_box ( size_t instance_idx , bool dont_translate ) const
2014-09-21 10:51:36 +02:00
{
2016-11-16 11:53:29 +01:00
BoundingBoxf3 bb ;
2019-01-14 13:29:06 +01:00
const Transform3d & inst_matrix = this -> instances [ instance_idx ] -> get_transformation (). get_matrix ( dont_translate );
2018-11-09 10:31:58 +01:00
for ( ModelVolume * v : this -> volumes )
{
if ( v -> is_model_part ())
2019-06-11 17:08:47 +02:00
bb . merge ( v -> mesh (). transformed_bounding_box ( inst_matrix * v -> get_matrix ()));
2018-11-09 10:31:58 +01:00
}
2016-11-16 11:53:29 +01:00
return bb ;
2014-09-21 10:51:36 +02:00
}
2019-01-28 10:10:23 +01:00
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
// This method is used by the auto arrange function.
2021-05-20 09:32:17 +02:00
#if ENABLE_ALLOW_NEGATIVE_Z
Polygon ModelObject :: convex_hull_2d ( const Transform3d & trafo_instance ) const
{
Points pts ;
for ( const ModelVolume * v : volumes ) {
if ( v -> is_model_part ())
append ( pts , its_convex_hull_2d_above ( v -> mesh (). its , ( trafo_instance * v -> get_matrix ()). cast < float > (), 0.0f ). points );
}
return Geometry :: convex_hull ( std :: move ( pts ));
}
#else
2019-04-18 12:45:43 +02:00
Polygon ModelObject :: convex_hull_2d ( const Transform3d & trafo_instance ) const
2019-01-28 10:10:23 +01:00
{
Points pts ;
for ( const ModelVolume * v : this -> volumes )
if ( v -> is_model_part ()) {
Transform3d trafo = trafo_instance * v -> get_matrix ();
2019-06-11 17:08:47 +02:00
const indexed_triangle_set & its = v -> mesh (). its ;
2019-06-10 18:30:54 +02:00
if ( its . vertices . empty ()) {
2019-01-28 10:10:23 +01:00
// Using the STL faces.
2019-06-11 17:08:47 +02:00
const stl_file & stl = v -> mesh (). stl ;
2019-06-04 22:06:42 +02:00
for ( const stl_facet & facet : stl . facet_start )
2019-01-28 10:10:23 +01:00
for ( size_t j = 0 ; j < 3 ; ++ j ) {
Vec3d p = trafo * facet . vertex [ j ]. cast < double > ();
2021-05-14 15:02:54 +02:00
pts . emplace_back ( coord_t ( scale_ ( p . x ())), coord_t ( scale_ ( p . y ())));
2019-01-28 10:10:23 +01:00
}
} else {
// Using the shared vertices should be a bit quicker than using the STL faces.
2019-06-10 18:30:54 +02:00
for ( size_t i = 0 ; i < its . vertices . size (); ++ i ) {
Vec3d p = trafo * its . vertices [ i ]. cast < double > ();
2021-05-14 15:02:54 +02:00
pts . emplace_back ( coord_t ( scale_ ( p . x ())), coord_t ( scale_ ( p . y ())));
2019-01-28 10:10:23 +01:00
}
}
}
2021-05-19 13:23:19 +02:00
return Geometry :: convex_hull ( std :: move ( pts ));
2019-01-28 10:10:23 +01:00
}
2021-05-20 09:32:17 +02:00
#endif // ENABLE_ALLOW_NEGATIVE_Z
2019-01-28 10:10:23 +01:00
2019-03-22 09:47:40 +01:00
void ModelObject :: center_around_origin ( bool include_modifiers )
2014-09-21 10:51:36 +02:00
{
// calculate the displacements needed to
// center this object around the origin
2021-04-28 11:07:15 +02:00
const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box () : raw_mesh_bounding_box ();
2019-01-18 12:52:09 +01:00
2018-10-08 14:02:12 +02:00
// Shift is the vector from the center of the bounding box to the origin
2021-04-28 11:07:15 +02:00
const Vec3d shift = - bb . center ();
2018-09-04 14:42:14 +02:00
this -> translate ( shift );
this -> origin_translation += shift ;
2014-09-21 10:51:36 +02:00
}
2021-04-28 11:07:15 +02:00
#if ENABLE_ALLOW_NEGATIVE_Z
void ModelObject :: ensure_on_bed ( bool allow_negative_z )
{
const double min_z = get_min_z ();
if ( ! allow_negative_z || min_z > 0.0 )
translate_instances ({ 0.0 , 0.0 , - min_z });
}
#else
2018-10-30 16:03:03 +01:00
void ModelObject :: ensure_on_bed ()
{
2021-04-28 11:07:15 +02:00
translate_instances ({ 0.0 , 0.0 , - get_min_z () });
2018-10-30 16:03:03 +01:00
}
2021-04-28 11:07:15 +02:00
#endif // ENABLE_ALLOW_NEGATIVE_Z
2018-10-30 16:03:03 +01:00
void ModelObject :: translate_instances ( const Vec3d & vector )
{
2021-04-28 11:07:15 +02:00
for ( size_t i = 0 ; i < instances . size (); ++ i ) {
2018-10-30 16:03:03 +01:00
translate_instance ( i , vector );
}
}
void ModelObject :: translate_instance ( size_t instance_idx , const Vec3d & vector )
{
2021-04-28 11:07:15 +02:00
assert ( instance_idx < instances . size ());
2018-10-30 16:03:03 +01:00
ModelInstance * i = instances [ instance_idx ];
i -> set_offset ( i -> get_offset () + vector );
invalidate_bounding_box ();
}
2018-11-01 14:25:10 +01:00
void ModelObject :: translate ( double x , double y , double z )
2014-12-12 22:43:04 +01:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2018-11-02 12:11:28 +01:00
v -> translate ( x , y , z );
2018-08-15 12:50:06 +02:00
}
if ( m_bounding_box_valid )
2017-06-13 11:35:24 +02:00
m_bounding_box . translate ( x , y , z );
2014-12-12 22:43:04 +01:00
}
2018-08-21 17:43:05 +02:00
void ModelObject :: scale ( const Vec3d & versor )
2014-09-21 10:51:36 +02:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2018-11-02 14:41:08 +01:00
v -> scale ( versor );
2018-08-15 12:50:06 +02:00
}
2014-09-21 10:51:36 +02:00
this -> invalidate_bounding_box ();
}
2018-11-05 08:31:54 +01:00
void ModelObject :: rotate ( double angle , Axis axis )
2015-04-16 21:22:04 +02:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2018-11-05 08:31:54 +01:00
v -> rotate ( angle , axis );
2018-07-30 11:38:36 +02:00
}
2018-08-13 16:16:37 +02:00
center_around_origin ();
2018-08-27 14:00:53 +02:00
this -> invalidate_bounding_box ();
}
2018-11-05 08:31:54 +01:00
void ModelObject :: rotate ( double angle , const Vec3d & axis )
2015-04-16 21:22:04 +02:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2018-11-05 08:31:54 +01:00
v -> rotate ( angle , axis );
2018-07-30 11:38:36 +02:00
}
2018-08-13 16:16:37 +02:00
center_around_origin ();
2015-04-16 21:22:04 +02:00
this -> invalidate_bounding_box ();
}
2018-11-05 08:51:00 +01:00
void ModelObject :: mirror ( Axis axis )
2015-04-16 21:22:04 +02:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2018-11-05 08:51:00 +01:00
v -> mirror ( axis );
2018-08-15 12:50:06 +02:00
}
2015-04-16 21:22:04 +02:00
this -> invalidate_bounding_box ();
}
2019-06-11 17:08:47 +02:00
// This method could only be called before the meshes of this ModelVolumes are not shared!
2019-07-02 16:42:23 +02:00
void ModelObject :: scale_mesh_after_creation ( const Vec3d & versor )
2018-12-19 09:54:15 +01:00
{
2021-04-28 11:07:15 +02:00
for ( ModelVolume * v : this -> volumes ) {
2019-07-02 16:42:23 +02:00
v -> scale_geometry_after_creation ( versor );
2018-12-19 09:54:15 +01:00
v -> set_offset ( versor . cwiseProduct ( v -> get_offset ()));
}
this -> invalidate_bounding_box ();
}
2021-02-10 20:31:17 +01:00
void ModelObject :: convert_units ( ModelObjectPtrs & new_objects , ConversionType conv_type , std :: vector < int > volume_idxs )
2020-05-20 20:07:31 +02:00
{
BOOST_LOG_TRIVIAL ( trace ) << "ModelObject::convert_units - start" ;
ModelObject * new_object = new_clone ( * this );
2021-02-10 20:31:17 +01:00
double koef = conv_type == ConversionType :: CONV_FROM_INCH ? 25.4 : conv_type == ConversionType :: CONV_TO_INCH ? 0.0393700787 :
conv_type == ConversionType :: CONV_FROM_METER ? 1000 : conv_type == ConversionType :: CONV_TO_METER ? 0.001 : 1 ;
2020-05-20 20:07:31 +02:00
const Vec3d versor = Vec3d ( koef , koef , koef );
new_object -> set_model ( nullptr );
new_object -> sla_support_points . clear ();
new_object -> sla_drain_holes . clear ();
new_object -> sla_points_status = sla :: PointsStatus :: NoPoints ;
new_object -> clear_volumes ();
new_object -> input_file . clear ();
int vol_idx = 0 ;
for ( ModelVolume * volume : volumes )
{
if ( ! volume -> mesh (). empty ()) {
TriangleMesh mesh ( volume -> mesh ());
mesh . require_shared_vertices ();
ModelVolume * vol = new_object -> add_volume ( mesh );
vol -> name = volume -> name ;
2020-10-22 15:44:58 +02:00
vol -> set_type ( volume -> type ());
2020-05-20 20:07:31 +02:00
// Don't copy the config's ID.
2020-09-24 15:34:13 +02:00
vol -> config . assign_config ( volume -> config );
2020-05-20 20:07:31 +02:00
assert ( vol -> config . id (). valid ());
assert ( vol -> config . id () != volume -> config . id ());
vol -> set_material ( volume -> material_id (), * volume -> material ());
2020-12-11 13:57:38 +01:00
vol -> source . input_file = volume -> source . input_file ;
vol -> source . object_idx = ( int ) new_objects . size ();
vol -> source . volume_idx = vol_idx ;
2021-02-10 20:31:17 +01:00
vol -> source . is_converted_from_inches = volume -> source . is_converted_from_inches ;
vol -> source . is_converted_from_meters = volume -> source . is_converted_from_meters ;
2020-05-20 20:07:31 +02:00
2020-12-14 14:22:43 +01:00
vol -> supported_facets . assign ( volume -> supported_facets );
vol -> seam_facets . assign ( volume -> seam_facets );
2021-04-19 07:01:11 +02:00
vol -> mmu_segmentation_facets . assign ( volume -> mmu_segmentation_facets );
2020-12-14 14:22:43 +01:00
2020-12-12 12:06:15 +01:00
// Perform conversion only if the target "imperial" state is different from the current one.
// This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
2020-12-12 18:54:34 +01:00
if ( //vol->source.is_converted_from_inches != from_imperial &&
2020-12-12 12:06:15 +01:00
( volume_idxs . empty () ||
std :: find ( volume_idxs . begin (), volume_idxs . end (), vol_idx ) != volume_idxs . end ())) {
2020-05-20 20:07:31 +02:00
vol -> scale_geometry_after_creation ( versor );
2020-10-22 15:44:58 +02:00
vol -> set_offset ( versor . cwiseProduct ( volume -> get_offset ()));
2021-02-10 20:31:17 +01:00
if ( conv_type == ConversionType :: CONV_FROM_INCH || conv_type == ConversionType :: CONV_TO_INCH )
vol -> source . is_converted_from_inches = conv_type == ConversionType :: CONV_FROM_INCH ;
if ( conv_type == ConversionType :: CONV_FROM_METER || conv_type == ConversionType :: CONV_TO_METER )
vol -> source . is_converted_from_meters = conv_type == ConversionType :: CONV_FROM_METER ;
2020-05-20 20:07:31 +02:00
}
else
vol -> set_offset ( volume -> get_offset ());
}
vol_idx ++ ;
}
new_object -> invalidate_bounding_box ();
new_objects . push_back ( new_object );
BOOST_LOG_TRIVIAL ( trace ) << "ModelObject::convert_units - end" ;
}
2017-06-13 11:35:24 +02:00
size_t ModelObject :: materials_count () const
2014-09-21 10:51:36 +02:00
{
std :: set < t_model_material_id > material_ids ;
2017-06-13 11:35:24 +02:00
for ( const ModelVolume * v : this -> volumes )
material_ids . insert ( v -> material_id ());
2014-09-21 10:51:36 +02:00
return material_ids . size ();
}
2017-06-13 11:35:24 +02:00
size_t ModelObject :: facets_count () const
2014-08-08 21:48:59 +02:00
{
size_t num = 0 ;
2017-06-13 11:35:24 +02:00
for ( const ModelVolume * v : this -> volumes )
2018-09-17 15:12:13 +02:00
if ( v -> is_model_part ())
2019-06-11 17:08:47 +02:00
num += v -> mesh (). stl . stats . number_of_facets ;
2014-08-08 21:48:59 +02:00
return num ;
}
2017-06-13 11:35:24 +02:00
bool ModelObject :: needed_repair () const
2014-08-08 21:48:59 +02:00
{
2017-06-13 11:35:24 +02:00
for ( const ModelVolume * v : this -> volumes )
2019-06-11 17:08:47 +02:00
if ( v -> is_model_part () && v -> mesh (). needed_repair ())
2017-06-13 11:35:24 +02:00
return true ;
2014-08-08 21:48:59 +02:00
return false ;
}
2018-11-26 10:56:07 +01:00
ModelObjectPtrs ModelObject :: cut ( size_t instance , coordf_t z , bool keep_upper , bool keep_lower , bool rotate_lower )
2014-09-21 10:51:36 +02:00
{
2018-12-07 12:36:30 +01:00
if ( ! keep_upper && ! keep_lower ) { return {}; }
2019-02-05 20:23:24 +01:00
BOOST_LOG_TRIVIAL ( trace ) << "ModelObject::cut - start" ;
2018-10-18 15:13:38 +02:00
// Clone the object to duplicate instances, materials etc.
2018-11-26 10:56:07 +01:00
ModelObject * upper = keep_upper ? ModelObject :: new_clone ( * this ) : nullptr ;
ModelObject * lower = keep_lower ? ModelObject :: new_clone ( * this ) : nullptr ;
if ( keep_upper ) {
upper -> set_model ( nullptr );
upper -> sla_support_points . clear ();
2020-01-31 08:54:41 +01:00
upper -> sla_drain_holes . clear ();
2019-07-25 11:23:23 +02:00
upper -> sla_points_status = sla :: PointsStatus :: NoPoints ;
2018-11-26 10:56:07 +01:00
upper -> clear_volumes ();
2020-02-07 09:49:48 +01:00
upper -> input_file . clear ();
2018-11-26 10:56:07 +01:00
}
if ( keep_lower ) {
lower -> set_model ( nullptr );
lower -> sla_support_points . clear ();
2019-11-08 14:05:56 +01:00
lower -> sla_drain_holes . clear ();
2019-07-25 11:23:23 +02:00
lower -> sla_points_status = sla :: PointsStatus :: NoPoints ;
2018-11-26 10:56:07 +01:00
lower -> clear_volumes ();
2020-02-07 09:49:48 +01:00
lower -> input_file . clear ();
2018-11-26 10:56:07 +01:00
}
2018-10-18 15:13:38 +02:00
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
2018-11-26 17:08:36 +01:00
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
2018-10-18 15:13:38 +02:00
2018-11-26 17:08:36 +01:00
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = Geometry :: assemble_transform (
Vec3d :: Zero (), // don't apply offset
instances [ instance ] -> get_rotation (). cwiseProduct ( Vec3d ( 1.0 , 1.0 , 0.0 )), // don't apply Z-rotation
instances [ instance ] -> get_scaling_factor (),
instances [ instance ] -> get_mirror ()
);
2018-11-28 13:36:07 +01:00
z -= instances [ instance ] -> get_offset ()( 2 );
2018-11-26 17:08:36 +01:00
// Lower part per-instance bounding boxes
std :: vector < BoundingBoxf3 > lower_bboxes { instances . size () };
2018-10-18 15:13:38 +02:00
for ( ModelVolume * volume : volumes ) {
2018-12-07 12:36:30 +01:00
const auto volume_matrix = volume -> get_matrix ();
2020-10-09 13:09:03 +02:00
volume -> supported_facets . clear ();
volume -> seam_facets . clear ();
2021-04-19 07:01:11 +02:00
volume -> mmu_segmentation_facets . clear ();
2020-04-16 14:42:42 +02:00
2018-09-17 15:12:13 +02:00
if ( ! volume -> is_model_part ()) {
2018-12-07 12:36:30 +01:00
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume -> set_transformation ( Geometry :: Transformation ( instance_matrix * volume_matrix ));
2018-11-26 10:56:07 +01:00
if ( keep_upper ) { upper -> add_volume ( * volume ); }
if ( keep_lower ) { lower -> add_volume ( * volume ); }
2019-01-18 12:52:09 +01:00
}
2019-11-06 13:38:43 +01:00
else if ( ! volume -> mesh (). empty ()) {
2019-04-03 12:07:58 +02:00
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
2019-06-11 17:08:47 +02:00
TriangleMesh mesh ( volume -> mesh ());
mesh . transform ( instance_matrix * volume_matrix , true );
volume -> reset_mesh ();
2018-11-26 17:08:36 +01:00
// Reset volume transformation except for offset
const Vec3d offset = volume -> get_offset ();
volume -> set_transformation ( Geometry :: Transformation ());
volume -> set_offset ( offset );
2017-02-26 22:03:03 +01:00
2021-05-18 15:05:23 +02:00
// Perform cut
TriangleMesh upper_mesh , lower_mesh ;
{
indexed_triangle_set upper_its , lower_its ;
mesh . require_shared_vertices ();
cut_mesh ( mesh . its , float ( z ), & upper_its , & lower_its );
if ( keep_upper ) {
upper_mesh = TriangleMesh ( upper_its );
upper_mesh . repair ();
upper_mesh . reset_repair_stats ();
}
if ( keep_lower ) {
lower_mesh = TriangleMesh ( lower_its );
lower_mesh . repair ();
lower_mesh . reset_repair_stats ();
}
2018-11-26 10:56:07 +01:00
}
2018-10-18 15:13:38 +02:00
2018-11-26 10:56:07 +01:00
if ( keep_upper && upper_mesh . facets_count () > 0 ) {
2019-01-18 12:52:09 +01:00
ModelVolume * vol = upper -> add_volume ( upper_mesh );
2019-06-11 17:08:47 +02:00
vol -> name = volume -> name ;
2019-07-03 13:43:54 +02:00
// Don't copy the config's ID.
2020-09-24 15:34:13 +02:00
vol -> config . assign_config ( volume -> config );
2019-07-03 13:43:54 +02:00
assert ( vol -> config . id (). valid ());
assert ( vol -> config . id () != volume -> config . id ());
2014-09-21 10:51:36 +02:00
vol -> set_material ( volume -> material_id (), * volume -> material ());
}
2018-11-26 10:56:07 +01:00
if ( keep_lower && lower_mesh . facets_count () > 0 ) {
2019-01-18 12:52:09 +01:00
ModelVolume * vol = lower -> add_volume ( lower_mesh );
2019-06-11 17:08:47 +02:00
vol -> name = volume -> name ;
2019-07-03 13:43:54 +02:00
// Don't copy the config's ID.
2020-09-24 15:34:13 +02:00
vol -> config . assign_config ( volume -> config );
assert ( vol -> config . id (). valid ());
2019-07-03 13:43:54 +02:00
assert ( vol -> config . id () != volume -> config . id ());
2014-09-21 10:51:36 +02:00
vol -> set_material ( volume -> material_id (), * volume -> material ());
2018-10-18 15:13:38 +02:00
2018-11-26 17:08:36 +01:00
// Compute the lower part instances' bounding boxes to figure out where to place
// the upper part
if ( keep_upper ) {
for ( size_t i = 0 ; i < instances . size (); i ++ ) {
lower_bboxes [ i ]. merge ( instances [ i ] -> transform_mesh_bounding_box ( lower_mesh , true ));
}
}
}
2018-11-26 10:56:07 +01:00
}
}
2018-10-18 15:13:38 +02:00
ModelObjectPtrs res ;
2018-11-26 10:56:07 +01:00
if ( keep_upper && upper -> volumes . size () > 0 ) {
upper -> invalidate_bounding_box ();
2018-11-26 13:35:09 +01:00
upper -> center_around_origin ();
2018-11-26 17:08:36 +01:00
// Reset instance transformation except offset and Z-rotation
for ( size_t i = 0 ; i < instances . size (); i ++ ) {
auto & instance = upper -> instances [ i ];
const Vec3d offset = instance -> get_offset ();
const double rot_z = instance -> get_rotation ()( 2 );
2018-11-28 13:36:07 +01:00
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
2018-11-26 17:08:36 +01:00
const Vec3d displace = lower_bboxes [ i ]. size (). cwiseProduct ( Vec3d ( - 0.5 , - 0.5 , 0.0 ));
instance -> set_transformation ( Geometry :: Transformation ());
instance -> set_offset ( offset + displace );
instance -> set_rotation ( Vec3d ( 0.0 , 0.0 , rot_z ));
}
2018-11-26 10:56:07 +01:00
res . push_back ( upper );
}
if ( keep_lower && lower -> volumes . size () > 0 ) {
lower -> invalidate_bounding_box ();
2018-11-26 13:35:09 +01:00
lower -> center_around_origin ();
2018-11-26 17:08:36 +01:00
// Reset instance transformation except offset and Z-rotation
for ( auto * instance : lower -> instances ) {
const Vec3d offset = instance -> get_offset ();
const double rot_z = instance -> get_rotation ()( 2 );
2018-11-26 17:43:27 +01:00
2018-11-26 17:08:36 +01:00
instance -> set_transformation ( Geometry :: Transformation ());
instance -> set_offset ( offset );
2018-11-26 17:43:27 +01:00
instance -> set_rotation ( Vec3d ( rotate_lower ? Geometry :: deg2rad ( 180.0 ) : 0.0 , 0.0 , rot_z ));
2018-11-26 17:08:36 +01:00
}
2018-11-26 10:56:07 +01:00
res . push_back ( lower );
}
2018-10-18 15:13:38 +02:00
2019-02-05 20:23:24 +01:00
BOOST_LOG_TRIVIAL ( trace ) << "ModelObject::cut - end" ;
2018-10-18 15:13:38 +02:00
return res ;
2014-09-21 10:51:36 +02:00
}
2017-06-13 11:35:24 +02:00
void ModelObject :: split ( ModelObjectPtrs * new_objects )
2014-11-12 23:50:09 +01:00
{
2021-03-15 10:04:45 +01:00
for ( ModelVolume * volume : this -> volumes ) {
if ( volume -> type () != ModelVolumeType :: MODEL_PART )
continue ;
2020-12-03 14:58:56 +01:00
2021-03-15 10:04:45 +01:00
TriangleMeshPtrs meshptrs = volume -> mesh (). split ();
size_t counter = 1 ;
for ( TriangleMesh * mesh : meshptrs ) {
2020-12-03 14:58:56 +01:00
2021-03-15 10:04:45 +01:00
// FIXME: crashes if not satisfied
if ( mesh -> facets_count () < 3 ) continue ;
2021-02-07 15:41:21 -08:00
2021-03-15 10:04:45 +01:00
mesh -> repair ();
2018-11-21 14:30:15 +01:00
2021-03-15 10:04:45 +01:00
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject * new_object = m_model -> add_object ();
if ( meshptrs . size () == 1 ) {
new_object -> name = volume -> name ;
// Don't copy the config's ID.
new_object -> config . assign_config ( this -> config . size () > 0 ? this -> config : volume -> config );
}
else {
new_object -> name = this -> name + ( meshptrs . size () > 1 ? "_" + std :: to_string ( counter ++ ) : "" );
// Don't copy the config's ID.
new_object -> config . assign_config ( this -> config );
}
assert ( new_object -> config . id (). valid ());
assert ( new_object -> config . id () != this -> config . id ());
new_object -> instances . reserve ( this -> instances . size ());
for ( const ModelInstance * model_instance : this -> instances )
new_object -> add_instance ( * model_instance );
ModelVolume * new_vol = new_object -> add_volume ( * volume , std :: move ( * mesh ));
for ( ModelInstance * model_instance : new_object -> instances )
{
Vec3d shift = model_instance -> get_transformation (). get_matrix ( true ) * new_vol -> get_offset ();
model_instance -> set_offset ( model_instance -> get_offset () + shift );
}
new_vol -> set_offset ( Vec3d :: Zero ());
// reset the source to disable reload from disk
new_vol -> source = ModelVolume :: Source ();
new_objects -> emplace_back ( new_object );
delete mesh ;
2018-11-21 14:30:15 +01:00
}
2014-11-12 23:50:09 +01:00
}
}
2021-03-15 10:04:45 +01:00
2020-05-27 10:55:48 +02:00
void ModelObject :: merge ()
{
if ( this -> volumes . size () == 1 ) {
// We can't merge meshes if there's just one volume
return ;
}
TriangleMesh mesh ;
for ( ModelVolume * volume : volumes )
if ( ! volume -> mesh (). empty ())
mesh . merge ( volume -> mesh ());
mesh . repair ();
this -> clear_volumes ();
ModelVolume * vol = this -> add_volume ( mesh );
if ( ! vol )
return ;
}
2019-04-26 17:28:31 +02:00
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
// This situation is solved by baking in the instance transformation into the mesh vertices.
// Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well.
void ModelObject :: bake_xy_rotation_into_meshes ( size_t instance_idx )
{
assert ( instance_idx < this -> instances . size ());
const Geometry :: Transformation reference_trafo = this -> instances [ instance_idx ] -> get_transformation ();
if ( Geometry :: is_rotation_ninety_degrees ( reference_trafo . get_rotation ()))
// nothing to do, scaling in the world coordinate space is possible in the representation of Geometry::Transformation.
return ;
bool left_handed = reference_trafo . is_left_handed ();
bool has_mirrorring = ! reference_trafo . get_mirror (). isApprox ( Vec3d ( 1. , 1. , 1. ));
bool uniform_scaling = std :: abs ( reference_trafo . get_scaling_factor (). x () - reference_trafo . get_scaling_factor (). y ()) < EPSILON &&
std :: abs ( reference_trafo . get_scaling_factor (). x () - reference_trafo . get_scaling_factor (). z ()) < EPSILON ;
double new_scaling_factor = uniform_scaling ? reference_trafo . get_scaling_factor (). x () : 1. ;
// Adjust the instances.
for ( size_t i = 0 ; i < this -> instances . size (); ++ i ) {
ModelInstance & model_instance = * this -> instances [ i ];
model_instance . set_rotation ( Vec3d ( 0. , 0. , Geometry :: rotation_diff_z ( reference_trafo . get_rotation (), model_instance . get_rotation ())));
model_instance . set_scaling_factor ( Vec3d ( new_scaling_factor , new_scaling_factor , new_scaling_factor ));
model_instance . set_mirror ( Vec3d ( 1. , 1. , 1. ));
}
// Adjust the meshes.
// Transformation to be applied to the meshes.
2019-06-11 17:08:47 +02:00
Eigen :: Matrix3d mesh_trafo_3x3 = reference_trafo . get_matrix ( true , false , uniform_scaling , ! has_mirrorring ). matrix (). block < 3 , 3 > ( 0 , 0 );
Transform3d volume_offset_correction = this -> instances [ instance_idx ] -> get_transformation (). get_matrix (). inverse () * reference_trafo . get_matrix ();
2019-04-26 17:28:31 +02:00
for ( ModelVolume * model_volume : this -> volumes ) {
const Geometry :: Transformation volume_trafo = model_volume -> get_transformation ();
bool volume_left_handed = volume_trafo . is_left_handed ();
bool volume_has_mirrorring = ! volume_trafo . get_mirror (). isApprox ( Vec3d ( 1. , 1. , 1. ));
bool volume_uniform_scaling = std :: abs ( volume_trafo . get_scaling_factor (). x () - volume_trafo . get_scaling_factor (). y ()) < EPSILON &&
std :: abs ( volume_trafo . get_scaling_factor (). x () - volume_trafo . get_scaling_factor (). z ()) < EPSILON ;
double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo . get_scaling_factor (). x () : 1. ;
// Transform the mesh.
Matrix3d volume_trafo_3x3 = volume_trafo . get_matrix ( true , false , volume_uniform_scaling , ! volume_has_mirrorring ). matrix (). block < 3 , 3 > ( 0 , 0 );
2019-06-11 17:08:47 +02:00
// Following method creates a new shared_ptr<TriangleMesh>
model_volume -> transform_this_mesh ( mesh_trafo_3x3 * volume_trafo_3x3 , left_handed != volume_left_handed );
2019-04-26 17:28:31 +02:00
// Reset the rotation, scaling and mirroring.
model_volume -> set_rotation ( Vec3d ( 0. , 0. , 0. ));
model_volume -> set_scaling_factor ( Vec3d ( volume_new_scaling_factor , volume_new_scaling_factor , volume_new_scaling_factor ));
model_volume -> set_mirror ( Vec3d ( 1. , 1. , 1. ));
// Move the reference point of the volume to compensate for the change of the instance trafo.
model_volume -> set_offset ( volume_offset_correction * volume_trafo . get_offset ());
2019-12-19 09:21:57 +01:00
// reset the source to disable reload from disk
model_volume -> source = ModelVolume :: Source ();
2019-04-26 17:28:31 +02:00
}
this -> invalidate_bounding_box ();
}
2018-10-30 16:03:03 +01:00
double ModelObject :: get_min_z () const
{
if ( instances . empty ())
return 0.0 ;
2021-04-23 08:29:29 +02:00
else {
2018-10-30 16:03:03 +01:00
double min_z = DBL_MAX ;
2021-04-23 08:29:29 +02:00
for ( size_t i = 0 ; i < instances . size (); ++ i ) {
2018-10-30 16:03:03 +01:00
min_z = std :: min ( min_z , get_instance_min_z ( i ));
}
return min_z ;
}
}
double ModelObject :: get_instance_min_z ( size_t instance_idx ) const
{
double min_z = DBL_MAX ;
2021-04-23 08:29:29 +02:00
const ModelInstance * inst = instances [ instance_idx ];
2018-11-02 12:11:28 +01:00
const Transform3d & mi = inst -> get_matrix ( true );
2018-10-30 16:03:03 +01:00
2021-04-23 08:29:29 +02:00
for ( const ModelVolume * v : volumes ) {
2018-11-06 15:51:33 +01:00
if ( ! v -> is_model_part ())
continue ;
2021-04-23 08:29:29 +02:00
const Transform3d mv = mi * v -> get_matrix ();
2018-11-02 12:11:28 +01:00
const TriangleMesh & hull = v -> get_convex_hull ();
2019-06-04 22:06:42 +02:00
for ( const stl_facet & facet : hull . stl . facet_start )
for ( int i = 0 ; i < 3 ; ++ i )
min_z = std :: min ( min_z , ( mv * facet . vertex [ i ]. cast < double > ()). z ());
2018-10-30 16:03:03 +01:00
}
return min_z + inst -> get_offset ( Z );
}
2018-10-23 15:27:31 +02:00
unsigned int ModelObject :: check_instances_print_volume_state ( const BoundingBoxf3 & print_volume )
2018-07-18 09:37:25 +02:00
{
2018-10-23 15:27:31 +02:00
unsigned int num_printable = 0 ;
enum {
INSIDE = 1 ,
OUTSIDE = 2
};
for ( ModelInstance * model_instance : this -> instances ) {
unsigned int inside_outside = 0 ;
for ( const ModelVolume * vol : this -> volumes )
if ( vol -> is_model_part ()) {
2018-11-06 16:04:30 +01:00
BoundingBoxf3 bb = vol -> get_convex_hull (). transformed_bounding_box ( model_instance -> get_matrix () * vol -> get_matrix ());
2018-07-18 14:26:42 +02:00
if ( print_volume . contains ( bb ))
2018-10-23 15:27:31 +02:00
inside_outside |= INSIDE ;
2018-07-18 14:26:42 +02:00
else if ( print_volume . intersects ( bb ))
2018-10-23 15:27:31 +02:00
inside_outside |= INSIDE | OUTSIDE ;
2018-07-18 14:26:42 +02:00
else
2018-10-23 15:27:31 +02:00
inside_outside |= OUTSIDE ;
2018-07-18 09:37:25 +02:00
}
2018-10-23 15:27:31 +02:00
model_instance -> print_volume_state =
2020-05-24 00:01:34 +02:00
( inside_outside == ( INSIDE | OUTSIDE )) ? ModelInstancePVS_Partly_Outside :
( inside_outside == INSIDE ) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside ;
2018-10-23 15:27:31 +02:00
if ( inside_outside == INSIDE )
++ num_printable ;
2018-07-18 09:37:25 +02:00
}
2018-10-23 15:27:31 +02:00
return num_printable ;
2018-07-18 09:37:25 +02:00
}
2017-08-02 16:05:18 +02:00
void ModelObject :: print_info () const
{
using namespace std ;
cout << fixed ;
2017-08-03 17:31:31 +02:00
boost :: nowide :: cout << "[" << boost :: filesystem :: path ( this -> input_file ). filename (). string () << "]" << endl ;
2017-08-02 16:05:18 +02:00
TriangleMesh mesh = this -> raw_mesh ();
mesh . check_topology ();
BoundingBoxf3 bb = mesh . bounding_box ();
2018-08-21 17:43:05 +02:00
Vec3d size = bb . size ();
2018-08-17 15:53:43 +02:00
cout << "size_x = " << size ( 0 ) << endl ;
cout << "size_y = " << size ( 1 ) << endl ;
cout << "size_z = " << size ( 2 ) << endl ;
cout << "min_x = " << bb . min ( 0 ) << endl ;
cout << "min_y = " << bb . min ( 1 ) << endl ;
cout << "min_z = " << bb . min ( 2 ) << endl ;
cout << "max_x = " << bb . max ( 0 ) << endl ;
cout << "max_y = " << bb . max ( 1 ) << endl ;
cout << "max_z = " << bb . max ( 2 ) << endl ;
2017-08-02 16:05:18 +02:00
cout << "number_of_facets = " << mesh . stl . stats . number_of_facets << endl ;
cout << "manifold = " << ( mesh . is_manifold () ? "yes" : "no" ) << endl ;
mesh . repair (); // this calculates number_of_parts
if ( mesh . needed_repair ()) {
mesh . repair ();
if ( mesh . stl . stats . degenerate_facets > 0 )
cout << "degenerate_facets = " << mesh . stl . stats . degenerate_facets << endl ;
if ( mesh . stl . stats . edges_fixed > 0 )
cout << "edges_fixed = " << mesh . stl . stats . edges_fixed << endl ;
if ( mesh . stl . stats . facets_removed > 0 )
cout << "facets_removed = " << mesh . stl . stats . facets_removed << endl ;
if ( mesh . stl . stats . facets_added > 0 )
cout << "facets_added = " << mesh . stl . stats . facets_added << endl ;
if ( mesh . stl . stats . facets_reversed > 0 )
cout << "facets_reversed = " << mesh . stl . stats . facets_reversed << endl ;
if ( mesh . stl . stats . backwards_edges > 0 )
cout << "backwards_edges = " << mesh . stl . stats . backwards_edges << endl ;
}
cout << "number_of_parts = " << mesh . stl . stats . number_of_parts << endl ;
cout << "volume = " << mesh . volume () << endl ;
}
2019-04-04 14:00:31 +02:00
std :: string ModelObject :: get_export_filename () const
{
std :: string ret = input_file ;
if ( ! name . empty ())
{
if ( ret . empty ())
// input_file was empty, just use name
ret = name ;
else
{
// Replace file name in input_file with name, but keep the path and file extension.
ret = ( boost :: filesystem :: path ( name ). parent_path (). empty ()) ?
( boost :: filesystem :: path ( ret ). parent_path () / name ). make_preferred (). string () : name ;
}
}
return ret ;
}
2019-04-24 16:04:47 +02:00
stl_stats ModelObject :: get_object_stl_stats () const
{
if ( this -> volumes . size () == 1 )
2019-06-11 17:08:47 +02:00
return this -> volumes [ 0 ] -> mesh (). stl . stats ;
2019-04-24 16:04:47 +02:00
2019-05-14 16:37:32 +02:00
stl_stats full_stats ;
2019-09-24 17:09:07 +02:00
full_stats . volume = 0.f ;
2019-04-24 16:04:47 +02:00
// fill full_stats from all objet's meshes
for ( ModelVolume * volume : this -> volumes )
{
2019-06-11 17:08:47 +02:00
const stl_stats & stats = volume -> mesh (). stl . stats ;
2019-04-24 16:04:47 +02:00
2019-05-02 16:20:50 +02:00
// initialize full_stats (for repaired errors)
2019-05-03 13:09:42 +02:00
full_stats . degenerate_facets += stats . degenerate_facets ;
full_stats . edges_fixed += stats . edges_fixed ;
full_stats . facets_removed += stats . facets_removed ;
full_stats . facets_added += stats . facets_added ;
full_stats . facets_reversed += stats . facets_reversed ;
full_stats . backwards_edges += stats . backwards_edges ;
2019-05-02 16:20:50 +02:00
// another used satistics value
2019-05-03 13:09:42 +02:00
if ( volume -> is_model_part ()) {
full_stats . volume += stats . volume ;
full_stats . number_of_parts += stats . number_of_parts ;
}
2019-04-24 16:04:47 +02:00
}
return full_stats ;
}
int ModelObject :: get_mesh_errors_count ( const int vol_idx /*= -1*/ ) const
{
2019-04-29 15:27:59 +02:00
if ( vol_idx >= 0 )
return this -> volumes [ vol_idx ] -> get_mesh_errors_count ();
const stl_stats & stats = get_object_stl_stats ();
2019-04-24 16:04:47 +02:00
return stats . degenerate_facets + stats . edges_fixed + stats . facets_removed +
stats . facets_added + stats . facets_reversed + stats . backwards_edges ;
}
2018-10-17 11:12:38 +02:00
void ModelVolume :: set_material_id ( t_model_material_id material_id )
2014-05-10 16:59:17 +02:00
{
2018-10-17 11:12:38 +02:00
m_material_id = material_id ;
// ensure m_material_id references an existing material
2018-11-05 17:52:55 +01:00
if ( ! material_id . empty ())
this -> object -> get_model () -> add_material ( material_id );
2014-05-10 16:59:17 +02:00
}
2017-06-13 11:35:24 +02:00
ModelMaterial * ModelVolume :: material () const
{
2018-10-17 11:12:38 +02:00
return this -> object -> get_model () -> get_material ( m_material_id );
2014-05-10 16:59:17 +02:00
}
2014-04-30 02:04:49 +03:00
2017-06-13 11:35:24 +02:00
void ModelVolume :: set_material ( t_model_material_id material_id , const ModelMaterial & material )
2014-09-21 10:51:36 +02:00
{
2018-10-17 11:12:38 +02:00
m_material_id = material_id ;
2018-11-05 17:52:55 +01:00
if ( ! material_id . empty ())
this -> object -> get_model () -> add_material ( material_id , material );
2014-08-03 20:33:16 +02:00
}
2018-11-22 13:20:13 +01:00
// Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.
int ModelVolume :: extruder_id () const
{
int extruder_id = - 1 ;
if ( this -> is_model_part ()) {
const ConfigOption * opt = this -> config . option ( "extruder" );
2019-02-05 15:56:06 +01:00
if (( opt == nullptr ) || ( opt -> getInt () == 0 ))
2018-11-22 13:20:13 +01:00
opt = this -> object -> config . option ( "extruder" );
extruder_id = ( opt == nullptr ) ? 0 : opt -> getInt ();
}
return extruder_id ;
}
2019-03-13 14:04:59 +01:00
bool ModelVolume :: is_splittable () const
{
2019-04-01 12:27:45 +02:00
// the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
if ( m_is_splittable == - 1 )
2019-06-11 17:08:47 +02:00
m_is_splittable = ( int ) this -> mesh (). is_splittable ();
2019-04-01 12:27:45 +02:00
return m_is_splittable == 1 ;
2019-03-13 14:04:59 +01:00
}
2019-09-19 12:39:59 +02:00
void ModelVolume :: center_geometry_after_creation ( bool update_source_offset )
2018-11-06 15:51:33 +01:00
{
2019-06-11 17:08:47 +02:00
Vec3d shift = this -> mesh (). bounding_box (). center ();
2019-01-18 12:52:09 +01:00
if ( ! shift . isApprox ( Vec3d :: Zero ()))
{
2019-06-26 13:29:49 +02:00
if ( m_mesh )
2019-07-04 20:49:46 +02:00
const_cast < TriangleMesh *> ( m_mesh . get ()) -> translate ( - ( float ) shift ( 0 ), - ( float ) shift ( 1 ), - ( float ) shift ( 2 ));
2019-06-26 13:29:49 +02:00
if ( m_convex_hull )
2019-07-04 20:49:46 +02:00
const_cast < TriangleMesh *> ( m_convex_hull . get ()) -> translate ( - ( float ) shift ( 0 ), - ( float ) shift ( 1 ), - ( float ) shift ( 2 ));
2019-01-18 12:52:09 +01:00
translate ( shift );
}
2019-09-23 15:35:49 +02:00
2019-09-19 12:39:59 +02:00
if ( update_source_offset )
source . mesh_offset = shift ;
2018-11-06 15:51:33 +01:00
}
2018-08-15 12:50:06 +02:00
void ModelVolume :: calculate_convex_hull ()
{
2019-06-11 17:08:47 +02:00
m_convex_hull = std :: make_shared < TriangleMesh > ( this -> mesh (). convex_hull_3d ());
2018-08-15 12:50:06 +02:00
}
2019-04-29 15:27:59 +02:00
int ModelVolume :: get_mesh_errors_count () const
{
2019-06-11 17:08:47 +02:00
const stl_stats & stats = this -> mesh (). stl . stats ;
2019-04-29 15:27:59 +02:00
return stats . degenerate_facets + stats . edges_fixed + stats . facets_removed +
stats . facets_added + stats . facets_reversed + stats . backwards_edges ;
}
2018-08-15 12:50:06 +02:00
const TriangleMesh & ModelVolume :: get_convex_hull () const
{
2019-06-11 17:08:47 +02:00
return * m_convex_hull . get ();
2018-08-15 12:50:06 +02:00
}
2019-02-22 12:12:10 +01:00
ModelVolumeType ModelVolume :: type_from_string ( const std :: string & s )
2018-09-17 15:12:13 +02:00
{
// Legacy support
if ( s == "1" )
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: PARAMETER_MODIFIER ;
2018-09-17 15:12:13 +02:00
// New type (supporting the support enforcers & blockers)
if ( s == "ModelPart" )
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: MODEL_PART ;
2021-05-19 09:38:51 +02:00
if ( s == "NegativeVolume" )
return ModelVolumeType :: NEGATIVE_VOLUME ;
2018-09-17 15:12:13 +02:00
if ( s == "ParameterModifier" )
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: PARAMETER_MODIFIER ;
2018-09-17 15:12:13 +02:00
if ( s == "SupportEnforcer" )
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: SUPPORT_ENFORCER ;
2018-09-17 15:12:13 +02:00
if ( s == "SupportBlocker" )
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: SUPPORT_BLOCKER ;
2018-09-17 15:12:13 +02:00
assert ( s == "0" );
// Default value if invalud type string received.
2019-02-22 12:12:10 +01:00
return ModelVolumeType :: MODEL_PART ;
2018-09-17 15:12:13 +02:00
}
2019-02-22 12:12:10 +01:00
std :: string ModelVolume :: type_to_string ( const ModelVolumeType t )
2018-09-17 15:12:13 +02:00
{
switch ( t ) {
2019-02-22 12:12:10 +01:00
case ModelVolumeType :: MODEL_PART : return "ModelPart" ;
2021-05-19 09:38:51 +02:00
case ModelVolumeType :: NEGATIVE_VOLUME : return "NegativeVolume" ;
2019-02-22 12:12:10 +01:00
case ModelVolumeType :: PARAMETER_MODIFIER : return "ParameterModifier" ;
case ModelVolumeType :: SUPPORT_ENFORCER : return "SupportEnforcer" ;
case ModelVolumeType :: SUPPORT_BLOCKER : return "SupportBlocker" ;
2018-09-17 15:12:13 +02:00
default :
assert ( false );
return "ModelPart" ;
}
}
2017-06-15 15:38:15 +02:00
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
2018-05-07 16:13:58 +02:00
size_t ModelVolume :: split ( unsigned int max_extruders )
2017-06-15 15:38:15 +02:00
{
2019-06-11 17:08:47 +02:00
TriangleMeshPtrs meshptrs = this -> mesh (). split ();
2017-06-15 15:38:15 +02:00
if ( meshptrs . size () <= 1 ) {
delete meshptrs . front ();
return 1 ;
}
size_t idx = 0 ;
size_t ivolume = std :: find ( this -> object -> volumes . begin (), this -> object -> volumes . end (), this ) - this -> object -> volumes . begin ();
std :: string name = this -> name ;
2018-04-10 12:17:55 +02:00
2019-08-05 17:49:21 +02:00
unsigned int extruder_counter = 0 ;
2018-11-13 08:36:10 +01:00
Vec3d offset = this -> get_offset ();
2018-04-10 12:17:55 +02:00
2017-06-15 15:38:15 +02:00
for ( TriangleMesh * mesh : meshptrs ) {
mesh -> repair ();
if ( idx == 0 )
2018-11-13 10:12:42 +01:00
{
2019-06-11 17:08:47 +02:00
this -> set_mesh ( std :: move ( * mesh ));
2018-11-13 10:12:42 +01:00
this -> calculate_convex_hull ();
2018-11-21 09:35:49 +01:00
// Assign a new unique ID, so that a new GLVolume will be generated.
this -> set_new_unique_id ();
2019-12-19 08:05:57 +01:00
// reset the source to disable reload from disk
this -> source = ModelVolume :: Source ();
2018-11-13 10:12:42 +01:00
}
2017-06-15 15:38:15 +02:00
else
2018-11-13 08:36:10 +01:00
this -> object -> volumes . insert ( this -> object -> volumes . begin () + ( ++ ivolume ), new ModelVolume ( object , * this , std :: move ( * mesh )));
2018-11-13 10:12:42 +01:00
this -> object -> volumes [ ivolume ] -> set_offset ( Vec3d :: Zero ());
2019-06-11 17:08:47 +02:00
this -> object -> volumes [ ivolume ] -> center_geometry_after_creation ();
2018-11-13 08:36:10 +01:00
this -> object -> volumes [ ivolume ] -> translate ( offset );
this -> object -> volumes [ ivolume ] -> name = name + "_" + std :: to_string ( idx + 1 );
2019-08-05 17:49:21 +02:00
this -> object -> volumes [ ivolume ] -> config . set_deserialize ( "extruder" , auto_extruder_id ( max_extruders , extruder_counter ));
2021-03-15 10:04:45 +01:00
this -> object -> volumes [ ivolume ] -> m_is_splittable = 0 ;
2017-06-15 15:38:15 +02:00
delete mesh ;
++ idx ;
}
return idx ;
}
2018-11-01 14:25:10 +01:00
void ModelVolume :: translate ( const Vec3d & displacement )
{
2018-11-07 14:01:03 +01:00
set_offset ( get_offset () + displacement );
2018-11-01 14:25:10 +01:00
}
2018-11-02 14:41:08 +01:00
void ModelVolume :: scale ( const Vec3d & scaling_factors )
{
2018-11-07 14:01:03 +01:00
set_scaling_factor ( get_scaling_factor (). cwiseProduct ( scaling_factors ));
2018-11-02 14:41:08 +01:00
}
2019-03-13 15:44:50 +01:00
void ModelObject :: scale_to_fit ( const Vec3d & size )
{
/*
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
Vec3d orig_size = this->bounding_box().size();
float factor = fminf(
size.x / orig_size.x,
fminf(
size.y / orig_size.y,
size.z / orig_size.z
)
);
this->scale(factor);
*/
}
2020-10-09 12:25:57 +02:00
void ModelVolume :: assign_new_unique_ids_recursive ()
{
ObjectBase :: set_new_unique_id ();
config . set_new_unique_id ();
2020-10-09 13:09:03 +02:00
supported_facets . set_new_unique_id ();
seam_facets . set_new_unique_id ();
2021-04-19 07:01:11 +02:00
mmu_segmentation_facets . set_new_unique_id ();
2020-10-09 12:25:57 +02:00
}
2018-11-05 08:31:54 +01:00
void ModelVolume :: rotate ( double angle , Axis axis )
{
switch ( axis )
{
case X : { rotate ( angle , Vec3d :: UnitX ()); break ; }
case Y : { rotate ( angle , Vec3d :: UnitY ()); break ; }
case Z : { rotate ( angle , Vec3d :: UnitZ ()); break ; }
2019-03-28 12:37:10 +01:00
default : break ;
2018-11-05 08:31:54 +01:00
}
}
void ModelVolume :: rotate ( double angle , const Vec3d & axis )
{
2018-11-07 14:01:03 +01:00
set_rotation ( get_rotation () + Geometry :: extract_euler_angles ( Eigen :: Quaterniond ( Eigen :: AngleAxisd ( angle , axis )). toRotationMatrix ()));
2018-11-05 08:31:54 +01:00
}
2018-11-05 08:51:00 +01:00
void ModelVolume :: mirror ( Axis axis )
{
2018-11-07 14:01:03 +01:00
Vec3d mirror = get_mirror ();
2018-11-05 08:51:00 +01:00
switch ( axis )
{
case X : { mirror ( 0 ) *= - 1.0 ; break ; }
case Y : { mirror ( 1 ) *= - 1.0 ; break ; }
case Z : { mirror ( 2 ) *= - 1.0 ; break ; }
2019-03-28 12:37:10 +01:00
default : break ;
2018-11-05 08:51:00 +01:00
}
2018-11-07 14:01:03 +01:00
set_mirror ( mirror );
2018-11-05 08:51:00 +01:00
}
2019-06-11 17:08:47 +02:00
// This method could only be called before the meshes of this ModelVolumes are not shared!
2019-07-02 16:42:23 +02:00
void ModelVolume :: scale_geometry_after_creation ( const Vec3d & versor )
2018-12-19 09:54:15 +01:00
{
2019-07-02 16:42:23 +02:00
const_cast < TriangleMesh *> ( m_mesh . get ()) -> scale ( versor );
const_cast < TriangleMesh *> ( m_convex_hull . get ()) -> scale ( versor );
2019-04-26 17:28:31 +02:00
}
2019-06-11 17:08:47 +02:00
void ModelVolume :: transform_this_mesh ( const Transform3d & mesh_trafo , bool fix_left_handed )
2019-04-26 17:28:31 +02:00
{
2019-06-11 17:08:47 +02:00
TriangleMesh mesh = this -> mesh ();
mesh . transform ( mesh_trafo , fix_left_handed );
this -> set_mesh ( std :: move ( mesh ));
TriangleMesh convex_hull = this -> get_convex_hull ();
convex_hull . transform ( mesh_trafo , fix_left_handed );
2021-05-06 14:43:36 +02:00
m_convex_hull = std :: make_shared < TriangleMesh > ( std :: move ( convex_hull ));
2019-04-26 17:28:31 +02:00
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this -> set_new_unique_id ();
}
2019-06-11 17:08:47 +02:00
void ModelVolume :: transform_this_mesh ( const Matrix3d & matrix , bool fix_left_handed )
2019-04-26 17:28:31 +02:00
{
2019-06-11 17:08:47 +02:00
TriangleMesh mesh = this -> mesh ();
mesh . transform ( matrix , fix_left_handed );
this -> set_mesh ( std :: move ( mesh ));
TriangleMesh convex_hull = this -> get_convex_hull ();
convex_hull . transform ( matrix , fix_left_handed );
2021-05-06 14:43:36 +02:00
m_convex_hull = std :: make_shared < TriangleMesh > ( std :: move ( convex_hull ));
2019-04-26 17:28:31 +02:00
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this -> set_new_unique_id ();
2018-12-19 09:54:15 +01:00
}
2020-12-11 13:57:38 +01:00
void ModelVolume :: convert_from_imperial_units ()
{
double in_to_mm = 25.4 ;
this -> scale_geometry_after_creation ( Vec3d ( in_to_mm , in_to_mm , in_to_mm ));
this -> set_offset ( Vec3d ( 0 , 0 , 0 ));
this -> source . is_converted_from_inches = true ;
}
2021-02-10 20:31:17 +01:00
void ModelVolume :: convert_from_meters ()
{
double m_to_mm = 1000 ;
this -> scale_geometry_after_creation ( Vec3d ( m_to_mm , m_to_mm , m_to_mm ));
this -> set_offset ( Vec3d ( 0 , 0 , 0 ));
this -> source . is_converted_from_meters = true ;
}
2017-06-13 11:35:24 +02:00
void ModelInstance :: transform_mesh ( TriangleMesh * mesh , bool dont_translate ) const
2014-08-03 20:33:16 +02:00
{
2018-11-02 13:47:47 +01:00
mesh -> transform ( get_matrix ( dont_translate ));
2014-08-03 20:33:16 +02:00
}
2018-11-09 10:31:58 +01:00
BoundingBoxf3 ModelInstance :: transform_mesh_bounding_box ( const TriangleMesh & mesh , bool dont_translate ) const
2016-11-16 11:53:29 +01:00
{
2017-06-13 11:35:24 +02:00
// Rotate around mesh origin.
2018-11-09 10:31:58 +01:00
TriangleMesh copy ( mesh );
2018-11-02 13:47:47 +01:00
copy . transform ( get_matrix ( true , false , true , true ));
2018-09-04 14:42:14 +02:00
BoundingBoxf3 bbox = copy . bounding_box ();
if ( ! empty ( bbox )) {
2018-09-24 15:54:09 +02:00
// Scale the bounding box along the three axes.
for ( unsigned int i = 0 ; i < 3 ; ++ i )
{
2018-11-07 14:01:03 +01:00
if ( std :: abs ( get_scaling_factor (( Axis ) i ) - 1.0 ) > EPSILON )
2018-10-31 14:56:51 +01:00
{
2018-11-07 14:01:03 +01:00
bbox . min ( i ) *= get_scaling_factor (( Axis ) i );
bbox . max ( i ) *= get_scaling_factor (( Axis ) i );
2018-09-24 15:54:09 +02:00
}
}
2018-09-25 10:42:11 +02:00
// Translate the bounding box.
if ( ! dont_translate ) {
2018-11-07 14:01:03 +01:00
bbox . min += get_offset ();
bbox . max += get_offset ();
2018-09-25 10:42:11 +02:00
}
2017-06-13 11:35:24 +02:00
}
2016-11-16 11:53:29 +01:00
return bbox ;
}
BoundingBoxf3 ModelInstance :: transform_bounding_box ( const BoundingBoxf3 & bbox , bool dont_translate ) const
{
2018-11-01 14:25:10 +01:00
return bbox . transformed ( get_matrix ( dont_translate ));
2016-11-16 11:53:29 +01:00
}
2018-09-06 09:16:32 +02:00
Vec3d ModelInstance :: transform_vector ( const Vec3d & v , bool dont_translate ) const
{
2018-11-01 14:25:10 +01:00
return get_matrix ( dont_translate ) * v ;
2018-09-06 09:16:32 +02:00
}
2017-06-13 11:35:24 +02:00
void ModelInstance :: transform_polygon ( Polygon * polygon ) const
2014-08-03 20:33:16 +02:00
{
2018-10-31 14:56:51 +01:00
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
2018-11-07 14:01:03 +01:00
polygon -> rotate ( get_rotation ( Z )); // rotate around polygon origin
2018-10-31 14:56:51 +01:00
// CHECK_ME -> Is the following correct ?
2018-11-07 14:01:03 +01:00
polygon -> scale ( get_scaling_factor ( X ), get_scaling_factor ( Y )); // scale around polygon origin
2014-08-03 20:33:16 +02:00
}
2019-07-12 21:03:49 +02:00
arrangement :: ArrangePolygon ModelInstance :: get_arrange_polygon () const
2019-06-27 21:13:44 +02:00
{
2020-12-03 12:00:12 +01:00
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
2019-07-12 21:03:49 +02:00
2019-07-18 12:58:28 +02:00
Vec3d rotation = get_rotation ();
rotation . z () = 0. ;
Transform3d trafo_instance =
2021-05-17 12:53:05 +02:00
#if ENABLE_ALLOW_NEGATIVE_Z
Geometry :: assemble_transform ( get_offset (). z () * Vec3d :: UnitZ (), rotation ,
get_scaling_factor (), get_mirror ());
#else
2019-07-18 12:58:28 +02:00
Geometry :: assemble_transform ( Vec3d :: Zero (), rotation ,
get_scaling_factor (), get_mirror ());
2021-05-17 12:53:05 +02:00
#endif // ENABLE_ALLOW_NEGATIVE_Z
2019-07-15 17:30:44 +02:00
2019-07-18 12:58:28 +02:00
Polygon p = get_object () -> convex_hull_2d ( trafo_instance );
2021-05-19 08:39:04 +02:00
#if !ENABLE_ALLOW_NEGATIVE_Z
2019-07-18 12:58:28 +02:00
assert ( ! p . points . empty ());
2021-05-19 08:39:04 +02:00
#endif // !ENABLE_ALLOW_NEGATIVE_Z
2019-07-18 12:58:28 +02:00
2020-12-03 12:00:12 +01:00
// if (!p.points.empty()) {
// Polygons pp{p};
// pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
// if (!pp.empty()) p = pp.front();
// }
2019-07-18 12:58:28 +02:00
2019-07-16 18:33:42 +02:00
arrangement :: ArrangePolygon ret ;
2019-07-18 12:58:28 +02:00
ret . poly . contour = std :: move ( p );
ret . translation = Vec2crd { scaled ( get_offset ( X )), scaled ( get_offset ( Y ))};
ret . rotation = get_rotation ( Z );
2019-07-16 18:33:42 +02:00
2019-07-15 17:30:44 +02:00
return ret ;
2019-06-27 21:13:44 +02:00
}
2020-08-05 14:03:22 +02:00
indexed_triangle_set FacetsAnnotation :: get_facets ( const ModelVolume & mv , EnforcerBlockerType type ) const
2020-04-16 14:42:42 +02:00
{
2020-07-15 10:28:20 +02:00
TriangleSelector selector ( mv . mesh ());
selector . deserialize ( m_data );
indexed_triangle_set out = selector . get_facets ( type );
2020-04-16 14:42:42 +02:00
return out ;
}
2020-07-15 12:28:52 +02:00
bool FacetsAnnotation :: set ( const TriangleSelector & selector )
2020-04-16 14:42:42 +02:00
{
2020-07-09 16:25:34 +02:00
std :: map < int , std :: vector < bool >> sel_map = selector . serialize ();
if ( sel_map != m_data ) {
m_data = sel_map ;
2020-09-24 15:34:13 +02:00
this -> touch ();
2020-07-15 12:28:52 +02:00
return true ;
2020-07-09 16:25:34 +02:00
}
2020-07-15 12:28:52 +02:00
return false ;
2020-04-16 14:42:42 +02:00
}
void FacetsAnnotation :: clear ()
{
m_data . clear ();
2020-09-24 15:34:13 +02:00
this -> reset_timestamp ();
2020-04-16 14:42:42 +02:00
}
2020-07-24 16:53:05 +02:00
// Following function takes data from a triangle and encodes it as string
// of hexadecimal numbers (one digit per triangle). Used for 3MF export,
// changing it may break backwards compatibility !!!!!
std :: string FacetsAnnotation :: get_triangle_as_string ( int triangle_idx ) const
{
std :: string out ;
auto triangle_it = m_data . find ( triangle_idx );
if ( triangle_it != m_data . end ()) {
const std :: vector < bool >& code = triangle_it -> second ;
int offset = 0 ;
while ( offset < int ( code . size ())) {
int next_code = 0 ;
for ( int i = 3 ; i >= 0 ; -- i ) {
next_code = next_code << 1 ;
next_code |= int ( code [ offset + i ]);
}
offset += 4 ;
assert ( next_code >= 0 && next_code <= 15 );
char digit = next_code < 10 ? next_code + '0' : ( next_code - 10 ) + 'A' ;
out . insert ( out . begin (), digit );
}
}
return out ;
}
// Recover triangle splitting & state from string of hexadecimal values previously
// generated by get_triangle_as_string. Used to load from 3MF.
void FacetsAnnotation :: set_triangle_from_string ( int triangle_id , const std :: string & str )
{
assert ( ! str . empty ());
m_data [ triangle_id ] = std :: vector < bool > (); // zero current state or create new
std :: vector < bool >& code = m_data [ triangle_id ];
for ( auto it = str . crbegin (); it != str . crend (); ++ it ) {
const char ch = * it ;
int dec = 0 ;
if ( ch >= '0' && ch <= '9' )
dec = int ( ch - '0' );
else if ( ch >= 'A' && ch <= 'F' )
dec = 10 + int ( ch - 'A' );
else
assert ( false );
// Convert to binary and append into code.
for ( int i = 0 ; i < 4 ; ++ i ) {
code . insert ( code . end (), bool ( dec & ( 1 << i )));
}
}
}
2018-11-21 17:35:35 +01:00
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal ( const Model & model_old , const Model & model_new )
{
if ( model_old . objects . size () != model_new . objects . size ())
return false ;
for ( size_t i = 0 ; i < model_old . objects . size (); ++ i )
if ( model_old . objects [ i ] -> id () != model_new . objects [ i ] -> id ())
return false ;
return true ;
}
// Test whether the new model is just an extension of the old model (new objects were added
// to the end of the original list. In that case it is not necessary to kill the background processing.
bool model_object_list_extended ( const Model & model_old , const Model & model_new )
{
if ( model_old . objects . size () >= model_new . objects . size ())
return false ;
for ( size_t i = 0 ; i < model_old . objects . size (); ++ i )
if ( model_old . objects [ i ] -> id () != model_new . objects [ i ] -> id ())
return false ;
return true ;
}
2021-05-26 12:41:06 +02:00
template < typename TypeFilterFn >
bool model_volume_list_changed ( const ModelObject & model_object_old , const ModelObject & model_object_new , TypeFilterFn type_filter )
2018-11-21 17:35:35 +01:00
{
size_t i_old , i_new ;
for ( i_old = 0 , i_new = 0 ; i_old < model_object_old . volumes . size () && i_new < model_object_new . volumes . size ();) {
const ModelVolume & mv_old = * model_object_old . volumes [ i_old ];
const ModelVolume & mv_new = * model_object_new . volumes [ i_new ];
2021-05-26 12:41:06 +02:00
if ( ! type_filter ( mv_old . type ())) {
2018-11-21 17:35:35 +01:00
++ i_old ;
continue ;
}
2021-05-26 12:41:06 +02:00
if ( ! type_filter ( mv_new . type ())) {
2018-11-21 17:35:35 +01:00
++ i_new ;
continue ;
}
2021-05-26 12:41:06 +02:00
if ( mv_old . type () != mv_new . type () || mv_old . id () != mv_new . id ())
2018-11-21 17:35:35 +01:00
return true ;
//FIXME test for the content of the mesh!
2021-05-26 12:41:06 +02:00
if ( ! mv_old . get_matrix (). isApprox ( mv_new . get_matrix ()))
2018-11-21 17:35:35 +01:00
return true ;
2019-06-20 16:15:09 +02:00
++ i_old ;
2018-11-21 17:35:35 +01:00
++ i_new ;
}
for (; i_old < model_object_old . volumes . size (); ++ i_old ) {
const ModelVolume & mv_old = * model_object_old . volumes [ i_old ];
2021-05-26 12:41:06 +02:00
if ( type_filter ( mv_old . type ()))
2018-11-21 17:35:35 +01:00
// ModelVolume was deleted.
return true ;
}
for (; i_new < model_object_new . volumes . size (); ++ i_new ) {
const ModelVolume & mv_new = * model_object_new . volumes [ i_new ];
2021-05-26 12:41:06 +02:00
if ( type_filter ( mv_new . type ()))
2018-11-21 17:35:35 +01:00
// ModelVolume was added.
return true ;
}
return false ;
}
2021-05-26 12:41:06 +02:00
bool model_volume_list_changed ( const ModelObject & model_object_old , const ModelObject & model_object_new , const ModelVolumeType type )
{
return model_volume_list_changed ( model_object_old , model_object_new , [ type ]( const ModelVolumeType t ) { return t == type ; });
}
bool model_volume_list_changed ( const ModelObject & model_object_old , const ModelObject & model_object_new , const std :: initializer_list < ModelVolumeType > & types )
{
return model_volume_list_changed ( model_object_old , model_object_new , [ & types ]( const ModelVolumeType t ) {
return std :: find ( types . begin (), types . end (), t ) != types . end ();
});
}
2020-04-22 11:16:28 +02:00
bool model_custom_supports_data_changed ( const ModelObject & mo , const ModelObject & mo_new ) {
assert ( ! model_volume_list_changed ( mo , mo_new , ModelVolumeType :: MODEL_PART ));
2021-04-19 07:04:50 +02:00
// FIXME Lukas H.: Because of adding another mesh modifiers when slicing, then assert triggered and possible crash. It requires changing the integration of MMU segmentation.
// assert(mo.volumes.size() == mo_new.volumes.size());
// for (size_t i=0; i<mo.volumes.size(); ++i) {
for ( size_t i = 0 ; i < std :: min ( mo . volumes . size (), mo_new . volumes . size ()); ++ i ) {
2020-10-09 13:09:03 +02:00
if ( ! mo_new . volumes [ i ] -> supported_facets . timestamp_matches ( mo . volumes [ i ] -> supported_facets ))
2020-04-22 11:16:28 +02:00
return true ;
}
return false ;
2020-07-15 10:28:20 +02:00
}
2020-04-22 11:16:28 +02:00
2020-08-31 07:25:43 +02:00
bool model_custom_seam_data_changed ( const ModelObject & mo , const ModelObject & mo_new ) {
assert ( ! model_volume_list_changed ( mo , mo_new , ModelVolumeType :: MODEL_PART ));
2021-04-19 07:04:50 +02:00
// FIXME Lukas H.: Because of adding another mesh modifiers when slicing, then assert triggered and possible crash. It requires changing the integration of MMU segmentation.
// assert(mo.volumes.size() == mo_new.volumes.size());
// for (size_t i=0; i<mo.volumes.size(); ++i) {
for ( size_t i = 0 ; i < std :: min ( mo . volumes . size (), mo_new . volumes . size ()); ++ i ) {
2020-10-09 13:09:03 +02:00
if ( ! mo_new . volumes [ i ] -> seam_facets . timestamp_matches ( mo . volumes [ i ] -> seam_facets ))
2020-08-31 07:25:43 +02:00
return true ;
}
return false ;
}
2021-04-19 07:01:11 +02:00
bool model_mmu_segmentation_data_changed ( const ModelObject & mo , const ModelObject & mo_new ) {
assert ( ! model_volume_list_changed ( mo , mo_new , ModelVolumeType :: MODEL_PART ));
2021-04-19 07:04:50 +02:00
// FIXME Lukas H.: Because of adding another mesh modifiers when slicing, then assert triggered and possible crash. It requires changing the integration of MMU segmentation.
// assert(mo.volumes.size() == mo_new.volumes.size());
// for (size_t i=0; i<mo.volumes.size(); ++i) {
for ( size_t i = 0 ; i < std :: min ( mo . volumes . size (), mo_new . volumes . size ()); ++ i ) {
2021-04-19 07:01:11 +02:00
if ( ! mo_new . volumes [ i ] -> mmu_segmentation_facets . timestamp_matches ( mo . volumes [ i ] -> mmu_segmentation_facets ))
return true ;
}
return false ;
}
2019-07-18 17:41:47 +02:00
extern bool model_has_multi_part_objects ( const Model & model )
{
for ( const ModelObject * model_object : model . objects )
if ( model_object -> volumes . size () != 1 || ! model_object -> volumes . front () -> is_model_part ())
return true ;
return false ;
}
extern bool model_has_advanced_features ( const Model & model )
{
2020-09-24 15:34:13 +02:00
auto config_is_advanced = []( const ModelConfig & config ) {
2019-07-18 17:41:47 +02:00
return ! ( config . empty () || ( config . size () == 1 && config . cbegin () -> first == "extruder" ));
};
for ( const ModelObject * model_object : model . objects ) {
// Is there more than one instance or advanced config data?
if ( model_object -> instances . size () > 1 || config_is_advanced ( model_object -> config ))
return true ;
// Is there any modifier or advanced config data?
for ( const ModelVolume * model_volume : model_object -> volumes )
if ( ! model_volume -> is_model_part () || config_is_advanced ( model_volume -> config ))
return true ;
}
return false ;
}
2019-01-31 08:47:23 +01:00
#ifndef NDEBUG
2018-11-16 18:28:50 +01:00
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity ( const Model & model )
{
2019-06-27 11:02:45 +02:00
std :: set < ObjectID > ids ;
auto check = [ & ids ]( ObjectID id ) {
2019-07-03 13:43:54 +02:00
assert ( id . valid ());
2018-11-16 18:28:50 +01:00
assert ( ids . find ( id ) == ids . end ());
ids . insert ( id );
};
for ( const ModelObject * model_object : model . objects ) {
check ( model_object -> id ());
2019-07-03 13:43:54 +02:00
check ( model_object -> config . id ());
for ( const ModelVolume * model_volume : model_object -> volumes ) {
2018-11-16 18:28:50 +01:00
check ( model_volume -> id ());
2019-07-03 13:43:54 +02:00
check ( model_volume -> config . id ());
}
2018-11-16 18:28:50 +01:00
for ( const ModelInstance * model_instance : model_object -> instances )
check ( model_instance -> id ());
}
2019-07-03 13:43:54 +02:00
for ( const auto mm : model . materials ) {
2018-11-16 18:28:50 +01:00
check ( mm . second -> id ());
2019-07-03 13:43:54 +02:00
check ( mm . second -> config . id ());
}
2018-11-16 18:28:50 +01:00
}
void check_model_ids_equal ( const Model & model1 , const Model & model2 )
{
// Verify whether the IDs of model1 and model match.
assert ( model1 . objects . size () == model2 . objects . size ());
for ( size_t idx_model = 0 ; idx_model < model2 . objects . size (); ++ idx_model ) {
const ModelObject & model_object1 = * model1 . objects [ idx_model ];
const ModelObject & model_object2 = * model2 . objects [ idx_model ];
assert ( model_object1 . id () == model_object2 . id ());
2019-07-03 13:43:54 +02:00
assert ( model_object1 . config . id () == model_object2 . config . id ());
2018-11-16 18:28:50 +01:00
assert ( model_object1 . volumes . size () == model_object2 . volumes . size ());
assert ( model_object1 . instances . size () == model_object2 . instances . size ());
2019-07-03 13:43:54 +02:00
for ( size_t i = 0 ; i < model_object1 . volumes . size (); ++ i ) {
2018-11-16 18:28:50 +01:00
assert ( model_object1 . volumes [ i ] -> id () == model_object2 . volumes [ i ] -> id ());
2019-07-03 13:43:54 +02:00
assert ( model_object1 . volumes [ i ] -> config . id () == model_object2 . volumes [ i ] -> config . id ());
}
2018-11-16 18:28:50 +01:00
for ( size_t i = 0 ; i < model_object1 . instances . size (); ++ i )
assert ( model_object1 . instances [ i ] -> id () == model_object2 . instances [ i ] -> id ());
}
assert ( model1 . materials . size () == model2 . materials . size ());
{
auto it1 = model1 . materials . begin ();
auto it2 = model2 . materials . begin ();
for (; it1 != model1 . materials . end (); ++ it1 , ++ it2 ) {
assert ( it1 -> first == it2 -> first ); // compare keys
assert ( it1 -> second -> id () == it2 -> second -> id ());
2019-07-03 13:43:54 +02:00
assert ( it1 -> second -> config . id () == it2 -> second -> config . id ());
2018-11-16 18:28:50 +01:00
}
}
}
2020-04-23 18:19:03 +02:00
2019-01-31 08:47:23 +01:00
#endif /* NDEBUG */
2018-11-16 18:28:50 +01:00
2014-04-30 02:04:49 +03:00
}
2019-06-26 16:29:12 +02:00
#if 0
CEREAL_REGISTER_TYPE(Slic3r::ModelObject)
CEREAL_REGISTER_TYPE(Slic3r::ModelVolume)
CEREAL_REGISTER_TYPE(Slic3r::ModelInstance)
CEREAL_REGISTER_TYPE(Slic3r::Model)
2019-06-27 11:02:45 +02:00
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model)
2019-07-17 18:10:08 +02:00
#endif