mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev_native' into tm_sla_supports
This commit is contained in:
		
						commit
						97b3d94760
					
				
					 55 changed files with 1903 additions and 808 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								resources/icons/support_blocker_.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/support_blocker_.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/icons/support_enforcer_.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/support_enforcer_.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.7 KiB | 
|  | @ -173,7 +173,7 @@ extern void stl_mirror_xy(stl_file *stl); | ||||||
| extern void stl_mirror_yz(stl_file *stl); | extern void stl_mirror_yz(stl_file *stl); | ||||||
| extern void stl_mirror_xz(stl_file *stl); | extern void stl_mirror_xz(stl_file *stl); | ||||||
| extern void stl_transform(stl_file *stl, float *trafo3x4); | extern void stl_transform(stl_file *stl, float *trafo3x4); | ||||||
| extern void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t); | extern void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>& t); | ||||||
| extern void stl_open_merge(stl_file *stl, char *file); | extern void stl_open_merge(stl_file *stl, char *file); | ||||||
| extern void stl_invalidate_shared_vertices(stl_file *stl); | extern void stl_invalidate_shared_vertices(stl_file *stl); | ||||||
| extern void stl_generate_shared_vertices(stl_file *stl); | extern void stl_generate_shared_vertices(stl_file *stl); | ||||||
|  | @ -189,7 +189,7 @@ inline void stl_normalize_vector(stl_normal &normal) { | ||||||
|   if (length < 0.000000000001) |   if (length < 0.000000000001) | ||||||
|     normal = stl_normal::Zero(); |     normal = stl_normal::Zero(); | ||||||
|   else |   else | ||||||
|     normal *= (1.0 / length); |     normal *= float(1.0 / length); | ||||||
| } | } | ||||||
| inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { | inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { | ||||||
|   return (a(0) != b(0)) ? (a(0) < b(0)) : |   return (a(0) != b(0)) ? (a(0) < b(0)) : | ||||||
|  |  | ||||||
|  | @ -155,7 +155,7 @@ void stl_transform(stl_file *stl, float *trafo3x4) { | ||||||
|   calculate_normals(stl); |   calculate_normals(stl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t) | void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>& t) | ||||||
| { | { | ||||||
|     if (stl->error) |     if (stl->error) | ||||||
|         return; |         return; | ||||||
|  | @ -178,7 +178,7 @@ void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Eigen::MatrixXf dst_vertices(3, vertices_count); |     Eigen::MatrixXf dst_vertices(3, vertices_count); | ||||||
|     dst_vertices = t * src_vertices.colwise().homogeneous(); |     dst_vertices = t.cast<float>() * src_vertices.colwise().homogeneous(); | ||||||
| 
 | 
 | ||||||
|     facet_ptr = stl->facet_start; |     facet_ptr = stl->facet_start; | ||||||
|     v_id = 0; |     v_id = 0; | ||||||
|  |  | ||||||
|  | @ -439,14 +439,16 @@ void ConfigBase::load_from_gcode_file(const std::string &file) | ||||||
|     ifs.read(data.data(), data_length); |     ifs.read(data.data(), data_length); | ||||||
|     ifs.close(); |     ifs.close(); | ||||||
| 
 | 
 | ||||||
|     load_from_gcode_string(data.data()); |     size_t key_value_pairs = load_from_gcode_string(data.data()); | ||||||
|  |     if (key_value_pairs < 80) | ||||||
|  |         throw std::runtime_error((boost::format("Suspiciously low number of configuration values extracted from %1: %2") % file % key_value_pairs).str()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Load the config keys from the given string.
 | // Load the config keys from the given string.
 | ||||||
| void ConfigBase::load_from_gcode_string(const char* str) | size_t ConfigBase::load_from_gcode_string(const char* str) | ||||||
| { | { | ||||||
|     if (str == nullptr) |     if (str == nullptr) | ||||||
|         return; |         return 0; | ||||||
| 
 | 
 | ||||||
|     // Walk line by line in reverse until a non-configuration key appears.
 |     // Walk line by line in reverse until a non-configuration key appears.
 | ||||||
|     char *data_start = const_cast<char*>(str); |     char *data_start = const_cast<char*>(str); | ||||||
|  | @ -497,11 +499,8 @@ void ConfigBase::load_from_gcode_string(const char* str) | ||||||
|         } |         } | ||||||
|         end = start; |         end = start; | ||||||
|     } |     } | ||||||
|     if (num_key_value_pairs < 90) { | 
 | ||||||
|         char msg[80]; | 	return num_key_value_pairs; | ||||||
|         sprintf(msg, "Suspiciously low number of configuration values extracted: %d", num_key_value_pairs); |  | ||||||
|         throw std::runtime_error(msg); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigBase::save(const std::string &file) const | void ConfigBase::save(const std::string &file) const | ||||||
|  |  | ||||||
|  | @ -1119,7 +1119,8 @@ public: | ||||||
|     void load(const std::string &file); |     void load(const std::string &file); | ||||||
|     void load_from_ini(const std::string &file); |     void load_from_ini(const std::string &file); | ||||||
|     void load_from_gcode_file(const std::string &file); |     void load_from_gcode_file(const std::string &file); | ||||||
|     void load_from_gcode_string(const char* str); |     // Returns number of key/value pairs extracted.
 | ||||||
|  |     size_t load_from_gcode_string(const char* str); | ||||||
|     void load(const boost::property_tree::ptree &tree); |     void load(const boost::property_tree::ptree &tree); | ||||||
|     void save(const std::string &file) const; |     void save(const std::string &file) const; | ||||||
| 
 | 
 | ||||||
|  | @ -1237,6 +1238,7 @@ public: | ||||||
|     ConfigOption*           optptr(const t_config_option_key &opt_key, bool create = false) override; |     ConfigOption*           optptr(const t_config_option_key &opt_key, bool create = false) override; | ||||||
|     // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
 |     // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
 | ||||||
|     t_config_option_keys    keys() const override; |     t_config_option_keys    keys() const override; | ||||||
|  |     bool                    empty() const { return options.empty(); } | ||||||
| 
 | 
 | ||||||
|     // Set a value for an opt_key. Returns true if the value did not exist yet.
 |     // Set a value for an opt_key. Returns true if the value did not exist yet.
 | ||||||
|     // This DynamicConfig will take ownership of opt.
 |     // This DynamicConfig will take ownership of opt.
 | ||||||
|  |  | ||||||
|  | @ -247,7 +247,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) | ||||||
|         // Only concentric fills are not sorted.
 |         // Only concentric fills are not sorted.
 | ||||||
|         eec->no_sort = f->no_sort(); |         eec->no_sort = f->no_sort(); | ||||||
|         extrusion_entities_append_paths( |         extrusion_entities_append_paths( | ||||||
|             eec->entities, STDMOVE(polylines), |             eec->entities, std::move(polylines), | ||||||
|             is_bridge ? |             is_bridge ? | ||||||
|                 erBridgeInfill : |                 erBridgeInfill : | ||||||
|                 (surface.is_solid() ? |                 (surface.is_solid() ? | ||||||
|  |  | ||||||
|  | @ -813,7 +813,7 @@ namespace Slic3r { | ||||||
|                 std::vector<Vec3f> sla_support_points; |                 std::vector<Vec3f> sla_support_points; | ||||||
| 
 | 
 | ||||||
|                 for (unsigned int i=0; i<object_data_points.size(); i+=3) |                 for (unsigned int i=0; i<object_data_points.size(); i+=3) | ||||||
|                     sla_support_points.push_back(Vec3f(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str()))); |                     sla_support_points.push_back(Vec3d(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())).cast<float>()); | ||||||
| 
 | 
 | ||||||
|                 if (!sla_support_points.empty()) |                 if (!sla_support_points.empty()) | ||||||
|                     m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points)); |                     m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points)); | ||||||
|  | @ -1608,10 +1608,10 @@ namespace Slic3r { | ||||||
|         IdToObjectDataMap m_objects_data; |         IdToObjectDataMap m_objects_data; | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|         bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); |         bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); |         bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); | ||||||
|         bool _add_content_types_file_to_archive(mz_zip_archive& archive); |         bool _add_content_types_file_to_archive(mz_zip_archive& archive); | ||||||
|         bool _add_relationships_file_to_archive(mz_zip_archive& archive); |         bool _add_relationships_file_to_archive(mz_zip_archive& archive); | ||||||
|         bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model); |         bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model); | ||||||
|  | @ -1620,17 +1620,17 @@ namespace Slic3r { | ||||||
|         bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); |         bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); | ||||||
|         bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); |         bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); | ||||||
|         bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); |         bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); | ||||||
|         bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print); |         bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config); | ||||||
|         bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model); |         bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) |     bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) | ||||||
|     { |     { | ||||||
|         clear_errors(); |         clear_errors(); | ||||||
|         return _save_model_to_file(filename, model, print, export_print_config); |         return _save_model_to_file(filename, model, config); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) |     bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) | ||||||
|     { |     { | ||||||
|         mz_zip_archive archive; |         mz_zip_archive archive; | ||||||
|         mz_zip_zero_struct(&archive); |         mz_zip_zero_struct(&archive); | ||||||
|  | @ -1685,9 +1685,9 @@ namespace Slic3r { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // adds slic3r print config file
 |         // adds slic3r print config file
 | ||||||
|         if (export_print_config) |         if (config != nullptr) | ||||||
|         { |         { | ||||||
|             if (!_add_print_config_file_to_archive(archive, print)) |             if (!_add_print_config_file_to_archive(archive, *config)) | ||||||
|             { |             { | ||||||
|                 mz_zip_writer_end(&archive); |                 mz_zip_writer_end(&archive); | ||||||
|                 boost::filesystem::remove(filename); |                 boost::filesystem::remove(filename); | ||||||
|  | @ -2017,13 +2017,15 @@ namespace Slic3r { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print) |     bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config) | ||||||
|     { |     { | ||||||
|         char buffer[1024]; |         char buffer[1024]; | ||||||
|         sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str()); |         sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str()); | ||||||
|         std::string out = buffer; |         std::string out = buffer; | ||||||
| 
 | 
 | ||||||
|         GCode::append_full_config(print, out); |         for (const std::string &key : config.keys()) | ||||||
|  |             if (key != "compatible_printers") | ||||||
|  |                 out += "; " + key + " = " + config.serialize(key) + "\n"; | ||||||
| 
 | 
 | ||||||
|         if (!out.empty()) |         if (!out.empty()) | ||||||
|         { |         { | ||||||
|  | @ -2123,13 +2125,13 @@ namespace Slic3r { | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config) |     bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config) | ||||||
|     { |     { | ||||||
|         if ((path == nullptr) || (model == nullptr) || (print == nullptr)) |         if ((path == nullptr) || (model == nullptr)) | ||||||
|             return false; |             return false; | ||||||
| 
 | 
 | ||||||
|         _3MF_Exporter exporter; |         _3MF_Exporter exporter; | ||||||
|         bool res = exporter.save_model_to_file(path, *model, *print, export_print_config); |         bool res = exporter.save_model_to_file(path, *model, config); | ||||||
| 
 | 
 | ||||||
|         if (!res) |         if (!res) | ||||||
|             exporter.log_errors(); |             exporter.log_errors(); | ||||||
|  |  | ||||||
|  | @ -4,14 +4,14 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|     class Model; |     class Model; | ||||||
|     class Print; |     class DynamicPrintConfig; | ||||||
| 
 | 
 | ||||||
|     // Load the content of a 3mf file into the given model and preset bundle.
 |     // Load the content of a 3mf file into the given model and preset bundle.
 | ||||||
|     extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model); |     extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model); | ||||||
| 
 | 
 | ||||||
|     // Save the given model and the config data contained in the given Print into a 3mf file.
 |     // Save the given model and the config data contained in the given Print into a 3mf file.
 | ||||||
|     // The model could be modified during the export process if meshes are not repaired or have no shared vertices
 |     // The model could be modified during the export process if meshes are not repaired or have no shared vertices
 | ||||||
|     extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config); |     extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config); | ||||||
| 
 | 
 | ||||||
| }; // namespace Slic3r
 | }; // namespace Slic3r
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -834,9 +834,9 @@ bool load_amf(const char *path, DynamicPrintConfig *config, Model *model) | ||||||
|         return false; |         return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool store_amf(const char *path, Model *model, Print* print, bool export_print_config) | bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) | ||||||
| { | { | ||||||
|     if ((path == nullptr) || (model == nullptr) || (print == nullptr)) |     if ((path == nullptr) || (model == nullptr)) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     // forces ".zip.amf" extension
 |     // forces ".zip.amf" extension
 | ||||||
|  | @ -857,11 +857,13 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c | ||||||
|     stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n"; |     stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n"; | ||||||
|     stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n"; |     stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n"; | ||||||
| 
 | 
 | ||||||
|     if (export_print_config) |     if (config != nullptr) | ||||||
|     { |     { | ||||||
|         std::string config = "\n"; |         std::string str_config = "\n"; | ||||||
|         GCode::append_full_config(*print, config); |         for (const std::string &key : config->keys()) | ||||||
|         stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(config) << "</metadata>\n"; |             if (key != "compatible_printers") | ||||||
|  |                 str_config += "; " + key + " = " + config->serialize(key) + "\n"; | ||||||
|  |         stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (const auto &material : model->materials) { |     for (const auto &material : model->materials) { | ||||||
|  |  | ||||||
|  | @ -4,14 +4,14 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| class Model; | class Model; | ||||||
| class Print; | class DynamicPrintConfig; | ||||||
| 
 | 
 | ||||||
| // Load the content of an amf file into the given model and configuration.
 | // Load the content of an amf file into the given model and configuration.
 | ||||||
| extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model); | extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model); | ||||||
| 
 | 
 | ||||||
| // Save the given model and the config data contained in the given Print into an amf file.
 | // Save the given model and the config data into an amf file.
 | ||||||
| // The model could be modified during the export process if meshes are not repaired or have no shared vertices
 | // The model could be modified during the export process if meshes are not repaired or have no shared vertices
 | ||||||
| extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config); | extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config); | ||||||
| 
 | 
 | ||||||
| }; // namespace Slic3r
 | }; // namespace Slic3r
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -445,7 +445,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ | ||||||
|             boost::nowide::remove(path_tmp.c_str()); |             boost::nowide::remove(path_tmp.c_str()); | ||||||
|             throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); |             throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); | ||||||
|         } |         } | ||||||
|     } catch (std::exception &ex) { |     } catch (std::exception & /* ex */) { | ||||||
|         // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
 |         // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
 | ||||||
|         // Close and remove the file.
 |         // Close and remove the file.
 | ||||||
|         fclose(file); |         fclose(file); | ||||||
|  | @ -601,15 +601,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) | ||||||
|     this->apply_print_config(print.config()); |     this->apply_print_config(print.config()); | ||||||
|     this->set_extruders(print.extruders()); |     this->set_extruders(print.extruders()); | ||||||
|      |      | ||||||
|  |     // Initialize colorprint.
 | ||||||
|  |     m_colorprint_heights = cast<float>(print.config().colorprint_heights.values); | ||||||
|  | 
 | ||||||
|     // Initialize autospeed.
 |     // Initialize autospeed.
 | ||||||
