diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 7f06f763eb..88e1a9047f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -34,10 +34,12 @@ namespace pt = boost::property_tree; // VERSION NUMBERS // 0 : .3mf, files saved by older slic3r or other applications. No version definition in them. // 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files. -// 2 : Meshes saved in their local system; Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file. +// 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading. // WARNING !! -> the version number has been rolled back to 1 // the next change should use 3 const unsigned int VERSION_3MF = 1; +// Allow loading version 2 file as well. +const unsigned int VERSION_3MF_COMPATIBLE = 2; const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file const std::string MODEL_FOLDER = "3D/"; @@ -1513,7 +1515,7 @@ namespace Slic3r { { m_version = (unsigned int)atoi(m_curr_characters.c_str()); - if (m_check_version && (m_version > VERSION_3MF)) + if (m_check_version && (m_version > VERSION_3MF_COMPATIBLE)) { // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); // throw version_error(msg.c_str()); @@ -1699,20 +1701,19 @@ namespace Slic3r { return false; } - Slic3r::Geometry::Transformation transform; - if (m_version > 1) + Transform3d volume_matrix_to_object = Transform3d::Identity(); + bool has_transform = false; + // extract the volume transformation from the volume's metadata, if present + for (const Metadata& metadata : volume_data.metadata) { - // extract the volume transformation from the volume's metadata, if present - for (const Metadata& metadata : volume_data.metadata) + if (metadata.key == MATRIX_KEY) { - if (metadata.key == MATRIX_KEY) - { - transform.set_from_string(metadata.value); - break; - } + volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value); + has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10); + break; } } - Transform3d inv_matrix = transform.get_matrix().inverse(); + Transform3d inv_matrix = volume_matrix_to_object.inverse(); // splits volume out of imported geometry TriangleMesh triangle_mesh; @@ -1733,10 +1734,10 @@ namespace Slic3r { { unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3; Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]); - if (m_version > 1) + facet.vertex[v] = has_transform ? // revert the vertices to the original mesh reference system - vertex = (inv_matrix * vertex.cast()).cast(); - ::memcpy(facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); + (inv_matrix * vertex.cast()).cast() : + vertex; } } @@ -1745,8 +1746,8 @@ namespace Slic3r { ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); // apply the volume matrix taken from the metadata, if present - if (m_version > 1) - volume->set_transformation(transform); + if (has_transform) + volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object)); volume->calculate_convex_hull(); // apply the remaining volume's metadata @@ -2471,6 +2472,9 @@ namespace Slic3r { bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data) { std::stringstream stream; + // 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); stream << "\n"; stream << "<" << CONFIG_TAG << ">\n"; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index fd4c8a3708..4b19b8060a 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -41,10 +41,11 @@ namespace pt = boost::property_tree; // Added x and y components of rotation // Added x, y and z components of scale // Added x, y and z components of mirror -// 3 : Meshes saved in their local system; Added volumes' matrices and source data +// 3 : Added volumes' matrices and source data, meshes transformed back to their coordinate system on loading. // WARNING !! -> the version number has been rolled back to 2 // the next change should use 4 const unsigned int VERSION_AMF = 2; +const unsigned int VERSION_AMF_COMPATIBLE = 3; const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -230,6 +231,8 @@ struct AMFParserContext ModelVolume *m_volume; // Faces collected for the current m_volume. std::vector m_volume_facets; + // Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system. + Transform3d m_volume_transform; // Current material allocated for an amf/metadata subtree. ModelMaterial *m_material; // Current instance allocated for an amf/constellation/instance subtree. @@ -321,6 +324,7 @@ void AMFParserContext::startElement(const char *name, const char **atts) else if (strcmp(name, "volume") == 0) { assert(! m_volume); m_volume = m_object->add_volume(TriangleMesh()); + m_volume_transform = Transform3d::Identity(); node_type_new = NODE_TYPE_VOLUME; } } else if (m_path[2] == NODE_TYPE_INSTANCE) { @@ -580,27 +584,25 @@ void AMFParserContext::endElement(const char * /* name */) stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); - Slic3r::Geometry::Transformation transform; - if (m_version > 2) - transform = m_volume->get_transformation(); - - Transform3d inv_matrix = transform.get_matrix().inverse(); - + bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); + Transform3d inv_matrix = m_volume_transform.inverse(); for (size_t i = 0; i < m_volume_facets.size();) { stl_facet &facet = stl.facet_start[i/3]; for (unsigned int v = 0; v < 3; ++v) { unsigned int tri_id = m_volume_facets[i++] * 3; Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]); - if (m_version > 2) + facet.vertex[v] = has_transform ? // revert the vertices to the original mesh reference system - vertex = (inv_matrix * vertex.cast()).cast(); - ::memcpy((void*)facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); + (inv_matrix * vertex.cast()).cast() : + vertex; } - } + } stl_get_size(&stl); mesh.repair(); m_volume->set_mesh(std::move(mesh)); + if (has_transform) + m_volume->set_transformation(m_volume_transform); if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) { m_volume->source.object_idx = (int)m_model.objects.size() - 1; @@ -720,9 +722,7 @@ void AMFParserContext::endElement(const char * /* name */) m_volume->set_type(ModelVolume::type_from_string(m_value[1])); } else if (strcmp(opt_key, "matrix") == 0) { - Geometry::Transformation transform; - transform.set_from_string(m_value[1]); - m_volume->set_transformation(transform); + m_volume_transform = Slic3r::Geometry::transform3d_from_string(m_value[1]); } else if (strcmp(opt_key, "source_file") == 0) { m_volume->source.input_file = m_value[1]; @@ -912,7 +912,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi ctx.endDocument(); - if (check_version && (ctx.m_version > VERSION_AMF)) + if (check_version && (ctx.m_version > VERSION_AMF_COMPATIBLE)) { // std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); // throw std::runtime_error(msg.c_str()); @@ -1148,6 +1148,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; stream << " "; const Transform3d& matrix = volume->get_matrix(); + stream << std::setprecision(std::numeric_limits::max_digits10); for (int r = 0; r < 4; ++r) { for (int c = 0; c < 4; ++c) @@ -1167,6 +1168,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " " << volume->source.mesh_offset(1) << "\n"; stream << " " << volume->source.mesh_offset(2) << "\n"; } + stream << std::setprecision(std::numeric_limits::max_digits10); const indexed_triangle_set &its = volume->mesh().its; for (size_t i = 0; i < its.indices.size(); ++i) { stream << " \n"; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 46d7ef1543..b9e4d6e78c 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1187,14 +1187,12 @@ MedialAxis::validate_edge(const VD::edge_type* edge) return true; } -const Line& -MedialAxis::retrieve_segment(const VD::cell_type* cell) const +const Line& MedialAxis::retrieve_segment(const VD::cell_type* cell) const { return this->lines[cell->source_index()]; } -const Point& -MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const +const Point& MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const { const Line& line = this->retrieve_segment(cell); if (cell->source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) { @@ -1208,11 +1206,8 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation, const { transform = Transform3d::Identity(); transform.translate(translation); - transform.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); - transform.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); - transform.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); - transform.scale(scale); - transform.scale(mirror); + transform.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); + transform.scale(scale.cwiseProduct(mirror)); } Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) @@ -1420,32 +1415,6 @@ void Transformation::set_from_transform(const Transform3d& transform) // std::cout << "something went wrong in extracting data from matrix" << std::endl; } -void Transformation::set_from_string(const std::string& transform_str) -{ - Transform3d transform = Transform3d::Identity(); - - if (!transform_str.empty()) - { - std::vector mat_elements_str; - boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); - - unsigned int size = (unsigned int)mat_elements_str.size(); - if (size == 16) - { - unsigned int i = 0; - for (unsigned int r = 0; r < 4; ++r) - { - for (unsigned int c = 0; c < 4; ++c) - { - transform(r, c) = ::atof(mat_elements_str[i++].c_str()); - } - } - } - } - - set_from_transform(transform); -} - void Transformation::reset() { m_offset = Vec3d::Zero(); @@ -1536,6 +1505,33 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation return out; } +// For parsing a transformation matrix from 3MF / AMF. +Transform3d transform3d_from_string(const std::string& transform_str) +{ + Transform3d transform = Transform3d::Identity(); + + if (!transform_str.empty()) + { + std::vector mat_elements_str; + boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); + + unsigned int size = (unsigned int)mat_elements_str.size(); + if (size == 16) + { + unsigned int i = 0; + for (unsigned int r = 0; r < 4; ++r) + { + for (unsigned int c = 0; c < 4; ++c) + { + transform(r, c) = ::atof(mat_elements_str[i++].c_str()); + } + } + } + } + + return transform; +} + Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { return diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 283d1edb02..1fa15ba635 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -360,7 +360,6 @@ public: void set_mirror(Axis axis, double mirror); void set_from_transform(const Transform3d& transform); - void set_from_string(const std::string& transform_str); void reset(); @@ -385,6 +384,9 @@ private: } }; +// For parsing a transformation matrix from 3MF / AMF. +extern Transform3d transform3d_from_string(const std::string& transform_str); + // Rotation when going from the first coordinate system with rotation rot_xyz_from applied // to a coordinate system with rot_xyz_to applied. extern Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9f173f6ea0..a6ee04600e 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -466,6 +466,7 @@ public: const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } + void set_transformation(const Transform3d &trafo) { m_transformation.set_from_transform(trafo); } const Vec3d& get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }