Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/GEL/GLGraphics/MeshEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,23 @@ namespace GLGraphics {
Serialization ser(file_name, std::ios_base::out);
me->active_mesh().serialize(ser);
return;
} else if (extension == ".stl") {
bool is_binary = false;
if (args.size() < 2) {
me->printf("please specify binary or ascii format");
return;
} else {
if (args[1] == "binary") {
is_binary = true;
} else if (args[1] == "ascii") {
is_binary = false;
} else {
me->printf("please specify binary or ascii format");
return;
}
stl_save(file_name, me->active_mesh(), is_binary);
}
return;
}
me->printf("unknown format");
return;
Expand Down
2 changes: 2 additions & 0 deletions src/GEL/HMesh/HMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <GEL/HMesh/off_load.h>
#include <GEL/HMesh/off_save.h>
#include <GEL/HMesh/ply_load.h>
#include <GEL/HMesh/stl_load.h>
#include <GEL/HMesh/stl_save.h>
#include <GEL/HMesh/polygonize.h>
#include <GEL/HMesh/quadric_simplify.h>
#include <GEL/HMesh/refine_edges.h>
Expand Down
4 changes: 4 additions & 0 deletions src/GEL/HMesh/load.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <GEL/HMesh/x3d_load.h>
#include <GEL/HMesh/obj_load.h>
#include <GEL/HMesh/off_load.h>
#include <GEL/HMesh/stl_load.h>
#include <GEL/HMesh/cleanup.h>

#include <GEL/Util/Serialization.h>
Expand Down Expand Up @@ -51,6 +52,9 @@ namespace HMesh
mani.deserialize(ser);
return true;
}
else if(ext==".stl") {
return stl_load(file_name, mani);
}
return false;
}
}
211 changes: 211 additions & 0 deletions src/GEL/HMesh/stl_load.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/* ----------------------------------------------------------------------- *
* This file is part of GEL, http://www.imm.dtu.dk/GEL
* Copyright (C) the authors and DTU Informatics
* For license and list of authors, see ../../doc/intro.pdf
* ----------------------------------------------------------------------- */

#include <GEL/HMesh/Manifold.h>
#include <GEL/HMesh/cleanup.h>
#include <GEL/HMesh/load.h>
#include <GEL/HMesh/refine_edges.h>
#include <GEL/HMesh/stl_load.h>
#include <fstream>
#include <sstream>

using namespace std;
using namespace CGLA;