|     { |     { | ||||||
|         // get the minimum cross-section used in the print
 |         // get the minimum cross-section used in the print
 | ||||||
|         std::vector<double> mm3_per_mm; |         std::vector<double> mm3_per_mm; | ||||||
|         for (auto object : printable_objects) { |         for (auto object : printable_objects) { | ||||||
|             for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) { |             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { | ||||||
|                 auto region = print.regions()[region_id]; |                 const PrintRegion* region = print.regions()[region_id]; | ||||||
|                 for (auto layer : object->layers()) { |                 for (auto layer : object->layers()) { | ||||||
|                     auto layerm = layer->regions()[region_id]; |                     const LayerRegion* layerm = layer->regions()[region_id]; | ||||||
|                     if (region->config().get_abs_value("perimeter_speed"          ) == 0 ||  |                     if (region->config().get_abs_value("perimeter_speed"          ) == 0 ||  | ||||||
|                         region->config().get_abs_value("small_perimeter_speed"    ) == 0 ||  |                         region->config().get_abs_value("small_perimeter_speed"    ) == 0 ||  | ||||||
|                         region->config().get_abs_value("external_perimeter_speed" ) == 0 ||  |                         region->config().get_abs_value("external_perimeter_speed" ) == 0 ||  | ||||||
|  | @ -673,8 +676,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) | ||||||
|     const PrintObject *first_object         = printable_objects.front(); |     const PrintObject *first_object         = printable_objects.front(); | ||||||
|     const double       layer_height         = first_object->config().layer_height.value; |     const double       layer_height         = first_object->config().layer_height.value; | ||||||
|     const double       first_layer_height   = first_object->config().first_layer_height.get_abs_value(layer_height); |     const double       first_layer_height   = first_object->config().first_layer_height.get_abs_value(layer_height); | ||||||
|     for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { |     for (const PrintRegion* region : print.regions()) { | ||||||
|         auto region = print.regions()[region_id]; |  | ||||||
|         _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); |         _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); | ||||||
|         _write_format(file, "; perimeters extrusion width = %.2fmm\n",          region->flow(frPerimeter,         layer_height, false, false, -1., *first_object).width); |         _write_format(file, "; perimeters extrusion width = %.2fmm\n",          region->flow(frPerimeter,         layer_height, false, false, -1., *first_object).width); | ||||||
|         _write_format(file, "; infill extrusion width = %.2fmm\n",              region->flow(frInfill,            layer_height, false, false, -1., *first_object).width); |         _write_format(file, "; infill extrusion width = %.2fmm\n",              region->flow(frInfill,            layer_height, false, false, -1., *first_object).width); | ||||||
|  | @ -1319,6 +1321,18 @@ void GCode::process_layer( | ||||||
|         m_second_layer_things_done = true; |         m_second_layer_things_done = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Let's issue a filament change command if requested at this layer.
 | ||||||
|  |     // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all.
 | ||||||
|  |     // (Layers can be close to each other, model could have been resliced with bigger layer height, ...).
 | ||||||
|  |     bool colorprint_change = false; | ||||||
|  |     while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { | ||||||
|  |         m_colorprint_heights.erase(m_colorprint_heights.begin()); | ||||||
|  |         colorprint_change = true; | ||||||
|  |     } | ||||||
|  |     if (colorprint_change) | ||||||
|  |         gcode += "M600\n"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     // Extrude skirt at the print_z of the raft layers and normal object layers
 |     // Extrude skirt at the print_z of the raft layers and normal object layers
 | ||||||
|     // not at the print_z of the interlaced support material layers.
 |     // not at the print_z of the interlaced support material layers.
 | ||||||
|     bool extrude_skirt =  |     bool extrude_skirt =  | ||||||
|  | @ -1442,7 +1456,7 @@ void GCode::process_layer( | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { |             for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { | ||||||
|                 const LayerRegion *layerm = layer.regions()[region_id]; |                 const LayerRegion *layerm = (region_id < layer.regions().size()) ? layer.regions()[region_id] : nullptr; | ||||||
|                 if (layerm == nullptr) |                 if (layerm == nullptr) | ||||||
|                     continue; |                     continue; | ||||||
|                 const PrintRegion ®ion = *print.regions()[region_id]; |                 const PrintRegion ®ion = *print.regions()[region_id]; | ||||||
|  |  | ||||||
|  | @ -317,6 +317,9 @@ protected: | ||||||
|     bool                                m_second_layer_things_done; |     bool                                m_second_layer_things_done; | ||||||
|     // Index of a last object copy extruded.
 |     // Index of a last object copy extruded.
 | ||||||
|     std::pair<const PrintObject*, Point> m_last_obj_copy; |     std::pair<const PrintObject*, Point> m_last_obj_copy; | ||||||
|  |     // Layer heights for colorprint - updated before the export and erased during the process
 | ||||||
|  |     // so no toolchange occurs twice.
 | ||||||
|  |     std::vector<float> m_colorprint_heights; | ||||||
| 
 | 
 | ||||||
|     // Time estimators
 |     // Time estimators
 | ||||||
|     GCodeTimeEstimator m_normal_time_estimator; |     GCodeTimeEstimator m_normal_time_estimator; | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object) | ||||||
|     for (auto layer : object.layers()) { |     for (auto layer : object.layers()) { | ||||||
|         LayerTools &layer_tools = this->tools_for_layer(layer->print_z); |         LayerTools &layer_tools = this->tools_for_layer(layer->print_z); | ||||||
|         // What extruders are required to print this object layer?
 |         // What extruders are required to print this object layer?
 | ||||||
|         for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) { |         for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { | ||||||
| 			const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; | 			const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; | ||||||
|             if (layerm == nullptr) |             if (layerm == nullptr) | ||||||
|                 continue; |                 continue; | ||||||
|  | @ -479,7 +479,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int | ||||||
| 
 | 
 | ||||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 |         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||||
| 
 | 
 | ||||||
|             for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) { |             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { | ||||||
|                 const auto& region = *object->print()->regions()[region_id]; |                 const auto& region = *object->print()->regions()[region_id]; | ||||||
| 
 | 
 | ||||||
|                 if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) |                 if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) | ||||||
|  | @ -557,7 +557,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) | ||||||
|         unsigned int num_of_copies = object->copies().size(); |         unsigned int num_of_copies = object->copies().size(); | ||||||
| 
 | 
 | ||||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 |         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||||
|             for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) { |             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { | ||||||
|                 const auto& region = *object->print()->regions()[region_id]; |                 const auto& region = *object->print()->regions()[region_id]; | ||||||
| 
 | 
 | ||||||
|                 if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) |                 if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) | ||||||
|  |  | ||||||
|  | @ -32,9 +32,8 @@ void Layer::make_slices() | ||||||
|         slices = m_regions.front()->slices; |         slices = m_regions.front()->slices; | ||||||
|     } else { |     } else { | ||||||
|         Polygons slices_p; |         Polygons slices_p; | ||||||
|         FOREACH_LAYERREGION(this, layerm) { |         for (LayerRegion *layerm : m_regions) | ||||||
|             polygons_append(slices_p, to_polygons((*layerm)->slices)); |             polygons_append(slices_p, to_polygons(layerm->slices)); | ||||||
|         } |  | ||||||
|         slices = union_ex(slices_p); |         slices = union_ex(slices_p); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  | @ -53,7 +52,7 @@ void Layer::make_slices() | ||||||
|      |      | ||||||
|     // populate slices vector
 |     // populate slices vector
 | ||||||
|     for (size_t i : order) |     for (size_t i : order) | ||||||
|         this->slices.expolygons.push_back(STDMOVE(slices[i])); |         this->slices.expolygons.push_back(std::move(slices[i])); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Layer::merge_slices() | void Layer::merge_slices() | ||||||
|  | @ -63,10 +62,9 @@ void Layer::merge_slices() | ||||||
|         // but use the non-split islands of a layer. For a single region print, these shall be equal.
 |         // but use the non-split islands of a layer. For a single region print, these shall be equal.
 | ||||||
|         m_regions.front()->slices.set(this->slices.expolygons, stInternal); |         m_regions.front()->slices.set(this->slices.expolygons, stInternal); | ||||||
|     } else { |     } else { | ||||||
|         FOREACH_LAYERREGION(this, layerm) { |         for (LayerRegion *layerm : m_regions) | ||||||
|             // without safety offset, artifacts are generated (GH #2494)
 |             // without safety offset, artifacts are generated (GH #2494)
 | ||||||
|             (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal); |             layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +78,7 @@ void Layer::make_perimeters() | ||||||
|     // keep track of regions whose perimeters we have already generated
 |     // keep track of regions whose perimeters we have already generated
 | ||||||
|     std::set<size_t> done; |     std::set<size_t> done; | ||||||
|      |      | ||||||
|     FOREACH_LAYERREGION(this, layerm) { |     for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) { | ||||||
|         size_t region_id = layerm - m_regions.begin(); |         size_t region_id = layerm - m_regions.begin(); | ||||||
|         if (done.find(region_id) != done.end()) continue; |         if (done.find(region_id) != done.end()) continue; | ||||||
|         BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; |         BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; | ||||||
|  | @ -137,7 +135,7 @@ void Layer::make_perimeters() | ||||||
|                     // Separate the fill surfaces.
 |                     // Separate the fill surfaces.
 | ||||||
|                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); |                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); | ||||||
|                     (*l)->fill_expolygons = expp; |                     (*l)->fill_expolygons = expp; | ||||||
|                     (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front()); |                     (*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front()); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() | ||||||
|     // in place. However we're now only using its boundaries (which are invariant)
 |     // in place. However we're now only using its boundaries (which are invariant)
 | ||||||
|     // so we're safe. This guarantees idempotence of prepare_infill() also in case
 |     // so we're safe. This guarantees idempotence of prepare_infill() also in case
 | ||||||
|     // that combine_infill() turns some fill_surface into VOID surfaces.
 |     // that combine_infill() turns some fill_surface into VOID surfaces.
 | ||||||
| //    Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces));
 | //    Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces));
 | ||||||
|     Polygons fill_boundaries = to_polygons(this->fill_expolygons); |     Polygons fill_boundaries = to_polygons(this->fill_expolygons); | ||||||
|     // Collect polygons per surface type.
 |     // Collect polygons per surface type.
 | ||||||
|     std::vector<Polygons> polygons_by_surface; |     std::vector<Polygons> polygons_by_surface; | ||||||
|  | @ -133,9 +133,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                 if (internal_surface) |                 if (internal_surface) | ||||||
|                     // Make a copy as the following line uses the move semantics.
 |                     // Make a copy as the following line uses the move semantics.
 | ||||||
|                     internal.push_back(surface); |                     internal.push_back(surface); | ||||||
|                 polygons_append(fill_boundaries, STDMOVE(surface.expolygon)); |                 polygons_append(fill_boundaries, std::move(surface.expolygon)); | ||||||
|             } else if (internal_surface) |             } else if (internal_surface) | ||||||
|                 internal.push_back(STDMOVE(surface)); |                 internal.push_back(std::move(surface)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -192,7 +192,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                     polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island])); |                     polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island])); | ||||||
|                 } |                 } | ||||||
|                 bridge_bboxes.push_back(get_extents(polys)); |                 bridge_bboxes.push_back(get_extents(polys)); | ||||||
|                 bridges_grown.push_back(STDMOVE(polys)); |                 bridges_grown.push_back(std::move(polys)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -243,7 +243,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                 for (size_t i = 0; i < bridges.size(); ++ i) { |                 for (size_t i = 0; i < bridges.size(); ++ i) { | ||||||
|                     if (bridge_group[i] != group_id) |                     if (bridge_group[i] != group_id) | ||||||
|                         continue; |                         continue; | ||||||
|                     initial.push_back(STDMOVE(bridges[i].expolygon)); |                     initial.push_back(std::move(bridges[i].expolygon)); | ||||||
|                     polygons_append(grown, bridges_grown[i]); |                     polygons_append(grown, bridges_grown[i]); | ||||||
|                 } |                 } | ||||||
|                 // detect bridge direction before merging grown surfaces otherwise adjacent bridges
 |                 // detect bridge direction before merging grown surfaces otherwise adjacent bridges
 | ||||||
|  | @ -269,7 +269,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                 surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]); |                 surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             fill_boundaries = STDMOVE(to_polygons(fill_boundaries_ex)); |             fill_boundaries = std::move(to_polygons(fill_boundaries_ex)); | ||||||
| 			BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; | 			BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -284,7 +284,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|     Surfaces new_surfaces; |     Surfaces new_surfaces; | ||||||
|     { |     { | ||||||
|         // Merge top and bottom in a single collection.
 |         // Merge top and bottom in a single collection.
 | ||||||
|         surfaces_append(top, STDMOVE(bottom)); |         surfaces_append(top, std::move(bottom)); | ||||||
|         // Intersect the grown surfaces with the actual fill boundaries.
 |         // Intersect the grown surfaces with the actual fill boundaries.
 | ||||||
|         Polygons bottom_polygons = to_polygons(bottom); |         Polygons bottom_polygons = to_polygons(bottom); | ||||||
|         for (size_t i = 0; i < top.size(); ++ i) { |         for (size_t i = 0; i < top.size(); ++ i) { | ||||||
|  | @ -292,11 +292,11 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|             if (s1.empty()) |             if (s1.empty()) | ||||||
|                 continue; |                 continue; | ||||||
|             Polygons polys; |             Polygons polys; | ||||||
|             polygons_append(polys, STDMOVE(s1)); |             polygons_append(polys, std::move(s1)); | ||||||
|             for (size_t j = i + 1; j < top.size(); ++ j) { |             for (size_t j = i + 1; j < top.size(); ++ j) { | ||||||
|                 Surface &s2 = top[j]; |                 Surface &s2 = top[j]; | ||||||
|                 if (! s2.empty() && surfaces_could_merge(s1, s2)) { |                 if (! s2.empty() && surfaces_could_merge(s1, s2)) { | ||||||
|                     polygons_append(polys, STDMOVE(s2)); |                     polygons_append(polys, std::move(s2)); | ||||||
|                     s2.clear(); |                     s2.clear(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -306,7 +306,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|             surfaces_append( |             surfaces_append( | ||||||
|                 new_surfaces, |                 new_surfaces, | ||||||
|                 // Don't use a safety offset as fill_boundaries were already united using the safety offset.
 |                 // Don't use a safety offset as fill_boundaries were already united using the safety offset.
 | ||||||
|                 STDMOVE(intersection_ex(polys, fill_boundaries, false)), |                 std::move(intersection_ex(polys, fill_boundaries, false)), | ||||||
|                 s1); |                 s1); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -318,20 +318,20 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|         if (s1.empty()) |         if (s1.empty()) | ||||||
|             continue; |             continue; | ||||||
|         Polygons polys; |         Polygons polys; | ||||||
|         polygons_append(polys, STDMOVE(s1)); |         polygons_append(polys, std::move(s1)); | ||||||
|         for (size_t j = i + 1; j < internal.size(); ++ j) { |         for (size_t j = i + 1; j < internal.size(); ++ j) { | ||||||
|             Surface &s2 = internal[j]; |             Surface &s2 = internal[j]; | ||||||
|             if (! s2.empty() && surfaces_could_merge(s1, s2)) { |             if (! s2.empty() && surfaces_could_merge(s1, s2)) { | ||||||
|                 polygons_append(polys, STDMOVE(s2)); |                 polygons_append(polys, std::move(s2)); | ||||||
|                 s2.clear(); |                 s2.clear(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         ExPolygons new_expolys = diff_ex(polys, new_polygons); |         ExPolygons new_expolys = diff_ex(polys, new_polygons); | ||||||
|         polygons_append(new_polygons, to_polygons(new_expolys)); |         polygons_append(new_polygons, to_polygons(new_expolys)); | ||||||
|         surfaces_append(new_surfaces, STDMOVE(new_expolys), s1); |         surfaces_append(new_surfaces, std::move(new_expolys), s1); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     this->fill_surfaces.surfaces = STDMOVE(new_surfaces); |     this->fill_surfaces.surfaces = std::move(new_surfaces); | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final"); |     export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final"); | ||||||
|  |  | ||||||
|  | @ -21,26 +21,58 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| unsigned int Model::s_auto_extruder_id = 1; | unsigned int Model::s_auto_extruder_id = 1; | ||||||
| 
 | 
 | ||||||
| ModelID ModelBase::s_last_id = 0; | size_t ModelBase::s_last_id = 0; | ||||||
| 
 | 
 | ||||||
| Model::Model(const Model &rhs) | Model& Model::assign_copy(const Model &rhs) | ||||||
| { | { | ||||||
|     *this = rhs; |     this->copy_id(rhs); | ||||||
|  |     // copy materials
 | ||||||
|  |     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); | ||||||
|  |     } | ||||||
|  |     // copy objects
 | ||||||
|  |     this->clear_objects(); | ||||||
|  |     this->objects.reserve(rhs.objects.size()); | ||||||
|  | 	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); | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Model& Model::operator=(const Model &rhs) | Model& Model::assign_copy(Model &&rhs) | ||||||
| { | { | ||||||
|     m_id = rhs.m_id; |     this->copy_id(rhs); | ||||||
|     // copy materials
 | 	// Move materials, adjust the parent pointer.
 | ||||||
|     for (const auto &m : rhs.materials) |     this->clear_materials(); | ||||||
|         this->add_material(m.first, *m.second); |     this->materials = std::move(rhs.materials); | ||||||
|     // copy objects
 |     for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials) | ||||||
|     this->objects.reserve(rhs.objects.size()); |         m.second->set_model(this); | ||||||
|     for (const ModelObject *o : rhs.objects) |     rhs.materials.clear(); | ||||||
|         this->add_object(*o, true); |     // Move objects, adjust the parent pointer.
 | ||||||
|  |     this->clear_objects(); | ||||||
|  | 	this->objects = std::move(rhs.objects); | ||||||
|  |     for (ModelObject *model_object : this->objects) | ||||||
|  |         model_object->set_model(this); | ||||||
|  | 	rhs.objects.clear(); | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) | Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) | ||||||
| { | { | ||||||
|     Model model; |     Model model; | ||||||
|  | @ -150,9 +182,10 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh | ||||||
|     return new_object; |     return new_object; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ModelObject* Model::add_object(const ModelObject &other, bool copy_volumes) | ModelObject* Model::add_object(const ModelObject &other) | ||||||
| { | { | ||||||
|     ModelObject* new_object = new ModelObject(this, other, copy_volumes); | 	ModelObject* new_object = ModelObject::new_clone(other); | ||||||
|  |     new_object->set_model(this); | ||||||
|     this->objects.push_back(new_object); |     this->objects.push_back(new_object); | ||||||
|     return new_object; |     return new_object; | ||||||
| } | } | ||||||
|  | @ -164,21 +197,36 @@ void Model::delete_object(size_t idx) | ||||||
|     this->objects.erase(i); |     this->objects.erase(i); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Model::delete_object(ModelObject* object) | bool Model::delete_object(ModelObject* object) | ||||||
| { | { | ||||||
|     if (object == nullptr) |     if (object != nullptr) { | ||||||
|         return; |         size_t idx = 0; | ||||||
|  |         for (ModelObject *model_object : objects) { | ||||||
|  |             if (model_object == object) { | ||||||
|  |                 delete model_object; | ||||||
|  |                 objects.erase(objects.begin() + idx); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             ++ idx; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it) | bool Model::delete_object(ModelID id) | ||||||
|     { | { | ||||||
|         ModelObject* obj = *it; |     if (id.id != 0) { | ||||||
|         if (obj == object) |         size_t idx = 0; | ||||||
|         { |         for (ModelObject *model_object : objects) { | ||||||
|             delete obj; |             if (model_object->id() == id) { | ||||||
|             objects.erase(it); |                 delete model_object; | ||||||
|             return; |                 objects.erase(objects.begin() + idx); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             ++ idx; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Model::clear_objects() | void Model::clear_objects() | ||||||
|  | @ -206,6 +254,7 @@ void Model::clear_materials() | ||||||
| 
 | 
 | ||||||
| ModelMaterial* Model::add_material(t_model_material_id material_id) | ModelMaterial* Model::add_material(t_model_material_id material_id) | ||||||
| { | { | ||||||
|  |     assert(! material_id.empty()); | ||||||
|     ModelMaterial* material = this->get_material(material_id); |     ModelMaterial* material = this->get_material(material_id); | ||||||
|     if (material == nullptr) |     if (material == nullptr) | ||||||
|         material = this->materials[material_id] = new ModelMaterial(this); |         material = this->materials[material_id] = new ModelMaterial(this); | ||||||
|  | @ -214,11 +263,13 @@ ModelMaterial* Model::add_material(t_model_material_id material_id) | ||||||
| 
 | 
 | ||||||
| ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other) | ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other) | ||||||
| { | { | ||||||
|  |     assert(! material_id.empty()); | ||||||
|     // delete existing material if any
 |     // delete existing material if any
 | ||||||
|     ModelMaterial* material = this->get_material(material_id); |     ModelMaterial* material = this->get_material(material_id); | ||||||
|     delete material; |     delete material; | ||||||
|     // set new material
 |     // set new material
 | ||||||
|     material = new ModelMaterial(this, other); | 	material = new ModelMaterial(other); | ||||||
|  | 	material->set_model(this); | ||||||
|     this->materials[material_id] = material; |     this->materials[material_id] = material; | ||||||
|     return material; |     return material; | ||||||
| } | } | ||||||
|  | @ -421,6 +472,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) | ||||||
|     ModelObject* object = new ModelObject(this); |     ModelObject* object = new ModelObject(this); | ||||||
|     object->input_file = this->objects.front()->input_file; |     object->input_file = this->objects.front()->input_file; | ||||||
|     object->name = this->objects.front()->name; |     object->name = this->objects.front()->name; | ||||||
|  |     //FIXME copy the config etc?
 | ||||||
| 
 | 
 | ||||||
|     reset_auto_extruder_id(); |     reset_auto_extruder_id(); | ||||||
| 
 | 
 | ||||||
|  | @ -483,53 +535,90 @@ void Model::reset_auto_extruder_id() | ||||||
|     s_auto_extruder_id = 1; |     s_auto_extruder_id = 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ModelObject::ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes) : |  | ||||||
|     m_model(model) |  | ||||||
| { |  | ||||||
|     this->assign(&rhs, copy_volumes); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ModelObject::~ModelObject() | ModelObject::~ModelObject() | ||||||
| { | { | ||||||
|     this->clear_volumes(); |     this->clear_volumes(); | ||||||
|     this->clear_instances(); |     this->clear_instances(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
 | // maintains the m_model pointer
 | ||||||
| // Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
 | ModelObject& ModelObject::assign_copy(const ModelObject &rhs) | ||||||
| ModelObject* ModelObject::clone(Model *parent) |  | ||||||
| { | { | ||||||
|     return new ModelObject(parent, *this, true); |     this->copy_id(rhs); | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| ModelObject& ModelObject::assign(const ModelObject *rhs, bool copy_volumes) |     this->name                        = rhs.name; | ||||||
| {  |     this->input_file                  = rhs.input_file; | ||||||
|     m_id                        = rhs->m_id; |     this->config                      = rhs.config; | ||||||
|     name                        = rhs->name; |     this->sla_support_points          = rhs.sla_support_points; | ||||||
|     input_file                  = rhs->input_file; |     this->layer_height_ranges         = rhs.layer_height_ranges; | ||||||
|     config                      = rhs->config; |     this->layer_height_profile        = rhs.layer_height_profile; | ||||||
|     sla_support_points          = rhs->sla_support_points; |     this->layer_height_profile_valid  = rhs.layer_height_profile_valid; | ||||||
|     layer_height_ranges         = rhs->layer_height_ranges; |     this->origin_translation          = rhs.origin_translation; | ||||||
|     layer_height_profile        = rhs->layer_height_profile; |     m_bounding_box                    = rhs.m_bounding_box; | ||||||
|     layer_height_profile_valid  = rhs->layer_height_profile_valid; |     m_bounding_box_valid              = rhs.m_bounding_box_valid; | ||||||
|     origin_translation          = rhs->origin_translation; |  | ||||||
|     m_bounding_box              = rhs->m_bounding_box; |  | ||||||
|     m_bounding_box_valid        = rhs->m_bounding_box_valid; |  | ||||||
| 
 | 
 | ||||||
|     volumes.clear(); |     this->clear_volumes(); | ||||||
|     instances.clear(); |     this->volumes.reserve(rhs.volumes.size()); | ||||||
|     if (copy_volumes) { |     for (ModelVolume *model_volume : rhs.volumes) { | ||||||
|         this->volumes.reserve(rhs->volumes.size()); |         this->volumes.emplace_back(new ModelVolume(*model_volume)); | ||||||
|         for (ModelVolume *model_volume : rhs->volumes) |         this->volumes.back()->set_model_object(this); | ||||||
|             this->add_volume(*model_volume); |     } | ||||||
|         this->instances.reserve(rhs->instances.size()); |     this->clear_instances(); | ||||||
|         for (const ModelInstance *model_instance : rhs->instances) | 	this->instances.reserve(rhs.instances.size()); | ||||||
|             this->add_instance(*model_instance); |     for (const ModelInstance *model_instance : rhs.instances) { | ||||||
|  |         this->instances.emplace_back(new ModelInstance(*model_instance)); | ||||||
|  |         this->instances.back()->set_model_object(this); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // maintains the m_model pointer
 | ||||||
|  | ModelObject& ModelObject::assign_copy(ModelObject &&rhs) | ||||||
|  | { | ||||||
|  |     this->copy_id(rhs); | ||||||
|  | 
 | ||||||
|  |     this->name                        = std::move(rhs.name); | ||||||
|  |     this->input_file                  = std::move(rhs.input_file); | ||||||
|  |     this->config                      = std::move(rhs.config); | ||||||
|  |     this->sla_support_points          = std::move(rhs.sla_support_points); | ||||||
|  |     this->layer_height_ranges         = std::move(rhs.layer_height_ranges); | ||||||
|  |     this->layer_height_profile        = std::move(rhs.layer_height_profile); | ||||||
|  |     this->layer_height_profile_valid  = std::move(rhs.layer_height_profile_valid); | ||||||
|  |     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); | ||||||
|  | 
 | ||||||
|  |     this->clear_volumes(); | ||||||
|  | 	this->volumes = std::move(rhs.volumes); | ||||||
|  | 	rhs.volumes.clear(); | ||||||
|  |     for (ModelVolume *model_volume : this->volumes) | ||||||
|  |         model_volume->set_model_object(this); | ||||||
|  |     this->clear_instances(); | ||||||
|  | 	this->instances = std::move(rhs.instances); | ||||||
|  | 	rhs.instances.clear(); | ||||||
|  |     for (ModelInstance *model_instance : this->instances) | ||||||
|  |         model_instance->set_model_object(this); | ||||||
|  | 
 | ||||||
|  | 	return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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);
 | ||||||
|  | //}
 | ||||||
|  | 
 | ||||||
| ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) | ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) | ||||||
| { | { | ||||||
|     ModelVolume* v = new ModelVolume(this, mesh); |     ModelVolume* v = new ModelVolume(this, mesh); | ||||||
|  | @ -554,6 +643,14 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other) | ||||||
|     return v; |     return v; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh) | ||||||
|  | { | ||||||
|  |     ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); | ||||||
|  |     this->volumes.push_back(v); | ||||||
|  |     this->invalidate_bounding_box(); | ||||||
|  |     return v; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ModelObject::delete_volume(size_t idx) | void ModelObject::delete_volume(size_t idx) | ||||||
| { | { | ||||||
|     ModelVolumePtrs::iterator i = this->volumes.begin() + idx; |     ModelVolumePtrs::iterator i = this->volumes.begin() + idx; | ||||||
|  | @ -624,8 +721,16 @@ const BoundingBoxf3& ModelObject::bounding_box() const | ||||||
|         BoundingBoxf3 raw_bbox; |         BoundingBoxf3 raw_bbox; | ||||||
|         for (const ModelVolume *v : this->volumes) |         for (const ModelVolume *v : this->volumes) | ||||||
|             if (v->is_model_part()) |             if (v->is_model_part()) | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             { | ||||||
|  |                 TriangleMesh m = v->mesh; | ||||||
|  |                 m.transform(v->get_matrix()); | ||||||
|  |                 raw_bbox.merge(m.bounding_box()); | ||||||
|  |             } | ||||||
|  | #else | ||||||
|                 // mesh.bounding_box() returns a cached value.
 |                 // mesh.bounding_box() returns a cached value.
 | ||||||
|                 raw_bbox.merge(v->mesh.bounding_box()); |                 raw_bbox.merge(v->mesh.bounding_box()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         BoundingBoxf3 bb; |         BoundingBoxf3 bb; | ||||||
|         for (const ModelInstance *i : this->instances) |         for (const ModelInstance *i : this->instances) | ||||||
|             bb.merge(i->transform_bounding_box(raw_bbox)); |             bb.merge(i->transform_bounding_box(raw_bbox)); | ||||||
|  | @ -656,7 +761,15 @@ TriangleMesh ModelObject::raw_mesh() const | ||||||
|     TriangleMesh mesh; |     TriangleMesh mesh; | ||||||
|     for (const ModelVolume *v : this->volumes) |     for (const ModelVolume *v : this->volumes) | ||||||
|         if (v->is_model_part()) |         if (v->is_model_part()) | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         { | ||||||
|  |             TriangleMesh vol_mesh(v->mesh); | ||||||
|  |             vol_mesh.transform(v->get_matrix()); | ||||||
|  |             mesh.merge(vol_mesh); | ||||||
|  |         } | ||||||
|  | #else | ||||||
|         mesh.merge(v->mesh); |         mesh.merge(v->mesh); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     return mesh; |     return mesh; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -699,12 +812,14 @@ void ModelObject::center_around_origin() | ||||||
|     this->translate(shift); |     this->translate(shift); | ||||||
|     this->origin_translation += shift; |     this->origin_translation += shift; | ||||||
| 
 | 
 | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     if (!this->instances.empty()) { |     if (!this->instances.empty()) { | ||||||
|         for (ModelInstance *i : this->instances) { |         for (ModelInstance *i : this->instances) { | ||||||
|             i->set_offset(i->get_offset() - shift); |             i->set_offset(i->get_offset() - shift); | ||||||
|         } |         } | ||||||
|         this->invalidate_bounding_box(); |         this->invalidate_bounding_box(); | ||||||
|     } |     } | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModelObject::ensure_on_bed() | void ModelObject::ensure_on_bed() | ||||||
|  | @ -731,8 +846,7 @@ void ModelObject::translate(double x, double y, double z) | ||||||
| { | { | ||||||
|     for (ModelVolume *v : this->volumes) |     for (ModelVolume *v : this->volumes) | ||||||
|     { |     { | ||||||
|         v->mesh.translate(float(x), float(y), float(z)); |         v->translate(x, y, z); | ||||||
|         v->m_convex_hull.translate(float(x), float(y), float(z)); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (m_bounding_box_valid) |     if (m_bounding_box_valid) | ||||||
|  | @ -743,51 +857,55 @@ void ModelObject::scale(const Vec3d &versor) | ||||||
| { | { | ||||||
|     for (ModelVolume *v : this->volumes) |     for (ModelVolume *v : this->volumes) | ||||||
|     { |     { | ||||||
|         v->mesh.scale(versor); |         v->scale(versor); | ||||||
|         v->m_convex_hull.scale(versor); |  | ||||||
|     } |     } | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     // reset origin translation since it doesn't make sense anymore
 |     // reset origin translation since it doesn't make sense anymore
 | ||||||
|     this->origin_translation = Vec3d::Zero(); |     this->origin_translation = Vec3d::Zero(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     this->invalidate_bounding_box(); |     this->invalidate_bounding_box(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModelObject::rotate(float angle, const Axis& axis) | void ModelObject::rotate(double angle, Axis axis) | ||||||
| { | { | ||||||
|     for (ModelVolume *v : this->volumes) |     for (ModelVolume *v : this->volumes) | ||||||
|     { |     { | ||||||
|         v->mesh.rotate(angle, axis); |         v->rotate(angle, axis); | ||||||
|         v->m_convex_hull.rotate(angle, axis); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     center_around_origin(); |     center_around_origin(); | ||||||
| 
 | 
 | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     this->origin_translation = Vec3d::Zero(); |     this->origin_translation = Vec3d::Zero(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     this->invalidate_bounding_box(); |     this->invalidate_bounding_box(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModelObject::rotate(float angle, const Vec3d& axis) | void ModelObject::rotate(double angle, const Vec3d& axis) | ||||||
| { | { | ||||||
|     for (ModelVolume *v : this->volumes) |     for (ModelVolume *v : this->volumes) | ||||||
|     { |     { | ||||||
|         v->mesh.rotate(angle, axis); |         v->rotate(angle, axis); | ||||||
|         v->m_convex_hull.rotate(angle, axis); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     center_around_origin(); |     center_around_origin(); | ||||||
| 
 | 
 | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     this->origin_translation = Vec3d::Zero(); |     this->origin_translation = Vec3d::Zero(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     this->invalidate_bounding_box(); |     this->invalidate_bounding_box(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModelObject::mirror(const Axis &axis) | void ModelObject::mirror(Axis axis) | ||||||
| { | { | ||||||
|     for (ModelVolume *v : this->volumes) |     for (ModelVolume *v : this->volumes) | ||||||
|     { |     { | ||||||
|         v->mesh.mirror(axis); |         v->mirror(axis); | ||||||
|         v->m_convex_hull.mirror(axis); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     this->origin_translation = Vec3d::Zero(); |     this->origin_translation = Vec3d::Zero(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     this->invalidate_bounding_box(); |     this->invalidate_bounding_box(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -864,7 +982,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) | ||||||
|     if (this->volumes.size() > 1) { |     if (this->volumes.size() > 1) { | ||||||
|         // We can't split meshes if there's more than one volume, because
 |         // We can't split meshes if there's more than one volume, because
 | ||||||
|         // we can't group the resulting meshes by object afterwards
 |         // we can't group the resulting meshes by object afterwards
 | ||||||
|         new_objects->push_back(this); |         new_objects->emplace_back(this); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  | @ -876,16 +994,14 @@ void ModelObject::split(ModelObjectPtrs* new_objects) | ||||||
|          |          | ||||||
|         mesh->repair(); |         mesh->repair(); | ||||||
|          |          | ||||||
|         ModelObject* new_object = m_model->add_object(*this, false); |         ModelObject* new_object = m_model->add_object(); | ||||||
|         new_object->sla_support_points.clear(); | 		new_object->name   = this->name; | ||||||
|         new_object->input_file  = ""; | 		new_object->config = this->config; | ||||||
|         ModelVolume* new_volume = new_object->add_volume(*mesh); | 		new_object->instances.reserve(this->instances.size()); | ||||||
|         new_volume->name        = volume->name; | 		for (const ModelInstance *model_instance : this->instances) | ||||||
|         new_volume->config      = volume->config; | 			new_object->add_instance(*model_instance); | ||||||
|         new_volume->set_type(volume->type()); |         new_object->add_volume(*volume, std::move(*mesh)); | ||||||
|         new_volume->set_material_id(volume->material_id()); |         new_objects->emplace_back(new_object); | ||||||
|          |  | ||||||
|         new_objects->push_back(new_object); |  | ||||||
|         delete mesh; |         delete mesh; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  | @ -918,17 +1034,32 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const | ||||||
|     double min_z = DBL_MAX; |     double min_z = DBL_MAX; | ||||||
| 
 | 
 | ||||||
|     ModelInstance* inst = instances[instance_idx]; |     ModelInstance* inst = instances[instance_idx]; | ||||||
|     const Transform3d& m = inst->get_matrix(true); |     const Transform3d& mi = inst->get_matrix(true); | ||||||
| 
 | 
 | ||||||
|     for (ModelVolume *v : volumes) |     for (const ModelVolume* v : volumes) | ||||||
|     { |     { | ||||||
|  |         if (!v->is_model_part()) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         Transform3d mv = mi * v->get_matrix(); | ||||||
|  |         const TriangleMesh& hull = v->get_convex_hull(); | ||||||
|  |         for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) | ||||||
|  |         { | ||||||
|  |             const stl_facet* facet = hull.stl.facet_start + f; | ||||||
|  |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast<double>())); | ||||||
|  |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast<double>())); | ||||||
|  |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>())); | ||||||
|  |         } | ||||||
|  | #else | ||||||
|         for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) |         for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) | ||||||
|         { |         { | ||||||
|             const stl_facet* facet = v->mesh.stl.facet_start + f; |             const stl_facet* facet = v->mesh.stl.facet_start + f; | ||||||
|             min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[0].cast<double>())); |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast<double>())); | ||||||
|             min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[1].cast<double>())); |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast<double>())); | ||||||
|             min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[2].cast<double>())); |             min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast<double>())); | ||||||
|         } |         } | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return min_z + inst->get_offset(Z); |     return min_z + inst->get_offset(Z); | ||||||
|  | @ -945,7 +1076,11 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 | ||||||
|         unsigned int inside_outside = 0; |         unsigned int inside_outside = 0; | ||||||
|         for (const ModelVolume *vol : this->volumes) |         for (const ModelVolume *vol : this->volumes) | ||||||
|             if (vol->is_model_part()) { |             if (vol->is_model_part()) { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |                 BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); | ||||||
|  | #else | ||||||
|                 BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix()); |                 BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|                 if (print_volume.contains(bb)) |                 if (print_volume.contains(bb)) | ||||||
|                     inside_outside |= INSIDE; |                     inside_outside |= INSIDE; | ||||||
|                 else if (print_volume.intersects(bb)) |                 else if (print_volume.intersects(bb)) | ||||||
|  | @ -1007,9 +1142,9 @@ void ModelObject::print_info() const | ||||||
| void ModelVolume::set_material_id(t_model_material_id material_id) | void ModelVolume::set_material_id(t_model_material_id material_id) | ||||||
| { | { | ||||||
|     m_material_id = material_id; |     m_material_id = material_id; | ||||||
|      |  | ||||||
|     // ensure m_material_id references an existing material
 |     // ensure m_material_id references an existing material
 | ||||||
|     (void)this->object->get_model()->add_material(material_id); |     if (! material_id.empty()) | ||||||
|  |         this->object->get_model()->add_material(material_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ModelMaterial* ModelVolume::material() const | ModelMaterial* ModelVolume::material() const | ||||||
|  | @ -1020,17 +1155,17 @@ ModelMaterial* ModelVolume::material() const | ||||||
| void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) | void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) | ||||||
| { | { | ||||||
|     m_material_id = material_id; |     m_material_id = material_id; | ||||||
|     (void)this->object->get_model()->add_material(material_id, material); |     if (! material_id.empty()) | ||||||
|  |         this->object->get_model()->add_material(material_id, material); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ModelMaterial* ModelVolume::assign_unique_material() | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  | void ModelVolume::translate_geometry(const Vec3d& displacement) | ||||||
| { | { | ||||||
|     Model* model = this->get_object()->get_model(); |     mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); | ||||||
|      |     m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); | ||||||
|     // as material-id "0" is reserved by the AMF spec we start from 1
 |  | ||||||
|     m_material_id = 1 + model->materials.size();  // watchout for implicit cast
 |  | ||||||
|     return model->add_material(m_material_id); |  | ||||||
| } | } | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
| void ModelVolume::calculate_convex_hull() | void ModelVolume::calculate_convex_hull() | ||||||
| { | { | ||||||
|  | @ -1108,15 +1243,66 @@ size_t ModelVolume::split(unsigned int max_extruders) | ||||||
|     return idx; |     return idx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ModelVolume::translate(double x, double y, double z) |  | ||||||
| { |  | ||||||
|     translate(Vec3d(x, y, z)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ModelVolume::translate(const Vec3d& displacement) | void ModelVolume::translate(const Vec3d& displacement) | ||||||
| { | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     set_offset(get_offset() + displacement); | ||||||
|  | #else | ||||||
|     mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); |     mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); | ||||||
|     m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); |     m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ModelVolume::scale(const Vec3d& scaling_factors) | ||||||
|  | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors)); | ||||||
|  | #else | ||||||
|  |     mesh.scale(scaling_factors); | ||||||
|  |     m_convex_hull.scale(scaling_factors); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ModelVolume::rotate(double angle, Axis axis) | ||||||
|  | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     switch (axis) | ||||||
|  |     { | ||||||
|  |     case X: { rotate(angle, Vec3d::UnitX()); break; } | ||||||
|  |     case Y: { rotate(angle, Vec3d::UnitY()); break; } | ||||||
|  |     case Z: { rotate(angle, Vec3d::UnitZ()); break; } | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     mesh.rotate(angle, axis); | ||||||
|  |     m_convex_hull.rotate(angle, axis); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ModelVolume::rotate(double angle, const Vec3d& axis) | ||||||
|  | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix())); | ||||||
|  | #else | ||||||
|  |     mesh.rotate(angle, axis); | ||||||
|  |     m_convex_hull.rotate(angle, axis); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ModelVolume::mirror(Axis axis) | ||||||
|  | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     Vec3d mirror = get_mirror(); | ||||||
|  |     switch (axis) | ||||||
|  |     { | ||||||
|  |     case X: { mirror(0) *= -1.0; break; } | ||||||
|  |     case Y: { mirror(1) *= -1.0; break; } | ||||||
|  |     case Z: { mirror(2) *= -1.0; break; } | ||||||
|  |     } | ||||||
|  |     set_mirror(mirror); | ||||||
|  | #else | ||||||
|  |     mesh.mirror(axis); | ||||||
|  |     m_convex_hull.mirror(axis); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_MODELVOLUME_TRANSFORM | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  | @ -1174,14 +1360,14 @@ void ModelInstance::set_mirror(Axis axis, double mirror) | ||||||
| 
 | 
 | ||||||
| void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const | void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const | ||||||
| { | { | ||||||
|     mesh->transform(get_matrix(dont_translate).cast<float>()); |     mesh->transform(get_matrix(dont_translate)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const | BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const | ||||||
| { | { | ||||||
|     // Rotate around mesh origin.
 |     // Rotate around mesh origin.
 | ||||||
|     TriangleMesh copy(*mesh); |     TriangleMesh copy(*mesh); | ||||||
|     copy.transform(get_matrix(true, false, true, true).cast<float>()); |     copy.transform(get_matrix(true, false, true, true)); | ||||||
|     BoundingBoxf3 bbox = copy.bounding_box(); |     BoundingBoxf3 bbox = copy.bounding_box(); | ||||||
| 
 | 
 | ||||||
|     if (!empty(bbox)) { |     if (!empty(bbox)) { | ||||||
|  | @ -1189,10 +1375,10 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes | ||||||
|         for (unsigned int i = 0; i < 3; ++i) |         for (unsigned int i = 0; i < 3; ++i) | ||||||
|         { |         { | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|             if (std::abs(m_transformation.get_scaling_factor((Axis)i)-1.0) > EPSILON) |             if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON) | ||||||
|             { |             { | ||||||
|                 bbox.min(i) *= m_transformation.get_scaling_factor((Axis)i); |                 bbox.min(i) *= get_scaling_factor((Axis)i); | ||||||
|                 bbox.max(i) *= m_transformation.get_scaling_factor((Axis)i); |                 bbox.max(i) *= get_scaling_factor((Axis)i); | ||||||
| #else | #else | ||||||
|             if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON) |             if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON) | ||||||
|             { |             { | ||||||
|  | @ -1205,8 +1391,8 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes | ||||||
|         // Translate the bounding box.
 |         // Translate the bounding box.
 | ||||||
|         if (! dont_translate) { |         if (! dont_translate) { | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|             bbox.min += m_transformation.get_offset(); |             bbox.min += get_offset(); | ||||||
|             bbox.max += m_transformation.get_offset(); |             bbox.max += get_offset(); | ||||||
| #else | #else | ||||||
|             bbox.min += this->m_offset; |             bbox.min += this->m_offset; | ||||||
|             bbox.max += this->m_offset; |             bbox.max += this->m_offset; | ||||||
|  | @ -1230,9 +1416,9 @@ void ModelInstance::transform_polygon(Polygon* polygon) const | ||||||
| { | { | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 |     // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 | ||||||
|     polygon->rotate(m_transformation.get_rotation(Z)); // rotate around polygon origin
 |     polygon->rotate(get_rotation(Z)); // rotate around polygon origin
 | ||||||
|     // CHECK_ME -> Is the following correct ?
 |     // CHECK_ME -> Is the following correct ?
 | ||||||
|     polygon->scale(m_transformation.get_scaling_factor(X), m_transformation.get_scaling_factor(Y)); // scale around polygon origin
 |     polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
 | ||||||
| #else | #else | ||||||
|     // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 |     // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 | ||||||
|     polygon->rotate(this->m_rotation(2));                // rotate around polygon origin
 |     polygon->rotate(this->m_rotation(2));                // rotate around polygon origin
 | ||||||
|  |  | ||||||
|  | @ -22,21 +22,35 @@ class ModelInstance; | ||||||
| class ModelMaterial; | class ModelMaterial; | ||||||
| class ModelObject; | class ModelObject; | ||||||
| class ModelVolume; | class ModelVolume; | ||||||
| class PresetBundle; |  | ||||||
| class Print; | class Print; | ||||||
| 
 | 
 | ||||||
| typedef std::string t_model_material_id; | typedef std::string t_model_material_id; | ||||||
| typedef std::string t_model_material_attribute; | typedef std::string t_model_material_attribute; | ||||||
| typedef std::map<t_model_material_attribute,std::string> t_model_material_attributes; | typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes; | ||||||
| 
 | 
 | ||||||
| typedef std::map<t_model_material_id,ModelMaterial*> ModelMaterialMap; | typedef std::map<t_model_material_id, ModelMaterial*> ModelMaterialMap; | ||||||
| typedef std::vector<ModelObject*> ModelObjectPtrs; | typedef std::vector<ModelObject*> ModelObjectPtrs; | ||||||
| typedef std::vector<ModelVolume*> ModelVolumePtrs; | typedef std::vector<ModelVolume*> ModelVolumePtrs; | ||||||
| typedef std::vector<ModelInstance*> ModelInstancePtrs; | typedef std::vector<ModelInstance*> ModelInstancePtrs; | ||||||
| 
 | 
 | ||||||
| // Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
 | // Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
 | ||||||
| // Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
 | // Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
 | ||||||
| typedef size_t ModelID; | // Valid IDs are strictly positive (non zero).
 | ||||||
|  | // It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
 | ||||||
|  | // for parameter overload.
 | ||||||
|  | struct ModelID  | ||||||
|  | { | ||||||
|  | 	ModelID(size_t id) : id(id) {} | ||||||
|  | 
 | ||||||
|  | 	bool operator==(const ModelID &rhs) const { return this->id == rhs.id; } | ||||||
|  | 	bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; } | ||||||
|  | 	bool operator< (const ModelID &rhs) const { return this->id <  rhs.id; } | ||||||
|  | 	bool operator> (const ModelID &rhs) const { return this->id >  rhs.id; } | ||||||
|  | 	bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; } | ||||||
|  | 	bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; } | ||||||
|  | 
 | ||||||
|  | 	size_t	id; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| // Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
 | // Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
 | ||||||
| // to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
 | // to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
 | ||||||
|  | @ -48,19 +62,66 @@ public: | ||||||
|     ModelID     id() const { return m_id; } |     ModelID     id() const { return m_id; } | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     // Constructor to be only called by derived classes.
 |     // Constructors to be only called by derived classes.
 | ||||||
|     ModelBase() {} |     // Default constructor to assign a unique ID.
 | ||||||
|     ModelID  m_id = generate_new_id(); |     ModelBase() : m_id(generate_new_id()) {} | ||||||
|  |     // Constructor with ignored int parameter to assign an invalid ID, to be replaced
 | ||||||
|  |     // by an existing ID copied from elsewhere.
 | ||||||
|  |     ModelBase(int) : m_id(ModelID(0)) {} | ||||||
|  | 
 | ||||||
|  |     // Use with caution!
 | ||||||
|  |     void        set_new_unique_id() { m_id = generate_new_id(); } | ||||||
|  |     void        set_invalid_id()    { m_id = 0; } | ||||||
|  |     // Use with caution!
 | ||||||
|  |     void        copy_id(const ModelBase &rhs) { m_id = rhs.id(); } | ||||||
|  | 
 | ||||||
|  |     // Override this method if a ModelBase derived class owns other ModelBase derived instances.
 | ||||||
|  |     void        assign_new_unique_ids_recursive() { this->set_new_unique_id(); } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     static inline ModelID generate_new_id() { return s_last_id ++; } |     ModelID                 m_id; | ||||||
|     static ModelID s_last_id; | 
 | ||||||
|  | 	static inline ModelID   generate_new_id() { return ModelID(++ s_last_id); } | ||||||
|  |     static size_t           s_last_id; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ | ||||||
|  |     /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ | ||||||
|  |     /* to make a private copy for background processing. */ \ | ||||||
|  |     static TYPE* new_copy(const TYPE &rhs)  { return new TYPE(rhs); } \ | ||||||
|  |     static TYPE* new_copy(TYPE &&rhs)       { return new TYPE(std::move(rhs)); } \ | ||||||
|  |     static TYPE  make_copy(const TYPE &rhs) { return TYPE(rhs); } \ | ||||||
|  |     static TYPE  make_copy(TYPE &&rhs)      { return TYPE(std::move(rhs)); } \ | ||||||
|  |     TYPE&        assign_copy(const TYPE &rhs); \ | ||||||
|  |     TYPE&        assign_copy(TYPE &&rhs); \ | ||||||
|  |     /* Copy a TYPE, generate new IDs. The front end will use this call. */ \ | ||||||
|  |     static TYPE* new_clone(const TYPE &rhs) { \ | ||||||
|  |         /* Default constructor assigning an invalid ID. */ \ | ||||||
|  |         auto obj = new TYPE(-1); \ | ||||||
|  |         obj->assign_clone(rhs); \ | ||||||
|  |         return obj; \ | ||||||
|  | 	} \ | ||||||
|  |     TYPE         make_clone(const TYPE &rhs) { \ | ||||||
|  |         /* Default constructor assigning an invalid ID. */ \ | ||||||
|  |         TYPE obj(-1); \ | ||||||
|  |         obj.assign_clone(rhs); \ | ||||||
|  |         return obj; \ | ||||||
|  |     } \ | ||||||
|  |     TYPE&        assign_clone(const TYPE &rhs) { \ | ||||||
|  |         this->assign_copy(rhs); \ | ||||||
|  |         this->assign_new_unique_ids_recursive(); \ | ||||||
|  | 		return *this; \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ | ||||||
|  | private: \ | ||||||
|  |     /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \ | ||||||
|  |     explicit TYPE(int) : ModelBase(-1) {}; \ | ||||||
|  |     void assign_new_unique_ids_recursive(); | ||||||
|  | 
 | ||||||
| // Material, which may be shared across multiple ModelObjects of a single Model.
 | // Material, which may be shared across multiple ModelObjects of a single Model.
 | ||||||
| class ModelMaterial : public ModelBase | class ModelMaterial : public ModelBase | ||||||
| { | { | ||||||
|     friend class Model; |  | ||||||
| public: | public: | ||||||
|     // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
 |     // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
 | ||||||
|     t_model_material_attributes attributes; |     t_model_material_attributes attributes; | ||||||
|  | @ -71,14 +132,22 @@ public: | ||||||
|     void apply(const t_model_material_attributes &attributes) |     void apply(const t_model_material_attributes &attributes) | ||||||
|         { this->attributes.insert(attributes.begin(), attributes.end()); } |         { this->attributes.insert(attributes.begin(), attributes.end()); } | ||||||
| 
 | 
 | ||||||
|  | protected: | ||||||
|  |     friend class Model; | ||||||
|  | 	// Constructor, which assigns a new unique ID.
 | ||||||
|  | 	ModelMaterial(Model *model) : m_model(model) {} | ||||||
|  | 	// Copy constructor copies the ID and m_model!
 | ||||||
|  | 	ModelMaterial(const ModelMaterial &rhs) = default; | ||||||
|  | 	void set_model(Model *model) { m_model = model; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     // Parent, owning this material.
 |     // Parent, owning this material.
 | ||||||
|     Model *m_model; |     Model *m_model; | ||||||
|      |      | ||||||
|     ModelMaterial(Model *model) : m_model(model) {} | 	ModelMaterial() = delete; | ||||||
|     ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), m_model(model) {} | 	ModelMaterial(ModelMaterial &&rhs) = delete; | ||||||
|     explicit ModelMaterial(ModelMaterial &rhs) = delete; | 	ModelMaterial& operator=(const ModelMaterial &rhs) = delete; | ||||||
|     ModelMaterial& operator=(ModelMaterial &rhs) = delete; |     ModelMaterial& operator=(ModelMaterial &&rhs) = delete; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
 | // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
 | ||||||
|  | @ -119,15 +188,12 @@ public: | ||||||
|         when user expects that. */ |         when user expects that. */ | ||||||
|     Vec3d                   origin_translation; |     Vec3d                   origin_translation; | ||||||
| 
 | 
 | ||||||
|     // Assign a ModelObject to this object while keeping the original pointer to the parent Model.
 |  | ||||||
| 	// Make a deep copy.
 |  | ||||||
| 	ModelObject&            assign(const ModelObject *rhs, bool copy_volumes = true); |  | ||||||
| 
 |  | ||||||
|     Model*                  get_model() const { return m_model; }; |     Model*                  get_model() const { return m_model; }; | ||||||
|      |      | ||||||
|     ModelVolume*            add_volume(const TriangleMesh &mesh); |     ModelVolume*            add_volume(const TriangleMesh &mesh); | ||||||
|     ModelVolume*            add_volume(TriangleMesh &&mesh); |     ModelVolume*            add_volume(TriangleMesh &&mesh); | ||||||
|     ModelVolume*            add_volume(const ModelVolume &volume); |     ModelVolume*            add_volume(const ModelVolume &volume); | ||||||
|  |     ModelVolume*            add_volume(const ModelVolume &volume, TriangleMesh &&mesh); | ||||||
|     void                    delete_volume(size_t idx); |     void                    delete_volume(size_t idx); | ||||||
|     void                    clear_volumes(); |     void                    clear_volumes(); | ||||||
|     bool                    is_multiparts() const { return volumes.size() > 1; } |     bool                    is_multiparts() const { return volumes.size() > 1; } | ||||||
|  | @ -163,9 +229,10 @@ public: | ||||||
|     void translate(double x, double y, double z); |     void translate(double x, double y, double z); | ||||||
|     void scale(const Vec3d &versor); |     void scale(const Vec3d &versor); | ||||||
|     void scale(const double s) { this->scale(Vec3d(s, s, s)); } |     void scale(const double s) { this->scale(Vec3d(s, s, s)); } | ||||||
|     void rotate(float angle, const Axis &axis); |     void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); } | ||||||
|     void rotate(float angle, const Vec3d& axis); |     void rotate(double angle, Axis axis); | ||||||
|     void mirror(const Axis &axis); |     void rotate(double angle, const Vec3d& axis); | ||||||
|  |     void mirror(Axis axis); | ||||||
|     size_t materials_count() const; |     size_t materials_count() const; | ||||||
|     size_t facets_count() const; |     size_t facets_count() const; | ||||||
|     bool needed_repair() const; |     bool needed_repair() const; | ||||||
|  | @ -184,22 +251,27 @@ public: | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     friend class Print; |     friend class Print; | ||||||
|     // Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
 |     // Called by Print::apply() to set the model pointer after making a copy.
 | ||||||
|     // Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
 |  | ||||||
|     ModelObject*          clone(Model *parent); |  | ||||||
|     void        set_model(Model *model) { m_model = model; } |     void        set_model(Model *model) { m_model = model; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {} |     ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {} | ||||||
|     ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes = true); |  | ||||||
|     explicit ModelObject(ModelObject &rhs) = delete; |  | ||||||
|     ~ModelObject(); |     ~ModelObject(); | ||||||
|     ModelObject& operator=(ModelObject &rhs) = default; |  | ||||||
| 
 | 
 | ||||||
|     // Parent object, owning this ModelObject.
 |     /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ | ||||||
|     Model                *m_model; |     /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ | ||||||
|  |     ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } | ||||||
|  |     explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } | ||||||
|  |     ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; } | ||||||
|  |     ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; } | ||||||
|  | 
 | ||||||
|  |     MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) | ||||||
|  | 	MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) | ||||||
|  | 
 | ||||||
|  |     // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
 | ||||||
|  |     Model                *m_model = nullptr; | ||||||
|  | 
 | ||||||
|     // Bounding box, cached.
 |     // Bounding box, cached.
 | ||||||
| 
 |  | ||||||
|     mutable BoundingBoxf3 m_bounding_box; |     mutable BoundingBoxf3 m_bounding_box; | ||||||
|     mutable bool          m_bounding_box_valid; |     mutable bool          m_bounding_box_valid; | ||||||
| }; | }; | ||||||
|  | @ -208,8 +280,6 @@ private: | ||||||
| // ModelVolume instances are owned by a ModelObject.
 | // ModelVolume instances are owned by a ModelObject.
 | ||||||
| class ModelVolume : public ModelBase | class ModelVolume : public ModelBase | ||||||
| { | { | ||||||
|     friend class ModelObject; |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|     std::string         name; |     std::string         name; | ||||||
|     // The triangular model.
 |     // The triangular model.
 | ||||||
|  | @ -226,9 +296,6 @@ public: | ||||||
|         SUPPORT_BLOCKER, |         SUPPORT_BLOCKER, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // Clone this ModelVolume, keep the ID identical, set the parent to the cloned volume.
 |  | ||||||
|     ModelVolume*        clone(ModelObject *parent) { return new ModelVolume(parent, *this); } |  | ||||||
| 
 |  | ||||||
|     // A parent object owning this modifier volume.
 |     // A parent object owning this modifier volume.
 | ||||||
|     ModelObject*        get_object() const { return this->object; }; |     ModelObject*        get_object() const { return this->object; }; | ||||||
|     Type                type() const { return m_type; } |     Type                type() const { return m_type; } | ||||||
|  | @ -246,10 +313,18 @@ public: | ||||||
|     // Return the number of volumes created from this one.
 |     // Return the number of volumes created from this one.
 | ||||||
|     // This is useful to assign different materials to different volumes of an object.
 |     // This is useful to assign different materials to different volumes of an object.
 | ||||||
|     size_t              split(unsigned int max_extruders); |     size_t              split(unsigned int max_extruders); | ||||||
|     void                translate(double x, double y, double z); |     void                translate(double x, double y, double z) { translate(Vec3d(x, y, z)); } | ||||||
|     void                translate(const Vec3d& displacement); |     void                translate(const Vec3d& displacement); | ||||||
|  |     void                scale(const Vec3d& scaling_factors); | ||||||
|  |     void                scale(double x, double y, double z) { scale(Vec3d(x, y, z)); } | ||||||
|  |     void                scale(double s) { scale(Vec3d(s, s, s)); } | ||||||
|  |     void                rotate(double angle, Axis axis); | ||||||
|  |     void                rotate(double angle, const Vec3d& axis); | ||||||
|  |     void                mirror(Axis axis); | ||||||
| 
 | 
 | ||||||
|     ModelMaterial*      assign_unique_material(); | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     void                translate_geometry(const Vec3d& displacement); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     void                calculate_convex_hull(); |     void                calculate_convex_hull(); | ||||||
|     const TriangleMesh& get_convex_hull() const; |     const TriangleMesh& get_convex_hull() const; | ||||||
|  | @ -289,6 +364,13 @@ public: | ||||||
|     const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } |     const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } | ||||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|  | protected: | ||||||
|  | 	friend class Print; | ||||||
|  | 	friend class ModelObject; | ||||||
|  | 
 | ||||||
|  | 	explicit ModelVolume(ModelVolume &rhs) = default; | ||||||
|  |     void     set_model_object(ModelObject *model_object) { object = model_object; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     // Parent object owning this ModelVolume.
 |     // Parent object owning this ModelVolume.
 | ||||||
|     ModelObject*            object; |     ModelObject*            object; | ||||||
|  | @ -308,22 +390,43 @@ private: | ||||||
|     } |     } | ||||||
|     ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : |     ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : | ||||||
|         mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} |         mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} | ||||||
|  | 
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     // Copying an existing volume, therefore this volume will get a copy of the ID assigned.
 | ||||||
|     ModelVolume(ModelObject *object, const ModelVolume &other) : |     ModelVolume(ModelObject *object, const ModelVolume &other) : | ||||||
|         ModelBase(other), // copy the ID
 |         ModelBase(other), // copy the ID
 | ||||||
|         name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) |         name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) | ||||||
|     { |     { | ||||||
|         this->set_material_id(other.material_id()); |         this->set_material_id(other.material_id()); | ||||||
|     } |     } | ||||||
|  |     // Providing a new mesh, therefore this volume will get a new unique ID assigned.
 | ||||||
|     ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : |     ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : | ||||||
|         ModelBase(other), // copy the ID
 |         name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) | ||||||
|         name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) |  | ||||||
|     { |     { | ||||||
|         this->set_material_id(other.material_id()); |         this->set_material_id(other.material_id()); | ||||||
|         if (mesh.stl.stats.number_of_facets > 1) |         if (mesh.stl.stats.number_of_facets > 1) | ||||||
|             calculate_convex_hull(); |             calculate_convex_hull(); | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  |     // Copying an existing volume, therefore this volume will get a copy of the ID assigned.
 | ||||||
|  |     ModelVolume(ModelObject *object, const ModelVolume &other) : | ||||||
|  |         ModelBase(other), // copy the ID
 | ||||||
|  |         name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) | ||||||
|  |     { | ||||||
|  | 		if (! other.material_id().empty()) | ||||||
|  | 			this->set_material_id(other.material_id()); | ||||||
|  |     } | ||||||
|  |     // Providing a new mesh, therefore this volume will get a new unique ID assigned.
 | ||||||
|  |     ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : | ||||||
|  |         name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) | ||||||
|  |     { | ||||||
|  | 		if (! other.material_id().empty()) | ||||||
|  | 			this->set_material_id(other.material_id()); | ||||||
|  |         if (mesh.stl.stats.number_of_facets > 1) | ||||||
|  |             calculate_convex_hull(); | ||||||
|  |     } | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     explicit ModelVolume(ModelVolume &rhs) = delete; |  | ||||||
|     ModelVolume& operator=(ModelVolume &rhs) = delete; |     ModelVolume& operator=(ModelVolume &rhs) = delete; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -340,8 +443,6 @@ public: | ||||||
|         Num_BedStates |         Num_BedStates | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     friend class ModelObject; |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     Geometry::Transformation m_transformation; |     Geometry::Transformation m_transformation; | ||||||
|  | @ -430,22 +531,33 @@ public: | ||||||
| 
 | 
 | ||||||
|     bool is_printable() const { return print_volume_state == PVS_Inside; } |     bool is_printable() const { return print_volume_state == PVS_Inside; } | ||||||
| 
 | 
 | ||||||
|  | protected: | ||||||
|  |     friend class Print; | ||||||
|  |     friend class ModelObject; | ||||||
|  | 
 | ||||||
|  |     explicit ModelInstance(const ModelInstance &rhs) = default; | ||||||
|  |     void     set_model_object(ModelObject *model_object) { object = model_object; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     // Parent object, owning this instance.
 |     // Parent object, owning this instance.
 | ||||||
|     ModelObject* object; |     ModelObject* object; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} |     // Constructor, which assigns a new unique ID.
 | ||||||
|     ModelInstance(ModelObject *object, const ModelInstance &other) : |     explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} | ||||||
|  |     // Constructor, which assigns a new unique ID.
 | ||||||
|  |     explicit ModelInstance(ModelObject *object, const ModelInstance &other) : | ||||||
|         m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} |         m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} | ||||||
| #else | #else | ||||||
|     ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} |     explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} | ||||||
|     ModelInstance(ModelObject *object, const ModelInstance &other) : |     explicit ModelInstance(ModelObject *object, const ModelInstance &other) : | ||||||
|         m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} |         m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} | ||||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     explicit ModelInstance(ModelInstance &rhs) = delete; |     ModelInstance() = delete; | ||||||
|     ModelInstance& operator=(ModelInstance &rhs) = delete; |     explicit ModelInstance(ModelInstance &&rhs) = delete; | ||||||
|  |     ModelInstance& operator=(const ModelInstance &rhs) = delete; | ||||||
|  |     ModelInstance& operator=(ModelInstance &&rhs) = delete; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // The print bed content.
 | // The print bed content.
 | ||||||
|  | @ -464,12 +576,19 @@ public: | ||||||
|     // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
 |     // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
 | ||||||
|     ModelObjectPtrs     objects; |     ModelObjectPtrs     objects; | ||||||
|      |      | ||||||
|  |     // Default constructor assigns a new ID to the model.
 | ||||||
|     Model() {} |     Model() {} | ||||||
|     Model(const Model &rhs); |  | ||||||
|     Model& operator=(const Model &rhs); |  | ||||||
|     ~Model() { this->clear_objects(); this->clear_materials(); } |     ~Model() { this->clear_objects(); this->clear_materials(); } | ||||||
| 
 | 
 | ||||||
|     // XXX: use fs::path ?
 |     /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ | ||||||
|  |     /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ | ||||||
|  |     Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); } | ||||||
|  |     explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } | ||||||
|  |     Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } | ||||||
|  |     Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; } | ||||||
|  | 
 | ||||||
|  |     MODELBASE_DERIVED_COPY_MOVE_CLONE(Model) | ||||||
|  | 
 | ||||||
|     static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); |     static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); | ||||||
|     static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); |     static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); | ||||||
| 
 | 
 | ||||||
|  | @ -477,12 +596,14 @@ public: | ||||||
|     /// This function calls repair function on each TriangleMesh of each model object volume
 |     /// This function calls repair function on each TriangleMesh of each model object volume
 | ||||||
|     void         repair(); |     void         repair(); | ||||||
| 
 | 
 | ||||||
|  |     // Add a new ModelObject to this Model, generate a new ID for this ModelObject.
 | ||||||
|     ModelObject* add_object(); |     ModelObject* add_object(); | ||||||
|     ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); |     ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); | ||||||
|     ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh); |     ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh); | ||||||
|     ModelObject* add_object(const ModelObject &other, bool copy_volumes = true); |     ModelObject* add_object(const ModelObject &other); | ||||||
|     void         delete_object(size_t idx); |     void         delete_object(size_t idx); | ||||||
|     void delete_object(ModelObject* object); |     bool         delete_object(ModelID id); | ||||||
|  |     bool         delete_object(ModelObject* object); | ||||||
|     void         clear_objects(); |     void         clear_objects(); | ||||||
|      |      | ||||||
|     ModelMaterial* add_material(t_model_material_id material_id); |     ModelMaterial* add_material(t_model_material_id material_id); | ||||||
|  | @ -520,8 +641,14 @@ public: | ||||||
|     static unsigned int get_auto_extruder_id(unsigned int max_extruders); |     static unsigned int get_auto_extruder_id(unsigned int max_extruders); | ||||||
|     static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); |     static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); | ||||||
|     static void reset_auto_extruder_id(); |     static void reset_auto_extruder_id(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #undef MODELBASE_DERIVED_COPY_MOVE_CLONE | ||||||
|  | #undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ inline Vec2d   to_2d(const Vec3d   &pt3) { return Vec2d  (pt3(0), pt3(1)); } | ||||||
| 
 | 
 | ||||||
| inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } | inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } | ||||||
| inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } | inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } | ||||||
| inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(v(0), v(1), z); } | inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); } | ||||||
| inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } | inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } | ||||||
| 
 | 
 | ||||||
| inline Vec2d   unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); } | inline Vec2d   unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); } | ||||||
|  |  | ||||||
|  | @ -124,6 +124,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option | ||||||
|         "between_objects_gcode", |         "between_objects_gcode", | ||||||
|         "bridge_acceleration", |         "bridge_acceleration", | ||||||
|         "bridge_fan_speed", |         "bridge_fan_speed", | ||||||
|  |         "colorprint_heights", | ||||||
|         "cooling", |         "cooling", | ||||||
|         "default_acceleration", |         "default_acceleration", | ||||||
|         "deretract_speed", |         "deretract_speed", | ||||||
|  | @ -603,7 +604,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old | ||||||
|     size_t i_old, i_new; |     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();) { |     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_old = *model_object_old.volumes[i_old]; | ||||||
|         const ModelVolume &mv_new = *model_object_new.volumes[i_old]; | 		const ModelVolume &mv_new = *model_object_new.volumes[i_new]; | ||||||
|         if (mv_old.type() != type) { |         if (mv_old.type() != type) { | ||||||
|             ++ i_old; |             ++ i_old; | ||||||
|             continue; |             continue; | ||||||
|  | @ -615,8 +616,12 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old | ||||||
|         if (mv_old.id() != mv_new.id()) |         if (mv_old.id() != mv_new.id()) | ||||||
|             return true; |             return true; | ||||||
|         //FIXME test for the content of the mesh!
 |         //FIXME test for the content of the mesh!
 | ||||||
|         //FIXME test for the transformation matrices!
 | 
 | ||||||
|         ++ i_old; | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) | ||||||
|  |             return true; | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  |         ++i_old; | ||||||
|         ++ i_new; |         ++ i_new; | ||||||
|     } |     } | ||||||
|     for (; i_old < model_object_old.volumes.size(); ++ i_old) { |     for (; i_old < model_object_old.volumes.size(); ++ i_old) { | ||||||
|  | @ -634,7 +639,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src) | void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src) | ||||||
| { | { | ||||||
|     // 1) Delete the support volumes from model_object_dst.
 |     // 1) Delete the support volumes from model_object_dst.
 | ||||||
|     { |     { | ||||||
|  | @ -650,8 +655,10 @@ static inline void model_volume_list_update_supports(ModelObject &model_object_d | ||||||
|     } |     } | ||||||
|     // 2) Copy the support volumes from model_object_src to the end of model_object_dst.
 |     // 2) Copy the support volumes from model_object_src to the end of model_object_dst.
 | ||||||
|     for (ModelVolume *vol : model_object_src.volumes) { |     for (ModelVolume *vol : model_object_src.volumes) { | ||||||
|         if (vol->is_support_modifier()) | 		if (vol->is_support_modifier()) { | ||||||
|             model_object_dst.volumes.emplace_back(vol->clone(&model_object_dst)); | 			model_object_dst.volumes.emplace_back(new ModelVolume(*vol)); | ||||||
|  | 			model_object_dst.volumes.back()->set_model_object(&model_object_dst); | ||||||
|  | 		} | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -709,8 +716,60 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb | ||||||
|     return std::vector<PrintInstances>(trafos.begin(), trafos.end()); |     return std::vector<PrintInstances>(trafos.begin(), trafos.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef _DEBUG | ||||||
|  | // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
 | ||||||
|  | static inline void check_model_ids_validity(const Model &model) | ||||||
|  | { | ||||||
|  |     std::set<ModelID> ids; | ||||||
|  |     auto check = [&ids](ModelID id) {  | ||||||
|  |         assert(id.id > 0); | ||||||
|  |         assert(ids.find(id) == ids.end()); | ||||||
|  |         ids.insert(id); | ||||||
|  |     }; | ||||||
|  |     for (const ModelObject *model_object : model.objects) { | ||||||
|  |         check(model_object->id()); | ||||||
|  |         for (const ModelVolume *model_volume : model_object->volumes) | ||||||
|  |             check(model_volume->id()); | ||||||
|  |         for (const ModelInstance *model_instance : model_object->instances) | ||||||
|  |             check(model_instance->id()); | ||||||
|  |     } | ||||||
|  |     for (const auto mm : model.materials) | ||||||
|  |         check(mm.second->id()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline 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()); | ||||||
|  |         assert(model_object1.volumes.size() == model_object2.volumes.size()); | ||||||
|  |         assert(model_object1.instances.size() == model_object2.instances.size()); | ||||||
|  |         for (size_t i = 0; i < model_object1.volumes.size(); ++ i) | ||||||
|  | 			assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); | ||||||
|  |         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()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif /* _DEBUG */ | ||||||
|  | 
 | ||||||
| Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) | Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) | ||||||
| { | { | ||||||
|  | #ifdef _DEBUG | ||||||
|  |     check_model_ids_validity(model); | ||||||
|  | #endif /* _DEBUG */ | ||||||
|  | 
 | ||||||
|     // Make a copy of the config, normalize it.
 |     // Make a copy of the config, normalize it.
 | ||||||
|     DynamicPrintConfig config(config_in); |     DynamicPrintConfig config(config_in); | ||||||
|     config.normalize(); |     config.normalize(); | ||||||
|  | @ -774,7 +833,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|         for (PrintRegion *region : m_regions) |         for (PrintRegion *region : m_regions) | ||||||
|             delete region; |             delete region; | ||||||
|         m_regions.clear(); |         m_regions.clear(); | ||||||
|         m_model = model; |         m_model.assign_copy(model); | ||||||
| 		for (const ModelObject *model_object : m_model.objects) | 		for (const ModelObject *model_object : m_model.objects) | ||||||
| 			model_object_status.emplace(model_object->id(), ModelObjectStatus::New); | 			model_object_status.emplace(model_object->id(), ModelObjectStatus::New); | ||||||
|     } else { |     } else { | ||||||
|  | @ -789,7 +848,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|                 model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); |                 model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); | ||||||
|             for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { |             for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { | ||||||
|                 model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); |                 model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); | ||||||
|                 m_model.objects.emplace_back(model.objects[i]->clone(&m_model)); |                 m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i])); | ||||||
|  | 				m_model.objects.back()->set_model(&m_model); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             // Reorder the objects, add new objects.
 |             // Reorder the objects, add new objects.
 | ||||||
|  | @ -806,7 +866,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|                 auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); |                 auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower); | ||||||
|                 if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { |                 if (it == model_objects_old.end() || (*it)->id() != mobj->id()) { | ||||||
|                     // New ModelObject added.
 |                     // New ModelObject added.
 | ||||||
|                     m_model.objects.emplace_back((*it)->clone(&m_model)); | 					m_model.objects.emplace_back(ModelObject::new_copy(*mobj)); | ||||||
|  | 					m_model.objects.back()->set_model(&m_model); | ||||||
|                     model_object_status.emplace(mobj->id(), ModelObjectStatus::New); |                     model_object_status.emplace(mobj->id(), ModelObjectStatus::New); | ||||||
|                 } else { |                 } else { | ||||||
|                     // Existing ModelObject re-added (possibly moved in the list).
 |                     // Existing ModelObject re-added (possibly moved in the list).
 | ||||||
|  | @ -899,8 +960,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|                 update_apply_status(it->print_object->invalidate_all_steps()); |                 update_apply_status(it->print_object->invalidate_all_steps()); | ||||||
|                 const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted; |                 const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted; | ||||||
|             } |             } | ||||||
|             // Copy content of the ModelObject including its ID, reset the parent.
 |             // Copy content of the ModelObject including its ID, do not change the parent.
 | ||||||
|             model_object.assign(&model_object_new); |             model_object.assign_copy(model_object_new); | ||||||
|         } else if (support_blockers_differ || support_enforcers_differ) { |         } else if (support_blockers_differ || support_enforcers_differ) { | ||||||
|             // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
 |             // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
 | ||||||
|             m_cancel_callback(); |             m_cancel_callback(); | ||||||
|  | @ -933,8 +994,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|             model_object.name       = model_object_new.name; |             model_object.name       = model_object_new.name; | ||||||
|             model_object.input_file = model_object_new.input_file; |             model_object.input_file = model_object_new.input_file; | ||||||
|             model_object.clear_instances(); |             model_object.clear_instances(); | ||||||
|             for (const ModelInstance *model_instance : model_object_new.instances) |             model_object.instances.reserve(model_object_new.instances.size()); | ||||||
|                 model_object.add_instance(*model_instance); |             for (const ModelInstance *model_instance : model_object_new.instances) { | ||||||
|  |                 model_object.instances.emplace_back(new ModelInstance(*model_instance)); | ||||||
|  |                 model_object.instances.back()->set_model_object(&model_object); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1136,6 +1200,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co | ||||||
|             object->update_layer_height_profile(); |             object->update_layer_height_profile(); | ||||||
| 
 | 
 | ||||||
|     this->update_object_placeholders(); |     this->update_object_placeholders(); | ||||||
|  | 
 | ||||||
|  | #ifdef _DEBUG | ||||||
|  |     check_model_ids_equal(m_model, model); | ||||||
|  | #endif /* _DEBUG */ | ||||||
|  | 
 | ||||||
| 	return static_cast<ApplyStatus>(apply_status); | 	return static_cast<ApplyStatus>(apply_status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -564,6 +564,9 @@ private: | ||||||
|     void                _make_wipe_tower(); |     void                _make_wipe_tower(); | ||||||
|     void                _simplify_slices(double distance); |     void                _simplify_slices(double distance); | ||||||
| 
 | 
 | ||||||
|  |     // Declared here to have access to Model / ModelObject / ModelInstance
 | ||||||
|  |     static void         model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); | ||||||
|  | 
 | ||||||
|     PrintState<PrintStep, psCount>          m_state; |     PrintState<PrintStep, psCount>          m_state; | ||||||
|     // Mutex used for synchronization of the worker thread with the UI thread:
 |     // Mutex used for synchronization of the worker thread with the UI thread:
 | ||||||
|     // The mutex will be used to guard the worker thread against entering a stage
 |     // The mutex will be used to guard the worker thread against entering a stage
 | ||||||
|  | @ -601,12 +604,6 @@ private: | ||||||
|     friend class PrintObject; |     friend class PrintObject; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) |  | ||||||
| #define FOREACH_OBJECT(print, object)       FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object) |  | ||||||
| #define FOREACH_LAYER(object, layer)        FOREACH_BASE(LayerPtrs, (object)->m_layers, layer) |  | ||||||
| #define FOREACH_LAYERREGION(layer, layerm)  FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm) |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -192,6 +192,12 @@ void PrintConfigDef::init_fff_params() | ||||||
|     def->mode = comExpert; |     def->mode = comExpert; | ||||||
|     def->default_value = new ConfigOptionBool(false); |     def->default_value = new ConfigOptionBool(false); | ||||||
| 
 | 
 | ||||||
|  |     def = this->add("colorprint_heights", coFloats); | ||||||
|  |     def->label = L("Colorprint height"); | ||||||
|  |     def->tooltip = L("Heights at which a filament change is to occur. "); | ||||||
|  |     def->cli = "colorprint-heights=f@"; | ||||||
|  |     def->default_value = new ConfigOptionFloats { }; | ||||||
|  | 
 | ||||||
|     def = this->add("compatible_printers", coStrings); |     def = this->add("compatible_printers", coStrings); | ||||||
|     def->label = L("Compatible printers"); |     def->label = L("Compatible printers"); | ||||||
|     def->mode = comAdvanced; |     def->mode = comAdvanced; | ||||||
|  |  | ||||||
|  | @ -704,6 +704,7 @@ public: | ||||||
|     ConfigOptionInts                bridge_fan_speed; |     ConfigOptionInts                bridge_fan_speed; | ||||||
|     ConfigOptionFloat               brim_width; |     ConfigOptionFloat               brim_width; | ||||||
|     ConfigOptionBool                complete_objects; |     ConfigOptionBool                complete_objects; | ||||||
|  |     ConfigOptionFloats              colorprint_heights; | ||||||
|     ConfigOptionBools               cooling; |     ConfigOptionBools               cooling; | ||||||
|     ConfigOptionFloat               default_acceleration; |     ConfigOptionFloat               default_acceleration; | ||||||
|     ConfigOptionInts                disable_fan_first_layers; |     ConfigOptionInts                disable_fan_first_layers; | ||||||
|  | @ -781,6 +782,7 @@ protected: | ||||||
|         OPT_PTR(bridge_fan_speed); |         OPT_PTR(bridge_fan_speed); | ||||||
|         OPT_PTR(brim_width); |         OPT_PTR(brim_width); | ||||||
|         OPT_PTR(complete_objects); |         OPT_PTR(complete_objects); | ||||||
|  |         OPT_PTR(colorprint_heights); | ||||||
|         OPT_PTR(cooling); |         OPT_PTR(cooling); | ||||||
|         OPT_PTR(default_acceleration); |         OPT_PTR(default_acceleration); | ||||||
|         OPT_PTR(disable_fan_first_layers); |         OPT_PTR(disable_fan_first_layers); | ||||||
|  |  | ||||||
|  | @ -170,8 +170,8 @@ void PrintObject::make_perimeters() | ||||||
|      |      | ||||||
|     // merge slices if they were split into types
 |     // merge slices if they were split into types
 | ||||||
|     if (this->typed_slices) { |     if (this->typed_slices) { | ||||||
|         FOREACH_LAYER(this, layer_it) { |         for (Layer *layer : m_layers) { | ||||||
|             (*layer_it)->merge_slices(); |             layer->merge_slices(); | ||||||
|             m_print->throw_if_canceled(); |             m_print->throw_if_canceled(); | ||||||
|         } |         } | ||||||
|         this->typed_slices = false; |         this->typed_slices = false; | ||||||
|  | @ -184,7 +184,7 @@ void PrintObject::make_perimeters() | ||||||
|     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 |     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 | ||||||
|     // inside the object - infill_only_where_needed should be the method of choice for printing
 |     // inside the object - infill_only_where_needed should be the method of choice for printing
 | ||||||
|     // hollow objects
 |     // hollow objects
 | ||||||
|     for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         const PrintRegion ®ion = *m_print->regions()[region_id]; |         const PrintRegion ®ion = *m_print->regions()[region_id]; | ||||||
|         if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) |         if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) | ||||||
|             continue; |             continue; | ||||||
|  | @ -313,7 +313,7 @@ void PrintObject::prepare_infill() | ||||||
| 
 | 
 | ||||||
|     // Debugging output.
 |     // Debugging output.
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (const Layer *layer : m_layers) { |         for (const Layer *layer : m_layers) { | ||||||
|             LayerRegion *layerm = layer->m_regions[region_id]; |             LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); |             layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); | ||||||
|  | @ -332,7 +332,7 @@ void PrintObject::prepare_infill() | ||||||
|     m_print->throw_if_canceled(); |     m_print->throw_if_canceled(); | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (const Layer *layer : m_layers) { |         for (const Layer *layer : m_layers) { | ||||||
|             LayerRegion *layerm = layer->m_regions[region_id]; |             LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); |             layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); | ||||||
|  | @ -351,7 +351,7 @@ void PrintObject::prepare_infill() | ||||||
|     m_print->throw_if_canceled(); |     m_print->throw_if_canceled(); | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (const Layer *layer : m_layers) { |         for (const Layer *layer : m_layers) { | ||||||
|             LayerRegion *layerm = layer->m_regions[region_id]; |             LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); |             layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); | ||||||
|  | @ -370,7 +370,7 @@ void PrintObject::prepare_infill() | ||||||
|     m_print->throw_if_canceled(); |     m_print->throw_if_canceled(); | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (const Layer *layer : m_layers) { |         for (const Layer *layer : m_layers) { | ||||||
|             LayerRegion *layerm = layer->m_regions[region_id]; |             LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); |             layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); | ||||||
|  | @ -633,7 +633,7 @@ void PrintObject::detect_surfaces_type() | ||||||
|     // should be visible.
 |     // should be visible.
 | ||||||
|     bool interface_shells = m_config.interface_shells.value; |     bool interface_shells = m_config.interface_shells.value; | ||||||
| 
 | 
 | ||||||
|     for (int idx_region = 0; idx_region < m_print->m_regions.size(); ++ idx_region) { |     for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { | ||||||
|         BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; |         BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|         for (Layer *layer : m_layers) |         for (Layer *layer : m_layers) | ||||||
|  | @ -818,7 +818,7 @@ void PrintObject::process_external_surfaces() | ||||||
| { | { | ||||||
|     BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; |     BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; | ||||||
| 
 | 
 | ||||||
|     for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { | 	for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { | ||||||
|         const PrintRegion ®ion = *m_print->regions()[region_id]; |         const PrintRegion ®ion = *m_print->regions()[region_id]; | ||||||
|          |          | ||||||
|         BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; |         BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; | ||||||
|  | @ -851,13 +851,13 @@ void PrintObject::discover_vertical_shells() | ||||||
|         Polygons    holes; |         Polygons    holes; | ||||||
|     }; |     }; | ||||||
|     std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); |     std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); | ||||||
|     bool top_bottom_surfaces_all_regions = m_print->regions().size() > 1 && ! m_config.interface_shells.value; |     bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; | ||||||
|     if (top_bottom_surfaces_all_regions) { |     if (top_bottom_surfaces_all_regions) { | ||||||
|         // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
 |         // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
 | ||||||
|         // is calculated over all materials.
 |         // is calculated over all materials.
 | ||||||
|         // Is the "ensure vertical wall thickness" applicable to any region?
 |         // Is the "ensure vertical wall thickness" applicable to any region?
 | ||||||
|         bool has_extra_layers = false; |         bool has_extra_layers = false; | ||||||
|         for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) { |         for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { | ||||||
|             const PrintRegion ®ion = *m_print->get_region(idx_region); |             const PrintRegion ®ion = *m_print->get_region(idx_region); | ||||||
|             if (region.config().ensure_vertical_shell_thickness.value &&  |             if (region.config().ensure_vertical_shell_thickness.value &&  | ||||||
|                 (region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) { |                 (region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) { | ||||||
|  | @ -874,7 +874,7 @@ void PrintObject::discover_vertical_shells() | ||||||
|             tbb::blocked_range<size_t>(0, m_layers.size(), grain_size), |             tbb::blocked_range<size_t>(0, m_layers.size(), grain_size), | ||||||
|             [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { |             [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { | ||||||
|                 const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; |                 const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; | ||||||
|                 const size_t num_regions = m_print->regions().size(); |                 const size_t num_regions = this->region_volumes.size(); | ||||||
|                 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { |                 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { | ||||||
|                     m_print->throw_if_canceled(); |                     m_print->throw_if_canceled(); | ||||||
|                     const Layer                      &layer = *m_layers[idx_layer]; |                     const Layer                      &layer = *m_layers[idx_layer]; | ||||||
|  | @ -935,7 +935,7 @@ void PrintObject::discover_vertical_shells() | ||||||
|         BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; |         BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) { |     for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { | ||||||
|         PROFILE_BLOCK(discover_vertical_shells_region); |         PROFILE_BLOCK(discover_vertical_shells_region); | ||||||
| 
 | 
 | ||||||
|         const PrintRegion ®ion = *m_print->get_region(idx_region); |         const PrintRegion ®ion = *m_print->get_region(idx_region); | ||||||
|  | @ -1227,7 +1227,7 @@ void PrintObject::bridge_over_infill() | ||||||
| { | { | ||||||
|     BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; |     BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; | ||||||
| 
 | 
 | ||||||
|     for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         const PrintRegion ®ion = *m_print->regions()[region_id]; |         const PrintRegion ®ion = *m_print->regions()[region_id]; | ||||||
|          |          | ||||||
|         // skip bridging in case there are no voids
 |         // skip bridging in case there are no voids
 | ||||||
|  | @ -1243,9 +1243,10 @@ void PrintObject::bridge_over_infill() | ||||||
|             *this |             *this | ||||||
|         ); |         ); | ||||||
|          |          | ||||||
|         FOREACH_LAYER(this, layer_it) { | 		for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { | ||||||
|             // skip first layer
 |             // skip first layer
 | ||||||
|             if (layer_it == m_layers.begin()) continue; | 			if (layer_it == m_layers.begin()) | ||||||
|  |                 continue; | ||||||
|              |              | ||||||
|             Layer* layer        = *layer_it; |             Layer* layer        = *layer_it; | ||||||
|             LayerRegion* layerm = layer->m_regions[region_id]; |             LayerRegion* layerm = layer->m_regions[region_id]; | ||||||
|  | @ -1271,8 +1272,8 @@ void PrintObject::bridge_over_infill() | ||||||
|                      |                      | ||||||
|                     // iterate through regions and collect internal surfaces
 |                     // iterate through regions and collect internal surfaces
 | ||||||
|                     Polygons lower_internal; |                     Polygons lower_internal; | ||||||
|                     FOREACH_LAYERREGION(lower_layer, lower_layerm_it) |                     for (LayerRegion *lower_layerm : lower_layer->m_regions) | ||||||
|                         (*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal); |                         lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); | ||||||
|                      |                      | ||||||
|                     // intersect such lower internal surfaces with the candidate solid surfaces
 |                     // intersect such lower internal surfaces with the candidate solid surfaces
 | ||||||
|                     to_bridge_pp = intersection(to_bridge_pp, lower_internal); |                     to_bridge_pp = intersection(to_bridge_pp, lower_internal); | ||||||
|  | @ -1443,14 +1444,14 @@ void PrintObject::_slice() | ||||||
|                 layer->lower_layer = prev; |                 layer->lower_layer = prev; | ||||||
|             } |             } | ||||||
|             // Make sure all layers contain layer region objects for all regions.
 |             // Make sure all layers contain layer region objects for all regions.
 | ||||||
|             for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) |             for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) | ||||||
|                 layer->add_region(this->print()->regions()[region_id]); |                 layer->add_region(this->print()->regions()[region_id]); | ||||||
|             prev = layer; |             prev = layer; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Slice all non-modifier volumes.
 |     // Slice all non-modifier volumes.
 | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; |         BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; | ||||||
|         std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); |         std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); | ||||||
|         m_print->throw_if_canceled(); |         m_print->throw_if_canceled(); | ||||||
|  | @ -1462,14 +1463,14 @@ void PrintObject::_slice() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Slice all modifier volumes.
 |     // Slice all modifier volumes.
 | ||||||
|     if (this->print()->regions().size() > 1) { |     if (this->region_volumes.size() > 1) { | ||||||
|         for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; |             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; | ||||||
|             std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); |             std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); | ||||||
|             m_print->throw_if_canceled(); |             m_print->throw_if_canceled(); | ||||||
|             // loop through the other regions and 'steal' the slices belonging to this one
 |             // loop through the other regions and 'steal' the slices belonging to this one
 | ||||||
|             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; |             BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; | ||||||
|             for (size_t other_region_id = 0; other_region_id < this->print()->regions().size(); ++ other_region_id) { |             for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) { | ||||||
|                 if (region_id == other_region_id) |                 if (region_id == other_region_id) | ||||||
|                     continue; |                     continue; | ||||||
|                 for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { |                 for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { | ||||||
|  | @ -1496,7 +1497,7 @@ void PrintObject::_slice() | ||||||
|     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; |     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; | ||||||
|     while (! m_layers.empty()) { |     while (! m_layers.empty()) { | ||||||
|         const Layer *layer = m_layers.back(); |         const Layer *layer = m_layers.back(); | ||||||
|         for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) |         for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) | ||||||
|             if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty()) |             if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty()) | ||||||
|                 // Non empty layer.
 |                 // Non empty layer.
 | ||||||
|                 goto end; |                 goto end; | ||||||
|  | @ -1601,9 +1602,17 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, | ||||||
|         //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 |         //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||||
|         TriangleMesh mesh; |         TriangleMesh mesh; | ||||||
|         for (const ModelVolume *v : volumes) |         for (const ModelVolume *v : volumes) | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         { | ||||||
|  |             TriangleMesh vol_mesh(v->mesh); | ||||||
|  |             vol_mesh.transform(v->get_matrix()); | ||||||
|  |             mesh.merge(vol_mesh); | ||||||
|  |         } | ||||||
|  | #else | ||||||
|         mesh.merge(v->mesh); |         mesh.merge(v->mesh); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         if (mesh.stl.stats.number_of_facets > 0) { |         if (mesh.stl.stats.number_of_facets > 0) { | ||||||
|             mesh.transform(m_trafo.cast<float>()); |             mesh.transform(m_trafo); | ||||||
|             // apply XY shift
 |             // apply XY shift
 | ||||||
|             mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); |             mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); | ||||||
|             // perform actual slicing
 |             // perform actual slicing
 | ||||||
|  | @ -1734,8 +1743,8 @@ void PrintObject::_make_perimeters() | ||||||
|      |      | ||||||
|     // merge slices if they were split into types
 |     // merge slices if they were split into types
 | ||||||
|     if (this->typed_slices) { |     if (this->typed_slices) { | ||||||
|         FOREACH_LAYER(this, layer_it) |         for (Layer *layer : m_layers) | ||||||
|             (*layer_it)->merge_slices(); |             layer->merge_slices(); | ||||||
|         this->typed_slices = false; |         this->typed_slices = false; | ||||||
|         this->invalidate_step(posPrepareInfill); |         this->invalidate_step(posPrepareInfill); | ||||||
|     } |     } | ||||||
|  | @ -1747,7 +1756,7 @@ void PrintObject::_make_perimeters() | ||||||
|     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 |     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 | ||||||
|     // inside the object - infill_only_where_needed should be the method of choice for printing
 |     // inside the object - infill_only_where_needed should be the method of choice for printing
 | ||||||
|     // hollow objects
 |     // hollow objects
 | ||||||
|     for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         const PrintRegion ®ion = *m_print->regions()[region_id]; |         const PrintRegion ®ion = *m_print->regions()[region_id]; | ||||||
|         if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) |         if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) | ||||||
|             continue; |             continue; | ||||||
|  | @ -1919,7 +1928,7 @@ void PrintObject::discover_horizontal_shells() | ||||||
| { | { | ||||||
|     BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; |     BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; | ||||||
|      |      | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (int i = 0; i < int(m_layers.size()); ++ i) { |         for (int i = 0; i < int(m_layers.size()); ++ i) { | ||||||
|             m_print->throw_if_canceled(); |             m_print->throw_if_canceled(); | ||||||
|             LayerRegion             *layerm = m_layers[i]->regions()[region_id]; |             LayerRegion             *layerm = m_layers[i]->regions()[region_id]; | ||||||
|  | @ -2093,7 +2102,7 @@ void PrintObject::discover_horizontal_shells() | ||||||
|     } // for each region
 |     } // for each region
 | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         for (const Layer *layer : m_layers) { |         for (const Layer *layer : m_layers) { | ||||||
|             const LayerRegion *layerm = layer->m_regions[region_id]; |             const LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); |             layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); | ||||||
|  | @ -2109,7 +2118,7 @@ void PrintObject::discover_horizontal_shells() | ||||||
| void PrintObject::combine_infill() | void PrintObject::combine_infill() | ||||||
| { | { | ||||||
|     // Work on each region separately.
 |     // Work on each region separately.
 | ||||||
|     for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) { |     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||||
|         const PrintRegion *region = this->print()->regions()[region_id]; |         const PrintRegion *region = this->print()->regions()[region_id]; | ||||||
|         const int every = region->config().infill_every_layers.value; |         const int every = region->config().infill_every_layers.value; | ||||||
|         if (every < 2 || region->config().fill_density == 0.) |         if (every < 2 || region->config().fill_density == 0.) | ||||||
|  |  | ||||||
|  | @ -2450,7 +2450,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const | ||||||
|     // Transform loops into ExtrusionPath objects.
 |     // Transform loops into ExtrusionPath objects.
 | ||||||
|     extrusion_entities_append_paths( |     extrusion_entities_append_paths( | ||||||
|         top_contact_layer.extrusions, |         top_contact_layer.extrusions, | ||||||
|         STDMOVE(loop_lines), |         std::move(loop_lines), | ||||||
|         erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); |         erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2827,7 +2827,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( | ||||||
|                         to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing())); |                         to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing())); | ||||||
|                         extrusion_entities_append_paths( |                         extrusion_entities_append_paths( | ||||||
|                             support_layer.support_fills.entities,  |                             support_layer.support_fills.entities,  | ||||||
|                             to_polylines(STDMOVE(to_infill_polygons)), |                             to_polylines(std::move(to_infill_polygons)), | ||||||
|                             erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); |                             erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); | ||||||
|                     } |                     } | ||||||
|                     if (! to_infill.empty()) { |                     if (! to_infill.empty()) { | ||||||
|  | @ -2841,7 +2841,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( | ||||||
|                             // Destination
 |                             // Destination
 | ||||||
|                             support_layer.support_fills.entities,  |                             support_layer.support_fills.entities,  | ||||||
|                             // Regions to fill
 |                             // Regions to fill
 | ||||||
|                             STDMOVE(to_infill),  |                             std::move(to_infill),  | ||||||
|                             // Filler and its parameters
 |                             // Filler and its parameters
 | ||||||
|                             filler, float(support_density), |                             filler, float(support_density), | ||||||
|                             // Extrusion parameters
 |                             // Extrusion parameters
 | ||||||
|  | @ -3037,14 +3037,14 @@ void PrintObjectSupportMaterial::generate_toolpaths( | ||||||
|                     to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing())); |                     to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing())); | ||||||
|                     extrusion_entities_append_paths( |                     extrusion_entities_append_paths( | ||||||
|                         base_layer.extrusions,  |                         base_layer.extrusions,  | ||||||
|                         to_polylines(STDMOVE(to_infill_polygons)), |                         to_polylines(std::move(to_infill_polygons)), | ||||||
|                         erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); |                         erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); | ||||||
|                 } |                 } | ||||||
|                 fill_expolygons_generate_paths( |                 fill_expolygons_generate_paths( | ||||||
|                     // Destination
 |                     // Destination
 | ||||||
|                     base_layer.extrusions,  |                     base_layer.extrusions,  | ||||||
|                     // Regions to fill
 |                     // Regions to fill
 | ||||||
|                     STDMOVE(to_infill),  |                     std::move(to_infill),  | ||||||
|                     // Filler and its parameters
 |                     // Filler and its parameters
 | ||||||
|                     filler, density, |                     filler, density, | ||||||
|                     // Extrusion parameters
 |                     // Extrusion parameters
 | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
| // Uses a unique opengl context
 | // Uses a unique opengl context
 | ||||||
| #define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) | #define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) | ||||||
| // Disable synchronization of unselected instances
 | // Disable synchronization of unselected instances
 | ||||||
| #define DISABLE_INSTANCES_SYNCH (1 && ENABLE_1_42_0) | #define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) | ||||||
| // Modified camera target behavior
 | // Modified camera target behavior
 | ||||||
| #define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) | #define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) | ||||||
| // Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume
 | // Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume
 | ||||||
|  |  | ||||||
|  | @ -272,9 +272,9 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis) | ||||||
|     if (angle == 0.f) |     if (angle == 0.f) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     Vec3f axis_norm = axis.cast<float>().normalized(); |     Vec3d axis_norm = axis.normalized(); | ||||||
|     Transform3f m = Transform3f::Identity(); |     Transform3d m = Transform3d::Identity(); | ||||||
|     m.rotate(Eigen::AngleAxisf(angle, axis_norm)); |     m.rotate(Eigen::AngleAxisd(angle, axis_norm)); | ||||||
|     stl_transform(&stl, m); |     stl_transform(&stl, m); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -290,7 +290,7 @@ void TriangleMesh::mirror(const Axis &axis) | ||||||
|     stl_invalidate_shared_vertices(&this->stl); |     stl_invalidate_shared_vertices(&this->stl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TriangleMesh::transform(const Transform3f& t) | void TriangleMesh::transform(const Transform3d& t) | ||||||
| { | { | ||||||
|     stl_transform(&stl, t); |     stl_transform(&stl, t); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ public: | ||||||
|     void mirror_x() { this->mirror(X); } |     void mirror_x() { this->mirror(X); } | ||||||
|     void mirror_y() { this->mirror(Y); } |     void mirror_y() { this->mirror(Y); } | ||||||
|     void mirror_z() { this->mirror(Z); } |     void mirror_z() { this->mirror(Z); } | ||||||
|     void transform(const Transform3f& t); |     void transform(const Transform3d& t); | ||||||
|     void align_to_origin(); |     void align_to_origin(); | ||||||
|     void rotate(double angle, Point* center); |     void rotate(double angle, Point* center); | ||||||
|     TriangleMeshPtrs split() const; |     TriangleMeshPtrs split() const; | ||||||
|  |  | ||||||
|  | @ -47,19 +47,6 @@ typedef double  coordf_t; | ||||||
| #define scale_(val) ((val) / SCALING_FACTOR) | #define scale_(val) ((val) / SCALING_FACTOR) | ||||||
| #define SCALED_EPSILON scale_(EPSILON) | #define SCALED_EPSILON scale_(EPSILON) | ||||||
| 
 | 
 | ||||||
| // Which C++ version is supported?
 |  | ||||||
| // For example, could optimized functions with move semantics be used?
 |  | ||||||
| #if __cplusplus==201402L |  | ||||||
| 	#define SLIC3R_CPPVER 14 |  | ||||||
| 	#define STDMOVE(WHAT) std::move(WHAT) |  | ||||||
| #elif __cplusplus==201103L |  | ||||||
| 	#define SLIC3R_CPPVER 11 |  | ||||||
| 	#define STDMOVE(WHAT) std::move(WHAT) |  | ||||||
| #else |  | ||||||
| 	#define SLIC3R_CPPVER 0 |  | ||||||
| 	#define STDMOVE(WHAT) (WHAT) |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" | #define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" | ||||||
| 
 | 
 | ||||||
| inline std::string debug_out_path(const char *name, ...) | inline std::string debug_out_path(const char *name, ...) | ||||||
|  |  | ||||||
|  | @ -186,7 +186,7 @@ int main(int argc, char **argv) | ||||||
|             else |             else | ||||||
|                 // Remove the previous extension and add .3mf extention.
 |                 // Remove the previous extension and add .3mf extention.
 | ||||||
|                 outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf"; |                 outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf"; | ||||||
|             store_3mf(outfile.c_str(), &model, nullptr, false); |             store_3mf(outfile.c_str(), &model, nullptr); | ||||||
|             boost::nowide::cout << "File file exported to " << outfile << std::endl; |             boost::nowide::cout << "File file exported to " << outfile << std::endl; | ||||||
|         } else if (cli_config.cut > 0) { |         } else if (cli_config.cut > 0) { | ||||||
|             model.repair(); |             model.repair(); | ||||||
|  |  | ||||||
|  | @ -193,6 +193,7 @@ const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; | ||||||
| const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; | const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; | ||||||
| const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; | const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; | ||||||
| const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; | const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; | ||||||
|  | const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f }; | ||||||
| 
 | 
 | ||||||
| GLVolume::GLVolume(float r, float g, float b, float a) | GLVolume::GLVolume(float r, float g, float b, float a) | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  | @ -211,6 +212,7 @@ GLVolume::GLVolume(float r, float g, float b, float a) | ||||||
|     , composite_id(-1) |     , composite_id(-1) | ||||||
|     , extruder_id(0) |     , extruder_id(0) | ||||||
|     , selected(false) |     , selected(false) | ||||||
|  |     , disabled(false) | ||||||
|     , is_active(true) |     , is_active(true) | ||||||
|     , zoom_to_volumes(true) |     , zoom_to_volumes(true) | ||||||
|     , shader_outside_printer_detection_enabled(false) |     , shader_outside_printer_detection_enabled(false) | ||||||
|  | @ -252,6 +254,8 @@ void GLVolume::set_render_color() | ||||||
|         set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); |         set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); | ||||||
|     else if (hover) |     else if (hover) | ||||||
|         set_render_color(HOVER_COLOR, 4); |         set_render_color(HOVER_COLOR, 4); | ||||||
|  |     else if (disabled) | ||||||
|  |         set_render_color(DISABLED_COLOR, 4); | ||||||
|     else if (is_outside && shader_outside_printer_detection_enabled) |     else if (is_outside && shader_outside_printer_detection_enabled) | ||||||
|         set_render_color(OUTSIDE_COLOR, 4); |         set_render_color(OUTSIDE_COLOR, 4); | ||||||
|     else |     else | ||||||
|  | @ -723,7 +727,11 @@ std::vector<int> GLVolumeCollection::load_object( | ||||||
| 
 | 
 | ||||||
|         for (int instance_idx : instance_idxs) { |         for (int instance_idx : instance_idxs) { | ||||||
|             const ModelInstance *instance = model_object->instances[instance_idx]; |             const ModelInstance *instance = model_object->instances[instance_idx]; | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             const TriangleMesh& mesh = model_volume->mesh; | ||||||
|  | #else | ||||||
|             TriangleMesh mesh = model_volume->mesh; |             TriangleMesh mesh = model_volume->mesh; | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|             volumes_idx.push_back(int(this->volumes.size())); |             volumes_idx.push_back(int(this->volumes.size())); | ||||||
|             float color[4]; |             float color[4]; | ||||||
|             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); |             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); | ||||||
|  | @ -758,7 +766,8 @@ std::vector<int> GLVolumeCollection::load_object( | ||||||
|             v.is_modifier = ! model_volume->is_model_part(); |             v.is_modifier = ! model_volume->is_model_part(); | ||||||
|             v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); |             v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|             v.set_transformation(instance->get_transformation()); |             v.set_instance_transformation(instance->get_transformation()); | ||||||
|  |             v.set_volume_transformation(model_volume->get_transformation()); | ||||||
| #else | #else | ||||||
|             v.set_offset(instance->get_offset()); |             v.set_offset(instance->get_offset()); | ||||||
|             v.set_rotation(instance->get_rotation()); |             v.set_rotation(instance->get_rotation()); | ||||||
|  | @ -833,7 +842,11 @@ int GLVolumeCollection::load_wipe_tower_preview( | ||||||
|     else |     else | ||||||
|         v.indexed_vertex_array.load_mesh_flat_shading(mesh); |         v.indexed_vertex_array.load_mesh_flat_shading(mesh); | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); | ||||||
|  | #else | ||||||
|     v.set_offset(Vec3d(pos_x, pos_y, 0.0)); |     v.set_offset(Vec3d(pos_x, pos_y, 0.0)); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
 |     // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
 | ||||||
|     v.bounding_box = v.indexed_vertex_array.bounding_box(); |     v.bounding_box = v.indexed_vertex_array.bounding_box(); | ||||||
|  |  | ||||||
|  | @ -249,13 +249,15 @@ public: | ||||||
|     static const float HOVER_COLOR[4]; |     static const float HOVER_COLOR[4]; | ||||||
|     static const float OUTSIDE_COLOR[4]; |     static const float OUTSIDE_COLOR[4]; | ||||||
|     static const float SELECTED_OUTSIDE_COLOR[4]; |     static const float SELECTED_OUTSIDE_COLOR[4]; | ||||||
|  |     static const float DISABLED_COLOR[4]; | ||||||
| 
 | 
 | ||||||
|     GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); |     GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); | ||||||
|     GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} |     GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     Geometry::Transformation m_transformation; |     Geometry::Transformation m_instance_transformation; | ||||||
|  |     Geometry::Transformation m_volume_transformation; | ||||||
| #else | #else | ||||||
|     // Offset of the volume to be rendered.
 |     // Offset of the volume to be rendered.
 | ||||||
|     Vec3d                 m_offset; |     Vec3d                 m_offset; | ||||||
|  | @ -294,6 +296,8 @@ public: | ||||||
|     int                 extruder_id; |     int                 extruder_id; | ||||||
|     // Is this object selected?
 |     // Is this object selected?
 | ||||||
|     bool                selected; |     bool                selected; | ||||||
|  |     // Is this object disabled from selection?
 | ||||||
|  |     bool                disabled; | ||||||
|     // Whether or not this volume is active for rendering
 |     // Whether or not this volume is active for rendering
 | ||||||
|     bool                is_active; |     bool                is_active; | ||||||
|     // Whether or not to use this volume when applying zoom_to_volumes()
 |     // Whether or not to use this volume when applying zoom_to_volumes()
 | ||||||
|  | @ -329,32 +333,59 @@ public: | ||||||
|     void set_render_color(); |     void set_render_color(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     const Geometry::Transformation& get_transformation() const { return m_transformation; } |     const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } | ||||||
|     void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; set_bounding_boxes_as_dirty(); } |     void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } | ||||||
| 
 | 
 | ||||||
|     const Vec3d& get_offset() const { return m_transformation.get_offset(); } |     const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); } | ||||||
|     double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } |     double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); } | ||||||
| 
 | 
 | ||||||
|     void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } |     void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } | ||||||
|     void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } |     void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } | ||||||
| 
 | 
 | ||||||
