AMF I/O - Embedded config data + zip formatting
This commit is contained in:
parent
bacb36eb10
commit
f5f27859e0
@ -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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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<std::string> 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<std::string> 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<std::string>& 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<std::string>& _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<BuildItem> BuildItemsList;
|
||||
|
||||
std::vector<std::string> m_errors;
|
||||
|
||||
public:
|
||||
bool save_model_to_file(const std::string& filename, Model& model, const Print& print);
|
||||
|
||||
const std::vector<std::string>& 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<std::string>& _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<std::string>& 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<std::string>& errors = exporter.get_errors();
|
||||
exporter.log_errors();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -7,8 +7,14 @@
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../slic3r/GUI/PresetBundle.hpp"
|
||||
#include "AMF.hpp"
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <miniz/miniz_zip.h>
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
#define DEBUG
|
||||
@ -18,18 +24,22 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
fprintf(file, "<amf unit=\"millimeter\">\n");
|
||||
fprintf(file, "<metadata type=\"cad\">Slic3r %s</metadata>\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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<amf unit=\"millimeter\">\n";
|
||||
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
|
||||
|
||||
std::string config = "\n";
|
||||
GCode::append_full_config(*print, config);
|
||||
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << config << "</metadata>\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, " <material id=\"%s\">\n", material.first.c_str());
|
||||
stream << " <material id=\"" << material.first << "\">\n";
|
||||
for (const auto &attr : material.second->attributes)
|
||||
fprintf(file, " <metadata type=\"%s\">%s</metadata>\n", attr.first.c_str(), attr.second.c_str());
|
||||
stream << " <metadata type=\"" << attr.first << "\">" << attr.second << "</metadata>\n";
|
||||
for (const std::string &key : material.second->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), material.second->config.serialize(key).c_str());
|
||||
fprintf(file, " </material>\n");
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.serialize(key) << "</metadata>\n";
|
||||
stream << " </material>\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, " <object id=\"" PRINTF_ZU "\">\n", object_id);
|
||||
stream << " <object id=\"" << object_id << "\">\n";
|
||||
for (const std::string &key : object->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), object->config.serialize(key).c_str());
|
||||
if (! object->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", object->name.c_str());
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
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, " <metadata type=\"slic3r.layer_height_profile\">");
|
||||
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 </metadata>\n");
|
||||
stream << " <metadata type=\"slic3r.layer_height_profile\">";
|
||||
stream << layer_height_profile.front();
|
||||
for (size_t i = 1; i < layer_height_profile.size(); ++i)
|
||||
stream << ";" << layer_height_profile[i];
|
||||
stream << "\n </metadata>\n";
|
||||
}
|
||||
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
||||
fprintf(file, " <mesh>\n");
|
||||
fprintf(file, " <vertices>\n");
|
||||
stream << " <mesh>\n";
|
||||
stream << " <vertices>\n";
|
||||
std::vector<int> 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, " <vertex>\n");
|
||||
fprintf(file, " <coordinates>\n");
|
||||
fprintf(file, " <x>%f</x>\n", stl.v_shared[i].x);
|
||||
fprintf(file, " <y>%f</y>\n", stl.v_shared[i].y);
|
||||
fprintf(file, " <z>%f</z>\n", stl.v_shared[i].z);
|
||||
fprintf(file, " </coordinates>\n");
|
||||
fprintf(file, " </vertex>\n");
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
stream << " <x>" << stl.v_shared[i].x << "</x>\n";
|
||||
stream << " <y>" << stl.v_shared[i].y << "</y>\n";
|
||||
stream << " <z>" << stl.v_shared[i].z << "</z>\n";
|
||||
stream << " </coordinates>\n";
|
||||
stream << " </vertex>\n";
|
||||
}
|
||||
num_vertices += stl.stats.shared_vertices;
|
||||
}
|
||||
fprintf(file, " </vertices>\n");
|
||||
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++ i_volume) {
|
||||
stream << " </vertices>\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, " <volume>\n");
|
||||
stream << " <volume>\n";
|
||||
else
|
||||
fprintf(file, " <volume materialid=\"%s\">\n", volume->material_id().c_str());
|
||||
stream << " <volume materialid=\"" << volume->material_id() << "\">\n";
|
||||
for (const std::string &key : volume->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), volume->config.serialize(key).c_str());
|
||||
if (! volume->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", volume->name.c_str());
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
||||
if (!volume->name.empty())
|
||||
stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
|
||||
if (volume->modifier)
|
||||
fprintf(file, " <metadata type=\"slic3r.modifier\">1</metadata>\n");
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++ i) {
|
||||
fprintf(file, " <triangle>\n");
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
fprintf(file, " <v%d>%d</v%d>\n", j+1, volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset, j+1);
|
||||
fprintf(file, " </triangle>\n");
|
||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||
stream << " <triangle>\n";
|
||||
for (int j = 0; j < 3; ++j)
|
||||
stream << " <v" << j + 1 << ">" << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "</v" << j + 1 << ">\n";
|
||||
stream << " </triangle>\n";
|
||||
}
|
||||
fprintf(file, " </volume>\n");
|
||||
stream << " </volume>\n";
|
||||
}
|
||||
fprintf(file, " </mesh>\n");
|
||||
fprintf(file, " </object>\n");
|
||||
if (! object->instances.empty()) {
|
||||
stream << " </mesh>\n";
|
||||
stream << " </object>\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, " <constellation id=\"1\">\n");
|
||||
fwrite(instances.data(), instances.size(), 1, file);
|
||||
fprintf(file, " </constellation>\n");
|
||||
stream << " <constellation id=\"1\">\n";
|
||||
stream << instances;
|
||||
stream << " </constellation>\n";
|
||||
}
|
||||
fprintf(file, "</amf>\n");
|
||||
fclose(file);
|
||||
stream << "</amf>\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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user