namespace HMesh {
using std::string;

void right_trim_stl(std::string& str) {
if (!str.empty())
while (isspace(str.back())) str.pop_back();
}

istream& get_multi_line_stl(istream& is, string& buf) {
getline(is, buf);
right_trim_stl(buf);
if (!buf.empty())
while (buf.back() == '\\') {
buf.pop_back();
string continuation;
getline(is, continuation);
right_trim_stl(continuation);
buf += continuation;
}
return is;
}

bool stl_load_ascii(
const std::string& filename, Manifold& m,
VertexAttributeVector<int>& orig_vertex_indices) {
ifstream stl_file(filename.data());

if (stl_file) {
string buf;
vector<Vec3d> vertices;
vector<int> faces;
vector<int> indices;
Vec3d normal;
while (get_multi_line_stl(stl_file, buf)) {
istringstream iss(std::move(buf));
string code;
if (iss >> code) {
if (code == "facet") {
if (iss >> code && code == "normal") { iss >> normal; }
} else if (code == "vertex") {
Vec3d v;
iss >> v;
vertices.push_back(v);
} else if (code == "endfacet") {
// Determine the order of indices to match the normal
// direction
Vec3d n(normal);
Vec3d e1 = vertices[vertices.size() - 2] -
vertices[vertices.size() - 3];
Vec3d e2 = vertices[vertices.size() - 1] -
vertices[vertices.size() - 3];
Vec3d n2 = cross(e1, e2);

if (dot(n, n2) > 0) {
indices.push_back(vertices.size() - 3);
indices.push_back(vertices.size() - 2);
indices.push_back(vertices.size() - 1);
} else {
indices.push_back(vertices.size() - 3);
indices.push_back(vertices.size() - 1);
indices.push_back(vertices.size() - 2);
}
faces.push_back(3);
}
}
}
// cout << "Loaded " << filename << " : " << vertices.size()
// << " vertices and " << faces.size() << " faces" << endl;
m.clear();

orig_vertex_indices = build(
m, vertices.size(), reinterpret_cast<double*>(&vertices[0]),
faces.size(), &faces[0], &indices[0]);

return true;
}
return false;
}

bool stl_load_binary(
const std::string& filename, Manifold& m,
VertexAttributeVector<int>& orig_vertex_indices) {
ifstream stl_file(filename.data(), ios::binary);
if (stl_file) {

char header[80];
stl_file.read(header, 80);

uint32_t num_triangles;
stl_file.read(reinterpret_cast<char*>(&num_triangles), 4);

vector<Vec3d> vertices;
vector<int> faces;
vector<int> indices;
for (uint32_t i = 0; i < num_triangles; ++i) {
Vec3f normal;
stl_file.read(reinterpret_cast<char*>(&normal), 12);

Vec3f v[3];
for (int j = 0; j < 3; ++j) {
stl_file.read(reinterpret_cast<char*>(&v[j]), 12);
vertices.push_back(Vec3d(v[j]));
}
uint16_t attr;
stl_file.read(reinterpret_cast<char*>(&attr), 2);

// Determine the order of indices to match the normal direction
Vec3d n(normal);
Vec3d e1 = vertices[vertices.size() - 2] -
vertices[vertices.size() - 3];
Vec3d e2 = vertices[vertices.size() - 1] -
vertices[vertices.size() - 3];
Vec3d n2 = cross(e1, e2);
if (dot(n, n2) > 0) {
indices.push_back(vertices.size() - 3);
indices.push_back(vertices.size() - 2);
indices.push_back(vertices.size() - 1);
} else {
indices.push_back(vertices.size() - 3);
indices.push_back(vertices.size() - 1);
indices.push_back(vertices.size() - 2);
}
faces.push_back(3);
}

// cout << "Loaded " << filename << " : " << vertices.size()
// << " vertices and " << faces.size() << " faces" << endl;
m.clear();

orig_vertex_indices = build(
m, vertices.size(), reinterpret_cast<double*>(&vertices[0]),
faces.size(), &faces[0], &indices[0]);

return true;
}
return false;
}

bool stl_load(
const std::string& filename, Manifold& m,
VertexAttributeVector<int>& orig_vertex_indices) {

// Open the file
ifstream stl_file(filename.data(), ios::binary);
if (!stl_file) {
cerr << "Could not open file " << filename << endl;
return false;
}

// Read the first 5 bytes to determine if the file is ASCII or binary
char header[5];
stl_file.read(header, 5);
stl_file.close();

// Check if the file is ASCII or binary (ASCII files start with "solid")
bool is_ascii = (header[0] == 's' || header[0] == 'S') &&
(header[1] == 'o' || header[1] == 'O') &&
(header[2] == 'l' || header[2] == 'L') &&
(header[3] == 'i' || header[3] == 'I') &&
(header[4] == 'd' || header[4] == 'D');

// Call the appropriate loader
bool status = false;
if (is_ascii) {
status = stl_load_ascii(filename, m, orig_vertex_indices);
} else {
status = stl_load_binary(filename, m, orig_vertex_indices);
}

if (!status) {
cerr << "Could not load file " << filename << endl;
return false;
}

// The current state of the mesh is not valid, so we need to clean it up
// since it is just a triangle soup.
double e_len = HMesh::average_edge_length(m);

int missing_edges = stitch_mesh(m, 1e-6 * e_len);

if (missing_edges > 0) {
cerr << "Could not stitch " << missing_edges << " edges" << endl;
}

m.cleanup();
return true;
}

bool stl_load(const string& filename, Manifold& m) {
VertexAttributeVector<int> orig_vertex_indices;
return stl_load(filename, m, orig_vertex_indices);
}

} // namespace HMesh
29 changes: 29 additions & 0 deletions src/GEL/HMesh/stl_load.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* ----------------------------------------------------------------------- *
* This file is part of GEL, http://www.imm.dtu.dk/GEL
* Copyright (C) the authors and DTU Informatics
* For license and list of authors, see ../../doc/intro.pdf
* ----------------------------------------------------------------------- */

/**
* @file HMesh/stl_load.h
* @brief Load Manifold from stl file
*/

#ifndef __HMESH_STLLOAD__H__
#define __HMESH_STLLOAD__H__

#include <string>
#include <GEL/HMesh/Manifold.h>

namespace HMesh
{
/** Load a STL file.
The first argument is a string containing the file name (including path)
and the second is the Manifold into which the mesh is loaded. The third argument
is an attribute vector containing the indices of the original
points. */

bool stl_load(const std::string&, Manifold& m, VertexAttributeVector<int>& orig_vertex_indices);
bool stl_load(const std::string&, Manifold& m);
}
#endif
98 changes: 98 additions & 0 deletions src/GEL/HMesh/stl_save.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* ----------------------------------------------------------------------- *
* This file is part of GEL, http://www.imm.dtu.dk/GEL
* Copyright (C) the authors and DTU Informatics
* For license and list of authors, see ../../doc/intro.pdf
* ----------------------------------------------------------------------- */

#include <GEL/HMesh/stl_save.h>

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include <GEL/CGLA/Vec3f.h>

#include <GEL/HMesh/AttributeVector.h>
#include <GEL/HMesh/Manifold.h>

namespace HMesh {
using namespace std;
using namespace CGLA;

bool stl_save_ascii(const string& filename, Manifold& m) {

ofstream os(filename.data());
if (!os) {
cerr << "stl_save: could not open file " << filename << endl;
return false;
}

// Build the header
os << "solid " << filename << endl;

// Write the faces
for (FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) {
os << "facet normal ";
Vec3d n = normal(m, *f);
os << n[0] << " " << n[1] << " " << n[2] << endl;
os << "\touter loop" << endl;
for (Walker w = m.walker(*f); !w.full_circle();
w = w.circulate_face_ccw()) {
Vec3d p = m.pos(w.vertex());
os << "\t\tvertex " << p[0] << " " << p[1] << " " << p[2]
<< endl;
}
os << "\tendloop" << endl;
os << "endfacet" << endl;
}

// End the file
os << "endsolid " << filename << endl;

return true;
}

bool stl_save_binary(const string& filename, Manifold& m) {

ofstream os(filename.data(), ios::binary);
if (!os) {
cerr << "stl_save: could not open file " << filename << endl;
return false;
}

// Write the header
char header[80];
for (int i = 0; i < 80; ++i) header[i] = 0;
os.write(header, 80);

// Write the number of faces (4-byte little-endian unsigned integer)
unsigned int n_faces = m.no_faces();
os.write(reinterpret_cast<char*>(&n_faces), 4);

// Write the faces
for (FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) {
Vec3d n = normal(m, *f);
normalize(n);

Vec3f float_n(n[0], n[1], n[2]);
os.write(reinterpret_cast<char*>(&float_n[0]), 12);
for (Walker w = m.walker(*f); !w.full_circle();
w = w.circulate_face_ccw()) {
Vec3d p = m.pos(w.vertex());
Vec3f float_p(p[0], p[1], p[2]);
os.write(reinterpret_cast<char*>(&float_p[0]), 12);
}
unsigned short attr = 0;
os.write(reinterpret_cast<char*>(&attr), 2);
}
return false;
}

bool stl_save(const string& filename, Manifold& m, bool is_binary) {
if (is_binary)
return stl_save_binary(filename, m);
else
return stl_save_ascii(filename, m);
}
} // namespace HMesh
Loading