2015-01-13 21:14:09 +03:00
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <sstream>
|
|
|
|
#include <fstream>
|
2015-01-15 22:47:05 +03:00
|
|
|
#include <iostream>
|
2015-01-13 21:14:09 +03:00
|
|
|
#include <locale.h>
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
#include "GeometryUtils.h"
|
|
|
|
#include "Reindexer.h"
|
2015-01-13 21:14:09 +03:00
|
|
|
#include "linalg.h"
|
2015-01-15 22:47:05 +03:00
|
|
|
#include "grid.h"
|
2015-01-16 06:01:30 +03:00
|
|
|
#include "printutils.h"
|
2015-01-13 21:14:09 +03:00
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
static void export_stl(const IndexedTriangleMesh &trimesh, std::ostream &output)
|
2015-01-13 21:14:09 +03:00
|
|
|
{
|
|
|
|
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
|
|
|
|
output << "solid OpenSCAD_Model\n";
|
2015-01-15 22:47:05 +03:00
|
|
|
const Vector3f *verts = &trimesh.vertices.front();
|
|
|
|
BOOST_FOREACH(const IndexedTriangle &t, trimesh.triangles) {
|
2015-01-16 01:54:29 +03:00
|
|
|
assert(t[0] < trimesh.vertices.size());
|
|
|
|
assert(t[1] < trimesh.vertices.size());
|
|
|
|
assert(t[2] < trimesh.vertices.size());
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
Vector3f p[3];
|
|
|
|
p[0] = verts[t[0]];
|
|
|
|
p[1] = verts[t[1]];
|
|
|
|
p[2] = verts[t[2]];
|
2015-01-13 21:14:09 +03:00
|
|
|
std::stringstream stream;
|
|
|
|
stream << p[0][0] << " " << p[0][1] << " " << p[0][2];
|
|
|
|
std::string vs1 = stream.str();
|
|
|
|
stream.str("");
|
|
|
|
stream << p[1][0] << " " << p[1][1] << " " << p[1][2];
|
|
|
|
std::string vs2 = stream.str();
|
|
|
|
stream.str("");
|
|
|
|
stream << p[2][0] << " " << p[2][1] << " " << p[2][2];
|
|
|
|
std::string vs3 = stream.str();
|
|
|
|
// if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) {
|
|
|
|
// The above condition ensures that there are 3 distinct vertices, but
|
|
|
|
// they may be collinear. If they are, the unit normal is meaningless
|
|
|
|
// so the default value of "1 0 0" can be used. If the vertices are not
|
|
|
|
// collinear then the unit normal must be calculated from the
|
|
|
|
// components.
|
2015-01-15 22:47:05 +03:00
|
|
|
Vector3f normal = (p[1] - p[0]).cross(p[2] - p[0]);
|
2015-01-13 21:14:09 +03:00
|
|
|
normal.normalize();
|
|
|
|
output << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "\n";
|
|
|
|
output << " outer loop\n";
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
for (int i=0;i<3;i++) {
|
|
|
|
output << " vertex " << p[i][0] << " " << p[i][1] << " " << p[i][2] << "\n";
|
2015-01-13 21:14:09 +03:00
|
|
|
}
|
|
|
|
output << " endloop\n";
|
|
|
|
output << " endfacet\n";
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
output << "endsolid OpenSCAD_Model\n";
|
|
|
|
setlocale(LC_NUMERIC, ""); // Set default locale
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
file format:
|
|
|
|
1. polygon coordinates (x,y,z) are comma separated (+/- spaces) and
|
|
|
|
each coordinate is on a separate line
|
|
|
|
2. each polygon is separated by one or more blank lines
|
|
|
|
*/
|
2015-01-15 22:47:05 +03:00
|
|
|
bool import_polygon(IndexedPolygons &polyhole, const std::string &filename)
|
2015-01-13 21:14:09 +03:00
|
|
|
{
|
2015-01-15 22:47:05 +03:00
|
|
|
Reindexer<Vector3f> uniqueVertices;
|
2015-01-13 21:14:09 +03:00
|
|
|
std::ifstream ifs(filename.c_str());
|
|
|
|
if (!ifs) return false;
|
|
|
|
|
|
|
|
std::string line;
|
2015-01-15 22:47:05 +03:00
|
|
|
IndexedFace polygon;
|
2015-01-13 21:14:09 +03:00
|
|
|
while (std::getline(ifs, line)) {
|
|
|
|
std::stringstream ss(line);
|
|
|
|
double X = 0.0, Y = 0.0, Z = 0.0;
|
|
|
|
if (!(ss >> X)) {
|
|
|
|
//ie blank lines => flag start of next polygon
|
2015-01-15 22:47:05 +03:00
|
|
|
if (polygon.size() > 0) polyhole.faces.push_back(polygon);
|
2015-01-13 21:14:09 +03:00
|
|
|
polygon.clear();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
char c = ss.peek();
|
|
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
|
|
|
|
if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
|
|
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
|
|
|
|
if (!(ss >> Y)) {
|
|
|
|
std::cerr << "Y error\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
c = ss.peek();
|
|
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
|
|
|
|
if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
|
|
|
|
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
|
|
|
|
if (!(ss >> Z)) {
|
|
|
|
std::cerr << "Z error\n";
|
|
|
|
return false;
|
|
|
|
}
|
2015-01-15 22:47:05 +03:00
|
|
|
polygon.push_back(uniqueVertices.lookup(Vector3f(X, Y, Z)));
|
2015-01-13 21:14:09 +03:00
|
|
|
}
|
2015-01-15 22:47:05 +03:00
|
|
|
if (polygon.size() > 0) polyhole.faces.push_back(polygon);
|
2015-01-13 21:14:09 +03:00
|
|
|
ifs.close();
|
2015-01-15 22:47:05 +03:00
|
|
|
uniqueVertices.copy(std::back_inserter(polyhole.vertices));
|
2015-01-13 21:14:09 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2015-01-16 06:01:30 +03:00
|
|
|
OpenSCAD::debug = "GeometryUtils";
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
IndexedPolygons polyhole;
|
|
|
|
Vector3f *normal = NULL;
|
2015-01-13 21:14:09 +03:00
|
|
|
if (argc >= 2) {
|
|
|
|
if (!import_polygon(polyhole, argv[1])) {
|
|
|
|
std::cerr << "Error importing polygon" << std::endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
2015-01-15 22:47:05 +03:00
|
|
|
std::cerr << "Imported " << polyhole.faces.size() << " polygons" << std::endl;
|
2015-01-13 21:14:09 +03:00
|
|
|
|
|
|
|
if (argc == 3) {
|
|
|
|
std::vector<std::string> strs;
|
|
|
|
std::vector<double> normalvec;
|
|
|
|
std::string arg(argv[2]);
|
|
|
|
boost::split(strs, arg, boost::is_any_of(","));
|
|
|
|
assert(strs.size() == 3);
|
|
|
|
BOOST_FOREACH(const std::string &s, strs) normalvec.push_back(boost::lexical_cast<double>(s));
|
2015-01-15 22:47:05 +03:00
|
|
|
normal = new Vector3f(normalvec[0], normalvec[1], normalvec[2]);
|
2015-01-13 21:14:09 +03:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//construct two non-intersecting nested polygons
|
2015-01-15 22:47:05 +03:00
|
|
|
Reindexer<Vector3f> uniqueVertices;
|
|
|
|
IndexedFace polygon1;
|
|
|
|
polygon1.push_back(uniqueVertices.lookup(Vector3f(0,0,0)));
|
|
|
|
polygon1.push_back(uniqueVertices.lookup(Vector3f(2,0,0)));
|
|
|
|
polygon1.push_back(uniqueVertices.lookup(Vector3f(2,2,0)));
|
|
|
|
polygon1.push_back(uniqueVertices.lookup(Vector3f(0,2,0)));
|
|
|
|
IndexedFace polygon2;
|
|
|
|
polygon2.push_back(uniqueVertices.lookup(Vector3f(0.5,0.5,0)));
|
|
|
|
polygon2.push_back(uniqueVertices.lookup(Vector3f(1.5,0.5,0)));
|
|
|
|
polygon2.push_back(uniqueVertices.lookup(Vector3f(1.5,1.5,0)));
|
|
|
|
polygon2.push_back(uniqueVertices.lookup(Vector3f(0.5,1.5,0)));
|
|
|
|
polyhole.faces.push_back(polygon1);
|
|
|
|
polyhole.faces.push_back(polygon2);
|
|
|
|
uniqueVertices.copy(std::back_inserter(polyhole.vertices));
|
2015-01-13 21:14:09 +03:00
|
|
|
}
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
std::vector<IndexedTriangle> triangles;
|
|
|
|
bool ok = GeometryUtils::tessellatePolygonWithHoles(polyhole, triangles, normal);
|
2015-01-13 21:14:09 +03:00
|
|
|
std::cerr << "Tessellated into " << triangles.size() << " triangles" << std::endl;
|
|
|
|
|
2015-01-15 22:47:05 +03:00
|
|
|
IndexedTriangleMesh trimesh;
|
|
|
|
trimesh.vertices = polyhole.vertices;
|
|
|
|
trimesh.triangles = triangles;
|
|
|
|
|
|
|
|
export_stl(trimesh, std::cout);
|
2015-01-13 21:14:09 +03:00
|
|
|
}
|