|     const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } |     const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); } | ||||||
|     double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } |     double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); } | ||||||
| 
 | 
 | ||||||
|     void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } |     void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } | ||||||
|     void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } |     void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } | ||||||
| 
 | 
 | ||||||
|     Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } |     Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); } | ||||||
|     double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } |     double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); } | ||||||
| 
 | 
 | ||||||
|     void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } |     void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } | ||||||
|     void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } |     void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } | ||||||
| 
 | 
 | ||||||
|     const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } |     const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); } | ||||||
|     double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } |     double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); } | ||||||
| 
 | 
 | ||||||
|     void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } |     void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } | ||||||
|     void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } |     void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } | ||||||
|  | 
 | ||||||
|  |     const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; } | ||||||
|  |     void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); } | ||||||
|  | 
 | ||||||
|  |     const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); } | ||||||
|  |     double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); } | ||||||
|  | 
 | ||||||
|  |     void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } | ||||||
|  |     void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } | ||||||
|  | 
 | ||||||
|  |     const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); } | ||||||
|  |     double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); } | ||||||
|  | 
 | ||||||
|  |     void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } | ||||||
|  |     void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } | ||||||
|  | 
 | ||||||
|  |     Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); } | ||||||
|  |     double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); } | ||||||
|  | 
 | ||||||
