From 3f6a590a2fef198984f46aff83c65370c2864dea Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sat, 27 Dec 2014 17:58:20 -0500 Subject: [PATCH] Added stand-alone 2D tesselator --- cgal/README.md | 6 ++ cgal/data/quads.polygon | 10 +++ cgal/data/tri.polygon | 3 + cgal/polyhole-tessellator.cpp | 126 ++++++++++++++++++++++++++++++++++ cgal/polyhole-tessellator.pro | 90 ++++++++++++++++++++++++ 5 files changed, 235 insertions(+) create mode 100644 cgal/README.md create mode 100644 cgal/data/quads.polygon create mode 100644 cgal/data/tri.polygon create mode 100644 cgal/polyhole-tessellator.cpp create mode 100644 cgal/polyhole-tessellator.pro diff --git a/cgal/README.md b/cgal/README.md new file mode 100644 index 00000000..6b213744 --- /dev/null +++ b/cgal/README.md @@ -0,0 +1,6 @@ +This folder contains some CGAL test programs to easier develop and debug CGAL-based components. + +## polyhole-tessellator + +Tessellate an almost planar 3D polygon with holes into a vector of double precision 3D triangles. + diff --git a/cgal/data/quads.polygon b/cgal/data/quads.polygon new file mode 100644 index 00000000..a21c6ddb --- /dev/null +++ b/cgal/data/quads.polygon @@ -0,0 +1,10 @@ +0,0,0 +2,0,0 +2,2,0 +0,2,0 + +0.5,0.5,0 +1.5,0.5,0 +1.5,1.5,0 +0.5,1.5,0 + diff --git a/cgal/data/tri.polygon b/cgal/data/tri.polygon new file mode 100644 index 00000000..79d21286 --- /dev/null +++ b/cgal/data/tri.polygon @@ -0,0 +1,3 @@ +0,0,0 +2,0,0 +2,2,0 diff --git a/cgal/polyhole-tessellator.cpp b/cgal/polyhole-tessellator.cpp new file mode 100644 index 00000000..0fba0e36 --- /dev/null +++ b/cgal/polyhole-tessellator.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#include "cgalutils.h" + +// Nef polyhedron are using CGAL_Kernel3 (Cartesian) +// 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; + 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; + } + 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); + std::cerr << "Tessellated into " << triangles.size() << " triangles" << std::endl; + + export_stl(triangles, std::cout); +} diff --git a/cgal/polyhole-tessellator.pro b/cgal/polyhole-tessellator.pro new file mode 100644 index 00000000..906d089f --- /dev/null +++ b/cgal/polyhole-tessellator.pro @@ -0,0 +1,90 @@ +debug: DEFINES += DEBUG + +TEMPLATE = app + +INCLUDEPATH += ../src +DEPENDPATH += ../src + +# Handle custom library location. +# Used when manually installing 3rd party libraries +isEmpty(OPENSCAD_LIBDIR) OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES) +macx:isEmpty(OPENSCAD_LIBDIR) { + exists(/opt/local):exists(/usr/local/Cellar) { + error("It seems you might have libraries in both /opt/local and /usr/local. Please specify which one to use with qmake OPENSCAD_LIBDIR=") + } else { + exists(/opt/local) { + #Default to MacPorts on Mac OS X + message("Automatically searching for libraries in /opt/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /opt/local + } else:exists(/usr/local/Cellar) { + message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=") + OPENSCAD_LIBDIR = /usr/local + } + } +} +!isEmpty(OPENSCAD_LIBDIR) { + QMAKE_INCDIR = $$OPENSCAD_LIBDIR/include + QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib +} + +TARGET = polyhole-tessellator +mac { + CONFIG -= app_bundle +} + +macx { + # Mac needs special care to link against the correct C++ library + # We attempt to auto-detect it by inspecting Boost + dirs = $${BOOSTDIR} $${QMAKE_LIBDIR} + for(dir, dirs) { + system(grep -q __112basic_string $${dir}/libboost_thread* >& /dev/null) { + message("Detected libc++-linked boost in $${dir}") + CONFIG += libc++ + } + } + + libc++ { + QMAKE_CXXFLAGS += -stdlib=libc++ + QMAKE_LFLAGS += -stdlib=libc++ + QMAKE_OBJECTIVE_CFLAGS += -stdlib=libc++ + # libc++ on requires Mac OS X 10.7+ + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 + } +} + +# See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs. +*g++* { + QMAKE_CXXFLAGS *= -fno-strict-aliasing + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs # ignored before 4.8 +} + +*clang* { + # http://llvm.org/bugs/show_bug.cgi?id=9182 + QMAKE_CXXFLAGS_WARN_ON += -Wno-overloaded-virtual + # disable enormous amount of warnings about CGAL / boost / etc + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++11-extensions + # might want to actually turn this on once in a while + QMAKE_CXXFLAGS_WARN_ON += -Wno-format-security +} + + + +# Application configuration +CONFIG += cgal +CONFIG += boost +CONFIG += eigen +CONFIG += gettext + +include(../common.pri) + +HEADERS += ../src/cgal.h \ + ../src/cgalutils.h \ + ../src/linalg.h \ + ../src/printutils.h + +SOURCES += polyhole-tessellator.cpp \ + ../src/cgalutils-tess.cc \ + ../src/printutils.cc