File: polyhole-tessellator.cpp

package info (click to toggle)
openscad 2015.03-2%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 30,804 kB
  • ctags: 5,692
  • sloc: cpp: 39,386; sh: 3,856; ansic: 3,674; python: 1,393; yacc: 496; lex: 272; lisp: 159; makefile: 67; xml: 60
file content (140 lines) | stat: -rw-r--r-- 4,780 bytes parent folder | download | duplicates (2)
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);
}