|  |     void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } | ||||||
|  |     void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } | ||||||
|  | 
 | ||||||
|  |     const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); } | ||||||
|  |     double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); } | ||||||
|  | 
 | ||||||
|  |     void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } | ||||||
|  |     void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } | ||||||
| #else | #else | ||||||
|     const Vec3d& get_rotation() const; |     const Vec3d& get_rotation() const; | ||||||
|     void set_rotation(const Vec3d& rotation); |     void set_rotation(const Vec3d& rotation); | ||||||
|  | @ -378,7 +409,7 @@ public: | ||||||
|     int                 instance_idx() const { return this->composite_id % 1000; } |     int                 instance_idx() const { return this->composite_id % 1000; } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_MODELVOLUME_TRANSFORM | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     const Transform3d&   world_matrix() const { return m_transformation.get_matrix(); } |     Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } | ||||||
| #else | #else | ||||||
|     const Transform3f&   world_matrix() const; |     const Transform3f&   world_matrix() const; | ||||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include "BackgroundSlicingProcess.hpp" | #include "BackgroundSlicingProcess.hpp" | ||||||
| #include "GUI_App.hpp" | #include "GUI_App.hpp" | ||||||
| 
 | 
 | ||||||
|  | #include <wx/app.h> | ||||||
| #include <wx/event.h> | #include <wx/event.h> | ||||||
| #include <wx/panel.h> | #include <wx/panel.h> | ||||||
| #include <wx/stdpaths.h> | #include <wx/stdpaths.h> | ||||||
|  | @ -103,6 +104,15 @@ void BackgroundSlicingProcess::thread_proc() | ||||||
| 	// End of the background processing thread. The UI thread should join m_thread now.
 | 	// End of the background processing thread. The UI thread should join m_thread now.
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void BackgroundSlicingProcess::thread_proc_safe() | ||||||
|  | { | ||||||
|  | 	try { | ||||||
|  | 		this->thread_proc(); | ||||||
|  | 	} catch (...) { | ||||||
|  | 		wxTheApp->OnUnhandledException(); | ||||||
|  |    	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void BackgroundSlicingProcess::join_background_thread() | void BackgroundSlicingProcess::join_background_thread() | ||||||
| { | { | ||||||
| 	std::unique_lock<std::mutex> lck(m_mutex); | 	std::unique_lock<std::mutex> lck(m_mutex); | ||||||
|  | @ -127,7 +137,7 @@ bool BackgroundSlicingProcess::start() | ||||||
| 	if (m_state == STATE_INITIAL) { | 	if (m_state == STATE_INITIAL) { | ||||||
| 		// The worker thread is not running yet. Start it.
 | 		// The worker thread is not running yet. Start it.
 | ||||||
| 		assert(! m_thread.joinable()); | 		assert(! m_thread.joinable()); | ||||||
| 		m_thread = std::thread([this]{this->thread_proc();}); | 		m_thread = std::thread([this]{this->thread_proc_safe();}); | ||||||
| 		// Wait until the worker thread is ready to execute the background processing task.
 | 		// Wait until the worker thread is ready to execute the background processing task.
 | ||||||
| 		m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); | 		m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -80,6 +80,7 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	void 	thread_proc(); | 	void 	thread_proc(); | ||||||
|  | 	void 	thread_proc_safe(); | ||||||
| 	void 	join_background_thread(); | 	void 	join_background_thread(); | ||||||
| 	// To be called by Print::apply() through the Print::m_cancel_callback to stop the background
 | 	// To be called by Print::apply() through the Print::m_cancel_callback to stop the background
 | ||||||
| 	// processing before changing any data of running or finalized milestones.
 | 	// processing before changing any data of running or finalized milestones.
 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -358,9 +358,14 @@ public: | ||||||
| 
 | 
 | ||||||
|         enum EMode : unsigned char |         enum EMode : unsigned char | ||||||
|         { |         { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             Volume, | ||||||
|  |             Instance | ||||||
|  | #else | ||||||
|             Volume, |             Volume, | ||||||
|             Instance, |             Instance, | ||||||
|             Object |             Object | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         enum EType : unsigned char |         enum EType : unsigned char | ||||||
|  | @ -368,9 +373,13 @@ public: | ||||||
|             Invalid, |             Invalid, | ||||||
|             Empty, |             Empty, | ||||||
|             WipeTower, |             WipeTower, | ||||||
|             Modifier, |             SingleModifier, | ||||||
|  |             MultipleModifier, | ||||||
|  |             SingleVolume, | ||||||
|  |             MultipleVolume, | ||||||
|             SingleFullObject, |             SingleFullObject, | ||||||
|             SingleFullInstance, |             SingleFullInstance, | ||||||
|  |             MultipleFullInstance, | ||||||
|             Mixed |             Mixed | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  | @ -378,21 +387,57 @@ public: | ||||||
|         struct VolumeCache |         struct VolumeCache | ||||||
|         { |         { | ||||||
|         private: |         private: | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             struct TransformCache | ||||||
|  |             { | ||||||
|  |                 Vec3d position; | ||||||
|  |                 Vec3d rotation; | ||||||
|  |                 Vec3d scaling_factor; | ||||||
|  |                 Transform3d rotation_matrix; | ||||||
|  |                 Transform3d scale_matrix; | ||||||
|  | 
 | ||||||
|  |                 TransformCache(); | ||||||
|  |                 explicit TransformCache(const Geometry::Transformation& transform); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             TransformCache m_volume; | ||||||
|  |             TransformCache m_instance; | ||||||
|  | #else | ||||||
|             Vec3d m_position; |             Vec3d m_position; | ||||||
|             Vec3d m_rotation; |             Vec3d m_rotation; | ||||||
|             Vec3d m_scaling_factor; |             Vec3d m_scaling_factor; | ||||||
|             Transform3d m_rotation_matrix; |             Transform3d m_rotation_matrix; | ||||||
|             Transform3d m_scale_matrix; |             Transform3d m_scale_matrix; | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|         public: |         public: | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             VolumeCache() {} | ||||||
|  |             VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); | ||||||
|  | #else | ||||||
|             VolumeCache(); |             VolumeCache(); | ||||||
|             VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); |             VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             const Vec3d& get_volume_position() const { return m_volume.position; } | ||||||
|  |             const Vec3d& get_volume_rotation() const { return m_volume.rotation; } | ||||||
|  |             const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } | ||||||
|  |             const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } | ||||||
|  |             const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } | ||||||
|  | 
 | ||||||
|  |             const Vec3d& get_instance_position() const { return m_instance.position; } | ||||||
|  |             const Vec3d& get_instance_rotation() const { return m_instance.rotation; } | ||||||
|  |             const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } | ||||||
|  |             const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } | ||||||
|  |             const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } | ||||||
|  | #else | ||||||
|             const Vec3d& get_position() const { return m_position; } |             const Vec3d& get_position() const { return m_position; } | ||||||
|             const Vec3d& get_rotation() const { return m_rotation; } |             const Vec3d& get_rotation() const { return m_rotation; } | ||||||
|             const Vec3d& get_scaling_factor() const { return m_scaling_factor; } |             const Vec3d& get_scaling_factor() const { return m_scaling_factor; } | ||||||
|             const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } |             const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } | ||||||
|             const Transform3d& get_scale_matrix() const { return m_scale_matrix; } |             const Transform3d& get_scale_matrix() const { return m_scale_matrix; } | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         typedef std::map<unsigned int, VolumeCache> VolumesCache; |         typedef std::map<unsigned int, VolumeCache> VolumesCache; | ||||||
|  | @ -435,14 +480,14 @@ public: | ||||||
|         void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); |         void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); | ||||||
|         void remove_instance(unsigned int object_idx, unsigned int instance_idx); |         void remove_instance(unsigned int object_idx, unsigned int instance_idx); | ||||||
| 
 | 
 | ||||||
|         void add_volume(unsigned int object_idx, unsigned int volume_idx, bool as_single_selection = true); |         void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); | ||||||
|         void remove_volume(unsigned int object_idx, unsigned int volume_idx); |         void remove_volume(unsigned int object_idx, unsigned int volume_idx); | ||||||
| 
 | 
 | ||||||
|         void clear(); |         void clear(); | ||||||
| 
 | 
 | ||||||
|         bool is_empty() const { return m_type == Empty; } |         bool is_empty() const { return m_type == Empty; } | ||||||
|         bool is_wipe_tower() const { return m_type == WipeTower; } |         bool is_wipe_tower() const { return m_type == WipeTower; } | ||||||
|         bool is_modifier() const { return m_type == Modifier; } |         bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } | ||||||
|         bool is_single_full_instance() const; |         bool is_single_full_instance() const; | ||||||
|         bool is_single_full_object() const { return m_type == SingleFullObject; } |         bool is_single_full_object() const { return m_type == SingleFullObject; } | ||||||
|         bool is_mixed() const { return m_type == Mixed; } |         bool is_mixed() const { return m_type == Mixed; } | ||||||
|  | @ -455,6 +500,9 @@ public: | ||||||
|         int get_object_idx() const; |         int get_object_idx() const; | ||||||
|         // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1
 |         // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1
 | ||||||
|         int get_instance_idx() const; |         int get_instance_idx() const; | ||||||
|  |         // Returns the indices of selected instances.
 | ||||||
|  |         // Can only be called if selection is from a single object.
 | ||||||
|  |         const InstanceIdxsList& get_instance_idxs() const; | ||||||
| 
 | 
 | ||||||
|         const IndicesList& get_volume_idxs() const { return m_list; } |         const IndicesList& get_volume_idxs() const { return m_list; } | ||||||
|         const GLVolume* get_volume(unsigned int volume_idx) const; |         const GLVolume* get_volume(unsigned int volume_idx) const; | ||||||
|  | @ -466,13 +514,14 @@ public: | ||||||
| 
 | 
 | ||||||
|         void translate(const Vec3d& displacement); |         void translate(const Vec3d& displacement); | ||||||
|         void rotate(const Vec3d& rotation); |         void rotate(const Vec3d& rotation); | ||||||
|  |         void flattening_rotate(const Vec3d& normal); | ||||||
|         void scale(const Vec3d& scale); |         void scale(const Vec3d& scale); | ||||||
|         void mirror(Axis axis); |         void mirror(Axis axis); | ||||||
| 
 | 
 | ||||||
|         void translate(unsigned int object_idx, const Vec3d& displacement); |         void translate(unsigned int object_idx, const Vec3d& displacement); | ||||||
|         void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); |         void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); | ||||||
| 
 | 
 | ||||||
|         void render(bool show_indirect_selection) const; |         void render() const; | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         void _update_valid(); |         void _update_valid(); | ||||||
|  | @ -486,9 +535,10 @@ public: | ||||||
|         void _remove_object(unsigned int object_idx); |         void _remove_object(unsigned int object_idx); | ||||||
|         void _calc_bounding_box() const; |         void _calc_bounding_box() const; | ||||||
|         void _render_selected_volumes() const; |         void _render_selected_volumes() const; | ||||||
|         void _render_unselected_instances() const; |         void _render_synchronized_volumes() const; | ||||||
|         void _render_bounding_box(const BoundingBoxf3& box, float* color) const; |         void _render_bounding_box(const BoundingBoxf3& box, float* color) const; | ||||||
|         void _synchronize_unselected_instances(); |         void _synchronize_unselected_instances(); | ||||||
|  |         void _synchronize_unselected_volumes(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | @ -556,7 +606,7 @@ private: | ||||||
|         Vec3d get_rotation() const; |         Vec3d get_rotation() const; | ||||||
|         void set_rotation(const Vec3d& rotation); |         void set_rotation(const Vec3d& rotation); | ||||||
| 
 | 
 | ||||||
|         Vec3d get_flattening_rotation() const; |         Vec3d get_flattening_normal() const; | ||||||
| 
 | 
 | ||||||
|         void set_flattening_data(const ModelObject* model_object); |         void set_flattening_data(const ModelObject* model_object); | ||||||
|          |          | ||||||
|  |  | ||||||
|  | @ -720,7 +720,11 @@ void GLGizmoScale3D::on_process_double_click() | ||||||
| void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const | void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const | ||||||
| { | { | ||||||
|     bool single_instance = selection.is_single_full_instance(); |     bool single_instance = selection.is_single_full_instance(); | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>(); | ||||||
|  | #else | ||||||
|     Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>(); |     Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>(); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     if ((single_instance && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) |     if ((single_instance && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) | ||||||
|         set_tooltip("X: " + format(scale(0), 4) + "%"); |         set_tooltip("X: " + format(scale(0), 4) + "%"); | ||||||
|  | @ -762,10 +766,18 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const | ||||||
| #endif // ENABLE_MODELVOLUME_TRANSFORM
 | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|         // gets angles from first selected volume
 |         // gets angles from first selected volume
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         angles = v->get_instance_rotation(); | ||||||
|  | #else | ||||||
|         angles = v->get_rotation(); |         angles = v->get_rotation(); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|         // consider rotation+mirror only components of the transform for offsets
 |         // consider rotation+mirror only components of the transform for offsets
 | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); | ||||||
|  | #else | ||||||
|         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); |         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|         box = selection.get_bounding_box(); |         box = selection.get_bounding_box(); | ||||||
|  | @ -1165,40 +1177,43 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) | ||||||
| 
 | 
 | ||||||
| void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const | void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const | ||||||
| { | { | ||||||
|     // the dragged_offset is a vector measuring where was the object moved
 |     // The planes are rendered incorrectly when the object is being moved. We better won't render anything in that case.
 | ||||||
|     // with the gizmo being on. This is reset in set_flattening_data and
 |     // This indeed has a better solution (to be implemented when there is more time)
 | ||||||
|     // does not work correctly when there are multiple copies.
 |  | ||||||
|     Vec3d dragged_offset(Vec3d::Zero()); |     Vec3d dragged_offset(Vec3d::Zero()); | ||||||
|     if (m_starting_center == Vec3d::Zero()) |     if (m_starting_center == Vec3d::Zero()) | ||||||
|         m_starting_center = selection.get_bounding_box().center(); |         m_starting_center = selection.get_bounding_box().center(); | ||||||
|     dragged_offset = selection.get_bounding_box().center() - m_starting_center; |     dragged_offset = selection.get_bounding_box().center() - m_starting_center; | ||||||
|  |     if (dragged_offset.norm() > 0.001) | ||||||
|  |         return; | ||||||
| 
 | 
 | ||||||
|     ::glEnable(GL_BLEND); |     ::glEnable(GL_BLEND); | ||||||
|     ::glEnable(GL_DEPTH_TEST); |     ::glEnable(GL_DEPTH_TEST); | ||||||
|     ::glDisable(GL_CULL_FACE); |     ::glDisable(GL_CULL_FACE); | ||||||
| 
 | 
 | ||||||
|  |     if (selection.is_from_single_object()) { | ||||||
|  |         const std::set<int>& instances_list = selection.get_instance_idxs(); | ||||||
|  | 
 | ||||||
|  |         if (!instances_list.empty() && m_model_object) { | ||||||
|  |             for (const int instance_idx : instances_list) { | ||||||
|  |             Transform3d m = m_model_object->instances[instance_idx]->get_matrix(); | ||||||
|                 for (int i=0; i<(int)m_planes.size(); ++i) { |                 for (int i=0; i<(int)m_planes.size(); ++i) { | ||||||
|                     if (i == m_hover_id) |                     if (i == m_hover_id) | ||||||
|                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); |                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); | ||||||
|                     else |                     else | ||||||
|                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); |                         ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); | ||||||
| 
 | 
 | ||||||
|         int instance_idx = selection.get_instance_idx(); |  | ||||||
|         if ((instance_idx != -1) && (m_model_object != nullptr)) |  | ||||||
|         { |  | ||||||
|             Transform3d m = m_model_object->instances[instance_idx]->get_matrix(); |  | ||||||
|                     m.pretranslate(dragged_offset); |                     m.pretranslate(dragged_offset); | ||||||
|                     ::glPushMatrix(); |                     ::glPushMatrix(); | ||||||
|                     ::glMultMatrixd(m.data()); |                     ::glMultMatrixd(m.data()); | ||||||
|                     ::glBegin(GL_POLYGON); |                     ::glBegin(GL_POLYGON); | ||||||
|                     for (const Vec3d& vertex : m_planes[i].vertices) |                     for (const Vec3d& vertex : m_planes[i].vertices) | ||||||
|             { |  | ||||||
|                         ::glVertex3dv(vertex.data()); |                         ::glVertex3dv(vertex.data()); | ||||||
|             } |  | ||||||
|                     ::glEnd(); |                     ::glEnd(); | ||||||
|                     ::glPopMatrix(); |                     ::glPopMatrix(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     ::glEnable(GL_CULL_FACE); |     ::glEnable(GL_CULL_FACE); | ||||||
|     ::glDisable(GL_BLEND); |     ::glDisable(GL_BLEND); | ||||||
|  | @ -1208,24 +1223,23 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio | ||||||
| { | { | ||||||
|     ::glEnable(GL_DEPTH_TEST); |     ::glEnable(GL_DEPTH_TEST); | ||||||
|     ::glDisable(GL_CULL_FACE); |     ::glDisable(GL_CULL_FACE); | ||||||
| 
 |     if (selection.is_from_single_object()) { | ||||||
|     for (unsigned int i = 0; i < m_planes.size(); ++i) |         const std::set<int>& instances_list = selection.get_instance_idxs(); | ||||||
|     { |         if (!instances_list.empty() && m_model_object) { | ||||||
|  |             for (const int instance_idx : instances_list) { | ||||||
|  |                 for (int i=0; i<(int)m_planes.size(); ++i) { | ||||||
|                     ::glColor3f(1.0f, 1.0f, picking_color_component(i)); |                     ::glColor3f(1.0f, 1.0f, picking_color_component(i)); | ||||||
|         int instance_idx = selection.get_instance_idx(); |  | ||||||
|         if ((instance_idx != -1) && (m_model_object != nullptr)) |  | ||||||
|         { |  | ||||||
|                     ::glPushMatrix(); |                     ::glPushMatrix(); | ||||||
|                     ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); |                     ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); | ||||||
|                     ::glBegin(GL_POLYGON); |                     ::glBegin(GL_POLYGON); | ||||||
|                     for (const Vec3d& vertex : m_planes[i].vertices) |                     for (const Vec3d& vertex : m_planes[i].vertices) | ||||||
|             { |  | ||||||
|                         ::glVertex3dv(vertex.data()); |                         ::glVertex3dv(vertex.data()); | ||||||
|             } |  | ||||||
|                     ::glEnd(); |                     ::glEnd(); | ||||||
|                     ::glPopMatrix(); |                     ::glPopMatrix(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     ::glEnable(GL_CULL_FACE); |     ::glEnable(GL_CULL_FACE); | ||||||
| } | } | ||||||
|  | @ -1233,9 +1247,10 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio | ||||||
| void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) | void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) | ||||||
| { | { | ||||||
|     m_starting_center = Vec3d::Zero(); |     m_starting_center = Vec3d::Zero(); | ||||||
|  |     bool object_changed = m_model_object != model_object; | ||||||
|     m_model_object = model_object; |     m_model_object = model_object; | ||||||
| 
 | 
 | ||||||
|     if (is_plane_update_necessary()) |     if (object_changed && is_plane_update_necessary()) | ||||||
|         update_planes(); |         update_planes(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1243,7 +1258,15 @@ void GLGizmoFlatten::update_planes() | ||||||
| { | { | ||||||
|     TriangleMesh ch; |     TriangleMesh ch; | ||||||
|     for (const ModelVolume* vol : m_model_object->volumes) |     for (const ModelVolume* vol : m_model_object->volumes) | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     { | ||||||
|  |         TriangleMesh vol_ch = vol->get_convex_hull(); | ||||||
|  |         vol_ch.transform(vol->get_matrix()); | ||||||
|  |         ch.merge(vol_ch); | ||||||
|  |     } | ||||||
|  | #else | ||||||
|         ch.merge(vol->get_convex_hull()); |         ch.merge(vol->get_convex_hull()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|     ch = ch.convex_hull_3d(); |     ch = ch.convex_hull_3d(); | ||||||
| 
 | 
 | ||||||
|  | @ -1438,20 +1461,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Vec3d GLGizmoFlatten::get_flattening_rotation() const | Vec3d GLGizmoFlatten::get_flattening_normal() const | ||||||
| { | { | ||||||
|     // calculates the rotations in model space, taking in account the scaling factors
 |     Vec3d out = m_normal; | ||||||
|     Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = m_model_object->instances.front()->get_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose(); |  | ||||||
|     Eigen::Quaterniond q; |  | ||||||
|     Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix()); |  | ||||||
|     m_normal = Vec3d::Zero(); |     m_normal = Vec3d::Zero(); | ||||||
|     m_starting_center = Vec3d::Zero(); |     m_starting_center = Vec3d::Zero(); | ||||||
|     return angles; |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent) | GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent) | ||||||
|     : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()) |     : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()) | ||||||
| { | { | ||||||
|  | @ -1478,11 +1495,14 @@ bool GLGizmoSlaSupports::on_init() | ||||||
| 
 | 
 | ||||||
| void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) | void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) | ||||||
| { | { | ||||||
|  |     if (model_object != nullptr) | ||||||
|  |     { | ||||||
|         m_starting_center = Vec3d::Zero(); |         m_starting_center = Vec3d::Zero(); | ||||||
|         m_model_object = model_object; |         m_model_object = model_object; | ||||||
|         m_model_object_matrix = model_object->instances.front()->get_matrix(); |         m_model_object_matrix = model_object->instances.front()->get_matrix(); | ||||||
|         if (is_mesh_update_necessary()) |         if (is_mesh_update_necessary()) | ||||||
|             update_mesh(); |             update_mesh(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const | void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const | ||||||
|  |  | ||||||
|  | @ -359,6 +359,7 @@ private: | ||||||
|     std::vector<PlaneData> m_planes; |     std::vector<PlaneData> m_planes; | ||||||
|     mutable Vec3d m_starting_center; |     mutable Vec3d m_starting_center; | ||||||
|     const ModelObject* m_model_object = nullptr; |     const ModelObject* m_model_object = nullptr; | ||||||
|  |     std::vector<const Transform3d*> instances_matrices; | ||||||
| 
 | 
 | ||||||
|     void update_planes(); |     void update_planes(); | ||||||
|     bool is_plane_update_necessary() const; |     bool is_plane_update_necessary() const; | ||||||
|  | @ -367,12 +368,12 @@ public: | ||||||
|     explicit GLGizmoFlatten(GLCanvas3D& parent); |     explicit GLGizmoFlatten(GLCanvas3D& parent); | ||||||
| 
 | 
 | ||||||
|     void set_flattening_data(const ModelObject* model_object); |     void set_flattening_data(const ModelObject* model_object); | ||||||
|     Vec3d get_flattening_rotation() const; |     Vec3d get_flattening_normal() const; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     virtual bool on_init(); |     virtual bool on_init(); | ||||||
|     virtual std::string on_get_name() const; |     virtual std::string on_get_name() const; | ||||||
|     virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return selection.is_single_full_instance(); } |     virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return (selection.is_from_single_object() && !selection.is_wipe_tower() && !selection.is_modifier());  } | ||||||
|     virtual void on_start_dragging(const GLCanvas3D::Selection& selection); |     virtual void on_start_dragging(const GLCanvas3D::Selection& selection); | ||||||
|     virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {} |     virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {} | ||||||
|     virtual void on_render(const GLCanvas3D::Selection& selection) const; |     virtual void on_render(const GLCanvas3D::Selection& selection) const; | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ enum ConfigMenuIDs { | ||||||
| 
 | 
 | ||||||
| class Tab; | class Tab; | ||||||
| 
 | 
 | ||||||
| static wxString dots("…", wxConvUTF8); | static wxString dots("…", wxConvUTF8); | ||||||
| 
 | 
 | ||||||
| class GUI_App : public wxApp | class GUI_App : public wxApp | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -36,11 +36,11 @@ ObjectList::ObjectList(wxWindow* parent) : | ||||||
| 		CATEGORY_ICON[L("Advanced")]				= wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG); | 		CATEGORY_ICON[L("Advanced")]				= wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     init_icons(); |  | ||||||
| 
 |  | ||||||
|     // create control
 |     // create control
 | ||||||
|     create_objects_ctrl(); |     create_objects_ctrl(); | ||||||
| 
 | 
 | ||||||
|  |     init_icons(); | ||||||
|  | 
 | ||||||
|     // describe control behavior 
 |     // describe control behavior 
 | ||||||
|     Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { |     Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { | ||||||
|         selection_changed(); |         selection_changed(); | ||||||
|  | @ -213,17 +213,27 @@ void ObjectList::update_extruder_in_config(const wxString& selection) | ||||||
| 
 | 
 | ||||||
| void ObjectList::init_icons() | void ObjectList::init_icons() | ||||||
| { | { | ||||||
|     m_bmp_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
 |     m_bmp_modifiermesh      = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
 | ||||||
|     m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
 |     m_bmp_solidmesh         = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
 | ||||||
|  | 
 | ||||||
|  |     m_bmp_support_enforcer  = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG); | ||||||
|  |     m_bmp_support_blocker   = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG); | ||||||
|  | 
 | ||||||
|  |     m_bmp_vector.reserve(4); // bitmaps for different types of parts 
 | ||||||
|  |     m_bmp_vector.push_back(&m_bmp_solidmesh);         // Add part
 | ||||||
|  |     m_bmp_vector.push_back(&m_bmp_modifiermesh);      // Add modifier
 | ||||||
|  |     m_bmp_vector.push_back(&m_bmp_support_enforcer);  // Add support enforcer
 | ||||||
|  |     m_bmp_vector.push_back(&m_bmp_support_blocker);   // Add support blocker
 | ||||||
|  |     m_objects_model->SetVolumeBitmaps(m_bmp_vector); | ||||||
| 
 | 
 | ||||||
|     // init icon for manifold warning
 |     // init icon for manifold warning
 | ||||||
|     m_bmp_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
 |     m_bmp_manifold_warning  = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
 | ||||||
| 
 | 
 | ||||||
|     // init bitmap for "Split to sub-objects" context menu
 |     // init bitmap for "Split to sub-objects" context menu
 | ||||||
|     m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); |     m_bmp_split             = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG); | ||||||
| 
 | 
 | ||||||
|     // init bitmap for "Add Settings" context menu
 |     // init bitmap for "Add Settings" context menu
 | ||||||
|     m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); |     m_bmp_cog               = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -377,7 +387,8 @@ void ObjectList::on_drop(wxDataViewEvent &event) | ||||||
|     wxDataViewItem item(event.GetItem()); |     wxDataViewItem item(event.GetItem()); | ||||||
| 
 | 
 | ||||||
|     // only allow drops for item, not containers
 |     // only allow drops for item, not containers
 | ||||||
|     if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || |     if (m_selected_object_id < 0 || | ||||||
|  |         item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || | ||||||
|         event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) { |         event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) { | ||||||
|         event.Veto(); |         event.Veto(); | ||||||
|         return; |         return; | ||||||
|  | @ -518,16 +529,24 @@ void ObjectList::get_settings_choice(wxMenu *menu, int id, bool is_part) | ||||||
|         wxGetApp().obj_manipul()->update_settings_list(); |         wxGetApp().obj_manipul()->update_settings_list(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) { | void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id, const int type) { | ||||||
|     auto sub_menu = new wxMenu; |     auto sub_menu = new wxMenu; | ||||||
| 
 | 
 | ||||||
|  |     const wxString menu_load = _(L("Load")) +" "+ dots; | ||||||
|  |     sub_menu->Append(new wxMenuItem(sub_menu, id++, menu_load)); | ||||||
|  |     sub_menu->AppendSeparator(); | ||||||
|  | 
 | ||||||
|     std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; |     std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; | ||||||
|     for (auto& item : menu_items) |     for (auto& item : menu_items) | ||||||
|         sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); |         sub_menu->Append(new wxMenuItem(sub_menu, id++, _(item))); | ||||||
| 
 | 
 | ||||||
| #ifndef __WXMSW__ | #ifndef __WXMSW__ | ||||||
|     sub_menu->Bind(wxEVT_MENU, [this, sub_menu](wxEvent &event) { |     sub_menu->Bind(wxEVT_MENU, [sub_menu, type, menu_load, this](wxEvent &event) { | ||||||
|         load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); |         auto selection = sub_menu->GetLabel(event.GetId()); | ||||||
|  |         if (selection == menu_load) | ||||||
|  |             load_subobject(type); | ||||||
|  |         else | ||||||
|  |             load_generic_subobject(selection.ToStdString(), type); | ||||||
|     }); |     }); | ||||||
| #endif //no __WXMSW__
 | #endif //no __WXMSW__
 | ||||||
| 
 | 
 | ||||||
|  | @ -552,55 +571,53 @@ wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_p | ||||||
| wxMenu* ObjectList::create_add_part_popupmenu() | wxMenu* ObjectList::create_add_part_popupmenu() | ||||||
| { | { | ||||||
|     wxMenu *menu = new wxMenu; |     wxMenu *menu = new wxMenu; | ||||||
|     std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; |     // Note: id accords to type of the sub-object, so sequence of the menu items is important
 | ||||||
|  |     std::vector<std::string> menu_object_types_items = {L("Add part"),              // ~ModelVolume::MODEL_PART
 | ||||||
|  |                                                         L("Add modifier"),          // ~ModelVolume::PARAMETER_MODIFIER
 | ||||||
|  |                                                         L("Add support enforcer"),  // ~ModelVolume::SUPPORT_ENFORCER
 | ||||||
|  |                                                         L("Add support bloker") };  // ~ModelVolume::SUPPORT_BLOCKER
 | ||||||
|      |      | ||||||
|     wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size() + 4 + 2); |     const int obj_types_count = menu_object_types_items.size(); | ||||||
|  |     const int generics_count = 5; // "Load ...", "Box", "Cylinder", "Sphere", "Slab"
 | ||||||
| 
 | 
 | ||||||
|     int i = 0; |     wxWindowID config_id_base = NewControlId(generics_count*obj_types_count + 2); | ||||||
|     for (auto& item : menu_items) { | 
 | ||||||
|         auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); |     // Add first 4 menu items
 | ||||||
|         menu_item->SetBitmap(i == 0 ? m_bmp_solidmesh : m_bmp_modifiermesh); |     for (int type = 0; type < obj_types_count; type++) { | ||||||
|         if (item == "Add generic") |         auto& item = menu_object_types_items[type]; | ||||||
|             menu_item_add_generic(menu_item, config_id_base + i); |         auto menu_item = new wxMenuItem(menu, config_id_base + type, _(item)); | ||||||
|  |         menu_item->SetBitmap(*m_bmp_vector[type]); | ||||||
|  |         menu_item_add_generic(menu_item, config_id_base + type*generics_count, type); | ||||||
|         menu->Append(menu_item); |         menu->Append(menu_item); | ||||||
|         i++; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Split object to parts
 | ||||||
|     menu->AppendSeparator(); |     menu->AppendSeparator(); | ||||||
|     auto menu_item = menu_item_split(menu, config_id_base + i + 4); |     auto menu_item = menu_item_split(menu, config_id_base + obj_types_count * generics_count); | ||||||
|     menu->Append(menu_item); |     menu->Append(menu_item); | ||||||
|     menu_item->Enable(is_splittable_object(false)); |     menu_item->Enable(is_splittable_object(false)); | ||||||
| 
 | 
 | ||||||
|  |     // Settings
 | ||||||
|     menu->AppendSeparator(); |     menu->AppendSeparator(); | ||||||
|     // Append settings popupmenu
 |     // Append settings popupmenu
 | ||||||
|     menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); |     menu->Append(menu_item_settings(menu, config_id_base + obj_types_count * generics_count+1, false)); | ||||||
| 
 | 
 | ||||||
|     menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { |     menu->Bind(wxEVT_MENU, [config_id_base, menu, obj_types_count, generics_count, this](wxEvent &event) { | ||||||
|         switch (event.GetId() - config_id_base) { |         auto selection = event.GetId() - config_id_base; | ||||||
|         case 0: |          | ||||||
|             load_subobject(); |         if ( selection ==  0 * generics_count ||  // ~ModelVolume::MODEL_PART
 | ||||||
|             break; |              selection ==  1 * generics_count ||  // ~ModelVolume::PARAMETER_MODIFIER
 | ||||||
|         case 1: |              selection ==  2 * generics_count ||  // ~ModelVolume::SUPPORT_ENFORCER
 | ||||||
|             load_subobject(true); |              selection ==  3 * generics_count  )  // ~ModelVolume::SUPPORT_BLOCKER
 | ||||||
|             break; |             load_subobject(int(selection / generics_count)); | ||||||
|         case 2: |         else if ( selection == obj_types_count * generics_count) | ||||||
|         case 3: |  | ||||||
|         case 4: |  | ||||||
|         case 5: |  | ||||||
|         case 6: |  | ||||||
| #ifdef __WXMSW__ |  | ||||||
|             load_lambda(menu->GetLabel(event.GetId()).ToStdString()); |  | ||||||
| #endif // __WXMSW__
 |  | ||||||
|             break; |  | ||||||
|         case 7: //3:
 |  | ||||||
|             split(false); |             split(false); | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
| #ifdef __WXMSW__ | #ifdef __WXMSW__ | ||||||
|  |         else if ( selection > obj_types_count * generics_count) // "Add Settings" is selected 
 | ||||||
|             get_settings_choice(menu, event.GetId(), false); |             get_settings_choice(menu, event.GetId(), false); | ||||||
|  |         else // Some generic model is selected
 | ||||||
|  |             load_generic_subobject(menu->GetLabel(event.GetId()).ToStdString(), int(selection / generics_count)); | ||||||
| #endif // __WXMSW__
 | #endif // __WXMSW__
 | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     return menu; |     return menu; | ||||||
|  | @ -609,24 +626,33 @@ wxMenu* ObjectList::create_add_part_popupmenu() | ||||||
| wxMenu* ObjectList::create_part_settings_popupmenu() | wxMenu* ObjectList::create_part_settings_popupmenu() | ||||||
| { | { | ||||||
|     wxMenu *menu = new wxMenu; |     wxMenu *menu = new wxMenu; | ||||||
|     wxWindowID config_id_base = wxWindow::NewControlId(2); |     wxWindowID config_id_base = NewControlId(3); | ||||||
| 
 | 
 | ||||||
|     auto menu_item = menu_item_split(menu, config_id_base); |     auto menu_item = menu_item_split(menu, config_id_base); | ||||||
|     menu->Append(menu_item); |     menu->Append(menu_item); | ||||||
|     menu_item->Enable(is_splittable_object(true)); |     menu_item->Enable(is_splittable_object(true)); | ||||||
| 
 | 
 | ||||||
|  |     // Append change part type
 | ||||||
|     menu->AppendSeparator(); |     menu->AppendSeparator(); | ||||||
|  |     menu->Append(new wxMenuItem(menu, config_id_base + 1, _(L("Change type")))); | ||||||
|  | 
 | ||||||
|     // Append settings popupmenu
 |     // Append settings popupmenu
 | ||||||
|     menu->Append(menu_item_settings(menu, config_id_base + 1, true)); |     menu->AppendSeparator(); | ||||||
|  |     menu_item = menu_item_settings(menu, config_id_base + 2, true); | ||||||
|  |     menu->Append(menu_item); | ||||||
|  |     menu_item->Enable(get_selected_model_volume()->type() <= ModelVolume::PARAMETER_MODIFIER); | ||||||
| 
 | 
 | ||||||
|     menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { |     menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { | ||||||
|         switch (event.GetId() - config_id_base) { |         switch (event.GetId() - config_id_base) { | ||||||
|         case 0: |         case 0: | ||||||
|             split(true); |             split(true); | ||||||
|             break; |             break; | ||||||
|         default:{ |         case 1: | ||||||
|  |             change_part_type(); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|             get_settings_choice(menu, event.GetId(), true); |             get_settings_choice(menu, event.GetId(), true); | ||||||
|             break; } |             break; | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -655,31 +681,21 @@ wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part) | ||||||
|     return menu; |     return menu; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | void ObjectList::load_subobject(int type) | ||||||
| // Load SubObjects (parts and modifiers)
 |  | ||||||
| void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = false*/) |  | ||||||
| { | { | ||||||
|     auto item = GetSelection(); |     auto item = GetSelection(); | ||||||
|     if (!item) |     if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) | ||||||
|         return; |  | ||||||
|     int obj_idx = -1; |  | ||||||
|     if (m_objects_model->GetParent(item) == wxDataViewItem(0)) |  | ||||||
|         obj_idx = m_objects_model->GetIdByItem(item); |  | ||||||
|     else |  | ||||||
|         return; |         return; | ||||||
|  |     int obj_idx = m_objects_model->GetIdByItem(item); | ||||||
| 
 | 
 | ||||||
|     if (obj_idx < 0) return; |     if (obj_idx < 0) return; | ||||||
|     wxArrayString part_names; |     wxArrayString part_names; | ||||||
|     if (is_lambda) |     load_part((*m_objects)[obj_idx], part_names, type); | ||||||
|         load_lambda((*m_objects)[obj_idx], part_names, is_modifier); |  | ||||||
|     else |  | ||||||
|         load_part((*m_objects)[obj_idx], part_names, is_modifier); |  | ||||||
| 
 | 
 | ||||||
|     parts_changed(obj_idx); |     parts_changed(obj_idx); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < part_names.size(); ++i) { |     for (int i = 0; i < part_names.size(); ++i) { | ||||||
|         const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), |         const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), /**m_bmp_vector[*/type/*]*/); | ||||||
|             is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh); |  | ||||||
| 
 | 
 | ||||||
|         if (i == part_names.size() - 1) |         if (i == part_names.size() - 1) | ||||||
|             select_item(sel_item); |             select_item(sel_item); | ||||||
|  | @ -688,11 +704,12 @@ void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = | ||||||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||||
| //     selection_changed();
 | //     selection_changed();
 | ||||||
| #endif //no __WXOSX__//__WXMSW__
 | #endif //no __WXOSX__//__WXMSW__
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ObjectList::load_part( ModelObject* model_object, | void ObjectList::load_part( ModelObject* model_object, | ||||||
|                             wxArrayString& part_names,  |                             wxArrayString& part_names,  | ||||||
|                             const bool is_modifier) |                             int type) | ||||||
| { | { | ||||||
|     wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); |     wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); | ||||||
| 
 | 
 | ||||||
|  | @ -717,19 +734,27 @@ void ObjectList::load_part( ModelObject* model_object, | ||||||
|             if (model_object->origin_translation != Vec3d::Zero()) |             if (model_object->origin_translation != Vec3d::Zero()) | ||||||
|             { |             { | ||||||
|                 object->center_around_origin(); |                 object->center_around_origin(); | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|                 object->ensure_on_bed(); |                 object->ensure_on_bed(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|                 delta = model_object->origin_translation - object->origin_translation; |                 delta = model_object->origin_translation - object->origin_translation; | ||||||
|             } |             } | ||||||
|             for (auto volume : object->volumes) { |             for (auto volume : object->volumes) { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |                 Vec3d shift = volume->mesh.bounding_box().center(); | ||||||
|  |                 volume->translate_geometry(-shift); | ||||||
|  |                 volume->translate(delta + shift); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|                 auto new_volume = model_object->add_volume(*volume); |                 auto new_volume = model_object->add_volume(*volume); | ||||||
|                 new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); |                 new_volume->set_type(static_cast<ModelVolume::Type>(type)); | ||||||
|                 boost::filesystem::path(input_file).filename().string(); |  | ||||||
|                 new_volume->name = boost::filesystem::path(input_file).filename().string(); |                 new_volume->name = boost::filesystem::path(input_file).filename().string(); | ||||||
| 
 | 
 | ||||||
|                 part_names.Add(new_volume->name); |                 part_names.Add(new_volume->name); | ||||||
| 
 | 
 | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|                 if (delta != Vec3d::Zero()) |                 if (delta != Vec3d::Zero()) | ||||||
|                     new_volume->translate(delta); |                     new_volume->translate(delta); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
| 
 | 
 | ||||||
|                 // set a default extruder value, since user can't add it manually
 |                 // set a default extruder value, since user can't add it manually
 | ||||||
|                 new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); |                 new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||||
|  | @ -738,98 +763,46 @@ void ObjectList::load_part( ModelObject* model_object, | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ObjectList::load_lambda(   ModelObject* model_object, | void ObjectList::load_generic_subobject(const std::string& type_name, const int type) | ||||||
|                                 wxArrayString& part_names,  |  | ||||||
|                                 const bool is_modifier) |  | ||||||
| { | { | ||||||
|     auto dlg = new LambdaObjectDialog(GetMainWindow()); |     const auto obj_idx = get_selected_obj_idx(); | ||||||
|     if (dlg->ShowModal() == wxID_CANCEL) { |     if (obj_idx < 0) return; | ||||||
|         m_parts_changed = false; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string name = "lambda-"; |  | ||||||
|     TriangleMesh mesh; |  | ||||||
| 
 |  | ||||||
|     auto params = dlg->ObjectParameters(); |  | ||||||
|     switch (params.type) |  | ||||||
|     { |  | ||||||
|     case LambdaTypeBox:{ |  | ||||||
|         mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); |  | ||||||
|         name += "Box"; |  | ||||||
|         break; } |  | ||||||
|     case LambdaTypeCylinder:{ |  | ||||||
|         mesh = make_cylinder(params.cyl_r, params.cyl_h); |  | ||||||
|         name += "Cylinder"; |  | ||||||
|         break; } |  | ||||||
|     case LambdaTypeSphere:{ |  | ||||||
|         mesh = make_sphere(params.sph_rho); |  | ||||||
|         name += "Sphere"; |  | ||||||
|         break; } |  | ||||||
|     case LambdaTypeSlab:{ |  | ||||||
|         const auto& size = model_object->bounding_box().size(); |  | ||||||
|         mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); |  | ||||||
|         // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 |  | ||||||
|         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); |  | ||||||
|         name += "Slab"; |  | ||||||
|         break; } |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     mesh.repair(); |  | ||||||
| 
 |  | ||||||
|     auto new_volume = model_object->add_volume(mesh); |  | ||||||
|     new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); |  | ||||||
| 
 |  | ||||||
|     new_volume->name = name; |  | ||||||
|     // set a default extruder value, since user can't add it manually
 |  | ||||||
|     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); |  | ||||||
| 
 |  | ||||||
|     part_names.Add(name); |  | ||||||
| 
 |  | ||||||
|     m_parts_changed = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ObjectList::load_lambda(const std::string& type_name) |  | ||||||
| { |  | ||||||
|     if (m_selected_object_id < 0) return; |  | ||||||
| 
 |  | ||||||
|     auto dlg = new LambdaObjectDialog(GetMainWindow(), type_name); |  | ||||||
|     if (dlg->ShowModal() == wxID_CANCEL) |  | ||||||
|         return; |  | ||||||
| 
 | 
 | ||||||
|     const std::string name = "lambda-" + type_name; |     const std::string name = "lambda-" + type_name; | ||||||
|     TriangleMesh mesh; |     TriangleMesh mesh; | ||||||
| 
 | 
 | ||||||
|     const auto params = dlg->ObjectParameters(); |     auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionPoints>("bed_shape")->values; | ||||||
|  |     const auto& sz = BoundingBoxf(bed_shape).size(); | ||||||
|  |     const auto side = 0.1 * std::max(sz(0), sz(1)); | ||||||
|  | 
 | ||||||
|     if (type_name == _("Box")) |     if (type_name == _("Box")) | ||||||
|         mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); |         mesh = make_cube(side, side, side); | ||||||
|     else if (type_name == _("Cylinder")) |     else if (type_name == _("Cylinder")) | ||||||
|         mesh = make_cylinder(params.cyl_r, params.cyl_h); |         mesh = make_cylinder(0.5*side, side); | ||||||
|     else if (type_name == _("Sphere")) |     else if (type_name == _("Sphere")) | ||||||
|         mesh = make_sphere(params.sph_rho); |         mesh = make_sphere(side, PI/18); | ||||||
|     else if (type_name == _("Slab")) { |     else if (type_name == _("Slab")) { | ||||||
|         const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); |         const auto& size = (*m_objects)[obj_idx]->bounding_box().size(); | ||||||
|         mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); |         mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5); | ||||||
|         // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 |         // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 | ||||||
|         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); |         mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0); | ||||||
|     } |     } | ||||||
|     mesh.repair(); |     mesh.repair(); | ||||||
| 
 | 
 | ||||||
|     auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); |     auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); | ||||||
|     new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); |     new_volume->set_type(static_cast<ModelVolume::Type>(type)); | ||||||
| 
 | 
 | ||||||
|     new_volume->name = name; |     new_volume->name = name; | ||||||
|     // set a default extruder value, since user can't add it manually
 |     // set a default extruder value, since user can't add it manually
 | ||||||
|     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); |     new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); | ||||||
| 
 | 
 | ||||||
|     m_parts_changed = true; |     m_parts_changed = true; | ||||||
|     parts_changed(m_selected_object_id); |     parts_changed(obj_idx); | ||||||
| 
 | 
 | ||||||
|     select_item(m_objects_model->AddVolumeChild(GetSelection(), |     select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type)); | ||||||
|         name, m_bmp_modifiermesh)); |  | ||||||
| #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
 | ||||||
|     selection_changed(); |     selection_changed(); | ||||||
| #endif //no __WXOSX__ //__WXMSW__
 | #endif //no __WXOSX__ //__WXMSW__
 | ||||||
|  | @ -927,8 +900,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con | ||||||
| void ObjectList::split(const bool split_part) | void ObjectList::split(const bool split_part) | ||||||
| { | { | ||||||
|     const auto item = GetSelection(); |     const auto item = GetSelection(); | ||||||
|     if (!item || m_selected_object_id < 0) |     const int obj_idx = get_selected_obj_idx(); | ||||||
|  |     if (!item || obj_idx < 0) | ||||||
|         return; |         return; | ||||||
|  | 
 | ||||||
|     ModelVolume* volume; |     ModelVolume* volume; | ||||||
|     if (!get_volume_by_item(split_part, item, volume)) return; |     if (!get_volume_by_item(split_part, item, volume)) return; | ||||||
|     DynamicPrintConfig&	config = wxGetApp().preset_bundle->printers.get_edited_preset().config; |     DynamicPrintConfig&	config = wxGetApp().preset_bundle->printers.get_edited_preset().config; | ||||||
|  | @ -938,52 +913,54 @@ void ObjectList::split(const bool split_part) | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto model_object = (*m_objects)[m_selected_object_id]; |     auto model_object = (*m_objects)[obj_idx]; | ||||||
| 
 | 
 | ||||||
|     if (split_part) { |     auto parent = m_objects_model->GetTopParent(item); | ||||||
|         auto parent = m_objects_model->GetParent(item); |     if (parent) | ||||||
|         m_objects_model->DeleteChildren(parent); |         m_objects_model->DeleteVolumeChildren(parent); | ||||||
|  |     else | ||||||
|  |         parent = item; | ||||||
| 
 | 
 | ||||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) |     for (auto id = 0; id < model_object->volumes.size(); id++) { | ||||||
|             m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, |         const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, | ||||||
|             model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh, |                                             model_object->volumes[id]->is_modifier() ?  | ||||||
|  |                                                 ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, | ||||||
|                                             model_object->volumes[id]->config.has("extruder") ? |                                             model_object->volumes[id]->config.has("extruder") ? | ||||||
|                                                 model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, |                                                 model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, | ||||||
|                                             false); |                                             false); | ||||||
|  |         // add settings to the part, if it has those
 | ||||||
|  |         auto opt_keys = model_object->volumes[id]->config.keys(); | ||||||
|  |         if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { | ||||||
|  |             select_item(m_objects_model->AddSettingsChild(vol_item)); | ||||||
|  |             Collapse(vol_item); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     if (parent == item) | ||||||
|         Expand(parent); |         Expand(parent); | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) |  | ||||||
|             m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, |  | ||||||
|             m_bmp_solidmesh, |  | ||||||
|             model_object->volumes[id]->config.has("extruder") ? |  | ||||||
|             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, |  | ||||||
|             false); |  | ||||||
|         Expand(item); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     m_parts_changed = true; |     m_parts_changed = true; | ||||||
|     parts_changed(m_selected_object_id); |     parts_changed(obj_idx); | ||||||
| 
 |  | ||||||
|     // restores selection
 |  | ||||||
|     _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection().add_object(m_selected_object_id); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) | bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) | ||||||
| { | { | ||||||
|     if (!item || m_selected_object_id < 0) |     auto obj_idx = get_selected_obj_idx(); | ||||||
|  |     if (!item || obj_idx < 0) | ||||||
|         return false; |         return false; | ||||||
|     const auto volume_id = m_objects_model->GetVolumeIdByItem(item); |     const auto volume_id = m_objects_model->GetVolumeIdByItem(item); | ||||||
|  | 
 | ||||||
|  |     // object is selected
 | ||||||
|     if (volume_id < 0) { |     if (volume_id < 0) { | ||||||
|         if (split_part) return false; |         if ( split_part || (*m_objects)[obj_idx]->volumes.size() > 1 )  | ||||||
|         volume = (*m_objects)[m_selected_object_id]->volumes[0]; |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|         volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; |  | ||||||
|     if (volume) |  | ||||||
|         return true; |  | ||||||
|             return false; |             return false; | ||||||
|  |         volume = (*m_objects)[obj_idx]->volumes[0]; | ||||||
|  |     } | ||||||
|  |     // volume is selected
 | ||||||
|  |     else | ||||||
|  |         volume = (*m_objects)[obj_idx]->volumes[volume_id]; | ||||||
|  |      | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ObjectList::is_splittable_object(const bool split_part) | bool ObjectList::is_splittable_object(const bool split_part) | ||||||
|  | @ -991,20 +968,14 @@ bool ObjectList::is_splittable_object(const bool split_part) | ||||||
|     const wxDataViewItem item = GetSelection(); |     const wxDataViewItem item = GetSelection(); | ||||||
|     if (!item) return false; |     if (!item) return false; | ||||||
| 
 | 
 | ||||||
|     wxDataViewItemArray children; |  | ||||||
|     if (!split_part && m_objects_model->GetChildren(item, children) > 0) |  | ||||||
|         return false; |  | ||||||
| 
 |  | ||||||
|     ModelVolume* volume; |     ModelVolume* volume; | ||||||
|     if (!get_volume_by_item(split_part, item, volume) || !volume) |     if (!get_volume_by_item(split_part, item, volume) || !volume) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     TriangleMeshPtrs meshptrs = volume->mesh.split(); |     TriangleMeshPtrs meshptrs = volume->mesh.split(); | ||||||
|     bool splittable = meshptrs.size() > 1; |     bool splittable = meshptrs.size() > 1; | ||||||
|     for (TriangleMesh* m : meshptrs) |     for (TriangleMesh* m : meshptrs) { delete m; } | ||||||
|     { | 
 | ||||||
|         delete m; |  | ||||||
|     } |  | ||||||
|     return splittable; |     return splittable; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1017,7 +988,7 @@ void ObjectList::part_settings_changed() | ||||||
| 
 | 
 | ||||||
| void ObjectList::parts_changed(int obj_idx) | void ObjectList::parts_changed(int obj_idx) | ||||||
| { | { | ||||||
|     wxGetApp().plater()->changed_object(get_selected_obj_idx()); |     wxGetApp().plater()->changed_object(obj_idx); | ||||||
|     m_parts_changed = false; |     m_parts_changed = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1105,19 +1076,28 @@ void ObjectList::add_object_to_list(size_t obj_idx) | ||||||
|         m_objects_model->SetValue(variant, item, 0); |         m_objects_model->SetValue(variant, item, 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // add volumes to the object
 | ||||||
|     if (model_object->volumes.size() > 1) { |     if (model_object->volumes.size() > 1) { | ||||||
|         for (auto id = 0; id < model_object->volumes.size(); id++) |         for (auto id = 0; id < model_object->volumes.size(); id++) | ||||||
|             m_objects_model->AddVolumeChild(item, |             m_objects_model->AddVolumeChild(item, | ||||||
|             model_object->volumes[id]->name, |             model_object->volumes[id]->name, | ||||||
|             m_bmp_solidmesh, |             ModelVolume::MODEL_PART, | ||||||
|             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, |             model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, | ||||||
|             false); |             false); | ||||||
|         Expand(item); |         Expand(item); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // add instances to the object, if it has those
 | ||||||
|     if (model_object->instances.size()>1) |     if (model_object->instances.size()>1) | ||||||
|         increase_object_instances(obj_idx, model_object->instances.size()); |         increase_object_instances(obj_idx, model_object->instances.size()); | ||||||
| 
 | 
 | ||||||
|  |     // add settings to the object, if it has those
 | ||||||
|  |     auto opt_keys = model_object->config.keys(); | ||||||
|  |     if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { | ||||||
|  |         select_item(m_objects_model->AddSettingsChild(item)); | ||||||
|  |         Collapse(item); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| #ifndef __WXOSX__  | #ifndef __WXOSX__  | ||||||
|     selection_changed(); |     selection_changed(); | ||||||
| #endif //__WXMSW__
 | #endif //__WXMSW__
 | ||||||
|  | @ -1227,11 +1207,18 @@ void ObjectList::update_selections() | ||||||
|     auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); |     auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); | ||||||
|     wxDataViewItemArray sels; |     wxDataViewItemArray sels; | ||||||
| 
 | 
 | ||||||
|     for (auto idx: selection.get_volume_idxs()) |     if (selection.is_single_full_object()) { | ||||||
|     { |         for (auto idx : selection.get_volume_idxs()) { | ||||||
|             const auto gl_vol = selection.get_volume(idx); |             const auto gl_vol = selection.get_volume(idx); | ||||||
|             sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); |             sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |     else if (selection.is_single_full_instance()) { | ||||||
|  |         for (auto idx : selection.get_instance_idxs()) {             | ||||||
|  |             sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|     select_items(sels); |     select_items(sels); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1256,7 +1243,7 @@ void ObjectList::update_selections_on_canvas() | ||||||
|         if (m_objects_model->GetItemType(item) == itVolume) { |         if (m_objects_model->GetItemType(item) == itVolume) { | ||||||
|             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); |             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); | ||||||
|             const int vol_idx = m_objects_model->GetVolumeIdByItem(item); |             const int vol_idx = m_objects_model->GetVolumeIdByItem(item); | ||||||
|             selection.add_volume(obj_idx, vol_idx, as_single_selection); |             selection.add_volume(obj_idx, vol_idx, 0, as_single_selection); | ||||||
|         } |         } | ||||||
|         else if (m_objects_model->GetItemType(item) == itInstance) { |         else if (m_objects_model->GetItemType(item) == itInstance) { | ||||||
|             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); |             const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); | ||||||
|  | @ -1334,5 +1321,53 @@ void ObjectList::fix_multiselection_conflicts() | ||||||
|     m_prevent_list_events = false; |     m_prevent_list_events = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ModelVolume* ObjectList::get_selected_model_volume() | ||||||
|  | { | ||||||
|  |     auto item = GetSelection(); | ||||||
|  |     if (!item || m_objects_model->GetItemType(item) != itVolume) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     const auto vol_idx = m_objects_model->GetVolumeIdByItem(item); | ||||||
|  |     const auto obj_idx = get_selected_obj_idx(); | ||||||
|  |     if (vol_idx < 0 || obj_idx < 0) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     return (*m_objects)[obj_idx]->volumes[vol_idx]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ObjectList::change_part_type() | ||||||
|  | { | ||||||
|  |     ModelVolume* volume = get_selected_model_volume(); | ||||||
|  |     if (!volume) | ||||||
|  |         return; | ||||||
|  |     const auto type = volume->type(); | ||||||
|  | 
 | ||||||
|  |     const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" }; | ||||||
|  |      | ||||||
|  |     auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type); | ||||||
|  | 
 | ||||||
|  |     if (new_type == type || new_type < 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     const auto item = GetSelection(); | ||||||
|  |     volume->set_type(static_cast<ModelVolume::Type>(new_type)); | ||||||
|  |     m_objects_model->SetVolumeType(item, new_type); | ||||||
|  | 
 | ||||||
|  |     m_parts_changed = true; | ||||||
|  |     parts_changed(get_selected_obj_idx()); | ||||||
|  | 
 | ||||||
|  |     // Update settings showing, if we have it
 | ||||||
|  |     //(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
 | ||||||
|  |     const auto settings_item = m_objects_model->GetSettingsItem(item); | ||||||
|  |     if (settings_item &&  | ||||||
|  |         new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) { | ||||||
|  |         m_objects_model->Delete(settings_item); | ||||||
|  |     } | ||||||
|  |     else if (!settings_item &&  | ||||||
|  |               new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) { | ||||||
|  |         select_item(m_objects_model->AddSettingsChild(item)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } //namespace GUI
 | } //namespace GUI
 | ||||||
| } //namespace Slic3r 
 | } //namespace Slic3r 
 | ||||||
|  | @ -28,10 +28,14 @@ class ObjectList : public wxDataViewCtrl | ||||||
| 
 | 
 | ||||||
|     wxBitmap	m_bmp_modifiermesh; |     wxBitmap	m_bmp_modifiermesh; | ||||||
|     wxBitmap	m_bmp_solidmesh; |     wxBitmap	m_bmp_solidmesh; | ||||||
|  |     wxBitmap	m_bmp_support_enforcer; | ||||||
|  |     wxBitmap	m_bmp_support_blocker; | ||||||
|     wxBitmap	m_bmp_manifold_warning; |     wxBitmap	m_bmp_manifold_warning; | ||||||
|     wxBitmap	m_bmp_cog; |     wxBitmap	m_bmp_cog; | ||||||
|     wxBitmap	m_bmp_split; |     wxBitmap	m_bmp_split; | ||||||
| 
 | 
 | ||||||
|  |     std::vector<wxBitmap*> m_bmp_vector; | ||||||
|  | 
 | ||||||
|     int			m_selected_object_id = -1; |     int			m_selected_object_id = -1; | ||||||
|     bool		m_prevent_list_events = false;		// We use this flag to avoid circular event handling Select() 
 |     bool		m_prevent_list_events = false;		// We use this flag to avoid circular event handling Select() 
 | ||||||
|                                                     // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler 
 |                                                     // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler 
 | ||||||
|  | @ -78,17 +82,16 @@ public: | ||||||
|     void                on_drop(wxDataViewEvent &event); |     void                on_drop(wxDataViewEvent &event); | ||||||
| 
 | 
 | ||||||
|     void                get_settings_choice(wxMenu *menu, int id, bool is_part); |     void                get_settings_choice(wxMenu *menu, int id, bool is_part); | ||||||
|     void                menu_item_add_generic(wxMenuItem* &menu, int id); |     void                menu_item_add_generic(wxMenuItem* &menu, int id, const int type); | ||||||
|     wxMenuItem*         menu_item_split(wxMenu* menu, int id); |     wxMenuItem*         menu_item_split(wxMenu* menu, int id); | ||||||
|     wxMenuItem*         menu_item_settings(wxMenu* menu, int id, const bool is_part); |     wxMenuItem*         menu_item_settings(wxMenu* menu, int id, const bool is_part); | ||||||
|     wxMenu*             create_add_part_popupmenu(); |     wxMenu*             create_add_part_popupmenu(); | ||||||
|     wxMenu*             create_part_settings_popupmenu(); |     wxMenu*             create_part_settings_popupmenu(); | ||||||
|     wxMenu*             create_add_settings_popupmenu(bool is_part); |     wxMenu*             create_add_settings_popupmenu(bool is_part); | ||||||
| 
 | 
 | ||||||
|     void                load_subobject(bool is_modifier = false, bool is_lambda = false); |     void                load_subobject(int type); | ||||||
|     void                load_part(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); |     void                load_part(ModelObject* model_object, wxArrayString& part_names, int type); | ||||||
|     void                load_lambda(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); |     void                load_generic_subobject(const std::string& type_name, const int type); | ||||||
|     void                load_lambda(const std::string& type_name); |  | ||||||
|     void                del_subobject_item(wxDataViewItem& item); |     void                del_subobject_item(wxDataViewItem& item); | ||||||
|     void                del_settings_from_config(); |     void                del_settings_from_config(); | ||||||
|     void                del_instances_from_object(const int obj_idx); |     void                del_instances_from_object(const int obj_idx); | ||||||
|  | @ -142,6 +145,9 @@ public: | ||||||
|     void select_all(); |     void select_all(); | ||||||
|     // correct current selections to avoid of the possible conflicts
 |     // correct current selections to avoid of the possible conflicts
 | ||||||
|     void fix_multiselection_conflicts(); |     void fix_multiselection_conflicts(); | ||||||
|  | 
 | ||||||
|  |     ModelVolume* get_selected_model_volume(); | ||||||
|  |     void change_part_type(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -268,6 +268,9 @@ void ObjectManipulation::update_settings_list() | ||||||
| 
 | 
 | ||||||
| void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) | void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) | ||||||
| { | { | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |     if (selection.is_single_full_instance()) | ||||||
|  | #else | ||||||
|     if (selection.is_single_full_object()) |     if (selection.is_single_full_object()) | ||||||
|     { |     { | ||||||
|         auto obj_idx = selection.get_object_idx(); |         auto obj_idx = selection.get_object_idx(); | ||||||
|  | @ -284,30 +287,49 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele | ||||||
|             reset_settings_value(); |             reset_settings_value(); | ||||||
|     } |     } | ||||||
|     else if (selection.is_single_full_instance()) |     else if (selection.is_single_full_instance()) | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     { |     { | ||||||
|         // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
 |         // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
 | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         update_position_value(volume->get_instance_offset()); | ||||||
|  |         update_rotation_value(volume->get_instance_rotation()); | ||||||
|  |         update_scale_value(volume->get_instance_scaling_factor()); | ||||||
|  | #else | ||||||
|         update_position_value(volume->get_offset()); |         update_position_value(volume->get_offset()); | ||||||
|         update_rotation_value(volume->get_rotation()); |         update_rotation_value(volume->get_rotation()); | ||||||
|         update_scale_value(volume->get_scaling_factor()); |         update_scale_value(volume->get_scaling_factor()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         m_og->enable(); |         m_og->enable(); | ||||||
|     } |     } | ||||||
|     else if (selection.is_wipe_tower()) |     else if (selection.is_wipe_tower()) | ||||||
|     { |     { | ||||||
|         // the selection contains a single volume
 |         // the selection contains a single volume
 | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         update_position_value(volume->get_volume_offset()); | ||||||
|  |         update_rotation_value(volume->get_volume_rotation()); | ||||||
|  |         update_scale_value(volume->get_volume_scaling_factor()); | ||||||
|  | #else | ||||||
|         update_position_value(volume->get_offset()); |         update_position_value(volume->get_offset()); | ||||||
|         update_rotation_value(volume->get_rotation()); |         update_rotation_value(volume->get_rotation()); | ||||||
|         update_scale_value(volume->get_scaling_factor()); |         update_scale_value(volume->get_scaling_factor()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         m_og->enable(); |         m_og->enable(); | ||||||
|     } |     } | ||||||
|     else if (selection.is_modifier()) |     else if (selection.is_modifier()) | ||||||
|     { |     { | ||||||
|         // the selection contains a single volume
 |         // the selection contains a single volume
 | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |         update_position_value(volume->get_volume_offset()); | ||||||
|  |         update_rotation_value(volume->get_volume_rotation()); | ||||||
|  |         update_scale_value(volume->get_volume_scaling_factor()); | ||||||
|  | #else | ||||||
|         update_position_value(volume->get_offset()); |         update_position_value(volume->get_offset()); | ||||||
|         update_rotation_value(volume->get_rotation()); |         update_rotation_value(volume->get_rotation()); | ||||||
|         update_scale_value(volume->get_scaling_factor()); |         update_scale_value(volume->get_scaling_factor()); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         m_og->enable(); |         m_og->enable(); | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| #include "../../libslic3r/libslic3r.h" | #include "../../libslic3r/libslic3r.h" | ||||||
| #include "GUI_Preview.hpp" | #include "GUI_Preview.hpp" | ||||||
|  | #include "GUI_App.hpp" | ||||||
| #include "GUI.hpp" | #include "GUI.hpp" | ||||||
| #include "AppConfig.hpp" | #include "AppConfig.hpp" | ||||||
| #include "3DScene.hpp" | #include "3DScene.hpp" | ||||||
|  | @ -22,6 +23,7 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| namespace GUI { | namespace GUI { | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) | Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) | ||||||
|     : m_canvas(nullptr) |     : m_canvas(nullptr) | ||||||
|     , m_double_slider_sizer(nullptr) |     , m_double_slider_sizer(nullptr) | ||||||
|  | @ -482,6 +484,11 @@ void Preview::create_double_slider() | ||||||
|         if (IsShown()) |         if (IsShown()) | ||||||
|             m_canvas->Refresh(); |             m_canvas->Refresh(); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { | ||||||
|  |             auto& config = wxGetApp().preset_bundle->project_config; | ||||||
|  |             ((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues()); | ||||||
|  |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Preview::update_double_slider(bool force_sliders_full_range) | void Preview::update_double_slider(bool force_sliders_full_range) | ||||||
|  | @ -495,6 +502,11 @@ void Preview::update_double_slider(bool force_sliders_full_range) | ||||||
|     m_slider->SetMaxValue(layers_z.size() - 1); |     m_slider->SetMaxValue(layers_z.size() - 1); | ||||||
|     m_slider->SetSliderValues(values); |     m_slider->SetSliderValues(values); | ||||||
| 
 | 
 | ||||||
|  |     const auto& config = wxGetApp().preset_bundle->project_config; | ||||||
|  |     const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||||
|  | 
 | ||||||
|  |     m_slider->SetTicksValues(ticks_from_config); | ||||||
|  | 
 | ||||||
|     set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); |     set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -515,6 +527,15 @@ void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values, | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // All ticks that would end up outside the slider range should be erased.
 | ||||||
|  |     // TODO: this should probably be placed into more appropriate part of code,
 | ||||||
|  |     //  this way it relies on the Preview tab being active.
 | ||||||
|  |     auto& config = wxGetApp().preset_bundle->project_config; | ||||||
|  |     std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||||
|  |     ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), | ||||||
|  |                                            [values](double val) { return values.back().second < val; }), | ||||||
|  |                             ticks_from_config.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Preview::set_double_slider_thumbs(const bool force_sliders_full_range, | void Preview::set_double_slider_thumbs(const bool force_sliders_full_range, | ||||||
|  |  | ||||||
|  | @ -49,15 +49,15 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, | ||||||
| 
 | 
 | ||||||
| 		def.type = coFloat; | 		def.type = coFloat; | ||||||
| 		def.default_value = new ConfigOptionFloat{ 1.0 }; | 		def.default_value = new ConfigOptionFloat{ 1.0 }; | ||||||
| 		def.label = L("L"); | 		def.label = L("Length"); | ||||||
| 		Option option(def, "l"); | 		Option option(def, "l"); | ||||||
| 		optgroup->append_single_option_line(option); | 		optgroup->append_single_option_line(option); | ||||||
| 		 | 		 | ||||||
| 		def.label = L("W"); | 		def.label = L("Width"); | ||||||
| 		option = Option(def, "w"); | 		option = Option(def, "w"); | ||||||
| 		optgroup->append_single_option_line(option); | 		optgroup->append_single_option_line(option); | ||||||
| 		 | 		 | ||||||
| 		def.label = L("H"); | 		def.label = L("Height"); | ||||||
| 		option = Option(def, "h"); | 		option = Option(def, "h"); | ||||||
| 		optgroup->append_single_option_line(option); | 		optgroup->append_single_option_line(option); | ||||||
| 	} | 	} | ||||||
|  | @ -112,7 +112,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, | ||||||
| 
 | 
 | ||||||
| 		def.type = coFloat; | 		def.type = coFloat; | ||||||
| 		def.default_value = new ConfigOptionFloat{ 1.0 }; | 		def.default_value = new ConfigOptionFloat{ 1.0 }; | ||||||
| 		def.label = L("H"); | 		def.label = L("Height"); | ||||||
| 		auto option = Option(def, "slab_h"); | 		auto option = Option(def, "slab_h"); | ||||||
| 		optgroup->append_single_option_line(option); | 		optgroup->append_single_option_line(option); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -596,8 +596,7 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) | ||||||
|         //     if (Slic3r::GUI::catch_error(this))
 |         //     if (Slic3r::GUI::catch_error(this))
 | ||||||
|         //         return;
 |         //         return;
 | ||||||
|     } |     } | ||||||
|     for (auto tab : m_options_tabs ) | 	wxGetApp().load_current_presets(); | ||||||
|         tab.second->load_current_preset(); |  | ||||||
|     wxGetApp().app_config->update_config_dir(get_dir_name(file)); |     wxGetApp().app_config->update_config_dir(get_dir_name(file)); | ||||||
|     m_last_config = file; |     m_last_config = file; | ||||||
| } | } | ||||||
|  | @ -659,8 +658,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Load the currently selected preset into the GUI, update the preset selection box.
 |     // Load the currently selected preset into the GUI, update the preset selection box.
 | ||||||
|     for (auto tab : m_options_tabs) | 	wxGetApp().load_current_presets(); | ||||||
|         tab.second->load_current_preset(); |  | ||||||
| 
 | 
 | ||||||
|     const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); |     const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); | ||||||
|     Slic3r::GUI::show_info(this, message, "Info"); |     Slic3r::GUI::show_info(this, message, "Info"); | ||||||
|  |  | ||||||
|  | @ -22,18 +22,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co | ||||||
|     // is the normal type.
 |     // is the normal type.
 | ||||||
|     if (opt.gui_type.compare("select") == 0) { |     if (opt.gui_type.compare("select") == 0) { | ||||||
|     } else if (opt.gui_type.compare("select_open") == 0) { |     } else if (opt.gui_type.compare("select_open") == 0) { | ||||||
| 		m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | 		m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||||
|     } else if (opt.gui_type.compare("color") == 0) { |     } else if (opt.gui_type.compare("color") == 0) { | ||||||
| 		m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id))); | 		m_fields.emplace(id, std::move(ColourPicker::Create<ColourPicker>(parent(), opt, id))); | ||||||
|     } else if (opt.gui_type.compare("f_enum_open") == 0 ||  |     } else if (opt.gui_type.compare("f_enum_open") == 0 ||  | ||||||
|                 opt.gui_type.compare("i_enum_open") == 0 || |                 opt.gui_type.compare("i_enum_open") == 0 || | ||||||
|                 opt.gui_type.compare("i_enum_closed") == 0) { |                 opt.gui_type.compare("i_enum_closed") == 0) { | ||||||
| 		m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | 		m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||||
|     } else if (opt.gui_type.compare("slider") == 0) { |     } else if (opt.gui_type.compare("slider") == 0) { | ||||||
| 		m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id))); | 		m_fields.emplace(id, std::move(SliderCtrl::Create<SliderCtrl>(parent(), opt, id))); | ||||||
|     } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
 |     } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
 | ||||||
|     } else if (opt.gui_type.compare("legend") == 0) { // StaticText
 |     } else if (opt.gui_type.compare("legend") == 0) { // StaticText
 | ||||||
| 		m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id))); | 		m_fields.emplace(id, std::move(StaticText::Create<StaticText>(parent(), opt, id))); | ||||||
|     } else {  |     } else {  | ||||||
|         switch (opt.type) { |         switch (opt.type) { | ||||||
|             case coFloatOrPercent: |             case coFloatOrPercent: | ||||||
|  | @ -43,21 +43,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co | ||||||
| 			case coPercents: | 			case coPercents: | ||||||
| 			case coString: | 			case coString: | ||||||
| 			case coStrings: | 			case coStrings: | ||||||
| 				m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id))); | 				m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id))); | ||||||
|                 break; |                 break; | ||||||
| 			case coBool: | 			case coBool: | ||||||
| 			case coBools: | 			case coBools: | ||||||
| 				m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id))); | 				m_fields.emplace(id, std::move(CheckBox::Create<CheckBox>(parent(), opt, id))); | ||||||
| 				break; | 				break; | ||||||
| 			case coInt: | 			case coInt: | ||||||
| 			case coInts: | 			case coInts: | ||||||
| 				m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id))); | 				m_fields.emplace(id, std::move(SpinCtrl::Create<SpinCtrl>(parent(), opt, id))); | ||||||
| 				break; | 				break; | ||||||
|             case coEnum: |             case coEnum: | ||||||
| 				m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); | 				m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id))); | ||||||
| 				break; | 				break; | ||||||
|             case coPoints: |             case coPoints: | ||||||
| 				m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id))); | 				m_fields.emplace(id, std::move(PointCtrl::Create<PointCtrl>(parent(), opt, id))); | ||||||
| 				break; | 				break; | ||||||
|             case coNone:   break; |             case coNone:   break; | ||||||
|             default: |             default: | ||||||
|  |  | ||||||
|  | @ -1154,16 +1154,25 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_ | ||||||
|         try { |         try { | ||||||
|             if (type_3mf || type_zip_amf) { |             if (type_3mf || type_zip_amf) { | ||||||
|                 DynamicPrintConfig config; |                 DynamicPrintConfig config; | ||||||
|                 config.apply(FullPrintConfig::defaults()); |                 { | ||||||
|                 model = Slic3r::Model::read_from_archive(path.string(), &config, false); |                     DynamicPrintConfig config_loaded; | ||||||
|  |                     model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false); | ||||||
|  |                     if (! config_loaded.empty()) { | ||||||
|  | 						// Based on the printer technology field found in the loaded config, select the base for the config,
 | ||||||
|  | 						PrinterTechnology printer_technology = Preset::printer_technology(config_loaded); | ||||||
|  | 						config.apply(printer_technology == ptFFF ? | ||||||
|  |                             static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :  | ||||||
|  |                             static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults())); | ||||||
|  |                         // and place the loaded config over the base.
 | ||||||
|  |                         config += std::move(config_loaded); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (! config.empty()) { | ||||||
|                     Preset::normalize(config); |                     Preset::normalize(config); | ||||||
|                     wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); |                     wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); | ||||||
|                 for (const auto &kv : main_frame->options_tabs()) { kv.second->load_current_preset(); } | 					wxGetApp().load_current_presets(); | ||||||
|  | 				} | ||||||
|                 wxGetApp().app_config->update_config_dir(path.parent_path().string()); |                 wxGetApp().app_config->update_config_dir(path.parent_path().string()); | ||||||
|                 // forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
 |  | ||||||
|                 // and if the config contains a "layer_height" different from the current defined one
 |  | ||||||
|                 // TODO:
 |  | ||||||
|                 // $self->async_apply_config;
 |  | ||||||
|             } else { |             } else { | ||||||
|                 model = Slic3r::Model::read_from_file(path.string(), nullptr, false); |                 model = Slic3r::Model::read_from_file(path.string(), nullptr, false); | ||||||
|                 for (auto obj : model.objects) |                 for (auto obj : model.objects) | ||||||
|  | @ -1230,7 +1239,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_ | ||||||
| std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) | std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) | ||||||
| { | { | ||||||
|     const BoundingBoxf bed_shape = bed_shape_bb(); |     const BoundingBoxf bed_shape = bed_shape_bb(); | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|     const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0); |     const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|     const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0); |     const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0); | ||||||
| 
 | 
 | ||||||
|     bool need_arrange = false; |     bool need_arrange = false; | ||||||
|  | @ -1249,8 +1260,12 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode | ||||||
| 
 | 
 | ||||||
|             // add a default instance and center object around origin
 |             // add a default instance and center object around origin
 | ||||||
|             object->center_around_origin();  // also aligns object to Z = 0
 |             object->center_around_origin();  // also aligns object to Z = 0
 | ||||||
|             auto *instance = object->add_instance(); |             ModelInstance* instance = object->add_instance(); | ||||||
|  | #if ENABLE_MODELVOLUME_TRANSFORM | ||||||
|  |             instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2))); | ||||||
|  | #else | ||||||
|             instance->set_offset(bed_center); |             instance->set_offset(bed_center); | ||||||
|  | #endif // ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const Vec3d size = object->bounding_box().size(); |         const Vec3d size = object->bounding_box().size(); | ||||||
|  | @ -1438,7 +1453,9 @@ void Plater::priv::mirror(Axis axis) | ||||||
| 
 | 
 | ||||||
| void Plater::priv::arrange() | void Plater::priv::arrange() | ||||||
| { | { | ||||||
|  | 	this->background_process.stop(); | ||||||
|     main_frame->app_controller()->arrange_model(); |     main_frame->app_controller()->arrange_model(); | ||||||
|  |     this->schedule_background_process(); | ||||||
| 
 | 
 | ||||||
|     // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
 |     // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
 | ||||||
|     // when parts don't fit in print bed
 |     // when parts don't fit in print bed
 | ||||||
|  | @ -1471,13 +1488,7 @@ void Plater::priv::split_object() | ||||||
|     { |     { | ||||||
|         unsigned int counter = 1; |         unsigned int counter = 1; | ||||||
|         for (ModelObject* m : new_objects) |         for (ModelObject* m : new_objects) | ||||||
|         { |  | ||||||
|             m->name = current_model_object->name + "_" + std::to_string(counter++); |             m->name = current_model_object->name + "_" + std::to_string(counter++); | ||||||
|             for (ModelInstance* i : current_model_object->instances) |  | ||||||
|             { |  | ||||||
|                 m->add_instance(*i); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         remove(obj_idx); |         remove(obj_idx); | ||||||
| 
 | 
 | ||||||
|  | @ -2108,7 +2119,8 @@ void Plater::export_amf() | ||||||
|     wxString path = dialog->GetPath(); |     wxString path = dialog->GetPath(); | ||||||
|     auto path_cstr = path.c_str(); |     auto path_cstr = path.c_str(); | ||||||
| 
 | 
 | ||||||
|     if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { | 	DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); | ||||||
|  | 	if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { | ||||||
|         // Success
 |         // Success
 | ||||||
|         p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); |         p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); | ||||||
|     } else { |     } else { | ||||||
|  | @ -2127,7 +2139,8 @@ void Plater::export_3mf() | ||||||
|     wxString path = dialog->GetPath(); |     wxString path = dialog->GetPath(); | ||||||
|     auto path_cstr = path.c_str(); |     auto path_cstr = path.c_str(); | ||||||
| 
 | 
 | ||||||
|     if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { | 	DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); | ||||||
|  | 	if (Slic3r::store_3mf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { | ||||||
|         // Success
 |         // Success
 | ||||||
|         p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); |         p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); | ||||||
|     } else { |     } else { | ||||||
|  | @ -2256,7 +2269,9 @@ void Plater::changed_object(int obj_idx) | ||||||
|     if (list->is_parts_changed()) { |     if (list->is_parts_changed()) { | ||||||
|         // recenter and re - align to Z = 0
 |         // recenter and re - align to Z = 0
 | ||||||
|         auto model_object = p->model.objects[obj_idx]; |         auto model_object = p->model.objects[obj_idx]; | ||||||
|  | #if !ENABLE_MODELVOLUME_TRANSFORM | ||||||
|         model_object->center_around_origin(); |         model_object->center_around_origin(); | ||||||
|  | #endif // !ENABLE_MODELVOLUME_TRANSFORM
 | ||||||
|         model_object->ensure_on_bed(); |         model_object->ensure_on_bed(); | ||||||
|         _3DScene::reload_scene(p->canvas3D, false); |         _3DScene::reload_scene(p->canvas3D, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -170,10 +170,12 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr | ||||||
| { | { | ||||||
|     const auto &defaults = FullPrintConfig::defaults(); |     const auto &defaults = FullPrintConfig::defaults(); | ||||||
|     for (const std::string &key : Preset::nozzle_options()) { |     for (const std::string &key : Preset::nozzle_options()) { | ||||||
|  | 		if (key == "default_filament_profile") | ||||||
|  | 			continue; | ||||||
|         auto *opt = config.option(key, false); |         auto *opt = config.option(key, false); | ||||||
|         assert(opt != nullptr); |         assert(opt != nullptr); | ||||||
|         assert(opt->is_vector()); |         assert(opt->is_vector()); | ||||||
|         if (opt != nullptr && opt->is_vector() && key != "default_filament_profile") |         if (opt != nullptr && opt->is_vector()) | ||||||
|             static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key)); |             static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -202,8 +204,7 @@ void Preset::normalize(DynamicPrintConfig &config) | ||||||
|         // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
 |         // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
 | ||||||
|         for (const std::string &key : { "filament_settings_id" }) { |         for (const std::string &key : { "filament_settings_id" }) { | ||||||
|             auto *opt = config.option(key, false); |             auto *opt = config.option(key, false); | ||||||
|             assert(opt != nullptr); |             assert(opt == nullptr || opt->type() == coStrings); | ||||||
|             assert(opt->type() == coStrings); |  | ||||||
|             if (opt != nullptr && opt->type() == coStrings) |             if (opt != nullptr && opt->type() == coStrings) | ||||||
|                 static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string()); |                 static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string()); | ||||||
|         } |         } | ||||||
|  | @ -534,7 +535,8 @@ Preset& PresetCollection::load_external_preset( | ||||||
|     cfg.apply_only(config, cfg.keys(), true); |     cfg.apply_only(config, cfg.keys(), true); | ||||||
|     // Is there a preset already loaded with the name stored inside the config?
 |     // Is there a preset already loaded with the name stored inside the config?
 | ||||||
|     std::deque<Preset>::iterator it = this->find_preset_internal(original_name); |     std::deque<Preset>::iterator it = this->find_preset_internal(original_name); | ||||||
|     if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) { | 	bool                         found = it != m_presets.end() && it->name == original_name; | ||||||
|  |     if (found && profile_print_params_same(it->config, cfg)) { | ||||||
|         // The preset exists and it matches the values stored inside config.
 |         // The preset exists and it matches the values stored inside config.
 | ||||||
|         if (select) |         if (select) | ||||||
|             this->select_preset(it - m_presets.begin()); |             this->select_preset(it - m_presets.begin()); | ||||||
|  | @ -542,7 +544,7 @@ Preset& PresetCollection::load_external_preset( | ||||||
|     } |     } | ||||||
|     // Update the "inherits" field.
 |     // Update the "inherits" field.
 | ||||||
|     std::string &inherits = Preset::inherits(cfg); |     std::string &inherits = Preset::inherits(cfg); | ||||||
|     if (it != m_presets.end() && inherits.empty()) { |     if (found && inherits.empty()) { | ||||||
|         // There is a profile with the same name already loaded. Should we update the "inherits" field?
 |         // There is a profile with the same name already loaded. Should we update the "inherits" field?
 | ||||||
|         if (it->vendor == nullptr) |         if (it->vendor == nullptr) | ||||||
|             inherits = it->inherits(); |             inherits = it->inherits(); | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| static std::vector<std::string> s_project_options { | static std::vector<std::string> s_project_options { | ||||||
|  |     "colorprint_heights", | ||||||
|     "wiping_volumes_extruders", |     "wiping_volumes_extruders", | ||||||
|     "wiping_volumes_matrix" |     "wiping_volumes_matrix" | ||||||
| }; | }; | ||||||
|  | @ -617,18 +618,6 @@ void PresetBundle::load_config_file(const std::string &path) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PresetBundle::load_config_string(const char* str, const char* source_filename) |  | ||||||
| { |  | ||||||
|     if (str != nullptr) |  | ||||||
|     { |  | ||||||
|         DynamicPrintConfig config; |  | ||||||
|         config.apply(FullPrintConfig::defaults()); |  | ||||||
|         config.load_from_gcode_string(str); |  | ||||||
|         Preset::normalize(config); |  | ||||||
|         load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Load a config file from a boost property_tree. This is a private method called from load_config_file.
 | // Load a config file from a boost property_tree. This is a private method called from load_config_file.
 | ||||||
| void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) | void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) | ||||||
| { | { | ||||||
|  | @ -676,7 +665,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool | ||||||
|         compatible_printers_condition = compatible_printers_condition_values[idx]; |         compatible_printers_condition = compatible_printers_condition_values[idx]; | ||||||
| 		if (is_external) | 		if (is_external) | ||||||
|             presets.load_external_preset(name_or_path, name, |             presets.load_external_preset(name_or_path, name, | ||||||
|                 config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") :  "printer_settings_id", true), |                 config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") :  "printer_settings_id", true), | ||||||
|                 config); |                 config); | ||||||
|         else |         else | ||||||
|             presets.load_preset(presets.path_from_name(name), name, config).save(); |             presets.load_preset(presets.path_from_name(name), name, config).save(); | ||||||
|  |  | ||||||
|  | @ -84,11 +84,6 @@ public: | ||||||
|     // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 |     // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||||
|     void                        load_config_file(const std::string &path); |     void                        load_config_file(const std::string &path); | ||||||
| 
 | 
 | ||||||
|     // Load an external config source containing the print, filament and printer presets.
 |  | ||||||
|     // The given string must contain the full set of parameters (same as those exported to gcode).
 |  | ||||||
|     // If the string is parsed successfully, its print / filament / printer profiles will be activated.
 |  | ||||||
|     void                        load_config_string(const char* str, const char* source_filename = nullptr); |  | ||||||
| 
 |  | ||||||
|     // Load a config bundle file, into presets and store the loaded presets into separate files
 |     // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||||
|     // of the local configuration directory.
 |     // of the local configuration directory.
 | ||||||
|     // Load settings into the provided settings instance.
 |     // Load settings into the provided settings instance.
 | ||||||
|  |  | ||||||
|  | @ -108,7 +108,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) | ||||||
|     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
 |     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
 | ||||||
|     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   
 |     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value   
 | ||||||
|     Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); |     Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); | ||||||
|     Refresh(this); |     Refresh(true); // erase background
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RammingPanel::line_parameters_changed() { | void RammingPanel::line_parameters_changed() { | ||||||
|  |  | ||||||
|  | @ -9,6 +9,9 @@ | ||||||
| #include <wx/numformatter.h> | #include <wx/numformatter.h> | ||||||
| #include "GUI_App.hpp" | #include "GUI_App.hpp" | ||||||
| #include "GUI_ObjectList.hpp" | #include "GUI_ObjectList.hpp" | ||||||
|  | #include "Model.hpp" | ||||||
|  | 
 | ||||||
|  | wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); | ||||||
| 
 | 
 | ||||||
| wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, | wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, | ||||||
|     std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler) |     std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler) | ||||||
|  | @ -400,10 +403,9 @@ bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std: | ||||||
| 
 | 
 | ||||||
|     m_opt_categories = categories; |     m_opt_categories = categories; | ||||||
|     m_name = wxEmptyString; |     m_name = wxEmptyString; | ||||||
| //     m_icon = m_empty_icon;
 |  | ||||||
|     m_bmp = m_empty_bmp; |     m_bmp = m_empty_bmp; | ||||||
| 
 | 
 | ||||||
|     std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;//Slic3r::GUI::get_category_icon();
 |     std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; | ||||||
| 
 | 
 | ||||||
|     for (auto& cat : m_opt_categories) |     for (auto& cat : m_opt_categories) | ||||||
|         m_name += cat + "; "; |         m_name += cat + "; "; | ||||||
|  | @ -453,29 +455,34 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) | ||||||
| 
 | 
 | ||||||
| wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, | wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, | ||||||
| 													const wxString &name, | 													const wxString &name, | ||||||
|                                                     const wxBitmap& icon, |                                                     const int volume_type, | ||||||
|                                                     const int extruder/* = 0*/, |                                                     const int extruder/* = 0*/, | ||||||
|                                                     const bool create_frst_child/* = true*/) |                                                     const bool create_frst_child/* = true*/) | ||||||
| { | { | ||||||
| 	PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); | 	PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); | ||||||
| 	if (!root) return wxDataViewItem(0); | 	if (!root) return wxDataViewItem(0); | ||||||
| 
 | 
 | ||||||
|     const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); |     wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); | ||||||
|  | 
 | ||||||
|  |     // because of istance_root is a last item of the object
 | ||||||
|  |     int insert_position = root->GetChildCount() - 1; | ||||||
|  |     if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot) | ||||||
|  |         insert_position = -1; | ||||||
| 
 | 
 | ||||||
|     if (create_frst_child && root->m_volumes_cnt == 0) |     if (create_frst_child && root->m_volumes_cnt == 0) | ||||||
| 	{ | 	{ | ||||||
| 		const auto bmp_solid_mesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); | 		const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0); | ||||||
| 		const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, bmp_solid_mesh, extruder_str, 0); |         insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); | ||||||
| 		root->Append(node); |  | ||||||
| 		// notify control
 | 		// notify control
 | ||||||
| 		const wxDataViewItem child((void*)node); | 		const wxDataViewItem child((void*)node); | ||||||
| 		ItemAdded(parent_item, child); | 		ItemAdded(parent_item, child); | ||||||
| 
 | 
 | ||||||
|         root->m_volumes_cnt++; |         root->m_volumes_cnt++; | ||||||
|  |         if (insert_position > 0) insert_position++; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|     const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt); |     const auto node = new PrusaObjectDataViewModelNode(root, name, *m_volume_bmps[volume_type], extruder_str, root->m_volumes_cnt); | ||||||
| 	root->Append(node); |     insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); | ||||||
| 	// notify control
 | 	// notify control
 | ||||||
| 	const wxDataViewItem child((void*)node); | 	const wxDataViewItem child((void*)node); | ||||||
|     ItemAdded(parent_item, child); |     ItemAdded(parent_item, child); | ||||||
|  | @ -499,15 +506,13 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & | ||||||
| 
 | 
 | ||||||
| int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) | int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) | ||||||
| { | { | ||||||
|     int inst_root_id = -1; |     // because of istance_root is a last item of the object
 | ||||||
|     int stop_search_i = parent_node->GetChildCount(); |     const int inst_root_idx = parent_node->GetChildCount()-1; | ||||||
|     if (stop_search_i > 2) stop_search_i = 2; | 
 | ||||||
|     for (int i = 0; i < stop_search_i; ++i) |     if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->m_type == itInstanceRoot)  | ||||||
|         if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) { |         return inst_root_idx; | ||||||
|             inst_root_id = i; |      | ||||||
|             break; |     return -1; | ||||||
|         } |  | ||||||
|     return inst_root_id; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) | wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) | ||||||
|  | @ -524,8 +529,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem & | ||||||
|     const wxDataViewItem inst_root_item((void*)inst_root_node); |     const wxDataViewItem inst_root_item((void*)inst_root_node); | ||||||
| 
 | 
 | ||||||
|     if (inst_root_id < 0) { |     if (inst_root_id < 0) { | ||||||
|         const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1; |         parent_node->Append(inst_root_node); | ||||||
|         parent_node->Insert(inst_root_node, insert_pos); |  | ||||||
|         // notify control
 |         // notify control
 | ||||||
|         ItemAdded(parent_item, inst_root_item); |         ItemAdded(parent_item, inst_root_item); | ||||||
|         if (num == 1) num++; |         if (num == 1) num++; | ||||||
|  | @ -563,6 +567,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) | ||||||
| 		auto id = node_parent->GetChildren().Index(node); | 		auto id = node_parent->GetChildren().Index(node); | ||||||
|         auto idx = node->GetIdx(); |         auto idx = node->GetIdx(); | ||||||
| 		node_parent->GetChildren().Remove(node); | 		node_parent->GetChildren().Remove(node); | ||||||
|  | 
 | ||||||
|  |         if (node->m_type == itVolume) | ||||||
|  |             node_parent->m_volumes_cnt--; | ||||||
|  | 
 | ||||||
| 		if (id > 0) {  | 		if (id > 0) {  | ||||||
| 			if(id == node_parent->GetChildCount()) id--; | 			if(id == node_parent->GetChildCount()) id--; | ||||||
| 			ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); | 			ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); | ||||||
|  | @ -692,6 +700,42 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) | ||||||
|         auto item = wxDataViewItem(node); |         auto item = wxDataViewItem(node); | ||||||
|         children.RemoveAt(id); |         children.RemoveAt(id); | ||||||
| 
 | 
 | ||||||
|  |         if (node->m_type == itVolume) | ||||||
|  |             root->m_volumes_cnt--; | ||||||
|  | 
 | ||||||
|  |         // free the node
 | ||||||
|  |         delete node; | ||||||
|  | 
 | ||||||
|  |         // notify control
 | ||||||
|  |         ItemDeleted(parent, item); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // set m_containet to FALSE if parent has no child
 | ||||||
|  | #ifndef __WXGTK__ | ||||||
|  |         root->m_container = false; | ||||||
|  | #endif //__WXGTK__
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PrusaObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) | ||||||
|  | { | ||||||
|  |     PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); | ||||||
|  |     if (!root)      // happens if item.IsOk()==false
 | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     // first remove the node from the parent's array of children;
 | ||||||
|  |     // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
 | ||||||
|  |     //       thus removing the node from it doesn't result in freeing it
 | ||||||
|  |     auto& children = root->GetChildren(); | ||||||
|  |     for (int id = root->GetChildCount() - 1; id >= 0; --id) | ||||||
|  |     { | ||||||
|  |         auto node = children[id]; | ||||||
|  |         if (node->m_type != itVolume) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         auto item = wxDataViewItem(node); | ||||||
|  |         children.RemoveAt(id); | ||||||
|  |         root->m_volumes_cnt--; | ||||||
|  | 
 | ||||||
|         // free the node
 |         // free the node
 | ||||||
|         delete node; |         delete node; | ||||||
| 
 | 
 | ||||||
|  | @ -718,7 +762,7 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) | ||||||
| 
 | 
 | ||||||
| wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) | wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) | ||||||
| { | { | ||||||
| 	if (obj_idx >= m_objects.size()) { | 	if (obj_idx >= m_objects.size() || obj_idx < 0) { | ||||||
| 		printf("Error! Out of objects range.\n"); | 		printf("Error! Out of objects range.\n"); | ||||||
| 		return wxDataViewItem(0); | 		return wxDataViewItem(0); | ||||||
| 	} | 	} | ||||||
|  | @ -740,6 +784,25 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volu | ||||||
|     return wxDataViewItem(0); |     return wxDataViewItem(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) | ||||||
|  | { | ||||||
|  |     if (obj_idx >= m_objects.size() || obj_idx < 0) { | ||||||
|  |         printf("Error! Out of objects range.\n"); | ||||||
|  |         return wxDataViewItem(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx])); | ||||||
|  |     if (!instances_item) | ||||||
|  |         return wxDataViewItem(0); | ||||||
|  | 
 | ||||||
|  |     auto parent = (PrusaObjectDataViewModelNode*)instances_item.GetID();; | ||||||
|  |     for (size_t i = 0; i < parent->GetChildCount(); i++) | ||||||
|  |         if (parent->GetNthChild(i)->m_idx == inst_idx) | ||||||
|  |             return wxDataViewItem(parent->GetNthChild(i)); | ||||||
|  | 
 | ||||||
|  |     return wxDataViewItem(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) | int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) | ||||||
| { | { | ||||||
| 	wxASSERT(item.IsOk()); | 	wxASSERT(item.IsOk()); | ||||||
|  | @ -1015,21 +1078,33 @@ ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const | ||||||
|     return node->m_type; |     return node->m_type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const | wxDataViewItem PrusaObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const  | ||||||
| { | { | ||||||
|     if (!item.IsOk()) |     if (!parent_item.IsOk()) | ||||||
|         return wxDataViewItem(0); |         return wxDataViewItem(0); | ||||||
| 
 | 
 | ||||||
|     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); |     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); | ||||||
|     if (node->GetChildCount() == 0) |     if (node->GetChildCount() == 0) | ||||||
|         return wxDataViewItem(0); |         return wxDataViewItem(0); | ||||||
| 
 | 
 | ||||||
|     if (node->GetNthChild(0)->m_type == itSettings) |     for (int i = 0; i < node->GetChildCount(); i++) { | ||||||
|         return wxDataViewItem((void*)node->GetNthChild(0)); |         if (node->GetNthChild(i)->m_type == type) | ||||||
|  |             return wxDataViewItem((void*)node->GetNthChild(i)); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return wxDataViewItem(0); |     return wxDataViewItem(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const | ||||||
|  | { | ||||||
|  |     return GetItemByType(item, itSettings); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | wxDataViewItem PrusaObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const | ||||||
|  | { | ||||||
|  |     return GetItemByType(item, itInstanceRoot); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const | bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const | ||||||
| { | { | ||||||
|     if (!item.IsOk()) |     if (!item.IsOk()) | ||||||
|  | @ -1050,6 +1125,16 @@ void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, | ||||||
|     ItemChanged(item); |     ItemChanged(item); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const int type) | ||||||
|  | { | ||||||
|  |     if (!item.IsOk() || GetItemType(item) != itVolume)  | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); | ||||||
|  |     node->SetBitmap(*m_volume_bmps[type]); | ||||||
|  |     ItemChanged(item); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| //-----------------------------------------------------------------------------
 | //-----------------------------------------------------------------------------
 | ||||||
| // PrusaDataViewBitmapText
 | // PrusaDataViewBitmapText
 | ||||||
| //-----------------------------------------------------------------------------
 | //-----------------------------------------------------------------------------
 | ||||||
|  | @ -1106,7 +1191,6 @@ wxSize PrusaBitmapTextRenderer::GetSize() const | ||||||
| // ----------------------------------------------------------------------------
 | // ----------------------------------------------------------------------------
 | ||||||
| // PrusaDoubleSlider
 | // PrusaDoubleSlider
 | ||||||
| // ----------------------------------------------------------------------------
 | // ----------------------------------------------------------------------------
 | ||||||
| 
 |  | ||||||
| PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, | PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, | ||||||
|                                         wxWindowID id, |                                         wxWindowID id, | ||||||
|                                         int lowerValue,  |                                         int lowerValue,  | ||||||
|  | @ -1288,6 +1372,34 @@ double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) cons | ||||||
|     return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; |     return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::vector<double> PrusaDoubleSlider::GetTicksValues() const | ||||||
|  | { | ||||||
|  |     std::vector<double> values; | ||||||
|  | 
 | ||||||
|  |     if (!m_values.empty()) | ||||||
|  |         for (auto tick : m_ticks) | ||||||
|  |             values.push_back(m_values[tick].second); | ||||||
|  | 
 | ||||||
|  |     return values; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PrusaDoubleSlider::SetTicksValues(const std::vector<double>& heights) | ||||||
|  | { | ||||||
|  |     if (m_values.empty()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     m_ticks.clear(); | ||||||
|  |     unsigned int i = 0; | ||||||
|  |     for (auto h : heights) { | ||||||
|  |         while (i < m_values.size() && m_values[i].second - 1e-6 < h) | ||||||
|  |             ++i; | ||||||
|  |         if (i == m_values.size()) | ||||||
|  |             return; | ||||||
|  |         m_ticks.insert(i); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) | void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) | ||||||
| { | { | ||||||
|     const double step = get_scroll_step(); |     const double step = get_scroll_step(); | ||||||
|  | @ -1712,10 +1824,13 @@ void PrusaDoubleSlider::action_tick(const TicksAction action) | ||||||
|             m_ticks.insert(tick); |             m_ticks.insert(tick); | ||||||
|         else if (it != m_ticks.end() && action == taDel) |         else if (it != m_ticks.end() && action == taDel) | ||||||
|             m_ticks.erase(tick); |             m_ticks.erase(tick); | ||||||
|         else |         else { | ||||||
|  |             wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); | ||||||
|     Refresh(); |     Refresh(); | ||||||
|     Update(); |     Update(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -425,6 +425,8 @@ private: | ||||||
| class PrusaObjectDataViewModel :public wxDataViewModel | class PrusaObjectDataViewModel :public wxDataViewModel | ||||||
| { | { | ||||||
| 	std::vector<PrusaObjectDataViewModelNode*>  m_objects; | 	std::vector<PrusaObjectDataViewModelNode*>  m_objects; | ||||||
|  |     std::vector<wxBitmap*>                      m_volume_bmps; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     PrusaObjectDataViewModel(); |     PrusaObjectDataViewModel(); | ||||||
|     ~PrusaObjectDataViewModel(); |     ~PrusaObjectDataViewModel(); | ||||||
|  | @ -432,7 +434,7 @@ public: | ||||||
| 	wxDataViewItem Add(const wxString &name); | 	wxDataViewItem Add(const wxString &name); | ||||||
| 	wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,  | 	wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,  | ||||||
| 							const wxString &name,  | 							const wxString &name,  | ||||||
|                             const wxBitmap& icon, |                             const int volume_type, | ||||||
|                             const int extruder = 0, |                             const int extruder = 0, | ||||||
|                             const bool create_frst_child = true); |                             const bool create_frst_child = true); | ||||||
| 	wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); | 	wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); | ||||||
|  | @ -441,8 +443,10 @@ public: | ||||||
| 	wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); | 	wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); | ||||||
| 	void DeleteAll(); | 	void DeleteAll(); | ||||||
|     void DeleteChildren(wxDataViewItem& parent); |     void DeleteChildren(wxDataViewItem& parent); | ||||||
|  |     void DeleteVolumeChildren(wxDataViewItem& parent); | ||||||
| 	wxDataViewItem GetItemById(int obj_idx); | 	wxDataViewItem GetItemById(int obj_idx); | ||||||
| 	wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); | 	wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); | ||||||
|  | 	wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); | ||||||
| 	int GetIdByItem(const wxDataViewItem& item); | 	int GetIdByItem(const wxDataViewItem& item); | ||||||
|     int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; |     int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; | ||||||
|     int GetVolumeIdByItem(const wxDataViewItem& item) const; |     int GetVolumeIdByItem(const wxDataViewItem& item) const; | ||||||
|  | @ -488,9 +492,14 @@ public: | ||||||
| 	virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override {	return true; } | 	virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override {	return true; } | ||||||
| 
 | 
 | ||||||
|     ItemType GetItemType(const wxDataViewItem &item) const ; |     ItemType GetItemType(const wxDataViewItem &item) const ; | ||||||
|  |     wxDataViewItem    GetItemByType(const wxDataViewItem &parent_item, ItemType type) const; | ||||||
|     wxDataViewItem    GetSettingsItem(const wxDataViewItem &item) const; |     wxDataViewItem    GetSettingsItem(const wxDataViewItem &item) const; | ||||||
|  |     wxDataViewItem    GetInstanceRootItem(const wxDataViewItem &item) const; | ||||||
|     bool    IsSettingsItem(const wxDataViewItem &item) const; |     bool    IsSettingsItem(const wxDataViewItem &item) const; | ||||||
|     void    UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories); |     void    UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories); | ||||||
|  | 
 | ||||||
|  |     void    SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; } | ||||||
|  |     void    SetVolumeType(const wxDataViewItem &item, const int type); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ----------------------------------------------------------------------------
 | // ----------------------------------------------------------------------------
 | ||||||
|  | @ -613,6 +622,9 @@ private: | ||||||
| // PrusaDoubleSlider
 | // PrusaDoubleSlider
 | ||||||
| // ----------------------------------------------------------------------------
 | // ----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  | // custom message the slider sends to its parent to notify a tick-change:
 | ||||||
|  | wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); | ||||||
|  | 
 | ||||||
| enum SelectedSlider { | enum SelectedSlider { | ||||||
|     ssUndef, |     ssUndef, | ||||||
|     ssLower, |     ssLower, | ||||||
|  | @ -623,6 +635,7 @@ enum TicksAction{ | ||||||
|     taAdd, |     taAdd, | ||||||
|     taDel |     taDel | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
| class PrusaDoubleSlider : public wxControl | class PrusaDoubleSlider : public wxControl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -660,6 +673,8 @@ public: | ||||||
|         m_values = values; |         m_values = values; | ||||||
|     } |     } | ||||||
|     void ChangeOneLayerLock(); |     void ChangeOneLayerLock(); | ||||||
|  |     std::vector<double> GetTicksValues() const; | ||||||
|  |     void SetTicksValues(const std::vector<double>& heights); | ||||||
| 
 | 
 | ||||||
|     void OnPaint(wxPaintEvent& ) { render();} |     void OnPaint(wxPaintEvent& ) { render();} | ||||||
|     void OnLeftDown(wxMouseEvent& event); |     void OnLeftDown(wxMouseEvent& event); | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ | ||||||
|      |      | ||||||
|     %name{_add_object} Ref<ModelObject> add_object(); |     %name{_add_object} Ref<ModelObject> add_object(); | ||||||
|     Ref<ModelObject> _add_object_clone(ModelObject* other, bool copy_volumes = true) |     Ref<ModelObject> _add_object_clone(ModelObject* other, bool copy_volumes = true) | ||||||
|         %code%{ RETVAL = THIS->add_object(*other, copy_volumes); %}; |         %code%{ auto ptr = THIS->add_object(*other); if (! copy_volumes) ptr->clear_volumes(); RETVAL = ptr; %}; | ||||||
|     void delete_object(size_t idx); |     void delete_object(size_t idx); | ||||||
|     void clear_objects(); |     void clear_objects(); | ||||||
|     size_t objects_count() |     size_t objects_count() | ||||||
|  | @ -286,8 +286,6 @@ ModelMaterial::attributes() | ||||||
|         %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; |         %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; | ||||||
| 
 | 
 | ||||||
|     size_t split(unsigned int max_extruders); |     size_t split(unsigned int max_extruders); | ||||||
|      |  | ||||||
|     ModelMaterial* assign_unique_material(); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -44,8 +44,6 @@ _constant() | ||||||
|     int region_count() |     int region_count() | ||||||
|         %code%{ RETVAL = THIS->print()->regions().size(); %}; |         %code%{ RETVAL = THIS->print()->regions().size(); %}; | ||||||
| 
 | 
 | ||||||
|     int region_volumes_count() |  | ||||||
|         %code%{ RETVAL = THIS->region_volumes.size(); %}; |  | ||||||
|     Ref<Print> print(); |     Ref<Print> print(); | ||||||
|     Ref<ModelObject> model_object(); |     Ref<ModelObject> model_object(); | ||||||
|     Ref<StaticPrintConfig> config() |     Ref<StaticPrintConfig> config() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros