From f5f27859e026876de2501f11219f29702483b20f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 14 Feb 2018 14:30:03 +0100 Subject: [PATCH] AMF I/O - Embedded config data + zip formatting --- lib/Slic3r/GUI/Plater.pm | 21 ++- xs/src/libslic3r/Format/3mf.cpp | 107 +++++++------ xs/src/libslic3r/Format/AMF.cpp | 265 +++++++++++++++++++++++++------- xs/src/libslic3r/Format/AMF.hpp | 10 +- xs/src/libslic3r/Model.cpp | 10 +- xs/xsp/Model.xsp | 9 +- 6 files changed, 299 insertions(+), 123 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index be94095c9..82b4e3cfb 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -610,7 +610,7 @@ sub load_files { # One of the files is potentionally a bundle of files. Don't bundle them, but load them one by one. # Only bundle .stls or .objs if the printer has multiple extruders. my $one_by_one = (@$nozzle_dmrs <= 1) || (@$input_files == 1) || - defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files); + defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.[aA][mM][fF].[xX][mM][lL]$/ || $_ =~ /.[zZ][iI][pP].[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files); my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file\n" . basename($input_files->[0]), 100, $self, 0); $process_dialog->Pulse; @@ -629,7 +629,7 @@ sub load_files { $process_dialog->Update(100. * $i / @$input_files, "Processing input file\n" . basename($input_file)); my $model; - if ($input_file =~ /.3[mM][fF]$/) + if (($input_file =~ /.3[mM][fF]$/) || ($input_file =~ /.[zZ][iI][pP].[aA][mM][fF]$/)) { $model = eval { Slic3r::Model->read_from_archive($input_file, wxTheApp->{preset_bundle}, 0) }; Slic3r::GUI::show_error($self, $@) if $@; @@ -1557,8 +1557,15 @@ sub export_amf { return if !@{$self->{objects}}; # Ask user for a file name to write into. my $output_file = $self->_get_export_file('AMF') or return; - $self->{model}->store_amf($output_file); - $self->statusbar->SetStatusText("AMF file exported to $output_file"); + my $res = $self->{model}->store_amf($output_file, $self->{print}); + if ($res) + { + $self->statusbar->SetStatusText("AMF file exported to $output_file"); + } + else + { + $self->statusbar->SetStatusText("Error exporting AMF file $output_file"); + } } sub export_3mf { @@ -1588,7 +1595,7 @@ sub _get_export_file { } elsif ($format eq 'AMF') { - $suffix = '.amf.xml'; + $suffix = '.zip.amf'; } elsif ($format eq '3MF') { @@ -2102,8 +2109,8 @@ sub OnDropFiles { # stop scalars leaking on older perl # https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602 @_ = (); - # only accept STL, OBJ, AMF and 3MF files - return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF]|[3][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames; + # only accept STL, OBJ, AMF, 3MF and PRUSA files + return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF]|[3][mM][fF]|[aA][mM][fF].[xX][mM][lL]|[zZ][iI][pP].[aA][mM][lL]|[pP][rR][uU][sS][aA])$/, @$filenames; $self->{window}->load_files($filenames); } diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 60ddf5d02..029debf72 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -162,7 +162,26 @@ bool is_valid_object_type(const std::string& type) namespace Slic3r { - class _3MF_Importer + // Base class with error messages management + class _3MF_Base + { + std::vector m_errors; + + protected: + void add_error(const std::string& error) { m_errors.push_back(error); } + void clear_errors() { m_errors.clear(); } + + public: + void log_errors() + { + for (const std::string& error : m_errors) + { + printf("%s\n", error.c_str()); + } + } + }; + + class _3MF_Importer : public _3MF_Base { struct Component { @@ -216,7 +235,6 @@ namespace Slic3r { IdToModelObjectMap m_objects; IdToAliasesMap m_objects_aliases; InstancesList m_instances; - std::vector m_errors; public: _3MF_Importer(); @@ -224,8 +242,6 @@ namespace Slic3r { bool load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle); - const std::vector& get_errors() const; - private: void _destroy_xml_parser(); void _stop_xml_parser(); @@ -345,16 +361,11 @@ namespace Slic3r { m_objects.clear(); m_objects_aliases.clear(); m_instances.clear(); - m_errors.clear(); + clear_errors(); return _load_model_from_file(filename, model, bundle); } - const std::vector& _3MF_Importer::get_errors() const - { - return m_errors; - } - void _3MF_Importer::_destroy_xml_parser() { if (m_xml_parser != nullptr) @@ -378,7 +389,7 @@ namespace Slic3r { mz_bool res = mz_zip_reader_init_file(&archive, filename.c_str(), 0); if (res == 0) { - m_errors.push_back("Unable to open the file"); + add_error("Unable to open the file"); return false; } @@ -398,7 +409,7 @@ namespace Slic3r { if (!_extract_model_from_archive(archive, stat)) { mz_zip_reader_end(&archive); - m_errors.push_back("Archive does not contain a valid model"); + add_error("Archive does not contain a valid model"); return false; } } @@ -408,7 +419,7 @@ namespace Slic3r { if (!_extract_config_from_archive(archive, stat, bundle, filename)) { mz_zip_reader_end(&archive); - m_errors.push_back("Archive does not contain a valid config"); + add_error("Archive does not contain a valid config"); return false; } } @@ -421,12 +432,18 @@ namespace Slic3r { bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) { + if (stat.m_uncomp_size == 0) + { + add_error("Found invalid size"); + return false; + } + _destroy_xml_parser(); m_xml_parser = XML_ParserCreate(nullptr); if (m_xml_parser == nullptr) { - m_errors.push_back("Unable to create parser"); + add_error("Unable to create parser"); return false; } @@ -436,14 +453,14 @@ namespace Slic3r { void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size); if (parser_buffer == nullptr) { - m_errors.push_back("Unable to create buffer"); + add_error("Unable to create buffer"); return false; } mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0); if (res == 0) { - m_errors.push_back("Error while reading model data to buffer"); + add_error("Error while reading model data to buffer"); return false; } @@ -451,7 +468,7 @@ namespace Slic3r { { char error_buf[1024]; ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser)); - m_errors.push_back(error_buf); + add_error(error_buf); return false; } @@ -466,7 +483,7 @@ namespace Slic3r { mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { - m_errors.push_back("Error while reading config data to buffer"); + add_error("Error while reading config data to buffer"); return false; } @@ -613,7 +630,7 @@ namespace Slic3r { m_curr_object.object = m_model->add_object(); if (m_curr_object.object == nullptr) { - m_errors.push_back("Unable to create object"); + add_error("Unable to create object"); return false; } @@ -657,7 +674,7 @@ namespace Slic3r { ModelVolume* volume = m_curr_object.object->add_volume(m_curr_object.mesh); if (volume == nullptr) { - m_errors.push_back("Unable to add volume"); + add_error("Unable to add volume"); return false; } @@ -685,7 +702,7 @@ namespace Slic3r { } else { - m_errors.push_back("Found object with duplicate id"); + add_error("Found object with duplicate id"); return false; } } @@ -796,7 +813,7 @@ namespace Slic3r { IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id); if (alias_item == m_objects_aliases.end()) { - m_errors.push_back("Found component with invalid object id"); + add_error("Found component with invalid object id"); return false; } } @@ -852,14 +869,14 @@ namespace Slic3r { // escape from circular aliasing if (recur_counter > MAX_RECURSIONS) { - m_errors.push_back("Too many recursions"); + add_error("Too many recursions"); return false; } IdToAliasesMap::iterator it = m_objects_aliases.find(object_id); if (it == m_objects_aliases.end()) { - m_errors.push_back("Found item with invalid object id"); + add_error("Found item with invalid object id"); return false; } @@ -870,7 +887,7 @@ namespace Slic3r { IdToModelObjectMap::iterator object_item = m_objects.find(object_id); if ((object_item == m_objects.end()) || (object_item->second == nullptr)) { - m_errors.push_back("Found invalid object"); + add_error("Found invalid object"); return false; } else @@ -878,7 +895,7 @@ namespace Slic3r { ModelInstance* instance = object_item->second->add_instance(); if (instance == nullptr) { - m_errors.push_back("Unable to add object instance"); + add_error("Unable to add object instance"); return false; } @@ -1011,7 +1028,7 @@ namespace Slic3r { importer->_handle_end_xml_element(name); } - class _3MF_Exporter + class _3MF_Exporter : public _3MF_Base { struct BuildItem { @@ -1023,13 +1040,9 @@ namespace Slic3r { typedef std::vector BuildItemsList; - std::vector m_errors; - public: bool save_model_to_file(const std::string& filename, Model& model, const Print& print); - const std::vector& get_errors() const; - private: bool _save_model_to_file(const std::string& filename, Model& model, const Print& print); bool _add_content_types_file_to_archive(mz_zip_archive& archive); @@ -1049,14 +1062,10 @@ namespace Slic3r { bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print) { + clear_errors(); return _save_model_to_file(filename, model, print); } - const std::vector& _3MF_Exporter::get_errors() const - { - return m_errors; - } - bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print) { mz_zip_archive archive; @@ -1065,7 +1074,7 @@ namespace Slic3r { mz_bool res = mz_zip_writer_init_file(&archive, filename.c_str(), 0); if (res == 0) { - m_errors.push_back("Unable to open the file"); + add_error("Unable to open the file"); return false; } @@ -1105,7 +1114,7 @@ namespace Slic3r { { mz_zip_writer_end(&archive); boost::filesystem::remove(filename); - m_errors.push_back("Unable to finalize the archive"); + add_error("Unable to finalize the archive"); return false; } @@ -1127,7 +1136,7 @@ namespace Slic3r { if (!mz_zip_writer_add_mem(&archive, CONTENT_TYPES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - m_errors.push_back("Unable to add content types file to archive"); + add_error("Unable to add content types file to archive"); return false; } @@ -1146,7 +1155,7 @@ namespace Slic3r { if (!mz_zip_writer_add_mem(&archive, RELATIONSHIPS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - m_errors.push_back("Unable to add relationships file to archive"); + add_error("Unable to add relationships file to archive"); return false; } @@ -1170,7 +1179,7 @@ namespace Slic3r { if (!_add_object_to_model_stream(stream, object_id, *obj, build_items)) { - m_errors.push_back("Unable to add object to archive"); + add_error("Unable to add object to archive"); return false; } } @@ -1180,7 +1189,7 @@ namespace Slic3r { if (!_add_build_to_model_stream(stream, build_items)) { - m_errors.push_back("Unable to add build to archive"); + add_error("Unable to add build to archive"); return false; } @@ -1190,7 +1199,7 @@ namespace Slic3r { if (!mz_zip_writer_add_mem(&archive, MODEL_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - m_errors.push_back("Unable to add model file to archive"); + add_error("Unable to add model file to archive"); return false; } @@ -1212,7 +1221,7 @@ namespace Slic3r { { if (!_add_mesh_to_object_stream(stream, object)) { - m_errors.push_back("Unable to add mesh to archive"); + add_error("Unable to add mesh to archive"); return false; } } @@ -1262,7 +1271,7 @@ namespace Slic3r { if (stl.stats.shared_vertices == 0) { - m_errors.push_back("Found invalid mesh"); + add_error("Found invalid mesh"); return false; } @@ -1313,7 +1322,7 @@ namespace Slic3r { { if (build_items.size() == 0) { - m_errors.push_back("No build item found"); + add_error("No build item found"); return false; } @@ -1349,7 +1358,7 @@ namespace Slic3r { if (!mz_zip_writer_add_mem(&archive, CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - m_errors.push_back("Unable to add config file to archive"); + add_error("Unable to add config file to archive"); return false; } @@ -1365,7 +1374,7 @@ namespace Slic3r { bool res = importer.load_model_from_file(path, *model, *bundle); if (!res) - const std::vector& errors = importer.get_errors(); + importer.log_errors(); return res; } @@ -1379,7 +1388,7 @@ namespace Slic3r { bool res = exporter.save_model_to_file(path, *model, *print); if (!res) - const std::vector& errors = exporter.get_errors(); + exporter.log_errors(); return res; } diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index b8abf038b..06ba20120 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -7,8 +7,14 @@ #include "../libslic3r.h" #include "../Model.hpp" +#include "../GCode.hpp" +#include "../slic3r/GUI/PresetBundle.hpp" #include "AMF.hpp" +#include +#include +#include + #if 0 // Enable debugging and assert in this file. #define DEBUG @@ -18,18 +24,22 @@ #include +const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; + namespace Slic3r { struct AMFParserContext { - AMFParserContext(XML_Parser parser, Model *model) : + AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) : m_parser(parser), m_model(*model), m_object(nullptr), m_volume(nullptr), m_material(nullptr), - m_instance(nullptr) + m_instance(nullptr), + m_preset_bundle(preset_bundle), + m_archive_filename(archive_filename) { m_path.reserve(12); } @@ -149,6 +159,10 @@ struct AMFParserContext Instance *m_instance; // Generic string buffer for vertices, face indices, metadata etc. std::string m_value[3]; + // Pointer to preset bundle to update if config data are stored inside the amf file + PresetBundle* m_preset_bundle; + // Fullpath name of the amf file + std::string m_archive_filename; private: AMFParserContext& operator=(AMFParserContext&); @@ -403,7 +417,10 @@ void AMFParserContext::endElement(const char * /* name */) break; case NODE_TYPE_METADATA: - if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { + if ((m_preset_bundle != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) { + m_preset_bundle->load_config_string(m_value[1].c_str(), m_archive_filename.c_str()); + } + else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { const char *opt_key = m_value[0].c_str() + 7; if (print_config_def.options.find(opt_key) != print_config_def.options.end()) { DynamicPrintConfig *config = nullptr; @@ -474,10 +491,13 @@ void AMFParserContext::endDocument() } // Load an AMF file into a provided model. -bool load_amf(const char *path, Model *model) +bool load_amf_file(const char *path, PresetBundle* bundle, Model *model) { + if ((path == nullptr) || (model == nullptr)) + return false; + XML_Parser parser = XML_ParserCreate(nullptr); // encoding - if (! parser) { + if (!parser) { printf("Couldn't allocate memory for parser\n"); return false; } @@ -488,7 +508,7 @@ bool load_amf(const char *path, Model *model) return false; } - AMFParserContext ctx(parser, model); + AMFParserContext ctx(parser, path, bundle, model); XML_SetUserData(parser, (void*)&ctx); XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement); XML_SetCharacterDataHandler(parser, AMFParserContext::characters); @@ -519,49 +539,163 @@ bool load_amf(const char *path, Model *model) if (result) ctx.endDocument(); + return result; } -bool store_amf(const char *path, Model *model) +// Load an AMF archive into a provided model. +bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) { - FILE *file = boost::nowide::fopen(path, "wb"); - if (file == nullptr) + if ((path == nullptr) || (model == nullptr)) return false; - fprintf(file, "\n"); - fprintf(file, "\n"); - fprintf(file, "Slic3r %s\n", SLIC3R_VERSION); + mz_zip_archive archive; + mz_zip_zero_struct(&archive); + + mz_bool res = mz_zip_reader_init_file(&archive, path, 0); + if (res == 0) + { + printf("Unable to init zip reader\n"); + return false; + } + + mz_uint num_entries = mz_zip_reader_get_num_files(&archive); + if (num_entries != 1) + { + printf("Found invalid number of entries\n"); + mz_zip_reader_end(&archive); + return false; + } + + mz_zip_archive_file_stat stat; + res = mz_zip_reader_file_stat(&archive, 0, &stat); + if (res == 0) + { + printf("Unable to extract entry statistics\n"); + mz_zip_reader_end(&archive); + return false; + } + + std::string internal_aml_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); + if (internal_aml_filename != stat.m_filename) + { + printf("Found invalid internal filename\n"); + mz_zip_reader_end(&archive); + return false; + } + + if (stat.m_uncomp_size == 0) + { + printf("Found invalid size\n"); + mz_zip_reader_end(&archive); + return false; + } + + XML_Parser parser = XML_ParserCreate(nullptr); // encoding + if (!parser) { + printf("Couldn't allocate memory for parser\n"); + mz_zip_reader_end(&archive); + return false; + } + + AMFParserContext ctx(parser, path, bundle, model); + XML_SetUserData(parser, (void*)&ctx); + XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement); + XML_SetCharacterDataHandler(parser, AMFParserContext::characters); + + void* parser_buffer = XML_GetBuffer(parser, (int)stat.m_uncomp_size); + if (parser_buffer == nullptr) + { + printf("Unable to create buffer"); + mz_zip_reader_end(&archive); + return false; + } + + res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0); + if (res == 0) + { + printf("Error while reading model data to buffer"); + mz_zip_reader_end(&archive); + return false; + } + + if (!XML_ParseBuffer(parser, (int)stat.m_uncomp_size, 1)) + { + printf("Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); + mz_zip_reader_end(&archive); + return false; + } + + ctx.endDocument(); + + mz_zip_reader_end(&archive); + return true; +} + +// Load an AMF file into a provided model. +// If bundle is not a null pointer, updates it if the amf file/archive contains config data +bool load_amf(const char *path, PresetBundle* bundle, Model *model) +{ + if (boost::iends_with(path, ".zip.amf")) + return load_amf_archive(path, bundle, model); + else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml")) + return load_amf_file(path, bundle, model); + else + return false; +} + +bool store_amf(const char *path, Model *model, Print* print) +{ + if ((path == nullptr) || (model == nullptr) || (print == nullptr)) + return false; + + mz_zip_archive archive; + mz_zip_zero_struct(&archive); + + mz_bool res = mz_zip_writer_init_file(&archive, path, 0); + if (res == 0) + return false; + + std::stringstream stream; + stream << "\n"; + stream << "\n"; + stream << "Slic3r " << SLIC3R_VERSION << "\n"; + + std::string config = "\n"; + GCode::append_full_config(*print, config); + stream << "" << config << "\n"; + for (const auto &material : model->materials) { if (material.first.empty()) continue; // note that material-id must never be 0 since it's reserved by the AMF spec - fprintf(file, " \n", material.first.c_str()); + stream << " \n"; for (const auto &attr : material.second->attributes) - fprintf(file, " %s\n", attr.first.c_str(), attr.second.c_str()); + stream << " " << attr.second << "\n"; for (const std::string &key : material.second->config.keys()) - fprintf(file, " %s\n", key.c_str(), material.second->config.serialize(key).c_str()); - fprintf(file, " \n"); + stream << " " << material.second->config.serialize(key) << "\n"; + stream << " \n"; } std::string instances; for (size_t object_id = 0; object_id < model->objects.size(); ++ object_id) { ModelObject *object = model->objects[object_id]; - fprintf(file, " \n", object_id); + stream << " \n"; for (const std::string &key : object->config.keys()) - fprintf(file, " %s\n", key.c_str(), object->config.serialize(key).c_str()); - if (! object->name.empty()) - fprintf(file, " %s\n", object->name.c_str()); + stream << " " << object->config.serialize(key) << "\n"; + if (!object->name.empty()) + stream << " " << object->name << "\n"; std::vector layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector(); if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) { // Store the layer height profile as a single semicolon separated list. - fprintf(file, " "); - fprintf(file, "%f", layer_height_profile.front()); - for (size_t i = 1; i < layer_height_profile.size(); ++ i) - fprintf(file, ";%f", layer_height_profile[i]); - fprintf(file, "\n \n"); + stream << " "; + stream << layer_height_profile.front(); + for (size_t i = 1; i < layer_height_profile.size(); ++i) + stream << ";" << layer_height_profile[i]; + stream << "\n \n"; } //FIXME Store the layer height ranges (ModelObject::layer_height_ranges) - fprintf(file, " \n"); - fprintf(file, " \n"); + stream << " \n"; + stream << " \n"; std::vector vertices_offsets; int num_vertices = 0; for (ModelVolume *volume : object->volumes) { @@ -572,41 +706,41 @@ bool store_amf(const char *path, Model *model) if (stl.v_shared == nullptr) stl_generate_shared_vertices(&stl); for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) { - fprintf(file, " \n"); - fprintf(file, " \n"); - fprintf(file, " %f\n", stl.v_shared[i].x); - fprintf(file, " %f\n", stl.v_shared[i].y); - fprintf(file, " %f\n", stl.v_shared[i].z); - fprintf(file, " \n"); - fprintf(file, " \n"); + stream << " \n"; + stream << " \n"; + stream << " " << stl.v_shared[i].x << "\n"; + stream << " " << stl.v_shared[i].y << "\n"; + stream << " " << stl.v_shared[i].z << "\n"; + stream << " \n"; + stream << " \n"; } num_vertices += stl.stats.shared_vertices; } - fprintf(file, " \n"); - for (size_t i_volume = 0; i_volume < object->volumes.size(); ++ i_volume) { + stream << " \n"; + for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) { ModelVolume *volume = object->volumes[i_volume]; int vertices_offset = vertices_offsets[i_volume]; if (volume->material_id().empty()) - fprintf(file, " \n"); + stream << " \n"; else - fprintf(file, " \n", volume->material_id().c_str()); + stream << " material_id() << "\">\n"; for (const std::string &key : volume->config.keys()) - fprintf(file, " %s\n", key.c_str(), volume->config.serialize(key).c_str()); - if (! volume->name.empty()) - fprintf(file, " %s\n", volume->name.c_str()); + stream << " " << volume->config.serialize(key) << "\n"; + if (!volume->name.empty()) + stream << " " << volume->name << "\n"; if (volume->modifier) - fprintf(file, " 1\n"); - for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++ i) { - fprintf(file, " \n"); - for (int j = 0; j < 3; ++ j) - fprintf(file, " %d\n", j+1, volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset, j+1); - fprintf(file, " \n"); + stream << " 1\n"; + for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) { + stream << " \n"; + for (int j = 0; j < 3; ++j) + stream << " " << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "\n"; + stream << " \n"; } - fprintf(file, " \n"); + stream << " \n"; } - fprintf(file, " \n"); - fprintf(file, " \n"); - if (! object->instances.empty()) { + stream << " \n"; + stream << " \n"; + if (!object->instances.empty()) { for (ModelInstance *instance : object->instances) { char buf[512]; sprintf(buf, @@ -627,12 +761,31 @@ bool store_amf(const char *path, Model *model) } } if (! instances.empty()) { - fprintf(file, " \n"); - fwrite(instances.data(), instances.size(), 1, file); - fprintf(file, " \n"); + stream << " \n"; + stream << instances; + stream << " \n"; } - fprintf(file, "\n"); - fclose(file); + stream << "\n"; + + std::string internal_aml_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); + std::string out = stream.str(); + + if (!mz_zip_writer_add_mem(&archive, internal_aml_filename.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) + { + mz_zip_writer_end(&archive); + boost::filesystem::remove(path); + return false; + } + + if (!mz_zip_writer_finalize_archive(&archive)) + { + mz_zip_writer_end(&archive); + boost::filesystem::remove(path); + return false; + } + + mz_zip_writer_end(&archive); + return true; } diff --git a/xs/src/libslic3r/Format/AMF.hpp b/xs/src/libslic3r/Format/AMF.hpp index e58ce2f0f..027ebdab3 100644 --- a/xs/src/libslic3r/Format/AMF.hpp +++ b/xs/src/libslic3r/Format/AMF.hpp @@ -4,11 +4,15 @@ namespace Slic3r { class Model; +class Print; +class PresetBundle; -// Load an AMF file into a provided model. -extern bool load_amf(const char *path, Model *model); +// Load the content of an amf file into the given model and preset bundle. +extern bool load_amf(const char *path, PresetBundle* bundle, Model *model); -extern bool store_amf(const char *path, Model *model); +// Save the given model and the config data contained in the given Print into an amf file. +// 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); }; // namespace Slic3r diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 891438407..f65707ccb 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -47,9 +47,9 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst result = load_stl(input_file.c_str(), &model); else if (boost::algorithm::iends_with(input_file, ".obj")) result = load_obj(input_file.c_str(), &model); - else if (boost::algorithm::iends_with(input_file, ".amf") || - boost::algorithm::iends_with(input_file, ".amf.xml")) - result = load_amf(input_file.c_str(), &model); + else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") || + boost::algorithm::iends_with(input_file, ".amf.xml"))) + result = load_amf(input_file.c_str(), nullptr, &model); #ifdef SLIC3R_PRUS else if (boost::algorithm::iends_with(input_file, ".prusa")) result = load_prus(input_file.c_str(), &model); @@ -79,8 +79,10 @@ Model Model::read_from_archive(const std::string &input_file, PresetBundle* bund bool result = false; if (boost::algorithm::iends_with(input_file, ".3mf")) result = load_3mf(input_file.c_str(), bundle, &model); + else if (boost::algorithm::iends_with(input_file, ".zip.amf")) + result = load_amf(input_file.c_str(), bundle, &model); else - throw std::runtime_error("Unknown file format. Input file must have .3mf extension."); + throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension."); if (!result) throw std::runtime_error("Loading of a model file failed."); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index ae0621057..c0271ae75 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -101,8 +101,8 @@ bool store_stl(char *path, bool binary) %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; - bool store_amf(char *path) - %code%{ RETVAL = Slic3r::store_amf(path, THIS); %}; + bool store_amf(char *path, Print* print) + %code%{ RETVAL = Slic3r::store_amf(path, THIS, print); %}; bool store_3mf(char *path, Print* print) %code%{ RETVAL = Slic3r::store_3mf(path, THIS, print); %}; @@ -137,12 +137,13 @@ load_obj(CLASS, path, object_name) RETVAL Model* -load_amf(CLASS, path) +load_amf(CLASS, bundle, path) char* CLASS; + PresetBundle* bundle; char* path; CODE: RETVAL = new Model(); - if (! load_amf(path, RETVAL)) { + if (! load_amf(path, bundle, RETVAL)) { delete RETVAL; RETVAL = NULL; }