forked from openscad/openscad
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpolyhole-tessellator.cpp
140 lines (127 loc) · 4.67 KB
/
polyhole-tessellator.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <sstream>
#include <iostream>
#include <locale.h>
#include "cgalutils.h"
// Nef polyhedron are using CGAL_Kernel3 (Cartesian<Gmpq>)
// Triangulation will use Epick
static void export_stl(const Polygons &triangles, std::ostream &output)
{
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
output << "solid OpenSCAD_Model\n";
BOOST_FOREACH(const Polygon &p, triangles) {
assert(p.size() == 3); // STL only allows triangles
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.
Vector3d normal = (p[1] - p[0]).cross(p[2] - p[0]);
normal.normalize();
output << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "\n";
output << " outer loop\n";
BOOST_FOREACH(const Vector3d &v, p) {
output << " vertex " << v[0] << " " << v[1] << " " << v[2] << "\n";
}
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
*/
bool import_polygon(PolyholeK &polyhole, const std::string &filename)
{
std::ifstream ifs(filename.c_str());
if (!ifs) return false;
std::string line;
PolygonK polygon;
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
if (polygon.size() > 0) polyhole.push_back(polygon);
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;
}
polygon.push_back(Vertex3K(X, Y, Z));
}
if (polygon.size() > 0) polyhole.push_back(polygon);
ifs.close();
return true;
}
//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
PolyholeK polyhole;
K::Vector_3 *normal = NULL;
if (argc >= 2) {
if (!import_polygon(polyhole, argv[1])) {
std::cerr << "Error importing polygon" << std::endl;
exit(1);
}
std::cerr << "Imported " << polyhole.size() << " polygons" << std::endl;
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));
normal = new K::Vector_3(normalvec[0], normalvec[1], normalvec[2]);
}
}
else {
//construct two non-intersecting nested polygons
PolygonK polygon1;
polygon1.push_back(Vertex3K(0,0,0));
polygon1.push_back(Vertex3K(2,0,0));
polygon1.push_back(Vertex3K(2,2,0));
polygon1.push_back(Vertex3K(0,2,0));
PolygonK polygon2;
polygon2.push_back(Vertex3K(0.5,0.5,0));
polygon2.push_back(Vertex3K(1.5,0.5,0));
polygon2.push_back(Vertex3K(1.5,1.5,0));
polygon2.push_back(Vertex3K(0.5,1.5,0));
polyhole.push_back(polygon1);
polyhole.push_back(polygon2);
}
Polygons triangles;
bool ok = CGALUtils::tessellatePolygonWithHoles(polyhole, triangles, normal);
std::cerr << "Tessellated into " << triangles.size() << " triangles" << std::endl;
export_stl(triangles, std::cout);
}