diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index a7835f3e9b..f05af13b52 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -270,7 +270,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print return diff_ex(brim_area, no_brim_area); } -// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders +// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim, const float no_brim_offset, double& brim_width_max, std::map& brim_width_map, std::map& brimAreaMap, @@ -453,7 +453,7 @@ static ExPolygons inner_brim_area(const Print &print, return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area); } -// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders +// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim, const float no_brim_offset, std::map& brimAreaMap, std::map& supportBrimAreaMap, @@ -906,7 +906,11 @@ static ExPolygons outer_inner_brim_area(const Print& print, std::vector groupVolumePtrs; for (auto& volumeID : volumeGroup.volume_ids) { ModelVolume* currentModelVolumePtr = nullptr; - for (auto volumePtr : object->model_object()->volumes) { + //BBS: support shared object logic + const PrintObject* shared_object = object->get_shared_object(); + if (!shared_object) + shared_object = object; + for (auto volumePtr : shared_object->model_object()->volumes) { if (volumePtr->id() == volumeID) { currentModelVolumePtr = volumePtr; break; @@ -1066,7 +1070,7 @@ static void optimize_polylines_by_reversing(Polylines *polylines) double dist_to_start = (next.first_point() - prev.last_point()).cast().norm(); double dist_to_end = (next.last_point() - prev.last_point()).cast().norm(); - if (dist_to_end < dist_to_start) + if (dist_to_end < dist_to_start) next.reverse(); } } diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index f46a0b087e..0c119c13cc 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -258,6 +258,8 @@ static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z"; static constexpr const char* SOURCE_IN_INCHES = "source_in_inches"; static constexpr const char* SOURCE_IN_METERS = "source_in_meters"; +static constexpr const char* MESH_SHARED_KEY = "mesh_shared"; + static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed"; static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets"; static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed"; @@ -702,6 +704,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::string m_thumbnail_path; std::vector m_sub_model_paths; + std::map m_shared_meshes; + //BBS: plater related structures bool m_is_bbl_3mf { false }; bool m_parsing_slice_info { false }; @@ -847,8 +851,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _handle_start_relationship(const char** attributes, unsigned int num_attributes); - void _generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects); - bool _generate_volumes_new(ModelObject& object, const std::vector &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); + void _generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects); + bool _generate_volumes_new(ModelObject& object, const std::vector &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); // callbacks to parse the .model file @@ -1281,7 +1285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) add_error("3rd 3mf, can not find object, id " + std::to_string(object.first.second)); return false; } - std::vector object_id_list; + std::vector object_id_list; _generate_current_object_list(object_id_list, object.first, m_current_objects); ObjectMetadata::VolumeMetadataList volumes; @@ -1289,7 +1293,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) for (int k = 0; k < object_id_list.size(); k++) { - Id object_id = object_id_list[k]; + Id object_id = object_id_list[k].object_id; volumes.emplace_back(object_id.second); } @@ -1344,7 +1348,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) model_object->sla_drain_holes = std::move(obj_drain_holes->second); }*/ - std::vector object_id_list; + std::vector object_id_list; _generate_current_object_list(object_id_list, object.first, m_current_objects); ObjectMetadata::VolumeMetadataList volumes; @@ -1375,7 +1379,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1); for (int k = 0; k < object_id_list.size(); k++) { - Id object_id = object_id_list[k]; + Id object_id = object_id_list[k].object_id; volumes.emplace_back(object_id.second); } @@ -3279,34 +3283,34 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - void _BBS_3MF_Importer::_generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects) + void _BBS_3MF_Importer::_generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects) { - std::list id_list; - id_list.push_back(object_id); + std::list id_list; + id_list.push_back({ object_id, Transform3d::Identity() }); while (!id_list.empty()) { - Id current_id = id_list.front(); + Component current_id = id_list.front(); id_list.pop_front(); - IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id); + IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id); if (current_object != current_objects.end()) { //found one if (!current_object->second.components.empty()) { for (const Component& comp: current_object->second.components) { - id_list.push_back(comp.object_id); + id_list.push_back(comp); } } else if (!(current_object->second.geometry.empty())) { //CurrentObject* ptr = &(current_objects[current_id]); //CurrentObject* ptr2 = &(current_object->second); - sub_objects.push_back(current_object->first); + sub_objects.push_back({ current_object->first, current_id.transform }); } } } } - bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions) + bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions) { if (!object.volumes.empty()) { add_error("object already built with parts"); @@ -3319,7 +3323,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) for (unsigned int index = 0; index < sub_objects.size(); index++) { //find the volume metadata firstly - Id object_id = sub_objects[index]; + Component sub_comp = sub_objects[index]; + Id object_id = sub_comp.object_id; IdToCurrentObjectMap::iterator current_object = m_current_objects.find(object_id); if (current_object == m_current_objects.end()) { add_error("sub_objects can not be found, id=" + std::to_string(object_id.second)); @@ -3338,68 +3343,112 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) Transform3d volume_matrix_to_object = Transform3d::Identity(); bool has_transform = false; + int shared_mesh_id = -1; if (volume_data) { + int found_count = 0; // extract the volume transformation from the volume's metadata, if present for (const Metadata& metadata : volume_data->metadata) { if (metadata.key == MATRIX_KEY) { volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value); has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10); - break; + found_count++; } + else if (metadata.key == MESH_SHARED_KEY){ + //add the shared mesh logic + shared_mesh_id = ::atoi(metadata.value.c_str()); + found_count++; + } + + if (found_count >= 2) + break; } } else { //create a volume_data volume_data = &default_volume_data; } - // splits volume out of imported geometry - indexed_triangle_set its; - its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end()); - const size_t triangles_count = its.indices.size(); + + ModelVolume* volume = nullptr; + ModelVolume *shared_volume = nullptr; + if (shared_mesh_id != -1) { + std::map::iterator iter = m_shared_meshes.find(shared_mesh_id); + if (iter != m_shared_meshes.end()) { + shared_volume = iter->second; + } + } + + const size_t triangles_count = sub_object->geometry.triangles.size(); if (triangles_count == 0) { add_error("found no trianges in the object " + std::to_string(sub_object->id)); return false; } - for (const Vec3i& face : its.indices) { - for (const int tri_id : face) { - if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) { - add_error("invalid vertex id in object " + std::to_string(sub_object->id)); - return false; + if (!shared_volume){ + // splits volume out of imported geometry + indexed_triangle_set its; + its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end()); + //const size_t triangles_count = its.indices.size(); + //if (triangles_count == 0) { + // add_error("found no trianges in the object " + std::to_string(sub_object->id)); + // return false; + //} + for (const Vec3i& face : its.indices) { + for (const int tri_id : face) { + if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) { + add_error("invalid vertex id in object " + std::to_string(sub_object->id)); + return false; + } } } - } - its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end()); + its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end()); - // BBS - for (const std::string prop_str : sub_object->geometry.face_properties) { - FaceProperty face_prop; - face_prop.from_string(prop_str); - its.properties.push_back(face_prop); - } - - TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats); - - if (m_version == 0) { - // if the 3mf was not produced by BambuStudio and there is only one instance, - // bake the transformation into the geometry to allow the reload from disk command - // to work properly - if (object.instances.size() == 1) { - triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false); - object.instances.front()->set_transformation(Slic3r::Geometry::Transformation()); - //FIXME do the mesh fixing? + // BBS + for (const std::string prop_str : sub_object->geometry.face_properties) { + FaceProperty face_prop; + face_prop.from_string(prop_str); + its.properties.push_back(face_prop); } - } - if (triangle_mesh.volume() < 0) - triangle_mesh.flip_triangles(); - ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); + TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats); + + if (m_version == 0) { + // if the 3mf was not produced by BambuStudio and there is only one instance, + // bake the transformation into the geometry to allow the reload from disk command + // to work properly + if (object.instances.size() == 1) { + triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false); + object.instances.front()->set_transformation(Slic3r::Geometry::Transformation()); + //FIXME do the mesh fixing? + } + } + if (triangle_mesh.volume() < 0) + triangle_mesh.flip_triangles(); + + volume = object.add_volume(std::move(triangle_mesh)); + + m_shared_meshes[sub_object->id] = volume; + } + else { + //create volume to use shared mesh + volume = object.add_volume_with_shared_mesh(*shared_volume); + } // stores the volume matrix taken from the metadata, if present if (has_transform) volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); + volume->calculate_convex_hull(); + //set transform from 3mf + Slic3r::Geometry::Transformation comp_transformatino(sub_comp.transform); + volume->set_transformation(volume->get_transformation() * comp_transformatino); + if (shared_volume) { + const TriangleMesh& trangle_mesh = volume->mesh(); + Vec3d shift = trangle_mesh.get_init_shift(); + if (!shift.isApprox(Vec3d::Zero())) + volume->translate(shift); + } + // recreate custom supports, seam and mmu segmentation from previously loaded attribute if (m_load_config) { volume->supported_facets.reserve(triangles_count); @@ -3448,7 +3497,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) volume->source.is_converted_from_inches = metadata.value == "1"; else if (metadata.key == SOURCE_IN_METERS) volume->source.is_converted_from_meters = metadata.value == "1"; - else if (metadata.key == MATRIX_KEY) + else if ((metadata.key == MATRIX_KEY) || (metadata.key == MESH_SHARED_KEY)) continue; else volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions); @@ -4648,12 +4697,37 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) if (m_from_backup_save) { for (unsigned int index = 1; index <= object.volumes.size(); index ++) { unsigned int ref_id = object_id | (index << 16); - stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id << "\"/>\n"; + stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id; // << "\"/>\n"; + //add the transform of the volume + ModelVolume* volume = object.volumes[index - 1]; + const Transform3d& transf = volume->get_matrix(); + stream << "\" " << TRANSFORM_ATTR << "=\""; + for (unsigned c = 0; c < 4; ++c) { + for (unsigned r = 0; r < 3; ++r) { + stream << transf(r, c); + if (r != 2 || c != 3) + stream << " "; + } + } + stream << "\"/>\n"; } } else { - for (unsigned int index = object_id; index < volume_start_id; index ++) - stream << " <" << COMPONENT_TAG << " objectid=\"" << index << "\"/>\n"; + for (unsigned int index = object_id; index < volume_start_id; index ++) { + stream << " <" << COMPONENT_TAG << " objectid=\"" << index; // << "\"/>\n"; + //add the transform of the volume + ModelVolume* volume = object.volumes[index - object_id]; + const Transform3d& transf = volume->get_matrix(); + stream << "\" " << TRANSFORM_ATTR << "=\""; + for (unsigned c = 0; c < 4; ++c) { + for (unsigned r = 0; r < 3; ++r) { + stream << transf(r, c); + if (r != 2 || c != 3) + stream << " "; + } + } + stream << "\"/>\n"; + } } } else { @@ -4803,7 +4877,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) const Transform3d& matrix = volume->get_matrix(); for (size_t i = 0; i < its.vertices.size(); ++i) { - Vec3f v = (matrix * its.vertices[i].cast()).cast(); + //don't save the volume's matrix into vertex data + //add the shared mesh logic + //Vec3f v = (matrix * its.vertices[i].cast()).cast(); + Vec3f v = its.vertices[i]; char* ptr = buf; boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\""); ptr = format_coordinate(v.x(), ptr); @@ -5205,6 +5282,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode) { std::stringstream stream; + std::map shared_meshes; // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back // when loaded as accurately as possible. stream << std::setprecision(std::numeric_limits::max_digits10); @@ -5297,6 +5375,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n"; } + //add the shared mesh logic + const TriangleMesh* current_mesh = volume->mesh_ptr(); + std::map::iterator mesh_iter; + mesh_iter = shared_meshes.find(current_mesh); + if (mesh_iter != shared_meshes.end()) { + stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << MESH_SHARED_KEY << "\" " << VALUE_ATTR << "=\"" << mesh_iter->second << "\"/>\n"; + } + else { + shared_meshes[current_mesh] = it->second; + } + // stores mesh's statistics const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors; stream << " <" << MESH_STAT_TAG << " "; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3142d491a3..21ffc94d82 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -667,6 +667,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec --idx_tree_support_layer; } + layer_to_print.original_object = &object; layers_to_print.push_back(layer_to_print); bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) @@ -2151,7 +2152,9 @@ std::vector GCode::sort_print_object_instances( // Sequential print, single object is being printed. for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); - const PrintObject *print_object = layers[layer_id].object(); + //BBS:add the support of shared print object + const PrintObject *print_object = layers[layer_id].original_object; + //const PrintObject *print_object = layers[layer_id].object(); if (print_object) out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx); } @@ -2161,7 +2164,9 @@ std::vector GCode::sort_print_object_instances( sorted.reserve(objects_by_extruder.size()); for (ObjectByExtruder &object_by_extruder : objects_by_extruder) { const size_t layer_id = &object_by_extruder - objects_by_extruder.data(); - const PrintObject *print_object = layers[layer_id].object(); + //BBS:add the support of shared print object + const PrintObject *print_object = layers[layer_id].original_object; + //const PrintObject *print_object = layers[layer_id].object(); if (print_object) sorted.emplace_back(print_object, &object_by_extruder); } @@ -2171,6 +2176,10 @@ std::vector GCode::sort_print_object_instances( out.reserve(sorted.size()); for (const PrintInstance *instance : *ordering) { const PrintObject &print_object = *instance->print_object; + //BBS:add the support of shared print object + //const PrintObject* print_obj_ptr = &print_object; + //if (print_object.get_shared_object()) + // print_obj_ptr = print_object.get_shared_object(); std::pair key(&print_object, nullptr); auto it = std::lower_bound(sorted.begin(), sorted.end(), key); if (it != sorted.end() && it->first == &print_object) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 29abf88def..dc80fa0339 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -41,11 +41,11 @@ class OozePrevention { public: bool enable; Points standby_points; - + OozePrevention() : enable(false) {} std::string pre_toolchange(GCode &gcodegen); std::string post_toolchange(GCode &gcodegen); - + private: int _get_temp(GCode &gcodegen); }; @@ -54,7 +54,7 @@ class Wipe { public: bool enable; Polyline path; - + Wipe() : enable(false) {} bool has_path() const { return !this->path.points.empty(); } void reset_path() { this->path = Polyline(); } @@ -136,15 +136,15 @@ public: }; class GCode { -public: +public: GCode() : m_origin(Vec2d::Zero()), - m_enable_loop_clipping(true), - m_enable_cooling_markers(false), + m_enable_loop_clipping(true), + m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), m_last_processor_extrusion_role(erNone), m_layer_count(0), - m_layer_index(-1), + m_layer_index(-1), m_layer(nullptr), m_object_layer_over_raft(false), //m_volumetric_speed(0), @@ -201,10 +201,11 @@ public: // public, so that it could be accessed by free helper functions from GCode.cpp struct LayerToPrint { - LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr) {} + LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr), original_object(nullptr) {} const Layer* object_layer; const SupportLayer* support_layer; const TreeSupportLayer* tree_support_layer; + const PrintObject* original_object; //BBS: used for shared object logic const Layer* layer() const { if (object_layer != nullptr) @@ -218,7 +219,11 @@ public: return nullptr; } - const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } + + const PrintObject* object() const + { + return (this->layer() != nullptr) ? this->layer()->object() : nullptr; + } coordf_t print_z() const { coordf_t sum_z = 0.; @@ -249,7 +254,7 @@ private: bool is_open() const { return f; } bool is_error() const; - + void flush(); void close(); @@ -257,12 +262,12 @@ private: void write(const std::string& what) { this->write(what.c_str()); } void write(const char* what); - // Write a string into a file. + // Write a string into a file. // Add a newline, if the string does not end with a newline already. // Used to export a custom G-code section processed by the PlaceholderParser. void writeln(const std::string& what); - // Formats and write into a file the given data. + // Formats and write into a file the given data. void write_format(const char* format, ...); private: @@ -375,7 +380,7 @@ private: InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} - // Repository + // Repository ObjectByExtruder &object_by_extruder; // Index into std::vector, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const size_t layer_id; @@ -500,14 +505,14 @@ private: bool object_layer_over_raft() const { return m_object_layer_over_raft; } friend ObjectByExtruder& object_by_extruder( - std::map> &by_extruder, - unsigned int extruder_id, - size_t object_idx, + std::map> &by_extruder, + unsigned int extruder_id, + size_t object_idx, size_t num_objects); friend std::vector& object_islands_by_extruder( - std::map> &by_extruder, - unsigned int extruder_id, - size_t object_idx, + std::map> &by_extruder, + unsigned int extruder_id, + size_t object_idx, size_t num_objects, size_t num_islands); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3788b286b6..ba5516ba4e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -185,10 +185,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c if (model.objects.empty()) throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty"); - + for (ModelObject *o : model.objects) o->input_file = input_file; - + if (options & LoadStrategy::AddDefaultInstances) model.add_default_instances(); @@ -259,14 +259,14 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig //BBS //CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config); - + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_UPDATE_GCODE\n"); if (proFn) { proFn(IMPORT_STAGE_UPDATE_GCODE, 0, 1, cb_cancel); if (cb_cancel) throw Slic3r::RuntimeError("Canceled"); } - + CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_CHECK_MODE_GCODE\n"); @@ -416,7 +416,7 @@ void Model::collect_reusable_objects(std::vector& objects) std::mem_fn(&ObjectBase::id)); model_object->volumes.clear(); } - // we never own these objects + // we never own these objects this->objects.clear(); } @@ -591,7 +591,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) assert(this->objects.size() >= 2); if (this->objects.size() < 2) return; - + ModelObject* object = new ModelObject(this); object->input_file = this->objects.front()->input_file; object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string(); @@ -601,7 +601,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) for (const ModelObject* o : this->objects) for (const ModelVolume* v : o->volumes) { - // If there are more than one object, put all volumes together + // If there are more than one object, put all volumes together // Each object may contain any number of volumes and instances // The volumes transformations are relative to the object containing them... Geometry::Transformation trafo_volume = v->get_transformation(); @@ -620,7 +620,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) } else { for (const ModelInstance* i : o->instances) // ...so, transform everything to a common reference system (world) - copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume); + copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume); } } @@ -1010,6 +1010,20 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me return v; } +ModelVolume* ModelObject::add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type /*= ModelVolumeType::INVALID*/) +{ + ModelVolume* v = new ModelVolume(this, other.m_mesh); + if (type != ModelVolumeType::INVALID && v->type() != type) + v->set_type(type); + this->volumes.push_back(v); + // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. +// v->center_geometry_after_creation(); +// this->invalidate_bounding_box(); + // BBS: backup + Slic3r::save_object_mesh(*this); + return v; +} + void ModelObject::delete_volume(size_t idx) { ModelVolumePtrs::iterator i = this->volumes.begin() + idx; @@ -1324,7 +1338,7 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const void ModelObject::center_around_origin(bool include_modifiers) { - // calculate the displacements needed to + // calculate the displacements needed to // center this object around the origin const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); @@ -1476,8 +1490,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con // Perform conversion only if the target "imperial" state is different from the current one. // This check supports conversion of "mixed" set of volumes, each with different "imperial" state. - if (//vol->source.is_converted_from_inches != from_imperial && - (volume_idxs.empty() || + if (//vol->source.is_converted_from_inches != from_imperial && + (volume_idxs.empty() || std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) { vol->scale_geometry_after_creation(koef); vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset())); @@ -1598,7 +1612,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower->add_volume(*volume); } - else if (! volume->mesh().empty()) { + else if (! volume->mesh().empty()) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); @@ -1744,7 +1758,7 @@ ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - upper->add_volume(*volume); + upper->add_volume(*volume); } else if (!volume->mesh().empty()) { // Transform the mesh by the combined transformation matrix. @@ -2185,7 +2199,7 @@ void ModelObject::print_info() const using namespace std; cout << fixed; boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; - + TriangleMesh mesh = this->raw_mesh(); BoundingBoxf3 bb = mesh.bounding_box(); Vec3d size = bb.size(); @@ -2203,7 +2217,7 @@ void ModelObject::print_info() const cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl; if (! mesh.stats().manifold()) cout << "open_edges = " << mesh.stats().open_edges << endl; - + if (mesh.stats().repaired()) { const RepairedMeshErrors& stats = mesh.stats().repaired_errors; if (stats.degenerate_facets > 0) @@ -2286,7 +2300,7 @@ void ModelVolume::set_material_id(t_model_material_id material_id) } ModelMaterial* ModelVolume::material() const -{ +{ return this->object->get_model()->get_material(m_material_id); } @@ -2362,8 +2376,10 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset) Vec3d shift = this->mesh().bounding_box().center(); if (!shift.isApprox(Vec3d::Zero())) { - if (m_mesh) - const_cast(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + if (m_mesh) { + const_cast(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + const_cast(m_mesh.get())->set_init_shift(shift); + } if (m_convex_hull) const_cast(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); translate(shift); @@ -2803,7 +2819,7 @@ double Model::getThermalLength(const std::vector modelVolumePtrs) } return thermalLength; } -// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered +// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered double ModelInstance::get_auto_brim_width(double deltaT, double adhension) const { BoundingBoxf3 raw_bbox = object->raw_mesh_bounding_box(); @@ -2896,7 +2912,7 @@ double ModelInstance::get_auto_brim_width() const void ModelInstance::get_arrange_polygon(void* ap) const { // static const double SIMPLIFY_TOLERANCE_MM = 0.1; - + Vec3d rotation = get_rotation(); rotation.z() = 0.; Transform3d trafo_instance = @@ -2909,7 +2925,7 @@ void ModelInstance::get_arrange_polygon(void* ap) const // pp = p.simplify(scaled(SIMPLIFY_TOLERANCE_MM)); // if (!pp.empty()) p = pp.front(); // } - + arrangement::ArrangePolygon& ret = *(arrangement::ArrangePolygon*)ap; ret.poly.contour = std::move(p); ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; @@ -3039,6 +3055,12 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri } } +bool FacetsAnnotation::equals(const FacetsAnnotation &other) const +{ + const std::pair>, std::vector>& data = other.get_data(); + return (m_data == data); +} + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) @@ -3140,22 +3162,22 @@ bool model_property_changed(const ModelObject &model_object_old, const ModelObje bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) { - return model_property_changed(mo, mo_new, - [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, + return model_property_changed(mo, mo_new, + [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.supported_facets.timestamp_matches(mv_new.supported_facets); }); } bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) { - return model_property_changed(mo, mo_new, - [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, + return model_property_changed(mo, mo_new, + [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.seam_facets.timestamp_matches(mv_new.seam_facets); }); } bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new) { - return model_property_changed(mo, mo_new, - [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, + return model_property_changed(mo, mo_new, + [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); }); } @@ -3189,7 +3211,7 @@ bool model_has_advanced_features(const Model &model) void check_model_ids_validity(const Model &model) { std::set ids; - auto check = [&ids](ObjectID id) { + auto check = [&ids](ObjectID id) { assert(id.valid()); assert(ids.find(id) == ids.end()); ids.insert(id); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ef134fcfc0..ac78f5c981 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -308,6 +308,7 @@ public: ModelVolume* add_volume(TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART); ModelVolume* add_volume(const ModelVolume &volume, ModelVolumeType type = ModelVolumeType::INVALID); ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh); + ModelVolume* add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type = ModelVolumeType::MODEL_PART); void delete_volume(size_t idx); void clear_volumes(); void sort_volumes(bool full_sort); @@ -617,6 +618,7 @@ public: void set_triangle_from_string(int triangle_id, const std::string& str); // After deserializing the last triangle, shrink data to fit. void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); } + bool equals(const FacetsAnnotation &other) const; private: // Constructors to be only called by derived classes. @@ -676,6 +678,7 @@ public: // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } + const TriangleMesh* mesh_ptr() const { return m_mesh.get(); } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared(mesh); } @@ -859,6 +862,18 @@ private: if (mesh.facets_count() > 1) calculate_convex_hull(); } + ModelVolume(ModelObject *object, const std::shared_ptr &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(mesh), m_type(type), object(object) + { + assert(this->id().valid()); + assert(this->config.id().valid()); + assert(this->supported_facets.id().valid()); + assert(this->seam_facets.id().valid()); + assert(this->mmu_segmentation_facets.id().valid()); + assert(this->id() != this->config.id()); + assert(this->id() != this->supported_facets.id()); + assert(this->id() != this->seam_facets.id()); + assert(this->id() != this->mmu_segmentation_facets.id()); + } ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) { assert(this->id().valid()); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 53d4037012..9737f7742f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1130,6 +1130,46 @@ void Print::auto_assign_extruders(ModelObject* model_object) const } } +void PrintObject::set_shared_object(PrintObject *object) +{ + m_shared_object = object; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object; +} + +void PrintObject::clear_shared_object() +{ + if (m_shared_object) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object; + m_layers.clear(); + m_support_layers.clear(); + m_tree_support_layers.clear(); + + m_shared_object = nullptr; + + invalidate_all_steps_without_cancel(); + } +} + +void PrintObject::copy_layers_from_shared_object() +{ + if (m_shared_object) { + m_layers.clear(); + m_support_layers.clear(); + m_tree_support_layers.clear(); + + firstLayerObjSliceByVolume.clear(); + firstLayerObjSliceByGroups.clear(); + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object; + m_layers = m_shared_object->layers(); + m_support_layers = m_shared_object->support_layers(); + m_tree_support_layers = m_shared_object->tree_support_layers(); + + firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice(); + firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups(); + } +} + // BBS BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name) { @@ -1179,15 +1219,115 @@ void Print::process() { name_tbb_thread_pool_threads_set_locale(); + //compute the PrintObject with the same geometries + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter")%this; + for (PrintObject *obj : m_objects) + obj->clear_shared_object(); + + //add the print_object share check logic + auto is_print_object_the_same = [this](const PrintObject* object1, const PrintObject* object2) -> bool{ + if (object1->trafo().matrix() != object2->trafo().matrix()) + return false; + const ModelObject* model_obj1 = object1->model_object(); + const ModelObject* model_obj2 = object2->model_object(); + if (model_obj1->volumes.size() != model_obj2->volumes.size()) + return false; + bool has_extruder1 = model_obj1->config.has("extruder"); + bool has_extruder2 = model_obj2->config.has("extruder"); + if ((has_extruder1 != has_extruder2) + || (has_extruder1 && model_obj1->config.extruder() != model_obj2->config.extruder())) + return false; + for (int index = 0; index < model_obj1->volumes.size(); index++) { + const ModelVolume &model_volume1 = *model_obj1->volumes[index]; + const ModelVolume &model_volume2 = *model_obj2->volumes[index]; + if (model_volume1.type() != model_volume2.type()) + return false; + if (model_volume1.mesh_ptr() != model_volume2.mesh_ptr()) + return false; + has_extruder1 = model_volume1.config.has("extruder"); + has_extruder2 = model_volume2.config.has("extruder"); + if ((has_extruder1 != has_extruder2) + || (has_extruder1 && model_volume1.config.extruder() != model_volume2.config.extruder())) + return false; + if (!model_volume1.supported_facets.equals(model_volume2.supported_facets)) + return false; + if (!model_volume1.seam_facets.equals(model_volume2.seam_facets)) + return false; + if (!model_volume1.mmu_segmentation_facets.equals(model_volume2.mmu_segmentation_facets)) + return false; + if (model_volume1.config.get() != model_volume2.config.get()) + return false; + } + //if (!object1->config().equals(object2->config())) + // return false; + if (model_obj1->config.get() != model_obj2->config.get()) + return false; + return true; + }; + int object_count = m_objects.size(); + std::set need_slicing_objects; + for (int index = 0; index < object_count; index++) + { + PrintObject *obj = m_objects[index]; + for (PrintObject *slicing_obj : need_slicing_objects) + { + if (is_print_object_the_same(obj, slicing_obj)) { + obj->set_shared_object(slicing_obj); + break; + } + } + if (!obj->get_shared_object()) + need_slicing_objects.insert(obj); + } + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size(); BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->make_perimeters(); + } + else { + if (obj->set_started(posSlice)) + obj->set_done(posSlice); + if (obj->set_started(posPerimeters)) + obj->set_done(posPerimeters); + } + } + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->infill(); + } + else { + if (obj->set_started(posPrepareInfill)) + obj->set_done(posPrepareInfill); + if (obj->set_started(posInfill)) + obj->set_done(posInfill); + } + } + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->ironing(); + } + else { + if (obj->set_started(posIroning)) + obj->set_done(posIroning); + } + } + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->generate_support_material(); + } + else { + if (obj->set_started(posSupportMaterial)) + obj->set_done(posSupportMaterial); + } + } + for (PrintObject *obj : m_objects) - obj->make_perimeters(); - for (PrintObject *obj : m_objects) - obj->infill(); - for (PrintObject *obj : m_objects) - obj->ironing(); - for (PrintObject *obj : m_objects) - obj->generate_support_material(); + { + if (need_slicing_objects.count(obj) == 0) + obj->copy_layers_from_shared_object(); + } if (this->set_started(psWipeTower)) { m_wipe_tower_data.clear(); m_tool_ordering.clear(); @@ -1290,8 +1430,17 @@ void Print::process() this->set_done(psSkirtBrim); } //BBS - for (PrintObject *obj : m_objects) - obj->simplify_extrusion_path(); + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->simplify_extrusion_path(); + } + else { + if (obj->set_started(posSimplifyPath)) + obj->set_done(posSimplifyPath); + if (obj->set_started(posSimplifySupportPath)) + obj->set_done(posSimplifySupportPath); + } + } BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } @@ -1632,7 +1781,7 @@ void Print::_make_wipe_tower() for (LayerTools& layer_tools : layer_tools_array) { layer_tools.has_wipe_tower = true; if (layer_tools.wipe_tower_partitions == 0) { - layer_tools.wipe_tower_partitions = 1; + layer_tools.wipe_tower_partitions = 1; } } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 3007eef292..084763db3a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -415,6 +415,11 @@ public: //BBS BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name); + PrintObject* get_shared_object() const { return m_shared_object; } + void set_shared_object(PrintObject *object); + void clear_shared_object(); + void copy_layers_from_shared_object(); + // BBS: Boundingbox of the first layer BoundingBox firstLayerObjectBrimBoundingBox; private: @@ -495,6 +500,8 @@ private: // BBS: per object skirt ExtrusionEntityCollection m_skirt; + PrintObject* m_shared_object{ nullptr }; + public: //BBS: When printing multi-material objects, this settings will make slicer to clip the overlapping object parts one by the other. //(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc). diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 5196d63698..c6ecf4ebeb 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -629,6 +629,8 @@ protected: { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); } bool invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); } + bool invalidate_all_steps_without_cancel() + { return m_state.invalidate_all([](){}); } bool is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); } bool is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d6d47f5c7e..84429764eb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -86,6 +86,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor PrintObject::~PrintObject() { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, m_shared_object %2%")%this%m_shared_object; if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions; clear_layers(); clear_support_layers(); @@ -535,9 +536,11 @@ std::pair PrintObject::prepare void PrintObject::clear_layers() { - for (Layer *l : m_layers) - delete l; - m_layers.clear(); + if (!m_shared_object) { + for (Layer *l : m_layers) + delete l; + m_layers.clear(); + } } Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z) @@ -573,9 +576,11 @@ SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_ void PrintObject::clear_tree_support_layers() { - for (TreeSupportLayer* l : m_tree_support_layers) - delete l; - m_tree_support_layers.clear(); + if (!m_shared_object) { + for (TreeSupportLayer* l : m_tree_support_layers) + delete l; + m_tree_support_layers.clear(); + } } std::shared_ptr PrintObject::alloc_tree_support_preview_cache() @@ -602,9 +607,11 @@ TreeSupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, c void PrintObject::clear_support_layers() { - for (Layer *l : m_support_layers) - delete l; - m_support_layers.clear(); + if (!m_shared_object) { + for (Layer *l : m_support_layers) + delete l; + m_support_layers.clear(); + } } SupportLayer* PrintObject::add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z) diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 3fece644bd..79aac12cc6 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -152,10 +152,14 @@ public: const TriangleMeshStats& stats() const { return m_stats; } + void set_init_shift(const Vec3d &offset) { m_init_shift = offset; } + Vec3d get_init_shift() const { return m_init_shift; } + indexed_triangle_set its; private: TriangleMeshStats m_stats; + Vec3d m_init_shift {0.0, 0.0, 0.0}; }; // Index of face indices incident with a vertex index. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 858905065f..ef7ffc1fb7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2740,6 +2740,14 @@ std::vector Plater::priv::load_files(const std::vector& input_ context += append; show_info(q, context, _L("Newer 3mf version")); } + else { + //if the minor version is not matched + if (file_version.min() != app_version.min()) { + wxString text = wxString::Format(_L("The 3mf's version %s is newer than %s's version %s, Suggest to upgrade your software.\n"), + file_version.to_string(), std::string(SLIC3R_APP_FULL_NAME), app_version.to_string()); + show_info(q, text, _L("Newer 3mf version")); + } + } } else if (!load_config) { for (ModelObject *model_object : model.objects) { model_object->config.reset();