mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Merge remote-tracking branch 'remotes/origin/3mf_io'
This commit is contained in:
commit
b695089bc4
28 changed files with 10717 additions and 108 deletions
|
@ -324,7 +324,7 @@ void ConfigBase::setenv_()
|
|||
void ConfigBase::load(const std::string &file)
|
||||
{
|
||||
if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g"))
|
||||
this->load_from_gcode(file);
|
||||
this->load_from_gcode_file(file);
|
||||
else
|
||||
this->load_from_ini(file);
|
||||
}
|
||||
|
@ -349,10 +349,10 @@ void ConfigBase::load(const boost::property_tree::ptree &tree)
|
|||
}
|
||||
}
|
||||
|
||||
// Load the config keys from the tail of a G-code.
|
||||
void ConfigBase::load_from_gcode(const std::string &file)
|
||||
// Load the config keys from the tail of a G-code file.
|
||||
void ConfigBase::load_from_gcode_file(const std::string &file)
|
||||
{
|
||||
// 1) Read a 64k block from the end of the G-code.
|
||||
// Read a 64k block from the end of the G-code.
|
||||
boost::nowide::ifstream ifs(file);
|
||||
{
|
||||
const char slic3r_gcode_header[] = "; generated by Slic3r ";
|
||||
|
@ -365,30 +365,39 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
auto file_length = ifs.tellg();
|
||||
auto data_length = std::min<std::fstream::streampos>(65535, file_length);
|
||||
ifs.seekg(file_length - data_length, ifs.beg);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
ifs.close();
|
||||
|
||||
// 2) Walk line by line in reverse until a non-configuration key appears.
|
||||
char *data_start = data.data();
|
||||
load_from_gcode_string(data.data());
|
||||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
void ConfigBase::load_from_gcode_string(const char* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
return;
|
||||
|
||||
// Walk line by line in reverse until a non-configuration key appears.
|
||||
char *data_start = const_cast<char*>(str);
|
||||
// boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
|
||||
char *end = data_start + strlen(data.data());
|
||||
char *end = data_start + strlen(str);
|
||||
size_t num_key_value_pairs = 0;
|
||||
for (;;) {
|
||||
// Extract next line.
|
||||
for (-- end; end > data_start && (*end == '\r' || *end == '\n'); -- end);
|
||||
for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
|
||||
if (end == data_start)
|
||||
break;
|
||||
char *start = end;
|
||||
*(++ end) = 0;
|
||||
for (; start > data_start && *start != '\r' && *start != '\n'; -- start);
|
||||
*(++end) = 0;
|
||||
for (; start > data_start && *start != '\r' && *start != '\n'; --start);
|
||||
if (start == data_start)
|
||||
break;
|
||||
// Extracted a line from start to end. Extract the key = value pair.
|
||||
if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ')
|
||||
if (end - (++start) < 10 || start[0] != ';' || start[1] != ' ')
|
||||
break;
|
||||
char *key = start + 2;
|
||||
if (! (*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
|
||||
if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
|
||||
// A key must start with a letter.
|
||||
break;
|
||||
char *sep = strchr(key, '=');
|
||||
|
@ -402,8 +411,8 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
break;
|
||||
*key_end = 0;
|
||||
// The key may contain letters, digits and underscores.
|
||||
for (char *c = key; c != key_end; ++ c)
|
||||
if (! ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
|
||||
for (char *c = key; c != key_end; ++c)
|
||||
if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
|
||||
key = nullptr;
|
||||
break;
|
||||
}
|
||||
|
@ -411,8 +420,9 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
break;
|
||||
try {
|
||||
this->set_deserialize(key, value);
|
||||
++ num_key_value_pairs;
|
||||
} catch (UnknownOptionException & /* e */) {
|
||||
++num_key_value_pairs;
|
||||
}
|
||||
catch (UnknownOptionException & /* e */) {
|
||||
// ignore
|
||||
}
|
||||
end = start;
|
||||
|
|
|
@ -1056,7 +1056,8 @@ public:
|
|||
void setenv_();
|
||||
void load(const std::string &file);
|
||||
void load_from_ini(const std::string &file);
|
||||
void load_from_gcode(const std::string &file);
|
||||
void load_from_gcode_file(const std::string &file);
|
||||
void load_from_gcode_string(const char* str);
|
||||
void load(const boost::property_tree::ptree &tree);
|
||||
void save(const std::string &file) const;
|
||||
|
||||
|
|
1396
xs/src/libslic3r/Format/3mf.cpp
Normal file
1396
xs/src/libslic3r/Format/3mf.cpp
Normal file
File diff suppressed because it is too large
Load diff
19
xs/src/libslic3r/Format/3mf.hpp
Normal file
19
xs/src/libslic3r/Format/3mf.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef slic3r_Format_3mf_hpp_
|
||||
#define slic3r_Format_3mf_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class Print;
|
||||
class PresetBundle;
|
||||
|
||||
// Load the content of a 3mf file into the given model and preset bundle.
|
||||
extern bool load_3mf(const char* path, PresetBundle* bundle, Model* model);
|
||||
|
||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, Print* print);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_3mf_hpp_ */
|
|
@ -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_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf");
|
||||
if (internal_amf_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\n");
|
||||
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\n");
|
||||
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\n", 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_amf_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_amf_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
|
||||
|
||||
|
|
|
@ -816,13 +816,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
// Append full config.
|
||||
_write(file, "\n");
|
||||
{
|
||||
StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) {
|
||||
StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
if (key != "compatible_printers")
|
||||
_write_format(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
}
|
||||
std::string full_config = "";
|
||||
append_full_config(print, full_config);
|
||||
if (!full_config.empty())
|
||||
_write(file, full_config);
|
||||
}
|
||||
|
||||
// starts analizer calculations
|
||||
|
@ -1385,6 +1382,24 @@ void GCode::apply_print_config(const PrintConfig &print_config)
|
|||
m_config.apply(print_config);
|
||||
}
|
||||
|
||||
void GCode::append_full_config(const Print& print, std::string& str)
|
||||
{
|
||||
char buff[1024];
|
||||
|
||||
const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
|
||||
const StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
{
|
||||
if (key != "compatible_printers")
|
||||
{
|
||||
sprintf(buff, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
str += buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||
{
|
||||
m_writer.set_extruders(extruder_ids);
|
||||
|
|
|
@ -160,6 +160,9 @@ public:
|
|||
void set_layer_count(unsigned int value) { m_layer_count = value; }
|
||||
void apply_print_config(const PrintConfig &print_config);
|
||||
|
||||
// append full config to the given string
|
||||
static void append_full_config(const Print& print, std::string& str);
|
||||
|
||||
protected:
|
||||
void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Format/OBJ.hpp"
|
||||
#include "Format/PRUS.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
|
@ -46,16 +47,16 @@ 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);
|
||||
#endif /* SLIC3R_PRUS */
|
||||
else
|
||||
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||
|
||||
|
||||
if (! result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
|
||||
|
@ -71,6 +72,33 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst
|
|||
return model;
|
||||
}
|
||||
|
||||
Model Model::read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances)
|
||||
{
|
||||
Model model;
|
||||
|
||||
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 or .zip.amf extension.");
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
|
||||
if (model.objects.empty())
|
||||
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
||||
if (add_default_instances)
|
||||
model.add_default_instances();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
ModelObject* Model::add_object()
|
||||
{
|
||||
this->objects.emplace_back(new ModelObject(this));
|
||||
|
@ -115,6 +143,23 @@ void Model::delete_object(size_t idx)
|
|||
this->objects.erase(i);
|
||||
}
|
||||
|
||||
void Model::delete_object(ModelObject* object)
|
||||
{
|
||||
if (object == nullptr)
|
||||
return;
|
||||
|
||||
for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it)
|
||||
{
|
||||
ModelObject* obj = *it;
|
||||
if (obj == object)
|
||||
{
|
||||
delete obj;
|
||||
objects.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Model::clear_objects()
|
||||
{
|
||||
for (ModelObject *o : this->objects)
|
||||
|
@ -607,6 +652,20 @@ void ModelObject::rotate(float angle, const Axis &axis)
|
|||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::transform(const float* matrix3x4)
|
||||
{
|
||||
if (matrix3x4 == nullptr)
|
||||
return;
|
||||
|
||||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->mesh.transform(matrix3x4);
|
||||
}
|
||||
|
||||
origin_translation = Pointf3(0.0f, 0.0f, 0.0f);
|
||||
invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::mirror(const Axis &axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
|
|
|
@ -19,6 +19,7 @@ class ModelInstance;
|
|||
class ModelMaterial;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class PresetBundle;
|
||||
|
||||
typedef std::string t_model_material_id;
|
||||
typedef std::string t_model_material_attribute;
|
||||
|
@ -119,6 +120,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||
void scale(const Pointf3 &versor);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void transform(const float* matrix3x4);
|
||||
void mirror(const Axis &axis);
|
||||
size_t materials_count() const;
|
||||
size_t facets_count() const;
|
||||
|
@ -238,12 +240,14 @@ public:
|
|||
~Model() { this->clear_objects(); this->clear_materials(); }
|
||||
|
||||
static Model read_from_file(const std::string &input_file, bool add_default_instances = true);
|
||||
static Model read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances = true);
|
||||
|
||||
ModelObject* add_object();
|
||||
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
|
||||
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
|
||||
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
|
||||
void delete_object(size_t idx);
|
||||
void delete_object(ModelObject* object);
|
||||
void clear_objects();
|
||||
|
||||
ModelMaterial* add_material(t_model_material_id material_id);
|
||||
|
|
|
@ -375,6 +375,15 @@ void TriangleMesh::mirror_z()
|
|||
this->mirror(Z);
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const float* matrix3x4)
|
||||
{
|
||||
if (matrix3x4 == nullptr)
|
||||
return;
|
||||
|
||||
stl_transform(&stl, const_cast<float*>(matrix3x4));
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::align_to_origin()
|
||||
{
|
||||
this->translate(
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
void mirror_x();
|
||||
void mirror_y();
|
||||
void mirror_z();
|
||||
void transform(const float* matrix3x4);
|
||||
void align_to_origin();
|
||||
void rotate(double angle, Point* center);
|
||||
TriangleMeshPtrs split() const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue