Initial rewrite of the file accessors from Perl to C++.
This is especially important for the extremely slow AMF parser. Also there is a new file handler for the Prusa Control 'PRUS' format.
This commit is contained in:
parent
91af2ddd1e
commit
25dfe7278c
13 changed files with 1849 additions and 5 deletions
|
@ -33,14 +33,15 @@ my @early_includes = ();
|
|||
my @INC = qw(-Isrc/libslic3r -Isrc/glew/include);
|
||||
my @LIBS = $cpp_guess->is_msvc ? qw(-LIBPATH:src/libslic3r) : qw(-Lsrc/libslic3r);
|
||||
|
||||
if ($ENV{SLIC3R_GUI})
|
||||
if ($ENV{SLIC3R_GUI} || $ENV{SLIC3R_PRUS})
|
||||
{
|
||||
print "Slic3r will be built with GUI support\n";
|
||||
require Alien::wxWidgets;
|
||||
Alien::wxWidgets->load;
|
||||
push @INC, Alien::wxWidgets->include_path;
|
||||
push @cflags, qw(-DSLIC3R_GUI -DUNICODE), Alien::wxWidgets->defines, Alien::wxWidgets->c_flags;
|
||||
my $alienwx_libraries = Alien::wxWidgets->libraries(qw(gl html));
|
||||
push @cflags, qw(-DSLIC3R_GUI) if $ENV{SLIC3R_GUI};
|
||||
push @cflags, qw(-DSLIC3R_PRUS -DUNICODE), Alien::wxWidgets->defines, Alien::wxWidgets->c_flags;
|
||||
my $alienwx_libraries = Alien::wxWidgets->libraries($ENV{SLIC3R_GUI} ? qw(gl html) : qw(base));
|
||||
$alienwx_libraries =~ s/-L/-LIBPATH:/g if ($cpp_guess->is_msvc);
|
||||
push @ldflags, Alien::wxWidgets->link_flags, $alienwx_libraries;
|
||||
# push @early_includes, qw(slic3r/GUI/wxinit.h);
|
||||
|
@ -216,6 +217,8 @@ if ($cpp_guess->is_gcc) {
|
|||
}
|
||||
|
||||
print "\n";
|
||||
print 'With @cflags: ', join(', ', map "\"$_\"", @cflags), "\n";
|
||||
print 'With @ldflags: ', join(', ', map "\"$_\"", @ldflags), "\n";
|
||||
print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n";
|
||||
print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n";
|
||||
|
||||
|
|
11
xs/MANIFEST
11
xs/MANIFEST
|
@ -50,6 +50,16 @@ src/libslic3r/Fill/FillRectilinear2.cpp
|
|||
src/libslic3r/Fill/FillRectilinear2.hpp
|
||||
src/libslic3r/Flow.cpp
|
||||
src/libslic3r/Flow.hpp
|
||||
src/libslic3r/Format/AMF.cpp
|
||||
src/libslic3r/Format/AMF.hpp
|
||||
src/libslic3r/Format/OBJ.cpp
|
||||
src/libslic3r/Format/OBJ.hpp
|
||||
src/libslic3r/Format/objparser.cpp
|
||||
src/libslic3r/Format/objparser.hpp
|
||||
src/libslic3r/Format/PRUS.cpp
|
||||
src/libslic3r/Format/PRUS.hpp
|
||||
src/libslic3r/Format/STL.cpp
|
||||
src/libslic3r/Format/STL.hpp
|
||||
src/libslic3r/GCode.cpp
|
||||
src/libslic3r/GCode.hpp
|
||||
src/libslic3r/GCode/CoolingBuffer.cpp
|
||||
|
@ -67,7 +77,6 @@ src/libslic3r/Geometry.hpp
|
|||
src/libslic3r/Layer.cpp
|
||||
src/libslic3r/Layer.hpp
|
||||
src/libslic3r/LayerRegion.cpp
|
||||
src/libslic3r/LayerRegionFill.cpp
|
||||
src/libslic3r/libslic3r.h
|
||||
src/libslic3r/Line.cpp
|
||||
src/libslic3r/Line.hpp
|
||||
|
|
619
xs/src/libslic3r/Format/AMF.cpp
Normal file
619
xs/src/libslic3r/Format/AMF.cpp
Normal file
|
@ -0,0 +1,619 @@
|
|||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <expat/expat.h>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "AMF.hpp"
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
struct AMFParserContext
|
||||
{
|
||||
AMFParserContext(XML_Parser parser, Model *model) :
|
||||
m_parser(parser),
|
||||
m_model(*model),
|
||||
m_object(nullptr),
|
||||
m_volume(nullptr),
|
||||
m_material(nullptr),
|
||||
m_instance(nullptr)
|
||||
{
|
||||
m_path.reserve(12);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
XML_StopParser(m_parser, 0);
|
||||
}
|
||||
|
||||
void startElement(const char *name, const char **atts);
|
||||
void endElement(const char *name);
|
||||
void endDocument();
|
||||
void characters(const XML_Char *s, int len);
|
||||
|
||||
static void XMLCALL startElement(void *userData, const char *name, const char **atts)
|
||||
{
|
||||
AMFParserContext *ctx = (AMFParserContext*)userData;
|
||||
ctx->startElement(name, atts);
|
||||
}
|
||||
|
||||
static void XMLCALL endElement(void *userData, const char *name)
|
||||
{
|
||||
AMFParserContext *ctx = (AMFParserContext*)userData;
|
||||
ctx->endElement(name);
|
||||
}
|
||||
|
||||
/* s is not 0 terminated. */
|
||||
static void XMLCALL characters(void *userData, const XML_Char *s, int len)
|
||||
{
|
||||
AMFParserContext *ctx = (AMFParserContext*)userData;
|
||||
ctx->characters(s, len);
|
||||
}
|
||||
|
||||
static const char* get_attribute(const char **atts, const char *id) {
|
||||
if (atts == nullptr)
|
||||
return nullptr;
|
||||
while (*atts != nullptr) {
|
||||
if (strcmp(*(atts ++), id) == 0)
|
||||
return *atts;
|
||||
++ atts;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enum AMFNodeType {
|
||||
NODE_TYPE_INVALID = 0,
|
||||
NODE_TYPE_UNKNOWN,
|
||||
NODE_TYPE_AMF, // amf
|
||||
// amf/metadata
|
||||
NODE_TYPE_MATERIAL, // amf/material
|
||||
// amf/material/metadata
|
||||
NODE_TYPE_OBJECT, // amf/object
|
||||
// amf/object/metadata
|
||||
NODE_TYPE_MESH, // amf/object/mesh
|
||||
NODE_TYPE_VERTICES, // amf/object/mesh/vertices
|
||||
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
|
||||
NODE_TYPE_COORDINATES, // amf/object/mesh/vertices/vertex/coordinates
|
||||
NODE_TYPE_COORDINATE_X, // amf/object/mesh/vertices/vertex/coordinates/x
|
||||
NODE_TYPE_COORDINATE_Y, // amf/object/mesh/vertices/vertex/coordinates/y
|
||||
NODE_TYPE_COORDINATE_Z, // amf/object/mesh/vertices/vertex/coordinates/z
|
||||
NODE_TYPE_VOLUME, // amf/object/mesh/volume
|
||||
// amf/object/mesh/volume/metadata
|
||||
NODE_TYPE_TRIANGLE, // amf/object/mesh/volume/triangle
|
||||
NODE_TYPE_VERTEX1, // amf/object/mesh/volume/triangle/v1
|
||||
NODE_TYPE_VERTEX2, // amf/object/mesh/volume/triangle/v2
|
||||
NODE_TYPE_VERTEX3, // amf/object/mesh/volume/triangle/v3
|
||||
NODE_TYPE_CONSTELLATION, // amf/constellation
|
||||
NODE_TYPE_INSTANCE, // amf/constellation/instance
|
||||
NODE_TYPE_DELTAX, // amf/constellation/instance/deltax
|
||||
NODE_TYPE_DELTAY, // amf/constellation/instance/deltay
|
||||
NODE_TYPE_RZ, // amf/constellation/instance/rz
|
||||
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
|
||||
};
|
||||
|
||||
struct Instance {
|
||||
Instance() : deltax_set(false), deltay_set(false), rz_set(false) {}
|
||||
// Shift in the X axis.
|
||||
float deltax;
|
||||
bool deltax_set;
|
||||
// Shift in the Y axis.
|
||||
float deltay;
|
||||
bool deltay_set;
|
||||
// Rotation around the Z axis.
|
||||
float rz;
|
||||
bool rz_set;
|
||||
};
|
||||
|
||||
struct Object {
|
||||
Object() : idx(-1) {}
|
||||
int idx;
|
||||
std::vector<Instance> instances;
|
||||
};
|
||||
|
||||
// Current Expat XML parser instance.
|
||||
XML_Parser m_parser;
|
||||
// Model to receive objects extracted from an AMF file.
|
||||
Model &m_model;
|
||||
// Current parsing path in the XML file.
|
||||
std::vector<AMFNodeType> m_path;
|
||||
// Current object allocated for an amf/object XML subtree.
|
||||
ModelObject *m_object;
|
||||
// Map from obect name to object idx & instances.
|
||||
std::map<std::string, Object> m_object_instances_map;
|
||||
// Vertices parsed for the current m_object.
|
||||
std::vector<float> m_object_vertices;
|
||||
// Current volume allocated for an amf/object/mesh/volume subtree.
|
||||
ModelVolume *m_volume;
|
||||
// Faces collected for the current m_volume.
|
||||
std::vector<int> m_volume_facets;
|
||||
// Current material allocated for an amf/metadata subtree.
|
||||
ModelMaterial *m_material;
|
||||
// Current instance allocated for an amf/constellation/instance subtree.
|
||||
Instance *m_instance;
|
||||
// Generic string buffer for vertices, face indices, metadata etc.
|
||||
std::string m_value[3];
|
||||
};
|
||||
|
||||
void AMFParserContext::startElement(const char *name, const char **atts)
|
||||
{
|
||||
AMFNodeType node_type_new = NODE_TYPE_UNKNOWN;
|
||||
switch (m_path.size()) {
|
||||
case 0:
|
||||
// An AMF file must start with an <amf> tag.
|
||||
node_type_new = NODE_TYPE_AMF;
|
||||
if (strcmp(name, "amf") != 0)
|
||||
this->stop();
|
||||
break;
|
||||
case 1:
|
||||
if (strcmp(name, "metadata") == 0) {
|
||||
const char *type = get_attribute(atts, "type");
|
||||
if (type != nullptr) {
|
||||
m_value[0] = type;
|
||||
node_type_new = NODE_TYPE_METADATA;
|
||||
}
|
||||
} else if (strcmp(name, "material") == 0) {
|
||||
const char *material_id = get_attribute(atts, "id");
|
||||
m_material = m_model.add_material((material_id == nullptr) ? "_" : material_id);
|
||||
node_type_new = NODE_TYPE_MATERIAL;
|
||||
} else if (strcmp(name, "object") == 0) {
|
||||
const char *object_id = get_attribute(atts, "id");
|
||||
if (object_id == nullptr)
|
||||
this->stop();
|
||||
else {
|
||||
assert(m_object_vertices.empty());
|
||||
m_object = m_model.add_object();
|
||||
m_object_instances_map[object_id].idx = int(m_model.objects.size())-1;
|
||||
node_type_new = NODE_TYPE_OBJECT;
|
||||
}
|
||||
} else if (strcmp(name, "constellation") == 0) {
|
||||
node_type_new = NODE_TYPE_CONSTELLATION;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp(name, "metadata") == 0) {
|
||||
if (m_path[1] == NODE_TYPE_MATERIAL || m_path[1] == NODE_TYPE_OBJECT) {
|
||||
m_value[0] = get_attribute(atts, "type");
|
||||
node_type_new = NODE_TYPE_METADATA;
|
||||
}
|
||||
} else if (strcmp(name, "mesh") == 0) {
|
||||
if (m_path[1] == NODE_TYPE_OBJECT)
|
||||
node_type_new = NODE_TYPE_MESH;
|
||||
} else if (strcmp(name, "instance") == 0) {
|
||||
if (m_path[1] == NODE_TYPE_CONSTELLATION) {
|
||||
const char *object_id = get_attribute(atts, "objectid");
|
||||
if (object_id == nullptr)
|
||||
this->stop();
|
||||
else {
|
||||
m_object_instances_map[object_id].instances.push_back(AMFParserContext::Instance());
|
||||
m_instance = &m_object_instances_map[object_id].instances.back();
|
||||
node_type_new = NODE_TYPE_INSTANCE;
|
||||
}
|
||||
}
|
||||
else
|
||||
this->stop();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (m_path[2] == NODE_TYPE_MESH) {
|
||||
assert(m_object);
|
||||
if (strcmp(name, "vertices") == 0)
|
||||
node_type_new = NODE_TYPE_VERTICES;
|
||||
else if (strcmp(name, "volume") == 0) {
|
||||
assert(! m_volume);
|
||||
m_volume = m_object->add_volume(TriangleMesh());
|
||||
node_type_new = NODE_TYPE_VOLUME;
|
||||
}
|
||||
} else if (m_path[2] == NODE_TYPE_INSTANCE) {
|
||||
assert(m_instance);
|
||||
if (strcmp(name, "deltax") == 0)
|
||||
node_type_new = NODE_TYPE_DELTAX;
|
||||
else if (strcmp(name, "deltay") == 0)
|
||||
node_type_new = NODE_TYPE_DELTAY;
|
||||
else if (strcmp(name, "rz") == 0)
|
||||
node_type_new = NODE_TYPE_RZ;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (m_path[3] == NODE_TYPE_VERTICES) {
|
||||
if (strcmp(name, "vertex") == 0)
|
||||
node_type_new = NODE_TYPE_VERTEX;
|
||||
} else if (m_path[3] == NODE_TYPE_VOLUME) {
|
||||
if (strcmp(name, "metadata") == 0) {
|
||||
const char *type = get_attribute(atts, "type");
|
||||
if (type == nullptr)
|
||||
this->stop();
|
||||
else {
|
||||
m_value[0] = type;
|
||||
node_type_new = NODE_TYPE_METADATA;
|
||||
}
|
||||
} else if (strcmp(name, "triangle") == 0)
|
||||
node_type_new = NODE_TYPE_TRIANGLE;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (strcmp(name, "coordinates") == 0) {
|
||||
if (m_path[4] == NODE_TYPE_VERTEX) {
|
||||
node_type_new = NODE_TYPE_COORDINATES;
|
||||
} else
|
||||
this->stop();
|
||||
} else if (name[0] == 'v' && name[1] >= '1' && name[1] <= '3' && name[2] == 0) {
|
||||
if (m_path[4] == NODE_TYPE_TRIANGLE) {
|
||||
node_type_new = AMFNodeType(NODE_TYPE_VERTEX1 + name[1] - '1');
|
||||
} else
|
||||
this->stop();
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if ((name[0] == 'x' || name[0] == 'y' || name[0] == 'z') && name[1] == 0) {
|
||||
if (m_path[5] == NODE_TYPE_COORDINATES)
|
||||
node_type_new = AMFNodeType(NODE_TYPE_COORDINATE_X + name[0] - 'x');
|
||||
else
|
||||
this->stop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_path.push_back(node_type_new);
|
||||
}
|
||||
|
||||
void AMFParserContext::characters(const XML_Char *s, int len)
|
||||
{
|
||||
if (m_path.back() == NODE_TYPE_METADATA) {
|
||||
m_value[1].append(s, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_path.size()) {
|
||||
case 4:
|
||||
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ)
|
||||
m_value[0].append(s, len);
|
||||
break;
|
||||
case 6:
|
||||
switch (m_path.back()) {
|
||||
case NODE_TYPE_VERTEX1: m_value[0].append(s, len); break;
|
||||
case NODE_TYPE_VERTEX2: m_value[1].append(s, len); break;
|
||||
case NODE_TYPE_VERTEX3: m_value[2].append(s, len); break;
|
||||
default: break;
|
||||
}
|
||||
case 7:
|
||||
switch (m_path.back()) {
|
||||
case NODE_TYPE_COORDINATE_X: m_value[0].append(s, len); break;
|
||||
case NODE_TYPE_COORDINATE_Y: m_value[1].append(s, len); break;
|
||||
case NODE_TYPE_COORDINATE_Z: m_value[2].append(s, len); break;
|
||||
default: break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMFParserContext::endElement(const char *name)
|
||||
{
|
||||
switch (m_path.back()) {
|
||||
|
||||
// Constellation transformation:
|
||||
case NODE_TYPE_DELTAX:
|
||||
assert(m_instance);
|
||||
m_instance->deltax = float(atof(m_value[0].c_str()));
|
||||
m_instance->deltax_set = true;
|
||||
m_value[0].clear();
|
||||
break;
|
||||
case NODE_TYPE_DELTAY:
|
||||
assert(m_instance);
|
||||
m_instance->deltay = float(atof(m_value[0].c_str()));
|
||||
m_instance->deltay_set = true;
|
||||
m_value[0].clear();
|
||||
break;
|
||||
case NODE_TYPE_RZ:
|
||||
assert(m_instance);
|
||||
m_instance->rz = float(atof(m_value[0].c_str()));
|
||||
m_instance->rz_set = true;
|
||||
m_value[0].clear();
|
||||
break;
|
||||
|
||||
// Object vertices:
|
||||
case NODE_TYPE_VERTEX:
|
||||
assert(m_object);
|
||||
// Parse the vertex data
|
||||
m_object_vertices.emplace_back(atof(m_value[0].c_str()));
|
||||
m_object_vertices.emplace_back(atof(m_value[1].c_str()));
|
||||
m_object_vertices.emplace_back(atof(m_value[2].c_str()));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
break;
|
||||
|
||||
// Faces of the current volume:
|
||||
case NODE_TYPE_TRIANGLE:
|
||||
assert(m_object && m_volume);
|
||||
m_volume_facets.push_back(atoi(m_value[0].c_str()));
|
||||
m_volume_facets.push_back(atoi(m_value[1].c_str()));
|
||||
m_volume_facets.push_back(atoi(m_value[2].c_str()));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
break;
|
||||
|
||||
// Closing the current volume. Create an STL from m_volume_facets pointing to m_object_vertices.
|
||||
case NODE_TYPE_VOLUME:
|
||||
{
|
||||
assert(m_object && m_volume);
|
||||
stl_file &stl = m_volume->mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
for (size_t i = 0; i < m_volume_facets.size();) {
|
||||
stl_facet &facet = stl.facet_start[i/3];
|
||||
for (unsigned int v = 0; v < 3; ++ v)
|
||||
memcpy(&facet.vertex[v].x, &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_TYPE_OBJECT:
|
||||
assert(m_object);
|
||||
m_object_vertices.clear();
|
||||
m_object = nullptr;
|
||||
break;
|
||||
|
||||
case NODE_TYPE_MATERIAL:
|
||||
assert(m_material);
|
||||
m_material = nullptr;
|
||||
break;
|
||||
|
||||
case NODE_TYPE_INSTANCE:
|
||||
assert(m_instance);
|
||||
m_instance = nullptr;
|
||||
break;
|
||||
|
||||
case NODE_TYPE_METADATA:
|
||||
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;
|
||||
if (m_path.size() == 3) {
|
||||
if (m_path[1] == NODE_TYPE_MATERIAL && m_material)
|
||||
config = &m_material->config;
|
||||
else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
|
||||
config = &m_object->config;
|
||||
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
|
||||
config = &m_volume->config;
|
||||
if (config)
|
||||
config->set_deserialize(opt_key, m_value[1]);
|
||||
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
|
||||
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||
char *p = const_cast<char*>(m_value[1].c_str());
|
||||
for (;;) {
|
||||
char *end = strchr(p, ';');
|
||||
if (end != nullptr)
|
||||
*end = 0;
|
||||
m_object->layer_height_profile.push_back(float(atof(p)));
|
||||
if (end == nullptr)
|
||||
break;
|
||||
p = end + 1;
|
||||
}
|
||||
m_object->layer_height_profile_valid = true;
|
||||
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
|
||||
// Is this volume a modifier volume?
|
||||
m_volume->modifier = atoi(m_value[1].c_str()) == 1;
|
||||
}
|
||||
} else if (m_path.size() == 3) {
|
||||
if (m_path[1] == NODE_TYPE_MATERIAL) {
|
||||
if (m_material)
|
||||
m_material->attributes[m_value[0]] = m_value[1];
|
||||
} else if (m_path[1] == NODE_TYPE_OBJECT) {
|
||||
if (m_object && m_value[0] == "name")
|
||||
m_object->name = std::move(m_value[1]);
|
||||
}
|
||||
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME) {
|
||||
if (m_volume && m_value[0] == "name")
|
||||
m_volume->name = std::move(m_value[1]);
|
||||
}
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_path.pop_back();
|
||||
}
|
||||
|
||||
void AMFParserContext::endDocument()
|
||||
{
|
||||
for (const auto &object : m_object_instances_map) {
|
||||
if (object.second.idx == -1) {
|
||||
printf("Undefined object %s referenced in constellation\n", object.first.c_str());
|
||||
continue;
|
||||
}
|
||||
for (const Instance &instance : object.second.instances)
|
||||
if (instance.deltax_set && instance.deltay_set) {
|
||||
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
||||
mi->offset.x = instance.deltax;
|
||||
mi->offset.y = instance.deltay;
|
||||
mi->rotation = instance.rz_set ? instance.rz : 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load an AMF file into a provided model.
|
||||
bool load_amf(const char *path, Model *model)
|
||||
{
|
||||
XML_Parser parser = XML_ParserCreate(nullptr); // encoding
|
||||
if (! parser) {
|
||||
printf("Couldn't allocate memory for parser\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *pFile = ::fopen(path, "rt");
|
||||
if (pFile == nullptr) {
|
||||
printf("Cannot open file %s\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
AMFParserContext ctx(parser, model);
|
||||
XML_SetUserData(parser, (void*)&ctx);
|
||||
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
||||
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
||||
|
||||
char buff[8192];
|
||||
bool result = false;
|
||||
for (;;) {
|
||||
int len = (int)fread(buff, 1, 8192, pFile);
|
||||
if (ferror(pFile)) {
|
||||
printf("AMF parser: Read error\n");
|
||||
break;
|
||||
}
|
||||
int done = feof(pFile);
|
||||
if (XML_Parse(parser, buff, len, done) == XML_STATUS_ERROR) {
|
||||
printf("AMF parser: Parse error at line %u:\n%s\n",
|
||||
XML_GetCurrentLineNumber(parser),
|
||||
XML_ErrorString(XML_GetErrorCode(parser)));
|
||||
break;
|
||||
}
|
||||
if (done) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XML_ParserFree(parser);
|
||||
::fclose(pFile);
|
||||
|
||||
if (result)
|
||||
ctx.endDocument();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool store_amf(const char *path, Model *model)
|
||||
{
|
||||
FILE *file = ::fopen(path, "wb");
|
||||
if (file == 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);
|
||||
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());
|
||||
for (const auto &attr : material.second->attributes)
|
||||
fprintf(file, " <metadata type=\"%s\">%s</metadata>\n", attr.first.c_str(), attr.second.c_str());
|
||||
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));
|
||||
fprintf(file, " </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=\"%d\">\n", object_id);
|
||||
for (const std::string &key : object->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), object->config.serialize(key));
|
||||
if (! object->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", object->name.c_str());
|
||||
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");
|
||||
}
|
||||
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
||||
fprintf(file, " <mesh>\n");
|
||||
fprintf(file, " <vertices>\n");
|
||||
std::vector<int> vertices_offsets;
|
||||
int num_vertices = 0;
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
vertices_offsets.push_back(num_vertices);
|
||||
if (! volume->mesh.repaired)
|
||||
CONFESS("store_amf() requires repair()");
|
||||
auto &stl = volume->mesh.stl;
|
||||
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>%s</x>\n", stl.v_shared[i].x);
|
||||
fprintf(file, " <y>%s</y>\n", stl.v_shared[i].x);
|
||||
fprintf(file, " <z>%s</z>\n", stl.v_shared[i].x);
|
||||
fprintf(file, " </coordinates>\n");
|
||||
fprintf(file, " </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) {
|
||||
ModelVolume *volume = object->volumes[i_volume];
|
||||
int vertices_offset = vertices_offsets[i_volume];
|
||||
if (volume->material_id().empty())
|
||||
fprintf(file, " <volume>\n");
|
||||
else
|
||||
fprintf(file, " <volume materialid=\"%s\">\n", volume->material_id().c_str());
|
||||
for (const std::string &key : volume->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), volume->config.serialize(key));
|
||||
if (! volume->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", volume->name.c_str());
|
||||
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, volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset, j);
|
||||
fprintf(file, " </triangle>\n");
|
||||
}
|
||||
fprintf(file, " </volume>\n");
|
||||
}
|
||||
fprintf(file, " </mesh>\n");
|
||||
fprintf(file, " </object>\n");
|
||||
if (! object->instances.empty()) {
|
||||
for (ModelInstance *instance : object->instances) {
|
||||
char buf[512];
|
||||
sprintf(buf,
|
||||
" <instance objectid=\"%d\">\n"
|
||||
" <deltax>%s</deltax>\n"
|
||||
" <deltay>%s</deltay>\n"
|
||||
" <rz>%s</rz>\n"
|
||||
" </instance>\n",
|
||||
object_id,
|
||||
instance->offset.x,
|
||||
instance->offset.x,
|
||||
instance->rotation);
|
||||
//FIXME missing instance->scaling_factor
|
||||
instances.append(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! instances.empty()) {
|
||||
fprintf(file, " <constellation id=\"1\">\n");
|
||||
fwrite(instances.data(), instances.size(), 1, file);
|
||||
fprintf(file, " </constellation>\n");
|
||||
}
|
||||
fprintf(file, "</amf>\n");
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
15
xs/src/libslic3r/Format/AMF.hpp
Normal file
15
xs/src/libslic3r/Format/AMF.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef slic3r_Format_AMF_hpp_
|
||||
#define slic3r_Format_AMF_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
|
||||
// Load an AMF file into a provided model.
|
||||
extern bool load_amf(const char *path, Model *model);
|
||||
|
||||
extern bool store_amf(const char *path, Model *model);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_AMF_hpp_ */
|
79
xs/src/libslic3r/Format/OBJ.cpp
Normal file
79
xs/src/libslic3r/Format/OBJ.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
|
||||
#include "OBJ.hpp"
|
||||
#include "objparser.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool load_obj(const char *path, Model *model, const char *object_name_in)
|
||||
{
|
||||
// Parse the OBJ file.
|
||||
ObjParser::ObjData data;
|
||||
if (! ObjParser::objparse(path, data)) {
|
||||
// die "Failed to parse $file\n" if !-e $path;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count the faces and verify, that all faces are triangular.
|
||||
size_t num_faces = 0;
|
||||
for (size_t i = 0; i < data.vertices.size(); ) {
|
||||
size_t j = i;
|
||||
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
|
||||
if (i == j)
|
||||
continue;
|
||||
if (j - i != 3) {
|
||||
// Non-triangular faces are not supported as of now.
|
||||
return false;
|
||||
}
|
||||
num_faces ++;
|
||||
i = j;
|
||||
}
|
||||
|
||||
// Convert ObjData into STL.
|
||||
TriangleMesh mesh;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = num_faces;
|
||||
stl.stats.original_num_facets = num_faces;
|
||||
stl_allocate(&stl);
|
||||
size_t i_face = 0;
|
||||
for (size_t i = 0; i < data.vertices.size(); ++ i) {
|
||||
if (data.vertices[i].coordIdx == -1)
|
||||
continue;
|
||||
stl_facet &facet = stl.facet_start[i_face ++];
|
||||
for (unsigned int v = 0; v < 3; ++ v) {
|
||||
const ObjParser::ObjVertex &vertex = data.vertices[i++];
|
||||
memcpy(&facet.vertex[v].x, &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
|
||||
if (vertex.normalIdx != -1)
|
||||
memcpy(&facet.normal.x, &data.normals[vertex.normalIdx*3], 3 * sizeof(float));
|
||||
}
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
// die "This STL file couldn't be read because it's empty.\n"
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string object_name;
|
||||
if (object_name_in == nullptr) {
|
||||
const char *last_slash = strrchr(path, DIR_SEPARATOR);
|
||||
object_name.assign((last_slash == nullptr) ? path : last_slash + 1);
|
||||
} else
|
||||
object_name.assign(object_name_in);
|
||||
|
||||
model->add_object(object_name.c_str(), path, std::move(mesh));
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
14
xs/src/libslic3r/Format/OBJ.hpp
Normal file
14
xs/src/libslic3r/Format/OBJ.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef slic3r_Format_OBJ_hpp_
|
||||
#define slic3r_Format_OBJ_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Model;
|
||||
|
||||
// Load an OBJ file into a provided model.
|
||||
extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_OBJ_hpp_ */
|
306
xs/src/libslic3r/Format/PRUS.cpp
Normal file
306
xs/src/libslic3r/Format/PRUS.cpp
Normal file
|
@ -0,0 +1,306 @@
|
|||
#ifdef SLIC3R_PRUS
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
|
||||
#include "PRUS.hpp"
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
struct StlHeader
|
||||
{
|
||||
char comment[80];
|
||||
uint32_t nTriangles;
|
||||
};
|
||||
|
||||
// Buffered line reader for the wxInputStream.
|
||||
class LineReader
|
||||
{
|
||||
public:
|
||||
LineReader(wxInputStream &input_stream, const char *initial_data, int initial_len) :
|
||||
m_input_stream(input_stream),
|
||||
m_pos(0),
|
||||
m_len(initial_len)
|
||||
{
|
||||
assert(initial_len >= 0 && initial_len < m_bufsize);
|
||||
memcpy(m_buffer, initial_data, initial_len);
|
||||
}
|
||||
|
||||
const char* next_line() {
|
||||
for (;;) {
|
||||
// Skip empty lines.
|
||||
while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
|
||||
++ m_pos;
|
||||
if (m_pos == m_len) {
|
||||
// Empty buffer, fill it from the input stream.
|
||||
m_pos = 0;
|
||||
m_input_stream.Read(m_buffer, m_bufsize - 1);
|
||||
m_len = m_input_stream.LastRead();
|
||||
assert(m_len >= 0 && m_len < m_bufsize);
|
||||
if (m_len == 0)
|
||||
// End of file.
|
||||
return nullptr;
|
||||
// Skip empty lines etc.
|
||||
continue;
|
||||
}
|
||||
// The buffer is nonempty and it does not start with end of lines. Find the first end of line.
|
||||
int end = m_pos + 1;
|
||||
while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
|
||||
++ end;
|
||||
if (end == m_len && ! m_input_stream.Eof() && m_len < m_bufsize) {
|
||||
// Move the buffer content to the buffer start and fill the rest of the buffer.
|
||||
assert(m_pos > 0);
|
||||
memmove(m_buffer, m_buffer + m_pos, m_len - m_pos);
|
||||
m_len -= m_pos;
|
||||
assert(m_len >= 0 && m_len < m_bufsize);
|
||||
m_pos = 0;
|
||||
m_input_stream.Read(m_buffer + m_len, m_bufsize - 1 - m_len);
|
||||
int new_data = m_input_stream.LastRead();
|
||||
if (new_data > 0) {
|
||||
m_len += new_data;
|
||||
assert(m_len >= 0 && m_len < m_bufsize);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
char *ptr_out = m_buffer + m_pos;
|
||||
m_pos = end + 1;
|
||||
m_buffer[end] = 0;
|
||||
if (m_pos >= m_len) {
|
||||
m_pos = 0;
|
||||
m_len = 0;
|
||||
}
|
||||
return ptr_out;
|
||||
}
|
||||
}
|
||||
|
||||
int next_line_scanf(char *format, ...)
|
||||
{
|
||||
const char *line = next_line();
|
||||
if (line == nullptr)
|
||||
return -1;
|
||||
int result;
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
result = vsscanf(line, format, arglist);
|
||||
va_end(arglist);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
wxInputStream &m_input_stream;
|
||||
static const int m_bufsize = 4096;
|
||||
char m_buffer[m_bufsize];
|
||||
int m_pos = 0;
|
||||
int m_len = 0;
|
||||
};
|
||||
|
||||
// Load a PrusaControl project file into a provided model.
|
||||
bool load_prus(const char *path, Model *model)
|
||||
{
|
||||
// To receive the content of the zipped 'scene.xml' file.
|
||||
std::vector<char> scene_xml_data;
|
||||
wxFFileInputStream in(path);
|
||||
wxZipInputStream zip(in);
|
||||
std::unique_ptr<wxZipEntry> entry;
|
||||
size_t num_models = 0;
|
||||
while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) {
|
||||
wxString name = entry->GetName();
|
||||
if (name == "scene.xml") {
|
||||
if (! scene_xml_data.empty()) {
|
||||
// scene.xml has been found more than once in the archive.
|
||||
return false;
|
||||
}
|
||||
size_t size_last = 0;
|
||||
size_t size_incr = 4096;
|
||||
scene_xml_data.resize(size_incr);
|
||||
while (! zip.Read(scene_xml_data.data() + size_last, size_incr).Eof()) {
|
||||
size_last += zip.LastRead();
|
||||
if (scene_xml_data.size() < size_last + size_incr)
|
||||
scene_xml_data.resize(size_last + size_incr);
|
||||
}
|
||||
size_last += size_last + zip.LastRead();
|
||||
if (scene_xml_data.size() == size_last)
|
||||
scene_xml_data.resize(size_last + 1);
|
||||
else if (scene_xml_data.size() > size_last + 1)
|
||||
scene_xml_data.erase(scene_xml_data.begin() + size_last + 1, scene_xml_data.end());
|
||||
scene_xml_data[size_last] = 0;
|
||||
}
|
||||
else if (name.EndsWith(".stl") || name.EndsWith(".STL")) {
|
||||
// Find the model entry in the XML data.
|
||||
const wxScopedCharBuffer name_utf8 = name.ToUTF8();
|
||||
char model_name_tag[1024];
|
||||
sprintf(model_name_tag, "<model name=\"%s\">", name_utf8.data());
|
||||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||
float trafo[3][4] = { 0 };
|
||||
bool trafo_set = false;
|
||||
if (model_xml != nullptr) {
|
||||
model_xml += strlen(model_name_tag);
|
||||
const char *position_tag = "<position>";
|
||||
const char *position_xml = strstr(model_xml, position_tag);
|
||||
const char *rotation_tag = "<rotation>";
|
||||
const char *rotation_xml = strstr(model_xml, rotation_tag);
|
||||
const char *scale_tag = "<scale>";
|
||||
const char *scale_xml = strstr(model_xml, scale_tag);
|
||||
float position[3], rotation[3][3], scale[3][3];
|
||||
if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr &&
|
||||
sscanf(position_xml+strlen(position_tag),
|
||||
"[%f, %f, %f]", position, position+1, position+2) == 3 &&
|
||||
sscanf(rotation_xml+strlen(rotation_tag),
|
||||
"[[%f, %f, %f], [%f, %f, %f], [%f, %f, %f]]",
|
||||
rotation[0], rotation[0]+1, rotation[0]+2,
|
||||
rotation[1], rotation[1]+1, rotation[1]+2,
|
||||
rotation[2], rotation[2]+1, rotation[2]+2) == 9 &&
|
||||
sscanf(scale_xml+strlen(scale_tag),
|
||||
"[[%f, %f, %f], [%f, %f, %f], [%f, %f, %f]]",
|
||||
scale[0], scale[0]+1, scale[0]+2,
|
||||
scale[1], scale[1]+1, scale[1]+2,
|
||||
scale[2], scale[2]+1, scale[2]+2) == 9) {
|
||||
for (size_t r = 0; r < 3; ++ r) {
|
||||
for (size_t c = 0; c < 3; ++ c) {
|
||||
for (size_t i = 0; i < 3; ++ i)
|
||||
trafo[r][c] += rotation[r][i]*scale[i][c];
|
||||
}
|
||||
trafo[r][3] = position[r];
|
||||
}
|
||||
trafo_set = true;
|
||||
}
|
||||
}
|
||||
if (trafo_set) {
|
||||
// Extract the STL.
|
||||
StlHeader header;
|
||||
bool stl_ascii = false;
|
||||
if (!zip.Read((void*)&header, sizeof(StlHeader)).Eof()) {
|
||||
if (strncmp(header.comment, "solid ", 6) == 0)
|
||||
stl_ascii = true;
|
||||
else {
|
||||
// Header has been extracted. Now read the faces.
|
||||
TriangleMesh mesh;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.error = 0;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = header.nTriangles;
|
||||
stl.stats.original_num_facets = header.nTriangles;
|
||||
stl_allocate(&stl);
|
||||
if (zip.ReadAll((void*)stl.facet_start, 50 * header.nTriangles)) {
|
||||
// All the faces have been read.
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Transform the model.
|
||||
stl_transform(&stl, &trafo[0][0]);
|
||||
// Add a mesh to a model.
|
||||
if (mesh.facets_count() > 0) {
|
||||
model->add_object(name_utf8.data(), path, std::move(mesh));
|
||||
++ num_models;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
stl_ascii = true;
|
||||
if (stl_ascii) {
|
||||
// Try to parse ASCII STL.
|
||||
char normal_buf[3][32];
|
||||
stl_facet facet;
|
||||
std::vector<stl_facet> facets;
|
||||
LineReader line_reader(zip, (char*)&header, zip.LastRead());
|
||||
std::string solid_name;
|
||||
facet.extra[0] = facet.extra[1] = 0;
|
||||
for (;;) {
|
||||
const char *line = line_reader.next_line();
|
||||
if (line == nullptr)
|
||||
// End of file.
|
||||
break;
|
||||
if (strncmp(line, "solid", 5) == 0) {
|
||||
// Opening the "solid" block.
|
||||
if (! solid_name.empty()) {
|
||||
// Error, solid block is already open.
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
solid_name = line + 5;
|
||||
if (solid_name.empty())
|
||||
solid_name = "unknown";
|
||||
continue;
|
||||
}
|
||||
if (strncmp(line, "endsolid", 8) == 0) {
|
||||
// Closing the "solid" block.
|
||||
if (solid_name.empty()) {
|
||||
// Error, no solid block is open.
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
solid_name.clear();
|
||||
continue;
|
||||
}
|
||||
// Line has to start with the word solid.
|
||||
int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
||||
assert(res_normal == 3);
|
||||
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
|
||||
assert(res_outer_loop == 0);
|
||||
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z);
|
||||
assert(res_vertex1 == 3);
|
||||
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z);
|
||||
assert(res_vertex2 == 3);
|
||||
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z);
|
||||
assert(res_vertex3 == 3);
|
||||
int res_endloop = line_reader.next_line_scanf(" endloop");
|
||||
assert(res_endloop == 0);
|
||||
int res_endfacet = line_reader.next_line_scanf(" endfacet");
|
||||
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
||||
// perror("Something is syntactically very wrong with this ASCII STL!");
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
}
|
||||
facets.emplace_back(facet);
|
||||
}
|
||||
if (! facets.empty() && solid_name.empty()) {
|
||||
TriangleMesh mesh;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = facets.size();
|
||||
stl.stats.original_num_facets = facets.size();
|
||||
stl_allocate(&stl);
|
||||
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Transform the model.
|
||||
stl_transform(&stl, &trafo[0][0]);
|
||||
// Add a mesh to a model.
|
||||
if (mesh.facets_count() > 0) {
|
||||
model->add_object(name_utf8.data(), path, std::move(mesh));
|
||||
++ num_models;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_models > 0;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* SLIC3R_PRUS */
|
14
xs/src/libslic3r/Format/PRUS.hpp
Normal file
14
xs/src/libslic3r/Format/PRUS.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#if defined(SLIC3R_PRUS) && ! defined(slic3r_Format_PRUS_hpp_)
|
||||
#define slic3r_Format_PRUS_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Model;
|
||||
|
||||
// Load a PrusaControl project file into a provided model.
|
||||
extern bool load_prus(const char *path, Model *model);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* SLIC3R_PRUS && ! defined(slic3r_Format_PRUS_hpp_) */
|
58
xs/src/libslic3r/Format/STL.cpp
Normal file
58
xs/src/libslic3r/Format/STL.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
|
||||
#include "STL.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool load_stl(const char *path, Model *model, const char *object_name_in)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
mesh.ReadSTLFile(path);
|
||||
if (mesh.stl.error) {
|
||||
// die "Failed to open $file\n" if !-e $path;
|
||||
return false;
|
||||
}
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
// die "This STL file couldn't be read because it's empty.\n"
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string object_name;
|
||||
if (object_name_in == nullptr) {
|
||||
const char *last_slash = strrchr(path, DIR_SEPARATOR);
|
||||
object_name.assign((last_slash == nullptr) ? path : last_slash + 1);
|
||||
} else
|
||||
object_name.assign(object_name_in);
|
||||
|
||||
model->add_object(object_name.c_str(), path, std::move(mesh));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, TriangleMesh *mesh, bool binary)
|
||||
{
|
||||
if (binary)
|
||||
mesh->write_binary(path);
|
||||
else
|
||||
mesh->write_ascii(path);
|
||||
//FIXME returning false even if write failed.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, ModelObject *model_object, bool binary)
|
||||
{
|
||||
TriangleMesh mesh = model_object->mesh();
|
||||
return store_stl(path, &mesh, binary);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
17
xs/src/libslic3r/Format/STL.hpp
Normal file
17
xs/src/libslic3r/Format/STL.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef slic3r_Format_STL_hpp_
|
||||
#define slic3r_Format_STL_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class ModelObject;
|
||||
|
||||
// Load an STL file into a provided model.
|
||||
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr);
|
||||
|
||||
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
|
||||
extern bool store_stl(const char *path, ModelObject *model_object);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_STL_hpp_ */
|
537
xs/src/libslic3r/Format/objparser.cpp
Normal file
537
xs/src/libslic3r/Format/objparser.cpp
Normal file
|
@ -0,0 +1,537 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "objparser.hpp"
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
static bool obj_parseline(const char *line, ObjData &data)
|
||||
{
|
||||
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
|
||||
|
||||
if (*line == 0)
|
||||
return true;
|
||||
|
||||
// Ignore whitespaces at the beginning of the line.
|
||||
//FIXME is this a good idea?
|
||||
EATWS();
|
||||
|
||||
char c1 = *line ++;
|
||||
switch (c1) {
|
||||
case '#':
|
||||
// Comment, ignore the rest of the line.
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
// Parse vertex geometry (position, normal, texture coordinates)
|
||||
char c2 = *line ++;
|
||||
switch (c2) {
|
||||
case 't':
|
||||
{
|
||||
// vt - vertex texture parameter
|
||||
// u v [w], w == 0 (or w == 1)
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = 0;
|
||||
if (*line != 0) {
|
||||
v = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
double w = 0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.textureCoordinates.push_back((float)u);
|
||||
data.textureCoordinates.push_back((float)v);
|
||||
data.textureCoordinates.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
{
|
||||
// vn - vertex normal
|
||||
// x y z
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.normals.push_back((float)x);
|
||||
data.normals.push_back((float)y);
|
||||
data.normals.push_back((float)z);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
// vp - vertex parameter
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double w = 0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.parameters.push_back((float)u);
|
||||
data.parameters.push_back((float)v);
|
||||
data.parameters.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// v - vertex geometry
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double w = 1.0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.coordinates.push_back((float)x);
|
||||
data.coordinates.push_back((float)y);
|
||||
data.coordinates.push_back((float)z);
|
||||
data.coordinates.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
// face
|
||||
EATWS();
|
||||
if (*line == 0)
|
||||
return false;
|
||||
// number of vertices of this face
|
||||
int n = 0;
|
||||
// current vertex to be parsed
|
||||
ObjVertex vertex;
|
||||
char *endptr = 0;
|
||||
while (*line != 0) {
|
||||
// Parse a single vertex reference.
|
||||
vertex.coordIdx = 0;
|
||||
vertex.normalIdx = 0;
|
||||
vertex.textureCoordIdx = 0;
|
||||
vertex.coordIdx = strtol(line, &endptr, 10);
|
||||
// Coordinate has to be defined
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
if (*line == '/') {
|
||||
++ line;
|
||||
// Texture coordinate index may be missing after a 1st slash, but then the normal index has to be present.
|
||||
if (*line != '/') {
|
||||
// Parse the texture coordinate index.
|
||||
vertex.textureCoordIdx = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
}
|
||||
if (*line == '/') {
|
||||
// Parse normal index.
|
||||
++ line;
|
||||
vertex.normalIdx = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
}
|
||||
}
|
||||
if (vertex.coordIdx < 0)
|
||||
vertex.coordIdx += data.coordinates.size() / 4;
|
||||
else
|
||||
-- vertex.coordIdx;
|
||||
if (vertex.normalIdx < 0)
|
||||
vertex.normalIdx += data.normals.size() / 3;
|
||||
else
|
||||
-- vertex.normalIdx;
|
||||
if (vertex.textureCoordIdx < 0)
|
||||
vertex.textureCoordIdx += data.textureCoordinates.size() / 3;
|
||||
else
|
||||
-- vertex.textureCoordIdx;
|
||||
data.vertices.push_back(vertex);
|
||||
EATWS();
|
||||
}
|
||||
vertex.coordIdx = -1;
|
||||
vertex.normalIdx = -1;
|
||||
vertex.textureCoordIdx = -1;
|
||||
data.vertices.push_back(vertex);
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
{
|
||||
if (*(line ++) != 't' ||
|
||||
*(line ++) != 'l' ||
|
||||
*(line ++) != 'l' ||
|
||||
*(line ++) != 'i' ||
|
||||
*(line ++) != 'b')
|
||||
return false;
|
||||
// mtllib [external .mtl file name]
|
||||
// printf("mtllib %s\r\n", line);
|
||||
EATWS();
|
||||
data.mtllibs.push_back(std::string(line));
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
if (*(line ++) != 's' ||
|
||||
*(line ++) != 'e' ||
|
||||
*(line ++) != 'm' ||
|
||||
*(line ++) != 't' ||
|
||||
*(line ++) != 'l')
|
||||
return false;
|
||||
// usemtl [material name]
|
||||
// printf("usemtl %s\r\n", line);
|
||||
EATWS();
|
||||
ObjUseMtl usemtl;
|
||||
usemtl.vertexIdxFirst = data.vertices.size();
|
||||
usemtl.name = line;
|
||||
data.usemtls.push_back(usemtl);
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
{
|
||||
// o [object name]
|
||||
EATWS();
|
||||
const char *name = line;
|
||||
while (*line != ' ' && *line != '\t' && *line != 0)
|
||||
++ line;
|
||||
// copy name to line.
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
ObjObject object;
|
||||
object.vertexIdxFirst = data.vertices.size();
|
||||
object.name = line;
|
||||
data.objects.push_back(object);
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
{
|
||||
// g [group name]
|
||||
// printf("group %s\r\n", line);
|
||||
ObjGroup group;
|
||||
group.vertexIdxFirst = data.vertices.size();
|
||||
group.name = line;
|
||||
data.groups.push_back(group);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
// s 1 / off
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
long g = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
ObjSmoothingGroup group;
|
||||
group.vertexIdxFirst = data.vertices.size();
|
||||
group.smoothingGroupID = g;
|
||||
data.smoothingGroups.push_back(group);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("ObjParser: Unknown command: %c\r\n", c1);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objparse(const char *path, ObjData &data)
|
||||
{
|
||||
FILE *pFile = ::fopen(path, "rt");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
try {
|
||||
char buf[65536 * 2];
|
||||
size_t len = 0;
|
||||
size_t lenPrev = 0;
|
||||
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
|
||||
len += lenPrev;
|
||||
size_t lastLine = 0;
|
||||
for (size_t i = 0; i < len; ++ i)
|
||||
if (buf[i] == '\r' || buf[i] == '\n') {
|
||||
buf[i] = 0;
|
||||
char *c = buf + lastLine;
|
||||
while (*c == ' ' || *c == '\t')
|
||||
++ c;
|
||||
obj_parseline(c, data);
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
memmove(buf, buf + lastLine, lenPrev);
|
||||
}
|
||||
} catch (std::bad_alloc &ex) {
|
||||
printf("Out of memory\r\n");
|
||||
}
|
||||
::fclose(pFile);
|
||||
|
||||
printf("vertices: %d\r\n", data.vertices.size() / 4);
|
||||
printf("coords: %d\r\n", data.coordinates.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool savevector(FILE *pFile, const std::vector<T> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
||||
if (! v.empty())
|
||||
::fwrite(&v.front(), 1, sizeof(T) * cnt, pFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool savevector(FILE *pFile, const std::vector<std::string> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
size_t len = v[i].size();
|
||||
::fwrite(&len, 1, sizeof(cnt), pFile);
|
||||
::fwrite(v[i].c_str(), 1, len, pFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool savevectornameidx(FILE *pFile, const std::vector<T> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
::fwrite(&v[i].vertexIdxFirst, 1, sizeof(int), pFile);
|
||||
size_t len = v[i].name.size();
|
||||
::fwrite(&len, 1, sizeof(cnt), pFile);
|
||||
::fwrite(v[i].name.c_str(), 1, len, pFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool loadvector(FILE *pFile, std::vector<T> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
||||
if (cnt != 0) {
|
||||
v.assign(cnt, T());
|
||||
if (::fread(&v.front(), sizeof(T), cnt, pFile) != cnt)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadvector(FILE *pFile, std::vector<std::string> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
v.reserve(cnt);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
size_t len = 0;
|
||||
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
||||
return false;
|
||||
std::string s(" ", len);
|
||||
if (::fread(const_cast<char*>(s.c_str()), 1, len, pFile) != len)
|
||||
return false;
|
||||
v.push_back(std::move(s));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
v.assign(cnt, T());
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
if (::fread(&v[i].vertexIdxFirst, sizeof(int), 1, pFile) != 1)
|
||||
return false;
|
||||
size_t len = 0;
|
||||
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
||||
return false;
|
||||
v[i].name.assign(" ", len);
|
||||
if (::fread(const_cast<char*>(v[i].name.c_str()), 1, len, pFile) != len)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objbinsave(const char *path, const ObjData &data)
|
||||
{
|
||||
FILE *pFile = ::fopen(path, "wb");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
size_t version = 1;
|
||||
::fwrite(&version, 1, sizeof(version), pFile);
|
||||
|
||||
bool result =
|
||||
savevector(pFile, data.coordinates) &&
|
||||
savevector(pFile, data.textureCoordinates) &&
|
||||
savevector(pFile, data.normals) &&
|
||||
savevector(pFile, data.parameters) &&
|
||||
savevector(pFile, data.mtllibs) &&
|
||||
savevectornameidx(pFile, data.usemtls) &&
|
||||
savevectornameidx(pFile, data.objects) &&
|
||||
savevectornameidx(pFile, data.groups) &&
|
||||
savevector(pFile, data.smoothingGroups) &&
|
||||
savevector(pFile, data.vertices);
|
||||
|
||||
::fclose(pFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool objbinload(const char *path, ObjData &data)
|
||||
{
|
||||
FILE *pFile = ::fopen(path, "rb");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
data.version = 0;
|
||||
if (::fread(&data.version, sizeof(data.version), 1, pFile) != 1)
|
||||
return false;
|
||||
if (data.version != 1)
|
||||
return false;
|
||||
|
||||
bool result =
|
||||
loadvector(pFile, data.coordinates) &&
|
||||
loadvector(pFile, data.textureCoordinates) &&
|
||||
loadvector(pFile, data.normals) &&
|
||||
loadvector(pFile, data.parameters) &&
|
||||
loadvector(pFile, data.mtllibs) &&
|
||||
loadvectornameidx(pFile, data.usemtls) &&
|
||||
loadvectornameidx(pFile, data.objects) &&
|
||||
loadvectornameidx(pFile, data.groups) &&
|
||||
loadvector(pFile, data.smoothingGroups) &&
|
||||
loadvector(pFile, data.vertices);
|
||||
|
||||
::fclose(pFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool vectorequal(const std::vector<T> &v1, const std::vector<T> &v2)
|
||||
{
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < v1.size(); ++ i)
|
||||
if (! (v1[i] == v2[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vectorequal(const std::vector<std::string> &v1, const std::vector<std::string> &v2)
|
||||
{
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < v1.size(); ++ i)
|
||||
if (v1[i].compare(v2[i]) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern bool objequal(const ObjData &data1, const ObjData &data2)
|
||||
{
|
||||
//FIXME ignore version number
|
||||
// version;
|
||||
|
||||
return
|
||||
vectorequal(data1.coordinates, data2.coordinates) &&
|
||||
vectorequal(data1.textureCoordinates, data2.textureCoordinates) &&
|
||||
vectorequal(data1.normals, data2.normals) &&
|
||||
vectorequal(data1.parameters, data2.parameters) &&
|
||||
vectorequal(data1.mtllibs, data2.mtllibs) &&
|
||||
vectorequal(data1.usemtls, data2.usemtls) &&
|
||||
vectorequal(data1.objects, data2.objects) &&
|
||||
vectorequal(data1.groups, data2.groups) &&
|
||||
vectorequal(data1.vertices, data2.vertices);
|
||||
}
|
||||
|
||||
} // namespace ObjParser
|
109
xs/src/libslic3r/Format/objparser.hpp
Normal file
109
xs/src/libslic3r/Format/objparser.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#ifndef slic3r_Format_objparser_hpp_
|
||||
#define slic3r_Format_objparser_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
struct ObjVertex
|
||||
{
|
||||
int coordIdx;
|
||||
int textureCoordIdx;
|
||||
int normalIdx;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjVertex &v1, const ObjVertex &v2)
|
||||
{
|
||||
return
|
||||
v1.coordIdx == v2.coordIdx &&
|
||||
v1.textureCoordIdx == v2.textureCoordIdx &&
|
||||
v1.normalIdx == v2.normalIdx;
|
||||
}
|
||||
|
||||
struct ObjUseMtl
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjUseMtl &v1, const ObjUseMtl &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjObject
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjObject &v1, const ObjObject &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjGroup
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjGroup &v1, const ObjGroup &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjSmoothingGroup
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
int smoothingGroupID;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.smoothingGroupID == v2.smoothingGroupID;
|
||||
}
|
||||
|
||||
struct ObjData {
|
||||
// Version of the data structure for load / store in the private binary format.
|
||||
int version;
|
||||
|
||||
// x, y, z, w
|
||||
std::vector<float> coordinates;
|
||||
// u, v, w
|
||||
std::vector<float> textureCoordinates;
|
||||
// x, y, z
|
||||
std::vector<float> normals;
|
||||
// u, v, w
|
||||
std::vector<float> parameters;
|
||||
|
||||
std::vector<std::string> mtllibs;
|
||||
std::vector<ObjUseMtl> usemtls;
|
||||
std::vector<ObjObject> objects;
|
||||
std::vector<ObjGroup> groups;
|
||||
std::vector<ObjSmoothingGroup> smoothingGroups;
|
||||
|
||||
// List of faces, delimited by an ObjVertex with all members set to -1.
|
||||
std::vector<ObjVertex> vertices;
|
||||
};
|
||||
|
||||
extern bool objparse(const char *path, ObjData &data);
|
||||
|
||||
extern bool objbinsave(const char *path, const ObjData &data);
|
||||
|
||||
extern bool objbinload(const char *path, ObjData &data);
|
||||
|
||||
extern bool objequal(const ObjData &data1, const ObjData &data2);
|
||||
|
||||
} // namespace ObjParser
|
||||
|
||||
#endif /* slic3r_Format_objparser_hpp_ */
|
|
@ -5,6 +5,10 @@
|
|||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
#include "libslic3r/Format/AMF.hpp"
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
#include "libslic3r/Format/PRUS.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::Model} class Model {
|
||||
|
@ -71,8 +75,68 @@
|
|||
void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects_grid(unsigned int x, unsigned int y, double dist);
|
||||
};
|
||||
|
||||
%{
|
||||
|
||||
Model*
|
||||
load_stl(CLASS, path, object_name)
|
||||
char* CLASS;
|
||||
char* path;
|
||||
char* object_name;
|
||||
CODE:
|
||||
RETVAL = new Model();
|
||||
if (! load_stl(path, RETVAL, object_name)) {
|
||||
delete RETVAL;
|
||||
RETVAL = NULL;
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Model*
|
||||
load_obj(CLASS, path, object_name)
|
||||
char* CLASS;
|
||||
char* path;
|
||||
char* object_name;
|
||||
CODE:
|
||||
RETVAL = new Model();
|
||||
if (! load_obj(path, RETVAL, object_name)) {
|
||||
delete RETVAL;
|
||||
RETVAL = NULL;
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Model*
|
||||
load_amf(CLASS, path)
|
||||
char* CLASS;
|
||||
char* path;
|
||||
CODE:
|
||||
RETVAL = new Model();
|
||||
if (! load_amf(path, RETVAL)) {
|
||||
delete RETVAL;
|
||||
RETVAL = NULL;
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Model*
|
||||
load_prus(CLASS, path)
|
||||
char* CLASS;
|
||||
char* path;
|
||||
CODE:
|
||||
#ifdef SLIC3R_PRUS
|
||||
RETVAL = new Model();
|
||||
if (! load_prus(path, RETVAL)) {
|
||||
delete RETVAL;
|
||||
RETVAL = NULL;
|
||||
}
|
||||
#endif
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
%name{Slic3r::Model::Material} class ModelMaterial {
|
||||
Ref<Model> model()
|
||||
|
|
Loading…
Reference in a new issue