summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Kintel <marius@kintel.net>2010-03-29 01:31:47 (GMT)
committerMarius Kintel <marius@kintel.net>2010-10-31 00:42:35 (GMT)
commit184d0e041f6279dba5d1f6348f973478ae133a3a (patch)
treedccc0d26fd5b14e4f5ca834290e7f588febfcbe4
parent34a8206410f049f566535b1d795b4e89950c6a6f (diff)
Initial implementation of CGALRenderer
-rw-r--r--openscad.pro8
-rw-r--r--src/CGALRenderer.cc597
-rw-r--r--src/CGALRenderer.h34
-rw-r--r--src/PolySetCGALRenderer.cc384
-rw-r--r--src/PolySetCGALRenderer.h26
-rw-r--r--src/PolySetRenderer.cc5
-rw-r--r--src/PolySetRenderer.h23
-rw-r--r--src/cgaladv.cc59
-rw-r--r--src/csgnode.h6
-rw-r--r--src/csgops.cc58
-rw-r--r--src/dxflinextrude.cc167
-rw-r--r--src/dxfrotextrude.cc94
-rw-r--r--src/export.h1
-rw-r--r--src/mainwin.cc14
-rw-r--r--src/node.cc170
-rw-r--r--src/node.h13
-rw-r--r--src/nodecache.h4
-rw-r--r--src/nodedumper.cc1
-rw-r--r--src/nodedumper.h7
-rw-r--r--src/openscad.cc24
-rw-r--r--src/polyset.cc354
-rw-r--r--src/polyset.h7
-rw-r--r--src/projection.cc182
-rw-r--r--src/render.cc40
-rw-r--r--src/rendernode.h6
-rw-r--r--src/surface.cc2
-rw-r--r--src/transform.cc73
-rw-r--r--src/transformnode.h6
-rw-r--r--test-code/cgaltest.cc19
-rw-r--r--test-code/cgaltest.pro33
30 files changed, 1103 insertions, 1314 deletions
diff --git a/openscad.pro b/openscad.pro
index 2a1bdf7..a785259 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -118,7 +118,9 @@ HEADERS += src/CGAL_renderer.h \
src/traverser.h \
src/nodecache.h \
src/nodedumper.h \
- src/CGALRenderer.h
+ src/CGALRenderer.h \
+ src/PolySetRenderer.h \
+ src/PolySetCGALRenderer.h
SOURCES += src/openscad.cc \
src/mainwin.cc \
@@ -158,7 +160,9 @@ SOURCES += src/openscad.cc \
src/editor.cc \\
src/traverser.cc \
src/nodedumper.cc \
- src/CGALRenderer.cc
+ src/CGALRenderer.cc \
+ src/PolySetRenderer.cc \
+ src/PolySetCGALRenderer.cc
macx {
HEADERS += src/AppleEvents.h \
diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc
index 2926023..d9f4c69 100644
--- a/src/CGALRenderer.cc
+++ b/src/CGALRenderer.cc
@@ -9,27 +9,28 @@
#include "csgnode.h"
#include "transformnode.h"
+#include "polyset.h"
+#include "dxfdata.h"
+#include "dxftess.h"
#include <sstream>
#include <iostream>
#include <assert.h>
#include <QRegExp>
-string CGALRenderer::getCGALMesh() const
+CGALRenderer *CGALRenderer::global_renderer = NULL;
+
+CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const AbstractNode &node)
{
- assert(this->root);
- // FIXME: assert that cache contains root
- return this->cache[mk_cache_id(*this->root)];
+ if (!isCached(node)) {
+ Traverser render(*this, node, Traverser::PRE_AND_POSTFIX);
+ render.execute();
+ assert(isCached(node));
+ }
+ return this->cache[mk_cache_id(node)];
}
-// CGAL_Nef_polyhedron CGALRenderer::getCGALMesh() const
-// {
-// assert(this->root);
-// // FIXME: assert that cache contains root
-// return this->cache[*this->root];
-// }
-
-bool CGALRenderer::isCached(const AbstractNode &node)
+bool CGALRenderer::isCached(const AbstractNode &node) const
{
return this->cache.contains(mk_cache_id(node));
}
@@ -38,82 +39,55 @@ bool CGALRenderer::isCached(const AbstractNode &node)
Modifies target by applying op to target and src:
target = target [op] src
*/
-void
-CGALRenderer::process(string &target, const string &src, CGALRenderer::CsgOp op)
+void CGALRenderer::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CsgOp op)
{
-// if (target.dim != 2 && target.dim != 3) {
-// assert(false && "Dimension of Nef polyhedron must be 2 or 3");
-// }
-
- switch (op) {
- case UNION:
- target += "+" + src;
- break;
- case INTERSECTION:
- target += "*" + src;
- break;
- case DIFFERENCE:
- target += "-" + src;
- break;
- case MINKOWSKI:
- target += "M" + src;
- break;
+ if (target.dim != 2 && target.dim != 3) {
+ assert(false && "Dimension of Nef polyhedron must be 2 or 3");
+ }
+
+ if (target.dim == 2) {
+ switch (op) {
+ case UNION:
+ target.p2 += src.p2;
+ break;
+ case INTERSECTION:
+ target.p2 *= src.p2;
+ break;
+ case DIFFERENCE:
+ target.p2 -= src.p2;
+ break;
+ case MINKOWSKI:
+ target.p2 = minkowski2(target.p2, src.p2);
+ break;
+ }
+ }
+ else if (target.dim == 3) {
+ switch (op) {
+ case UNION:
+ target.p3 += src.p3;
+ break;
+ case INTERSECTION:
+ target.p3 *= src.p3;
+ break;
+ case DIFFERENCE:
+ target.p3 -= src.p3;
+ break;
+ case MINKOWSKI:
+ target.p3 = minkowski3(target.p3, src.p3);
+ break;
+ }
}
}
-// /*!
-// Modifies target by applying op to target and src:
-// target = target [op] src
-// */
-// void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CsgOp op)
-// {
-// if (target.dim == 2) {
-// switch (op) {
-// case UNION:
-// target.p2 += src.p2;
-// break;
-// case INTERSECTION:
-// target.p2 *= src.p2;
-// break;
-// case DIFFERENCE:
-// target.p2 -= src.p2;
-// break;
-// case MINKOWSKI:
-// target.p2 = minkowski2(target.p2, src.p2);
-// break;
-// }
-// }
-// else if (target.dim == 3) {
-// switch (op) {
-// case UNION:
-// target.p3 += src.p3;
-// break;
-// case INTERSECTION:
-// target.p3 *= src.p3;
-// break;
-// case DIFFERENCE:
-// target.p3 -= src.p3;
-// break;
-// case MINKOWSKI:
-// target.p3 = minkowski3(target.p3, src.p3);
-// break;
-// }
-// }
-// else {
-// assert(false && "Dimention of Nef polyhedron must be 2 or 3");
-// }
-// }
-
+/*!
+ FIXME: Let caller insert into the cache since caller might modify the result
+ (e.g. transform)
+*/
void CGALRenderer::applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp op)
{
- std::stringstream stream;
- stream << typeid(node).name();
- stream << "<" << node.index() << ">";
- string N = stream.str();
+ CGAL_Nef_polyhedron N;
if (this->visitedchildren[node.index()].size() > 0) {
- // FIXME: assert that cache contains nodes in code below
bool first = true;
-// CGAL_Nef_polyhedron N;
for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
iter != this->visitedchildren[node.index()].end();
iter++) {
@@ -121,16 +95,17 @@ void CGALRenderer::applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp
const QString &chcacheid = iter->second;
// FIXME: Don't use deep access to modinst members
if (chnode->modinst->tag_background) continue;
+ assert(isCached(*chnode));
if (first) {
- N += "(" + this->cache[chcacheid];
-// if (N.dim != 0) first = false; // FIXME: when can this happen?
- first = false;
+ N = this->cache[chcacheid];
+ // If the first child(ren) are empty (e.g. echo) nodes,
+ // ignore them (reset first flag)
+ if (N.dim != 0) first = false;
} else {
process(N, this->cache[chcacheid], op);
}
chnode->progress_report();
}
- N += ")";
}
QString cacheid = mk_cache_id(node);
this->cache.insert(cacheid, N);
@@ -153,6 +128,7 @@ Response CGALRenderer::visit(const State &state, const AbstractNode &node)
return ContinueTraversal;
}
+#if 0
Response CGALRenderer::visit(const State &state, const AbstractIntersectionNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@@ -186,6 +162,7 @@ Response CGALRenderer::visit(const State &state, const CsgNode &node)
}
return ContinueTraversal;
}
+#endif
Response CGALRenderer::visit(const State &state, const TransformNode &node)
{
@@ -194,7 +171,43 @@ Response CGALRenderer::visit(const State &state, const TransformNode &node)
if (!isCached(node)) {
// First union all children
applyToChildren(node, UNION);
- // FIXME: Then apply transform
+
+ // Then apply transform
+ QString cacheid = mk_cache_id(node);
+ CGAL_Nef_polyhedron N = this->cache[cacheid];
+ assert(N.dim >= 2 && N.dim <= 3);
+ if (N.dim == 2) {
+ // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2
+ // objects. So we convert in to our internal 2d data format, transform it,
+ // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack!
+
+ CGAL_Aff_transformation2 t(
+ node.m[0], node.m[4], node.m[12],
+ node.m[1], node.m[5], node.m[13], node.m[15]);
+
+ DxfData dd(N);
+ for (int i=0; i < dd.points.size(); i++) {
+ CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y);
+ p = t.transform(p);
+ dd.points[i].x = to_double(p.x());
+ dd.points[i].y = to_double(p.y());
+ }
+
+ PolySet ps;
+ ps.is2d = true;
+ dxf_tesselate(&ps, &dd, 0, true, false, 0);
+
+ N = renderCGALMesh(ps);
+ ps.refcount = 0;
+ }
+ else if (N.dim == 3) {
+ CGAL_Aff_transformation t(
+ node.m[0], node.m[4], node.m[ 8], node.m[12],
+ node.m[1], node.m[5], node.m[ 9], node.m[13],
+ node.m[2], node.m[6], node.m[10], node.m[14], node.m[15]);
+ N.p3.transform(t);
+ }
+ this->cache.insert(cacheid, N);
}
addToParent(state, node);
}
@@ -215,35 +228,27 @@ Response CGALRenderer::visit(const State &state, const AbstractPolyNode &node)
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
if (!isCached(node)) {
+ // First union all children
+ applyToChildren(node, UNION);
- // FIXME: Manage caching
- // FIXME: Will generate one single Nef polyhedron (no csg ops necessary)
-
-// PolySet *ps = render_polyset(RENDER_CGAL);
-// try {
-// CGAL_Nef_polyhedron N = ps->renderCSGMesh();
-// cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
-// print_messages_pop();
-// progress_report();
-
-// ps->unlink();
-// return N;
-// }
-// catch (...) { // Don't leak the PolySet on ProgressCancelException
-// ps->unlink();
-// throw;
-// }
-
- string N = typeid(node).name();
- QString cacheid = mk_cache_id(node);
- this->cache.insert(cacheid, N);
-
-// std::cout << "Insert: " << N << "\n";
-// std::cout << "Node: " << cacheid.toStdString() << "\n\n";
+ // Then apply polyset operation
+ PolySet *ps = node.render_polyset(AbstractPolyNode::RENDER_CGAL);
+ try {
+ CGAL_Nef_polyhedron N = renderCGALMesh(*ps);
+// print_messages_pop();
+ node.progress_report();
+
+ ps->unlink();
+ QString cacheid = mk_cache_id(node);
+ this->cache.insert(cacheid, N);
+ }
+ catch (...) { // Don't leak the PolySet on ProgressCancelException
+ ps->unlink();
+ throw;
+ }
}
addToParent(state, node);
}
-
return ContinueTraversal;
}
@@ -284,3 +289,375 @@ QString CGALRenderer::mk_cache_id(const AbstractNode &node) const
cache_id.remove('\n');
return cache_id;
}
+
+#if 0
+/*!
+ Static function to render CGAL meshes.
+ NB! This is just a support function used for development and debugging
+*/
+CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const AbstractPolyNode &node)
+{
+ // FIXME: Lookup Nef polyhedron in cache.
+
+ // print_messages_push();
+
+ PolySet *ps = node.render_polyset(AbstractPolyNode::RENDER_CGAL);
+ try {
+ CGAL_Nef_polyhedron N = ps->renderCSGMesh();
+ // FIXME: Insert into cache
+ // print_messages_pop();
+ node.progress_report();
+
+ ps->unlink();
+ return N;
+ }
+ catch (...) { // Don't leak the PolySet on ProgressCancelException
+ ps->unlink();
+ throw;
+ }
+}
+#endif
+
+#ifdef ENABLE_CGAL
+
+#undef GEN_SURFACE_DEBUG
+
+class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS>
+{
+public:
+ typedef CGAL_HDS::Vertex::Point Point;
+
+ const PolySet &ps;
+ CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { }
+
+ void operator()(CGAL_HDS& hds)
+ {
+ CGAL_Polybuilder B(hds, true);
+
+ QList<PolySet::Point> vertices;
+ Grid3d<int> vertices_idx(GRID_FINE);
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ const PolySet::Polygon *poly = &ps.polygons[i];
+ for (int j = 0; j < poly->size(); j++) {
+ const PolySet::Point *p = &poly->at(j);
+ if (!vertices_idx.has(p->x, p->y, p->z)) {
+ vertices_idx.data(p->x, p->y, p->z) = vertices.size();
+ vertices.append(*p);
+ }
+ }
+ }
+
+ B.begin_surface(vertices.size(), ps.polygons.size());
+#ifdef GEN_SURFACE_DEBUG
+ printf("=== CGAL Surface ===\n");
+#endif
+
+ for (int i = 0; i < vertices.size(); i++) {
+ const PolySet::Point *p = &vertices[i];
+ B.add_vertex(Point(p->x, p->y, p->z));
+#ifdef GEN_SURFACE_DEBUG
+ printf("%d: %f %f %f\n", i, p->x, p->y, p->z);
+#endif
+ }
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ const PolySet::Polygon *poly = &ps.polygons[i];
+ QHash<int,int> fc;
+ bool facet_is_degenerated = false;
+ for (int j = 0; j < poly->size(); j++) {
+ const PolySet::Point *p = &poly->at(j);
+ int v = vertices_idx.data(p->x, p->y, p->z);
+ if (fc[v]++ > 0)
+ facet_is_degenerated = true;
+ }
+
+ if (!facet_is_degenerated)
+ B.begin_facet();
+#ifdef GEN_SURFACE_DEBUG
+ printf("F:");
+#endif
+ for (int j = 0; j < poly->size(); j++) {
+ const PolySet::Point *p = &poly->at(j);
+#ifdef GEN_SURFACE_DEBUG
+ printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z);
+#endif
+ if (!facet_is_degenerated)
+ B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z));
+ }
+#ifdef GEN_SURFACE_DEBUG
+ if (facet_is_degenerated)
+ printf(" (degenerated)");
+ printf("\n");
+#endif
+ if (!facet_is_degenerated)
+ B.end_facet();
+ }
+
+#ifdef GEN_SURFACE_DEBUG
+ printf("====================\n");
+#endif
+ B.end_surface();
+
+ #undef PointKey
+ }
+};
+
+#endif /* ENABLE_CGAL */
+
+CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const PolySet &ps)
+{
+ if (ps.is2d)
+ {
+#if 0
+ // This version of the code causes problems in some cases.
+ // Example testcase: import_dxf("testdata/polygon8.dxf");
+ //
+ typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
+ typedef point_list_t::iterator point_list_it;
+ std::list< point_list_t > pdata_point_lists;
+ std::list < std::pair < point_list_it, point_list_it > > pdata;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ pdata_point_lists.push_back(point_list_t());
+ for (int j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j].x;
+ double y = ps.polygons[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ pdata_point_lists.back().push_back(p);
+ }
+ pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
+ pdata_point_lists.back().end()));
+ }
+
+ CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
+ return CGAL_Nef_polyhedron(N);
+#endif
+#if 0
+ // This version of the code works fine but is pretty slow.
+ //
+ CGAL_Nef_polyhedron2 N;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j].x;
+ double y = ps.polygons[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ plist.push_back(p);
+ }
+ N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return CGAL_Nef_polyhedron(N);
+#endif
+#if 1
+ // This version of the code does essentially the same thing as the 2nd
+ // version but merges some triangles before sending them to CGAL. This adds
+ // complexity but speeds up things..
+ //
+ struct PolyReducer
+ {
+ Grid2d<int> grid;
+ QHash< QPair<int,int>, QPair<int,int> > egde_to_poly;
+ QHash< int, CGAL_Nef_polyhedron2::Point > points;
+ QHash< int, QList<int> > polygons;
+ int poly_n;
+
+ void add_edges(int pn)
+ {
+ for (int j = 1; j <= this->polygons[pn].size(); j++) {
+ int a = this->polygons[pn][j-1];
+ int b = this->polygons[pn][j % this->polygons[pn].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0)
+ this->egde_to_poly[QPair<int,int>(a, b)].first = pn;
+ else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0)
+ this->egde_to_poly[QPair<int,int>(a, b)].second = pn;
+ else
+ abort();
+ }
+ }
+
+ void del_poly(int pn)
+ {
+ for (int j = 1; j <= this->polygons[pn].size(); j++) {
+ int a = this->polygons[pn][j-1];
+ int b = this->polygons[pn][j % this->polygons[pn].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn)
+ this->egde_to_poly[QPair<int,int>(a, b)].first = 0;
+ if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn)
+ this->egde_to_poly[QPair<int,int>(a, b)].second = 0;
+ }
+ this->polygons.remove(pn);
+ }
+
+ PolyReducer(const PolySet &ps) : grid(GRID_COARSE), poly_n(1)
+ {
+ int point_n = 1;
+ for (int i = 0; i < ps.polygons.size(); i++) {
+ for (int j = 0; j < ps.polygons[i].size(); j++) {
+ double x = ps.polygons[i][j].x;
+ double y = ps.polygons[i][j].y;
+ if (this->grid.has(x, y)) {
+ this->polygons[this->poly_n].append(this->grid.data(x, y));
+ } else {
+ this->grid.align(x, y) = point_n;
+ this->polygons[this->poly_n].append(point_n);
+ this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
+ point_n++;
+ }
+ }
+ add_edges(this->poly_n);
+ this->poly_n++;
+ }
+ }
+
+ int merge(int p1, int p1e, int p2, int p2e)
+ {
+ for (int i = 1; i < this->polygons[p1].size(); i++) {
+ int j = (p1e + i) % this->polygons[p1].size();
+ this->polygons[this->poly_n].append(this->polygons[p1][j]);
+ }
+ for (int i = 1; i < this->polygons[p2].size(); i++) {
+ int j = (p2e + i) % this->polygons[p2].size();
+ this->polygons[this->poly_n].append(this->polygons[p2][j]);
+ }
+ del_poly(p1);
+ del_poly(p2);
+ add_edges(this->poly_n);
+ return this->poly_n++;
+ }
+
+ void reduce()
+ {
+ QList<int> work_queue;
+ QHashIterator< int, QList<int> > it(polygons);
+ while (it.hasNext()) {
+ it.next();
+ work_queue.append(it.key());
+ }
+ while (!work_queue.isEmpty()) {
+ int poly1_n = work_queue.first();
+ work_queue.removeFirst();
+ if (!this->polygons.contains(poly1_n))
+ continue;
+ for (int j = 1; j <= this->polygons[poly1_n].size(); j++) {
+ int a = this->polygons[poly1_n][j-1];
+ int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
+ if (a > b) { a = a^b; b = a^b; a = a^b; }
+ if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 &&
+ this->egde_to_poly[QPair<int,int>(a, b)].second != 0) {
+ int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first +
+ this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n;
+ int poly2_edge = -1;
+ for (int k = 1; k <= this->polygons[poly2_n].size(); k++) {
+ int c = this->polygons[poly2_n][k-1];
+ int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
+ if (c > d) { c = c^d; d = c^d; c = c^d; }
+ if (a == c && b == d) {
+ poly2_edge = k-1;
+ continue;
+ }
+ int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first +
+ this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n;
+ if (poly3_n < 0)
+ continue;
+ if (poly3_n == poly1_n)
+ goto next_poly1_edge;
+ }
+ work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge));
+ goto next_poly1;
+ }
+ next_poly1_edge:;
+ }
+ next_poly1:;
+ }
+ }
+
+ CGAL_Nef_polyhedron2 toNef()
+ {
+ CGAL_Nef_polyhedron2 N;
+
+ QHashIterator< int, QList<int> > it(polygons);
+ while (it.hasNext()) {
+ it.next();
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < it.value().size(); j++) {
+ int p = it.value()[j];
+ plist.push_back(points[p]);
+ }
+ N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return N;
+ }
+ };
+
+ PolyReducer pr(ps);
+ // printf("Number of polygons before reduction: %d\n", pr.polygons.size());
+ pr.reduce();
+ // printf("Number of polygons after reduction: %d\n", pr.polygons.size());
+ return CGAL_Nef_polyhedron(pr.toNef());
+#endif
+#if 0
+ // This is another experimental version. I should run faster than the above,
+ // is a lot simpler and has only one known weakness: Degenerate polygons, which
+ // get repaired by GLUTess, might trigger a CGAL crash here. The only
+ // known case for this is triangle-with-duplicate-vertex.dxf
+ // FIXME: If we just did a projection, we need to recreate the border!
+ if (ps.polygons.size() > 0) assert(ps.borders.size() > 0);
+ CGAL_Nef_polyhedron2 N;
+ Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
+
+ for (int i = 0; i < ps.borders.size(); i++) {
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < ps.borders[i].size(); j++) {
+ double x = ps.borders[i][j].x;
+ double y = ps.borders[i][j].y;
+ CGAL_Nef_polyhedron2::Point p;
+ if (grid.has(x, y)) {
+ p = grid.data(x, y);
+ } else {
+ p = CGAL_Nef_polyhedron2::Point(x, y);
+ grid.data(x, y) = p;
+ }
+ plist.push_back(p);
+ }
+ // FIXME: If a border (path) has a duplicate vertex in dxf,
+ // the CGAL_Nef_polyhedron2 constructor will crash.
+ N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
+ }
+
+ return CGAL_Nef_polyhedron(N);
+
+#endif
+ }
+ else
+ {
+ CGAL_Polyhedron P;
+ CGAL_Build_PolySet builder(ps);
+ P.delegate(builder);
+#if 0
+ std::cout << P;
+#endif
+ CGAL_Nef_polyhedron3 N(P);
+ return CGAL_Nef_polyhedron(N);
+ }
+ return CGAL_Nef_polyhedron();
+}
diff --git a/src/CGALRenderer.h b/src/CGALRenderer.h
index 3933823..c14cacd 100644
--- a/src/CGALRenderer.h
+++ b/src/CGALRenderer.h
@@ -6,6 +6,12 @@
#include <list>
#include "visitor.h"
#include "nodecache.h"
+#include "cgal.h"
+
+#ifdef ENABLE_CGAL
+extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b);
+extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b);
+#endif
using std::string;
using std::map;
@@ -16,33 +22,41 @@ class CGALRenderer : public Visitor
{
public:
enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI};
+ // FIXME: If a cache is not give, we need to fix this ourselves
CGALRenderer(const NodeCache<string> &dumpcache) : root(NULL), dumpcache(dumpcache) {}
virtual ~CGALRenderer() {}
virtual Response visit(const State &state, const AbstractNode &node);
- virtual Response visit(const State &state, const AbstractIntersectionNode &node);
- virtual Response visit(const State &state, const CsgNode &node);
- virtual Response visit(const State &state, const TransformNode &node);
+// virtual Response visit(const State &state, const AbstractIntersectionNode &node);
+// virtual Response visit(const State &state, const CsgNode &node);
+ virtual Response visit(const State &state, const TransformNode &node);
virtual Response visit(const State &state, const AbstractPolyNode &node);
- string getCGALMesh() const;
-// CGAL_Nef_polyhedron getCGALMesh() const;
+ QHash<QString, CGAL_Nef_polyhedron> &getCache() { return this->cache; }
+
+ CGAL_Nef_polyhedron renderCGALMesh(const AbstractNode &node);
+ CGAL_Nef_polyhedron renderCGALMesh(const PolySet &polyset);
+
+ // FIXME: Questionable design...
+ static CGALRenderer *renderer() { return global_renderer; }
+ static void setRenderer(CGALRenderer *r) { global_renderer = r; }
private:
void addToParent(const State &state, const AbstractNode &node);
- bool isCached(const AbstractNode &node);
+ bool isCached(const AbstractNode &node) const;
QString mk_cache_id(const AbstractNode &node) const;
- void process(string &target, const string &src, CGALRenderer::CsgOp op);
+ void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALRenderer::CsgOp op);
void applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp op);
string currindent;
const AbstractNode *root;
typedef list<pair<const AbstractNode *, QString> > ChildList;
map<int, ChildList> visitedchildren;
-// hashmap<string, CGAL_Nef_polyhedron> cache;
- // For now use strings instead of Nef polyhedrons for testing caching
- QHash<QString, string> cache;
+ // FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
+ QHash<QString, CGAL_Nef_polyhedron> cache;
const NodeCache<string> &dumpcache;
+
+ static CGALRenderer *global_renderer;
};
#endif
diff --git a/src/PolySetCGALRenderer.cc b/src/PolySetCGALRenderer.cc
new file mode 100644
index 0000000..acd03eb
--- /dev/null
+++ b/src/PolySetCGALRenderer.cc
@@ -0,0 +1,384 @@
+#include "PolySetCGALRenderer.h"
+#include "polyset.h"
+#include "CGALRenderer.h"
+#include "projectionnode.h"
+#include "dxflinextrudenode.h"
+#include "dxfrotextrudenode.h"
+#include "dxfdata.h"
+#include "dxftess.h"
+#include "module.h"
+
+#include "printutils.h"
+#include "export.h" // void cgal_nef3_to_polyset()
+#include "openscad.h" // get_fragments_from_r()
+
+PolySet *PolySetCGALRenderer::renderPolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e)
+{
+ CGAL_Nef_polyhedron N = this->cgalrenderer.renderCGALMesh(node);
+
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+ ps->is2d = true;
+
+ if (node.cut_mode)
+ {
+ PolySet *cube = new PolySet();
+ double infval = 1e8, eps = 0.1;
+ double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps;
+
+ cube->append_poly(); // top
+ cube->append_vertex(x1, y1, z2);
+ cube->append_vertex(x2, y1, z2);
+ cube->append_vertex(x2, y2, z2);
+ cube->append_vertex(x1, y2, z2);
+
+ cube->append_poly(); // bottom
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x1, y1, z1);
+
+ cube->append_poly(); // side1
+ cube->append_vertex(x1, y1, z1);
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x2, y1, z2);
+ cube->append_vertex(x1, y1, z2);
+
+ cube->append_poly(); // side2
+ cube->append_vertex(x2, y1, z1);
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x2, y2, z2);
+ cube->append_vertex(x2, y1, z2);
+
+ cube->append_poly(); // side3
+ cube->append_vertex(x2, y2, z1);
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x1, y2, z2);
+ cube->append_vertex(x2, y2, z2);
+
+ cube->append_poly(); // side4
+ cube->append_vertex(x1, y2, z1);
+ cube->append_vertex(x1, y1, z1);
+ cube->append_vertex(x1, y1, z2);
+ cube->append_vertex(x1, y2, z2);
+ CGAL_Nef_polyhedron Ncube = this->cgalrenderer.renderCGALMesh(*cube);
+ cube->unlink();
+
+ // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED);
+ N.p3 *= Ncube.p3;
+ if (!N.p3.is_simple()) {
+ PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
+ goto cant_project_non_simple_polyhedron;
+ }
+
+ PolySet *ps3 = new PolySet();
+ cgal_nef3_to_polyset(ps3, &N);
+ Grid2d<int> conversion_grid(GRID_COARSE);
+ for (int i = 0; i < ps3->polygons.size(); i++) {
+ for (int j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j].x;
+ double y = ps3->polygons[i][j].y;
+ double z = ps3->polygons[i][j].z;
+ if (z != 0)
+ goto next_ps3_polygon_cut_mode;
+ if (conversion_grid.align(x, y) == i+1)
+ goto next_ps3_polygon_cut_mode;
+ conversion_grid.data(x, y) = i+1;
+ }
+ ps->append_poly();
+ for (int j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j].x;
+ double y = ps3->polygons[i][j].y;
+ conversion_grid.align(x, y);
+ ps->insert_vertex(x, y);
+ }
+ next_ps3_polygon_cut_mode:;
+ }
+ ps3->unlink();
+ }
+ else
+ {
+ if (!N.p3.is_simple()) {
+ PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
+ goto cant_project_non_simple_polyhedron;
+ }
+
+ PolySet *ps3 = new PolySet();
+ cgal_nef3_to_polyset(ps3, &N);
+ CGAL_Nef_polyhedron np;
+ np.dim = 2;
+ for (int i = 0; i < ps3->polygons.size(); i++)
+ {
+ int min_x_p = -1;
+ double min_x_val = 0;
+ for (int j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j].x;
+ if (min_x_p < 0 || x < min_x_val) {
+ min_x_p = j;
+ min_x_val = x;
+ }
+ }
+ int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size();
+ int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size();
+ double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x;
+ double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y;
+ double at = atan2(ay, ax);
+ double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x;
+ double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y;
+ double bt = atan2(by, bx);
+
+ double eps = 0.000001;
+ if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) ||
+ (fabs(bx) < eps && fabs(by) < eps)) {
+ // this triangle is degenerated in projection
+ continue;
+ }
+
+ std::list<CGAL_Nef_polyhedron2::Point> plist;
+ for (int j = 0; j < ps3->polygons[i].size(); j++) {
+ double x = ps3->polygons[i][j].x;
+ double y = ps3->polygons[i][j].y;
+ CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y);
+ if (at > bt)
+ plist.push_front(p);
+ else
+ plist.push_back(p);
+ }
+ np.p2 += CGAL_Nef_polyhedron2(plist.begin(), plist.end(),
+ CGAL_Nef_polyhedron2::INCLUDED);
+ }
+ DxfData dxf(np);
+ dxf_tesselate(ps, &dxf, 0, true, false, 0);
+ dxf_border_to_ps(ps, &dxf);
+ ps3->unlink();
+ }
+
+cant_project_non_simple_polyhedron:
+
+ return ps;
+}
+
+static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2)
+{
+ for (int j = 1; j < pt->points.count(); j++)
+ {
+ int k = j - 1;
+
+ double jx1 = pt->points[j]->x * cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180);
+ double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180);
+
+ double jx2 = pt->points[j]->x * cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180);
+ double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180);
+
+ double kx1 = pt->points[k]->x * cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180);
+ double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180);
+
+ double kx2 = pt->points[k]->x * cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180);
+ double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180);
+
+ double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2);
+ double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1);
+
+ if (dia1_len_sq > dia2_len_sq)
+ {
+ ps->append_poly();
+ if (pt->is_inner) {
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx1, jy1, h1);
+ ps->append_vertex(jx2, jy2, h2);
+ } else {
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx1, jy1, h1);
+ ps->insert_vertex(jx2, jy2, h2);
+ }
+
+ ps->append_poly();
+ if (pt->is_inner) {
+ ps->append_vertex(kx2, ky2, h2);
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx2, jy2, h2);
+ } else {
+ ps->insert_vertex(kx2, ky2, h2);
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx2, jy2, h2);
+ }
+ }
+ else
+ {
+ ps->append_poly();
+ if (pt->is_inner) {
+ ps->append_vertex(kx1, ky1, h1);
+ ps->append_vertex(jx1, jy1, h1);
+ ps->append_vertex(kx2, ky2, h2);
+ } else {
+ ps->insert_vertex(kx1, ky1, h1);
+ ps->insert_vertex(jx1, jy1, h1);
+ ps->insert_vertex(kx2, ky2, h2);
+ }
+
+ ps->append_poly();
+ if (pt->is_inner) {
+ ps->append_vertex(jx2, jy2, h2);
+ ps->append_vertex(kx2, ky2, h2);
+ ps->append_vertex(jx1, jy1, h1);
+ } else {
+ ps->insert_vertex(jx2, jy2, h2);
+ ps->insert_vertex(kx2, ky2, h2);
+ ps->insert_vertex(jx1, jy1, h1);
+ }
+ }
+ }
+}
+
+PolySet *PolySetCGALRenderer::renderPolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e)
+{
+ DxfData *dxf;
+
+ if (node.filename.isEmpty())
+ {
+ CGAL_Nef_polyhedron N = this->cgalrenderer.renderCGALMesh(node);
+ dxf = new DxfData(N);
+ } else {
+ dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale);
+ }
+
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+
+ double h1, h2;
+
+ if (node.center) {
+ h1 = -node.height/2.0;
+ h2 = +node.height/2.0;
+ } else {
+ h1 = 0;
+ h2 = node.height;
+ }
+
+ bool first_open_path = true;
+ for (int i = 0; i < dxf->paths.count(); i++)
+ {
+ if (dxf->paths[i].is_closed)
+ continue;
+ if (first_open_path) {
+ PRINTF("WARNING: Open paths in dxf_linear_extrude(file = \"%s\", layer = \"%s\"):",
+ node.filename.toAscii().data(), node.layername.toAscii().data());
+ first_open_path = false;
+ }
+ PRINTF(" %9.5f %10.5f ... %10.5f %10.5f",
+ dxf->paths[i].points.first()->x / node.scale + node.origin_x,
+ dxf->paths[i].points.first()->y / node.scale + node.origin_y,
+ dxf->paths[i].points.last()->x / node.scale + node.origin_x,
+ dxf->paths[i].points.last()->y / node.scale + node.origin_y);
+ }
+
+
+ if (node.has_twist)
+ {
+ dxf_tesselate(ps, dxf, 0, false, true, h1);
+ dxf_tesselate(ps, dxf, node.twist, true, true, h2);
+ for (int j = 0; j < node.slices; j++)
+ {
+ double t1 = node.twist*j / node.slices;
+ double t2 = node.twist*(j+1) / node.slices;
+ double g1 = h1 + (h2-h1)*j / node.slices;
+ double g2 = h1 + (h2-h1)*(j+1) / node.slices;
+ for (int i = 0; i < dxf->paths.count(); i++)
+ {
+ if (!dxf->paths[i].is_closed)
+ continue;
+ add_slice(ps, &dxf->paths[i], t1, t2, g1, g2);
+ }
+ }
+ }
+ else
+ {
+ dxf_tesselate(ps, dxf, 0, false, true, h1);
+ dxf_tesselate(ps, dxf, 0, true, true, h2);
+ for (int i = 0; i < dxf->paths.count(); i++)
+ {
+ if (!dxf->paths[i].is_closed)
+ continue;
+ add_slice(ps, &dxf->paths[i], 0, 0, h1, h2);
+ }
+ }
+
+ delete dxf;
+
+ return ps;
+}
+
+PolySet *PolySetCGALRenderer::renderPolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e)
+{
+ DxfData *dxf;
+
+ if (node.filename.isEmpty())
+ {
+ CGAL_Nef_polyhedron N = this->cgalrenderer.renderCGALMesh(node);
+ dxf = new DxfData(N);
+ } else {
+ dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale);
+ }
+
+ PolySet *ps = new PolySet();
+ ps->convexity = node.convexity;
+
+ for (int i = 0; i < dxf->paths.count(); i++)
+ {
+ double max_x = 0;
+ for (int j = 0; j < dxf->paths[i].points.count(); j++) {
+ max_x = fmax(max_x, dxf->paths[i].points[j]->x);
+ }
+
+ int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);
+
+ double points[fragments][dxf->paths[i].points.count()][3];
+
+ for (int j = 0; j < fragments; j++) {
+ double a = (j*2*M_PI) / fragments;
+ for (int k = 0; k < dxf->paths[i].points.count(); k++) {
+ if (dxf->paths[i].points[k]->x == 0) {
+ points[j][k][0] = 0;
+ points[j][k][1] = 0;
+ } else {
+ points[j][k][0] = dxf->paths[i].points[k]->x * sin(a);
+ points[j][k][1] = dxf->paths[i].points[k]->x * cos(a);
+ }
+ points[j][k][2] = dxf->paths[i].points[k]->y;
+ }
+ }
+
+ for (int j = 0; j < fragments; j++) {
+ int j1 = j + 1 < fragments ? j + 1 : 0;
+ for (int k = 0; k < dxf->paths[i].points.count(); k++) {
+ int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0;
+ if (points[j][k][0] != points[j1][k][0] ||
+ points[j][k][1] != points[j1][k][1] ||
+ points[j][k][2] != points[j1][k][2]) {
+ ps->append_poly();
+ ps->append_vertex(points[j ][k ][0],
+ points[j ][k ][1], points[j ][k ][2]);
+ ps->append_vertex(points[j1][k ][0],
+ points[j1][k ][1], points[j1][k ][2]);
+ ps->append_vertex(points[j ][k1][0],
+ points[j ][k1][1], points[j ][k1][2]);
+ }
+ if (points[j][k1][0] != points[j1][k1][0] ||
+ points[j][k1][1] != points[j1][k1][1] ||
+ points[j][k1][2] != points[j1][k1][2]) {
+ ps->append_poly();
+ ps->append_vertex(points[j ][k1][0],
+ points[j ][k1][1], points[j ][k1][2]);
+ ps->append_vertex(points[j1][k ][0],
+ points[j1][k ][1], points[j1][k ][2]);
+ ps->append_vertex(points[j1][k1][0],
+ points[j1][k1][1], points[j1][k1][2]);
+ }
+ }
+ }
+ }
+
+ delete dxf;
+
+ return ps;
+}
diff --git a/src/PolySetCGALRenderer.h b/src/PolySetCGALRenderer.h
new file mode 100644
index 0000000..fb708ed
--- /dev/null
+++ b/src/PolySetCGALRenderer.h
@@ -0,0 +1,26 @@
+#ifndef POLYSETCGALRENDERER_H_
+#define POLYSETCGALRENDERER_H_
+
+#include "PolySetRenderer.h"
+#include <QHash>
+#include "CGALRenderer.h"
+
+/*!
+ This is a PolySet renderer which uses the CGALRenderer to support building
+ polysets.
+*/
+class PolySetCGALRenderer : public PolySetRenderer
+{
+public:
+ PolySetCGALRenderer(CGALRenderer &cgalrenderer) :
+ PolySetRenderer(), cgalrenderer(cgalrenderer) { }
+ virtual ~PolySetCGALRenderer() { }
+ virtual PolySet *renderPolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *renderPolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e);
+ virtual PolySet *renderPolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e);
+
+private:
+ CGALRenderer &cgalrenderer;
+};
+
+#endif
diff --git a/src/PolySetRenderer.cc b/src/PolySetRenderer.cc
new file mode 100644
index 0000000..287cbb9
--- /dev/null
+++ b/src/PolySetRenderer.cc
@@ -0,0 +1,5 @@
+#include "PolySetRenderer.h"
+
+PolySetRenderer *PolySetRenderer::global_renderer = NULL;
+
+
diff --git a/src/PolySetRenderer.h b/src/PolySetRenderer.h
new file mode 100644
index 0000000..cff528f
--- /dev/null
+++ b/src/PolySetRenderer.h
@@ -0,0 +1,23 @@
+#ifndef POLYSETRENDERER_H_
+#define POLYSETRENDERER_H_
+
+#include "node.h"
+
+class PolySetRenderer
+{
+public:
+ enum RenderMode { RENDER_CGAL, RENDER_OPENCSG };
+ PolySetRenderer() {}
+ virtual ~PolySetRenderer() {}
+
+ virtual PolySet *renderPolySet(const class ProjectionNode &node, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *renderPolySet(const class DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e) = 0;
+ virtual PolySet *renderPolySet(const class DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e) = 0;
+
+ static PolySetRenderer *renderer() { return global_renderer; }
+ static void setRenderer(PolySetRenderer *r) { global_renderer = r; }
+private:
+ static PolySetRenderer *global_renderer;
+};
+
+#endif
diff --git a/src/cgaladv.cc b/src/cgaladv.cc
index 193bc27..260850a 100644
--- a/src/cgaladv.cc
+++ b/src/cgaladv.cc
@@ -68,9 +68,6 @@ public:
QString subdiv_type;
int convexity, level;
cgaladv_type_e type;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
#ifndef REMOVE_DUMP
virtual QString dump(QString indent) const;
@@ -137,60 +134,8 @@ void register_builtin_cgaladv()
builtin_modules["subdiv"] = new CgaladvModule(SUBDIV);
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron CgaladvNode::renderCSGMesh() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
- CGAL_Nef_polyhedron N;
-
- if (type == MINKOWSKI)
- {
- bool first = true;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->renderCSGMesh();
- if (N.dim != 0)
- first = false;
- } else {
- CGAL_Nef_polyhedron tmp = v->renderCSGMesh();
- if (N.dim == 3 && tmp.dim == 3) {
- N.p3 = minkowski3(N.p3, tmp.p3);
- }
- if (N.dim == 2 && tmp.dim == 2) {
- N.p2 = minkowski2(N.p2, tmp.p2);
- }
- }
- v->progress_report();
- }
- }
-
- if (type == GLIDE)
- {
- PRINT("WARNING: subdiv() is not implemented yet!");
- }
-
- if (type == SUBDIV)
- {
- PRINT("WARNING: subdiv() is not implemented yet!");
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
+// FIXME: #ifdef ENABLE_CGAL
+#if 0
CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
{
if (type == MINKOWSKI)
diff --git a/src/csgnode.h b/src/csgnode.h
index 3d08b81..4601dc8 100644
--- a/src/csgnode.h
+++ b/src/csgnode.h
@@ -3,9 +3,6 @@
#include "node.h"
#include "visitor.h"
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
enum csg_type_e {
CSG_TYPE_UNION,
@@ -23,9 +20,6 @@ public:
}
virtual std::string toString() const;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
#ifndef REMOVE_DUMP
virtual QString dump(QString indent) const;
diff --git a/src/csgops.cc b/src/csgops.cc
index 62859a5..5ab12bc 100644
--- a/src/csgops.cc
+++ b/src/csgops.cc
@@ -51,64 +51,6 @@ AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *ins
return node;
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron CsgNode::renderCSGMesh() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- bool first = true;
- CGAL_Nef_polyhedron N;
- try {
- foreach (AbstractNode *v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->renderCSGMesh();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- if (type == CSG_TYPE_UNION) {
- N.p2 += v->renderCSGMesh().p2;
- } else if (type == CSG_TYPE_DIFFERENCE) {
- N.p2 -= v->renderCSGMesh().p2;
- } else if (type == CSG_TYPE_INTERSECTION) {
- N.p2 *= v->renderCSGMesh().p2;
- }
- } else if (N.dim == 3) {
- if (type == CSG_TYPE_UNION) {
- N.p3 += v->renderCSGMesh().p3;
- } else if (type == CSG_TYPE_DIFFERENCE) {
- N.p3 -= v->renderCSGMesh().p3;
- } else if (type == CSG_TYPE_INTERSECTION) {
- N.p3 *= v->renderCSGMesh().p3;
- }
- }
- v->progress_report();
- }
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object.");
- }
- CGAL::set_error_behaviour(old_behaviour);
-
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
-#endif /* ENABLE_CGAL */
-
CSGTerm *CsgNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
{
CSGTerm *t1 = NULL;
diff --git a/src/dxflinextrude.cc b/src/dxflinextrude.cc
index 741602d..d162b9f 100644
--- a/src/dxflinextrude.cc
+++ b/src/dxflinextrude.cc
@@ -34,6 +34,7 @@
#include "polyset.h"
#include "progress.h"
#include "visitor.h"
+#include "PolySetRenderer.h"
#include "openscad.h" // get_fragments_from_r()
#include <sys/types.h>
@@ -125,80 +126,16 @@ void register_builtin_dxf_linear_extrude()
builtin_modules["linear_extrude"] = new DxfLinearExtrudeModule();
}
-static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2)
+PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e mode) const
{
- for (int j = 1; j < pt->points.count(); j++)
- {
- int k = j - 1;
-
- double jx1 = pt->points[j]->x * cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180);
- double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180);
-
- double jx2 = pt->points[j]->x * cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180);
- double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180);
-
- double kx1 = pt->points[k]->x * cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180);
- double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180);
-
- double kx2 = pt->points[k]->x * cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180);
- double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180);
-
- double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2);
- double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1);
-
- if (dia1_len_sq > dia2_len_sq)
- {
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx1, jy1, h1);
- ps->append_vertex(jx2, jy2, h2);
- } else {
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx1, jy1, h1);
- ps->insert_vertex(jx2, jy2, h2);
- }
-
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx2, ky2, h2);
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx2, jy2, h2);
- } else {
- ps->insert_vertex(kx2, ky2, h2);
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx2, jy2, h2);
- }
- }
- else
- {
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(kx1, ky1, h1);
- ps->append_vertex(jx1, jy1, h1);
- ps->append_vertex(kx2, ky2, h2);
- } else {
- ps->insert_vertex(kx1, ky1, h1);
- ps->insert_vertex(jx1, jy1, h1);
- ps->insert_vertex(kx2, ky2, h2);
- }
-
- ps->append_poly();
- if (pt->is_inner) {
- ps->append_vertex(jx2, jy2, h2);
- ps->append_vertex(kx2, ky2, h2);
- ps->append_vertex(jx1, jy1, h1);
- } else {
- ps->insert_vertex(jx2, jy2, h2);
- ps->insert_vertex(kx2, ky2, h2);
- ps->insert_vertex(jx1, jy1, h1);
- }
- }
+ PolySetRenderer *renderer = PolySetRenderer::renderer();
+ if (!renderer) {
+ PRINT("WARNING: No suitable PolySetRenderer found for linear_extrude() module!");
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
+ return ps;
}
-}
-PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e) const
-{
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
@@ -206,95 +143,11 @@ PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e) const
}
print_messages_push();
- DxfData *dxf;
-
- if (filename.isEmpty())
- {
-#ifdef ENABLE_CGAL
-
- // Before extruding, union all (2D) children nodes
- // to a single DxfData, then tesselate this into a PolySet
- CGAL_Nef_polyhedron N;
- N.dim = 2;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- N.p2 += v->renderCSGMesh().p2;
- }
- dxf = new DxfData(N);
-
-#else // ENABLE_CGAL
- PRINT("WARNING: Found linear_extrude() statement without dxf file but compiled without CGAL support!");
- dxf = new DxfData();
-#endif // ENABLE_CGAL
- } else {
- dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale);
- }
-
- PolySet *ps = new PolySet();
- ps->convexity = convexity;
-
- double h1, h2;
-
- if (center) {
- h1 = -height/2.0;
- h2 = +height/2.0;
- } else {
- h1 = 0;
- h2 = height;
- }
-
- bool first_open_path = true;
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (dxf->paths[i].is_closed)
- continue;
- if (first_open_path) {
- PRINTF("WARING: Open paths in dxf_liniear_extrude(file = \"%s\", layer = \"%s\"):",
- filename.toAscii().data(), layername.toAscii().data());
- first_open_path = false;
- }
- PRINTF(" %9.5f %10.5f ... %10.5f %10.5f",
- dxf->paths[i].points.first()->x / scale + origin_x,
- dxf->paths[i].points.first()->y / scale + origin_y,
- dxf->paths[i].points.last()->x / scale + origin_x,
- dxf->paths[i].points.last()->y / scale + origin_y);
- }
-
-
- if (has_twist)
- {
- dxf_tesselate(ps, dxf, 0, false, true, h1);
- dxf_tesselate(ps, dxf, twist, true, true, h2);
- for (int j = 0; j < slices; j++)
- {
- double t1 = twist*j / slices;
- double t2 = twist*(j+1) / slices;
- double g1 = h1 + (h2-h1)*j / slices;
- double g2 = h1 + (h2-h1)*(j+1) / slices;
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (!dxf->paths[i].is_closed)
- continue;
- add_slice(ps, &dxf->paths[i], t1, t2, g1, g2);
- }
- }
- }
- else
- {
- dxf_tesselate(ps, dxf, 0, false, true, h1);
- dxf_tesselate(ps, dxf, 0, true, true, h2);
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- if (!dxf->paths[i].is_closed)
- continue;
- add_slice(ps, &dxf->paths[i], 0, 0, h1, h2);
- }
- }
+ PolySet *ps = renderer->renderPolySet(*this, mode);
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
+
print_messages_pop();
- delete dxf;
return ps;
}
diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc
index b32a949..c1f8af1 100644
--- a/src/dxfrotextrude.cc
+++ b/src/dxfrotextrude.cc
@@ -32,6 +32,7 @@
#include "dxfdata.h"
#include "progress.h"
#include "visitor.h"
+#include "PolySetRenderer.h"
#include "openscad.h" // get_fragments_from_r()
#include <sys/types.h>
@@ -101,8 +102,16 @@ void register_builtin_dxf_rotate_extrude()
builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule();
}
-PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const
+PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e mode) const
{
+ PolySetRenderer *renderer = PolySetRenderer::renderer();
+ if (!renderer) {
+ PRINT("WARNING: No suitable PolySetRenderer found for rotate_extrude() module!");
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
+ return ps;
+ }
+
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
@@ -110,89 +119,12 @@ PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const
}
print_messages_push();
- DxfData *dxf;
-
- if (filename.isEmpty())
- {
-#ifdef ENABLE_CGAL
- CGAL_Nef_polyhedron N;
- N.dim = 2;
- foreach(AbstractNode * v, children) {
- if (v->modinst->tag_background)
- continue;
- N.p2 += v->renderCSGMesh().p2;
- }
- dxf = new DxfData(N);
-
-#else // ENABLE_CGAL
- PRINT("WARNING: Found rotate_extrude() statement without dxf file but compiled without CGAL support!");
- dxf = new DxfData();
-#endif // ENABLE_CGAL
- } else {
- dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale);
- }
-
- PolySet *ps = new PolySet();
- ps->convexity = convexity;
-
- for (int i = 0; i < dxf->paths.count(); i++)
- {
- double max_x = 0;
- for (int j = 0; j < dxf->paths[i].points.count(); j++) {
- max_x = fmax(max_x, dxf->paths[i].points[j]->x);
- }
-
- int fragments = get_fragments_from_r(max_x, fn, fs, fa);
-
- double points[fragments][dxf->paths[i].points.count()][3];
-
- for (int j = 0; j < fragments; j++) {
- double a = (j*2*M_PI) / fragments;
- for (int k = 0; k < dxf->paths[i].points.count(); k++) {
- if (dxf->paths[i].points[k]->x == 0) {
- points[j][k][0] = 0;
- points[j][k][1] = 0;
- } else {
- points[j][k][0] = dxf->paths[i].points[k]->x * sin(a);
- points[j][k][1] = dxf->paths[i].points[k]->x * cos(a);
- }
- points[j][k][2] = dxf->paths[i].points[k]->y;
- }
- }
-
- for (int j = 0; j < fragments; j++) {
- int j1 = j + 1 < fragments ? j + 1 : 0;
- for (int k = 0; k < dxf->paths[i].points.count(); k++) {
- int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0;
- if (points[j][k][0] != points[j1][k][0] ||
- points[j][k][1] != points[j1][k][1] ||
- points[j][k][2] != points[j1][k][2]) {
- ps->append_poly();
- ps->append_vertex(points[j ][k ][0],
- points[j ][k ][1], points[j ][k ][2]);
- ps->append_vertex(points[j1][k ][0],
- points[j1][k ][1], points[j1][k ][2]);
- ps->append_vertex(points[j ][k1][0],
- points[j ][k1][1], points[j ][k1][2]);
- }
- if (points[j][k1][0] != points[j1][k1][0] ||
- points[j][k1][1] != points[j1][k1][1] ||
- points[j][k1][2] != points[j1][k1][2]) {
- ps->append_poly();
- ps->append_vertex(points[j ][k1][0],
- points[j ][k1][1], points[j ][k1][2]);
- ps->append_vertex(points[j1][k ][0],
- points[j1][k ][1], points[j1][k ][2]);
- ps->append_vertex(points[j1][k1][0],
- points[j1][k1][1], points[j1][k1][2]);
- }
- }
- }
- }
+ PolySet *ps = renderer->renderPolySet(*this, mode);
+
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
+
print_messages_pop();
- delete dxf;
return ps;
}
diff --git a/src/export.h b/src/export.h
index 619d9e0..444b984 100644
--- a/src/export.h
+++ b/src/export.h
@@ -2,6 +2,7 @@
#define EXPORT_H_
#ifdef ENABLE_CGAL
+#include "cgal.h"
void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N);
void export_stl(class CGAL_Nef_polyhedron *root_N, QString filename, class QProgressDialog *pd);
void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd);
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 8b7fac5..5eb5498 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -44,6 +44,9 @@
#ifdef USE_PROGRESSWIDGET
#include "ProgressWidget.h"
#endif
+#include "CGALRenderer.h"
+#include "PolySetCGALRenderer.h"
+#include "NodeDumper.h"
#include <QMenu>
#include <QTime>
@@ -1128,7 +1131,7 @@ void MainWindow::actionRenderCGAL()
progress_report_prep(root_node, report_func, pd);
try {
- this->root_N = new CGAL_Nef_polyhedron(root_node->renderCSGMesh());
+ this->root_N = new CGAL_Nef_polyhedron(CGALRenderer::renderer()->renderCGALMesh(*root_node));
}
catch (ProgressCancelException e) {
PRINT("Rendering cancelled.");
@@ -1137,8 +1140,9 @@ void MainWindow::actionRenderCGAL()
if (this->root_N)
{
- PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost());
- PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());
+ // FIXME: Reenable cache cost info
+// PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost());
+// PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());
QApplication::processEvents();
if (this->root_N->dim == 2) {
@@ -1354,9 +1358,11 @@ void MainWindow::actionExportDXF()
void MainWindow::actionFlushCaches()
{
+// FIXME: Polycache -> PolySetRenderer
PolySet::ps_cache.clear();
#ifdef ENABLE_CGAL
- AbstractNode::cgal_nef_cache.clear();
+ CGALRenderer::renderer()->getCache().clear();
+ NodeDumper::dumper()->clearCache();
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();
diff --git a/src/node.cc b/src/node.cc
index f48e763..0cdf5d7 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -64,115 +64,6 @@ Response AbstractPolyNode::accept(const class State &state, Visitor &visitor) co
return visitor.visit(state, *this);
}
-/*!
- Create a cache id of the entire tree under this node. This cache id
- is a non-whitespace plaintext of the evaluated scad tree and is used
- for lookup in cgal_nef_cache.
-*/
-QString AbstractNode::mk_cache_id() const
-{
- QString cache_id = dump();
- // Remove all node indices and whitespace
- cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
- cache_id.remove(' ');
- cache_id.remove('\t');
- cache_id.remove('\n');
- return cache_id;
-}
-
-#ifdef ENABLE_CGAL
-
-AbstractNode::cgal_nef_cache_entry::cgal_nef_cache_entry(const CGAL_Nef_polyhedron &N) :
- N(N), msg(print_messages_stack.last()) { };
-
-QCache<QString, AbstractNode::cgal_nef_cache_entry> AbstractNode::cgal_nef_cache(100000);
-
-static CGAL_Nef_polyhedron render_cgal_nef_polyhedron_backend(const AbstractNode *that, bool intersect)
-{
- QString cache_id = that->mk_cache_id();
- if (that->cgal_nef_cache.contains(cache_id)) {
- that->progress_report();
- PRINT(that->cgal_nef_cache[cache_id]->msg);
- return that->cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
- foreach (AbstractNode *v, that->children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->renderCSGMesh();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- if (intersect)
- N.p2 *= v->renderCSGMesh().p2;
- else
- N.p2 += v->renderCSGMesh().p2;
- } else {
- if (intersect)
- N.p3 *= v->renderCSGMesh().p3;
- else
- N.p3 += v->renderCSGMesh().p3;
- }
- v->progress_report();
- }
-
- that->cgal_nef_cache.insert(cache_id, new AbstractNode::cgal_nef_cache_entry(N), N.weight());
- that->progress_report();
- print_messages_pop();
-
- return N;
-}
-
-CGAL_Nef_polyhedron AbstractNode::renderCSGMesh() const
-{
- return render_cgal_nef_polyhedron_backend(this, false);
-}
-
-CGAL_Nef_polyhedron AbstractIntersectionNode::renderCSGMesh() const
-{
- return render_cgal_nef_polyhedron_backend(this, true);
-}
-
-#endif /* ENABLE_CGAL */
-
-static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background)
-{
- CSGTerm *t1 = NULL;
- foreach(AbstractNode *v, that->children) {
- CSGTerm *t2 = v->render_csg_term(m, highlights, background);
- if (t2 && !t1) {
- t1 = t2;
- } else if (t2 && t1) {
- if (intersect)
- t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
- else
- t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
- }
- }
- if (t1 && that->modinst->tag_highlight && highlights)
- highlights->append(t1->link());
- if (t1 && that->modinst->tag_background && background) {
- background->append(t1);
- return NULL;
- }
- return t1;
-}
-
-CSGTerm *AbstractNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- return render_csg_term_backend(this, false, m, highlights, background);
-}
-
-CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
-{
- return render_csg_term_backend(this, true, m, highlights, background);
-}
-
#ifndef REMOVE_DUMP
QString AbstractNode::dump(QString indent) const
{
@@ -234,36 +125,38 @@ void AbstractNode::progress_report() const
progress_update(this, this->progress_mark);
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron AbstractPolyNode::renderCSGMesh() const
+static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background)
{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- PolySet *ps = render_polyset(RENDER_CGAL);
- try {
- CGAL_Nef_polyhedron N = ps->renderCSGMesh();
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- ps->unlink();
- return N;
+ CSGTerm *t1 = NULL;
+ foreach(AbstractNode *v, that->children) {
+ CSGTerm *t2 = v->render_csg_term(m, highlights, background);
+ if (t2 && !t1) {
+ t1 = t2;
+ } else if (t2 && t1) {
+ if (intersect)
+ t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
+ else
+ t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
+ }
}
- catch (...) { // Don't leak the PolySet on ProgressCancelException
- ps->unlink();
- throw;
+ if (t1 && that->modinst->tag_highlight && highlights)
+ highlights->append(t1->link());
+ if (t1 && that->modinst->tag_background && background) {
+ background->append(t1);
+ return NULL;
}
+ return t1;
+}
+
+CSGTerm *AbstractNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+{
+ return render_csg_term_backend(this, false, m, highlights, background);
}
-#endif /* ENABLE_CGAL */
+CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
+{
+ return render_csg_term_backend(this, true, m, highlights, background);
+}
CSGTerm *AbstractPolyNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
{
@@ -295,3 +188,12 @@ std::ostream &operator<<(std::ostream &stream, const AbstractNode &node)
return stream;
}
+QString AbstractNode::mk_cache_id() const
+{
+ QString cache_id = dump();
+ cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
+ cache_id.remove(' ');
+ cache_id.remove('\t');
+ cache_id.remove('\n');
+ return cache_id;
+}
diff --git a/src/node.h b/src/node.h
index ad5b033..bb3d645 100644
--- a/src/node.h
+++ b/src/node.h
@@ -63,13 +63,6 @@ public:
// FIXME: Rewrite to visitor
#ifdef ENABLE_CGAL
- struct cgal_nef_cache_entry {
- CGAL_Nef_polyhedron N;
- QString msg;
- cgal_nef_cache_entry(const CGAL_Nef_polyhedron &N);
- };
- static QCache<QString, cgal_nef_cache_entry> cgal_nef_cache;
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
class CSGTerm *render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const;
#endif
};
@@ -82,9 +75,6 @@ public:
virtual Response accept(const class State &state, class Visitor &visitor) const;
virtual std::string toString() const;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
#ifndef REMOVE_DUMP
virtual QString dump(QString indent) const;
@@ -103,9 +93,6 @@ public:
RENDER_OPENCSG
};
virtual class PolySet *render_polyset(render_mode_e mode) const = 0;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
static CSGTerm *render_csg_term_from_ps(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, PolySet *ps, const ModuleInstantiation *modinst, int idx);
};
diff --git a/src/nodecache.h b/src/nodecache.h
index 943f904..efd5104 100644
--- a/src/nodecache.h
+++ b/src/nodecache.h
@@ -25,6 +25,10 @@ public:
if (this->cache.size() > node.index()) this->cache[node.index()] = nullvalue;
}
+ void clear() {
+ this->cache.clear();
+ }
+
private:
std::vector<T> cache;
T nullvalue;
diff --git a/src/nodedumper.cc b/src/nodedumper.cc
index a8de089..a62ad98 100644
--- a/src/nodedumper.cc
+++ b/src/nodedumper.cc
@@ -18,6 +18,7 @@
#include "projectionnode.h"
#endif
+NodeDumper *NodeDumper::global_dumper = NULL;
bool NodeDumper::isCached(const AbstractNode &node)
{
diff --git a/src/nodedumper.h b/src/nodedumper.h
index a28b8ad..ca76814 100644
--- a/src/nodedumper.h
+++ b/src/nodedumper.h
@@ -21,6 +21,11 @@ public:
const string &getDump() const;
const NodeCache<string> &getCache() const { return this->cache; }
+ void clearCache() { this->cache.clear(); }
+
+ // FIXME: Questionable design...
+ static NodeDumper *dumper() { return global_dumper; }
+ static void setDumper(NodeDumper *d) { global_dumper = d; }
private:
void handleVisitedChildren(const State &state, const AbstractNode &node);
bool isCached(const AbstractNode &node);
@@ -32,6 +37,8 @@ private:
typedef list<const AbstractNode *> ChildList;
map<int, ChildList> visitedchildren;
NodeCache<string> cache;
+
+ static NodeDumper *global_dumper;
};
#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 86dce1b..1e78316 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -32,6 +32,8 @@
#include "export.h"
#include "builtin.h"
#include "nodedumper.h"
+#include "CGALRenderer.h"
+#include "PolySetCGALRenderer.h"
#ifdef ENABLE_CGAL
#include "cgal.h"
@@ -235,6 +237,14 @@ int main(int argc, char **argv)
librarydir = libdir.path();
}
+ // Initialize global visitors
+ NodeDumper dumper;
+ CGALRenderer cgalrenderer(dumper.getCache());
+ PolySetCGALRenderer psrenderer(cgalrenderer);
+ NodeDumper::setDumper(&dumper);
+ CGALRenderer::setRenderer(&cgalrenderer);
+ PolySetRenderer::setRenderer(&psrenderer);
+
if (stl_output_file || off_output_file || dxf_output_file)
{
if (!filename)
@@ -285,8 +295,11 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
- CGAL_Nef_polyhedron *root_N;
- root_N = new CGAL_Nef_polyhedron(root_node->renderCSGMesh());
+ // FIXME: It shouldn't be necessary to dump manually, only when
+ // the dumper and the renderer wants to share a cache
+ Traverser trav(*NodeDumper::dumper(), *root_node, Traverser::PRE_AND_POSTFIX);
+ trav.execute();
+ CGAL_Nef_polyhedron root_N = CGALRenderer::renderer()->renderCGALMesh(*root_node);
QDir::setCurrent(original_path.absolutePath());
@@ -305,16 +318,15 @@ int main(int argc, char **argv)
}
if (stl_output_file)
- export_stl(root_N, stl_output_file, NULL);
+ export_stl(&root_N, stl_output_file, NULL);
if (off_output_file)
- export_off(root_N, off_output_file, NULL);
+ export_off(&root_N, off_output_file, NULL);
if (dxf_output_file)
- export_dxf(root_N, dxf_output_file, NULL);
+ export_dxf(&root_N, dxf_output_file, NULL);
delete root_node;
- delete root_N;
#else
fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n");
exit(1);
diff --git a/src/polyset.cc b/src/polyset.cc
index 4548f8a..9f32f6e 100644
--- a/src/polyset.cc
+++ b/src/polyset.cc
@@ -325,357 +325,3 @@ void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const
}
}
}
-
-#ifdef ENABLE_CGAL
-
-#undef GEN_SURFACE_DEBUG
-
-class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS>
-{
-public:
- typedef CGAL_HDS::Vertex::Point Point;
-
- const PolySet *ps;
- CGAL_Build_PolySet(const PolySet *ps) : ps(ps) { }
-
- void operator()(CGAL_HDS& hds)
- {
- CGAL_Polybuilder B(hds, true);
-
- QList<PolySet::Point> vertices;
- Grid3d<int> vertices_idx(GRID_FINE);
-
- for (int i = 0; i < ps->polygons.size(); i++) {
- const PolySet::Polygon *poly = &ps->polygons[i];
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
- if (!vertices_idx.has(p->x, p->y, p->z)) {
- vertices_idx.data(p->x, p->y, p->z) = vertices.size();
- vertices.append(*p);
- }
- }
- }
-
- B.begin_surface(vertices.size(), ps->polygons.size());
-#ifdef GEN_SURFACE_DEBUG
- printf("=== CGAL Surface ===\n");
-#endif
-
- for (int i = 0; i < vertices.size(); i++) {
- const PolySet::Point *p = &vertices[i];
- B.add_vertex(Point(p->x, p->y, p->z));
-#ifdef GEN_SURFACE_DEBUG
- printf("%d: %f %f %f\n", i, p->x, p->y, p->z);
-#endif
- }
-
- for (int i = 0; i < ps->polygons.size(); i++) {
- const PolySet::Polygon *poly = &ps->polygons[i];
- QHash<int,int> fc;
- bool facet_is_degenerated = false;
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
- int v = vertices_idx.data(p->x, p->y, p->z);
- if (fc[v]++ > 0)
- facet_is_degenerated = true;
- }
-
- if (!facet_is_degenerated)
- B.begin_facet();
-#ifdef GEN_SURFACE_DEBUG
- printf("F:");
-#endif
- for (int j = 0; j < poly->size(); j++) {
- const PolySet::Point *p = &poly->at(j);
-#ifdef GEN_SURFACE_DEBUG
- printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z);
-#endif
- if (!facet_is_degenerated)
- B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z));
- }
-#ifdef GEN_SURFACE_DEBUG
- if (facet_is_degenerated)
- printf(" (degenerated)");
- printf("\n");
-#endif
- if (!facet_is_degenerated)
- B.end_facet();
- }
-
-#ifdef GEN_SURFACE_DEBUG
- printf("====================\n");
-#endif
- B.end_surface();
-
- #undef PointKey
- }
-};
-
-CGAL_Nef_polyhedron PolySet::renderCSGMesh() const
-{
- if (this->is2d)
- {
-#if 0
- // This version of the code causes problems in some cases.
- // Example testcase: import_dxf("testdata/polygon8.dxf");
- //
- typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
- typedef point_list_t::iterator point_list_it;
- std::list< point_list_t > pdata_point_lists;
- std::list < std::pair < point_list_it, point_list_it > > pdata;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->polygons.size(); i++) {
- pdata_point_lists.push_back(point_list_t());
- for (int j = 0; j < this->polygons[i].size(); j++) {
- double x = this->polygons[i][j].x;
- double y = this->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- pdata_point_lists.back().push_back(p);
- }
- pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
- pdata_point_lists.back().end()));
- }
-
- CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
- return CGAL_Nef_polyhedron(N);
-#endif
-#if 0
- // This version of the code works fine but is pretty slow.
- //
- CGAL_Nef_polyhedron2 N;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->polygons.size(); i++) {
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < this->polygons[i].size(); j++) {
- double x = this->polygons[i][j].x;
- double y = this->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- plist.push_back(p);
- }
- N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
- }
-
- return CGAL_Nef_polyhedron(N);
-#endif
-#if 1
- // This version of the code does essentially the same thing as the 2nd
- // version but merges some triangles before sending them to CGAL. This adds
- // complexity but speeds up things..
- //
- struct PolyReducer
- {
- Grid2d<int> grid;
- QHash< QPair<int,int>, QPair<int,int> > egde_to_poly;
- QHash< int, CGAL_Nef_polyhedron2::Point > points;
- QHash< int, QList<int> > polygons;
- int poly_n;
-
- void add_edges(int pn)
- {
- for (int j = 1; j <= this->polygons[pn].size(); j++) {
- int a = this->polygons[pn][j-1];
- int b = this->polygons[pn][j % this->polygons[pn].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0)
- this->egde_to_poly[QPair<int,int>(a, b)].first = pn;
- else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0)
- this->egde_to_poly[QPair<int,int>(a, b)].second = pn;
- else
- abort();
- }
- }
-
- void del_poly(int pn)
- {
- for (int j = 1; j <= this->polygons[pn].size(); j++) {
- int a = this->polygons[pn][j-1];
- int b = this->polygons[pn][j % this->polygons[pn].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn)
- this->egde_to_poly[QPair<int,int>(a, b)].first = 0;
- if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn)
- this->egde_to_poly[QPair<int,int>(a, b)].second = 0;
- }
- this->polygons.remove(pn);
- }
-
- PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1)
- {
- int point_n = 1;
- for (int i = 0; i < ps->polygons.size(); i++) {
- for (int j = 0; j < ps->polygons[i].size(); j++) {
- double x = ps->polygons[i][j].x;
- double y = ps->polygons[i][j].y;
- if (this->grid.has(x, y)) {
- this->polygons[this->poly_n].append(this->grid.data(x, y));
- } else {
- this->grid.align(x, y) = point_n;
- this->polygons[this->poly_n].append(point_n);
- this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
- point_n++;
- }
- }
- add_edges(this->poly_n);
- this->poly_n++;
- }
- }
-
- int merge(int p1, int p1e, int p2, int p2e)
- {
- for (int i = 1; i < this->polygons[p1].size(); i++) {
- int j = (p1e + i) % this->polygons[p1].size();
- this->polygons[this->poly_n].append(this->polygons[p1][j]);
- }
- for (int i = 1; i < this->polygons[p2].size(); i++) {
- int j = (p2e + i) % this->polygons[p2].size();
- this->polygons[this->poly_n].append(this->polygons[p2][j]);
- }
- del_poly(p1);
- del_poly(p2);
- add_edges(this->poly_n);
- return this->poly_n++;
- }
-
- void reduce()
- {
- QList<int> work_queue;
- QHashIterator< int, QList<int> > it(polygons);
- while (it.hasNext()) {
- it.next();
- work_queue.append(it.key());
- }
- while (!work_queue.isEmpty()) {
- int poly1_n = work_queue.first();
- work_queue.removeFirst();
- if (!this->polygons.contains(poly1_n))
- continue;
- for (int j = 1; j <= this->polygons[poly1_n].size(); j++) {
- int a = this->polygons[poly1_n][j-1];
- int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
- if (a > b) { a = a^b; b = a^b; a = a^b; }
- if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 &&
- this->egde_to_poly[QPair<int,int>(a, b)].second != 0) {
- int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first +
- this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n;
- int poly2_edge = -1;
- for (int k = 1; k <= this->polygons[poly2_n].size(); k++) {
- int c = this->polygons[poly2_n][k-1];
- int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
- if (c > d) { c = c^d; d = c^d; c = c^d; }
- if (a == c && b == d) {
- poly2_edge = k-1;
- continue;
- }
- int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first +
- this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n;
- if (poly3_n < 0)
- continue;
- if (poly3_n == poly1_n)
- goto next_poly1_edge;
- }
- work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge));
- goto next_poly1;
- }
- next_poly1_edge:;
- }
- next_poly1:;
- }
- }
-
- CGAL_Nef_polyhedron2 toNef()
- {
- CGAL_Nef_polyhedron2 N;
-
- QHashIterator< int, QList<int> > it(polygons);
- while (it.hasNext()) {
- it.next();
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < it.value().size(); j++) {
- int p = it.value()[j];
- plist.push_back(points[p]);
- }
- N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
- }
-
- return N;
- }
- };
-
- PolyReducer pr(this);
- // printf("Number of polygons before reduction: %d\n", pr.polygons.size());
- pr.reduce();
- // printf("Number of polygons after reduction: %d\n", pr.polygons.size());
- return CGAL_Nef_polyhedron(pr.toNef());
-#endif
-#if 0
- // This is another experimental version. I should run faster than the above,
- // is a lot simpler and has only one known weakness: Degenerate polygons, which
- // get repaired by GLUTess, might trigger a CGAL crash here. The only
- // known case for this is triangle-with-duplicate-vertex.dxf
- // FIXME: If we just did a projection, we need to recreate the border!
- if (this->polygons.size() > 0) assert(this->borders.size() > 0);
- CGAL_Nef_polyhedron2 N;
- Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
-
- for (int i = 0; i < this->borders.size(); i++) {
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < this->borders[i].size(); j++) {
- double x = this->borders[i][j].x;
- double y = this->borders[i][j].y;
- CGAL_Nef_polyhedron2::Point p;
- if (grid.has(x, y)) {
- p = grid.data(x, y);
- } else {
- p = CGAL_Nef_polyhedron2::Point(x, y);
- grid.data(x, y) = p;
- }
- plist.push_back(p);
- }
- // FIXME: If a border (path) has a duplicate vertex in dxf,
- // the CGAL_Nef_polyhedron2 constructor will crash.
- N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
- }
-
- return CGAL_Nef_polyhedron(N);
-
-#endif
- }
- else // not (this->is2d)
- {
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- try {
- CGAL_Polyhedron P;
- CGAL_Build_PolySet builder(this);
- P.delegate(builder);
-#if 0
- std::cout << P;
-#endif
- CGAL_Nef_polyhedron3 N(P);
- return CGAL_Nef_polyhedron(N);
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object.");
- CGAL::set_error_behaviour(old_behaviour);
- return CGAL_Nef_polyhedron();
- }
- CGAL::set_error_behaviour(old_behaviour);
- }
- return CGAL_Nef_polyhedron();
-}
-
-#endif /* ENABLE_CGAL */
-
diff --git a/src/polyset.h b/src/polyset.h
index 074f82e..3f0e725 100644
--- a/src/polyset.h
+++ b/src/polyset.h
@@ -11,9 +11,6 @@
#ifdef ENABLE_OPENCSG
# include <opencsg.h>
#endif
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
#include <QCache>
@@ -77,10 +74,6 @@ public:
void render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo = NULL) const;
void render_edges(colormode_e colormode, csgmode_e csgmode) const;
-#ifdef ENABLE_CGAL
- CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
-
int refcount;
PolySet *link();
void unlink();
diff --git a/src/projection.cc b/src/projection.cc
index 2176b4d..b4a19c7 100644
--- a/src/projection.cc
+++ b/src/projection.cc
@@ -34,6 +34,7 @@
#include "export.h"
#include "progress.h"
#include "visitor.h"
+#include "PolySetRenderer.h"
#ifdef ENABLE_CGAL
# include <CGAL/assertions_behaviour.h>
@@ -89,10 +90,16 @@ void register_builtin_projection()
builtin_modules["projection"] = new ProjectionModule();
}
-#ifdef ENABLE_CGAL
-
-PolySet *ProjectionNode::render_polyset(render_mode_e) const
+PolySet *ProjectionNode::render_polyset(render_mode_e mode) const
{
+ PolySetRenderer *renderer = PolySetRenderer::renderer();
+ if (!renderer) {
+ PRINT("WARNING: No suitable PolySetRenderer found for projection() module!");
+ PolySet *ps = new PolySet();
+ ps->is2d = true;
+ return ps;
+ }
+
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
@@ -101,179 +108,14 @@ PolySet *ProjectionNode::render_polyset(render_mode_e) const
print_messages_push();
- PolySet *ps = new PolySet();
- ps->convexity = this->convexity;
- ps->is2d = true;
-
- CGAL_Nef_polyhedron N;
- N.dim = 3;
- CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
- try {
- foreach(AbstractNode *v, this->children) {
- if (v->modinst->tag_background)
- continue;
- N.p3 += v->renderCSGMesh().p3;
- }
- }
- catch (CGAL::Assertion_exception e) {
- PRINTF("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object.");
- CGAL::set_error_behaviour(old_behaviour);
- return ps;
- }
- CGAL::set_error_behaviour(old_behaviour);
-
- if (cut_mode)
- {
- PolySet *cube = new PolySet();
- double infval = 1e8, eps = 0.1;
- double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps;
-
- cube->append_poly(); // top
- cube->append_vertex(x1, y1, z2);
- cube->append_vertex(x2, y1, z2);
- cube->append_vertex(x2, y2, z2);
- cube->append_vertex(x1, y2, z2);
-
- cube->append_poly(); // bottom
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x1, y1, z1);
-
- cube->append_poly(); // side1
- cube->append_vertex(x1, y1, z1);
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x2, y1, z2);
- cube->append_vertex(x1, y1, z2);
-
- cube->append_poly(); // side2
- cube->append_vertex(x2, y1, z1);
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x2, y2, z2);
- cube->append_vertex(x2, y1, z2);
-
- cube->append_poly(); // side3
- cube->append_vertex(x2, y2, z1);
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x1, y2, z2);
- cube->append_vertex(x2, y2, z2);
-
- cube->append_poly(); // side4
- cube->append_vertex(x1, y2, z1);
- cube->append_vertex(x1, y1, z1);
- cube->append_vertex(x1, y1, z2);
- cube->append_vertex(x1, y2, z2);
- CGAL_Nef_polyhedron Ncube = cube->renderCSGMesh();
- cube->unlink();
-
- // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED);
- N.p3 *= Ncube.p3;
- if (!N.p3.is_simple()) {
- PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
- PolySet *ps3 = new PolySet();
- cgal_nef3_to_polyset(ps3, &N);
- Grid2d<int> conversion_grid(GRID_COARSE);
- for (int i = 0; i < ps3->polygons.size(); i++) {
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- double z = ps3->polygons[i][j].z;
- if (z != 0)
- goto next_ps3_polygon_cut_mode;
- if (conversion_grid.align(x, y) == i+1)
- goto next_ps3_polygon_cut_mode;
- conversion_grid.data(x, y) = i+1;
- }
- ps->append_poly();
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- conversion_grid.align(x, y);
- ps->insert_vertex(x, y);
- }
- next_ps3_polygon_cut_mode:;
- }
- ps3->unlink();
- }
- else
- {
- if (!N.p3.is_simple()) {
- PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design..");
- goto cant_project_non_simple_polyhedron;
- }
-
- PolySet *ps3 = new PolySet();
- cgal_nef3_to_polyset(ps3, &N);
- CGAL_Nef_polyhedron np;
- np.dim = 2;
- for (int i = 0; i < ps3->polygons.size(); i++)
- {
- int min_x_p = -1;
- double min_x_val = 0;
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- if (min_x_p < 0 || x < min_x_val) {
- min_x_p = j;
- min_x_val = x;
- }
- }
- int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size();
- int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size();
- double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x;
- double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y;
- double at = atan2(ay, ax);
- double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x;
- double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y;
- double bt = atan2(by, bx);
-
- double eps = 0.000001;
- if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) ||
- (fabs(bx) < eps && fabs(by) < eps)) {
- // this triangle is degenerated in projection
- continue;
- }
-
- std::list<CGAL_Nef_polyhedron2::Point> plist;
- for (int j = 0; j < ps3->polygons[i].size(); j++) {
- double x = ps3->polygons[i][j].x;
- double y = ps3->polygons[i][j].y;
- CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y);
- if (at > bt)
- plist.push_front(p);
- else
- plist.push_back(p);
- }
- np.p2 += CGAL_Nef_polyhedron2(plist.begin(), plist.end(),
- CGAL_Nef_polyhedron2::INCLUDED);
- }
- DxfData dxf(np);
- dxf_tesselate(ps, &dxf, 0, true, false, 0);
- dxf_border_to_ps(ps, &dxf);
- ps3->unlink();
- }
-
-cant_project_non_simple_polyhedron:
+ PolySet *ps = renderer->renderPolySet(*this, mode);
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
- print_messages_pop();
-
- return ps;
-}
-#else // ENABLE_CGAL
+ print_messages_pop();
-PolySet *ProjectionNode::render_polyset(render_mode_e) const
-{
- PRINT("WARNING: Found projection() statement but compiled without CGAL support!");
- PolySet *ps = new PolySet();
- ps->is2d = true;
return ps;
}
-#endif // ENABLE_CGAL
-
#ifndef REMOVE_DUMP
QString ProjectionNode::dump(QString indent) const
{
diff --git a/src/render.cc b/src/render.cc
index 4778083..0e0ea51 100644
--- a/src/render.cc
+++ b/src/render.cc
@@ -77,44 +77,8 @@ void register_builtin_render()
builtin_modules["render"] = new RenderModule();
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron RenderNode::renderCSGMesh() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
- foreach(AbstractNode * v, children)
- {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->renderCSGMesh();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- N.p2 += v->renderCSGMesh().p2;
- } else if (N.dim == 3) {
- N.p3 += v->renderCSGMesh().p3;
- }
- v->progress_report();
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
+// FIXME: #ifdef ENABLE_CGAL
+#if 0
CSGTerm *AbstractNode::render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const
{
QString key = mk_cache_id();
diff --git a/src/rendernode.h b/src/rendernode.h
index 8bb2c9c..5561a36 100644
--- a/src/rendernode.h
+++ b/src/rendernode.h
@@ -3,9 +3,6 @@
#include "node.h"
#include "visitor.h"
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
class RenderNode : public AbstractNode
{
@@ -17,9 +14,6 @@ public:
virtual std::string toString() const;
int convexity;
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
#ifndef REMOVE_DUMP
virtual QString dump(QString indent) const;
diff --git a/src/surface.cc b/src/surface.cc
index c28d9ba..cd40e97 100644
--- a/src/surface.cc
+++ b/src/surface.cc
@@ -99,7 +99,7 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
QFile f(filename);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTF("WARNING: Can't open DXF file `%s'.", filename.toAscii().data());
+ PRINTF("WARNING: Can't open DAT file `%s'.", filename.toAscii().data());
return NULL;
}
diff --git a/src/transform.cc b/src/transform.cc
index f6e49c2..870ad32 100644
--- a/src/transform.cc
+++ b/src/transform.cc
@@ -228,79 +228,6 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
return node;
}
-#ifdef ENABLE_CGAL
-
-CGAL_Nef_polyhedron TransformNode::renderCSGMesh() const
-{
- QString cache_id = mk_cache_id();
- if (cgal_nef_cache.contains(cache_id)) {
- progress_report();
- PRINT(cgal_nef_cache[cache_id]->msg);
- return cgal_nef_cache[cache_id]->N;
- }
-
- print_messages_push();
-
- bool first = true;
- CGAL_Nef_polyhedron N;
-
- foreach (AbstractNode *v, children) {
- if (v->modinst->tag_background)
- continue;
- if (first) {
- N = v->renderCSGMesh();
- if (N.dim != 0)
- first = false;
- } else if (N.dim == 2) {
- N.p2 += v->renderCSGMesh().p2;
- } else if (N.dim == 3) {
- N.p3 += v->renderCSGMesh().p3;
- }
- v->progress_report();
- }
-
- if (N.dim == 2)
- {
- // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2
- // objects. So we convert in to our internal 2d data format, transform it,
- // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack!
-
- CGAL_Aff_transformation2 t(
- m[0], m[4], m[12],
- m[1], m[5], m[13], m[15]);
-
- DxfData dd(N);
- for (int i=0; i < dd.points.size(); i++) {
- CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y);
- p = t.transform(p);
- dd.points[i].x = to_double(p.x());
- dd.points[i].y = to_double(p.y());
- }
-
- PolySet ps;
- ps.is2d = true;
- dxf_tesselate(&ps, &dd, 0, true, false, 0);
-
- N = ps.renderCSGMesh();
- ps.refcount = 0;
- }
- if (N.dim == 3) {
- CGAL_Aff_transformation t(
- m[0], m[4], m[ 8], m[12],
- m[1], m[5], m[ 9], m[13],
- m[2], m[6], m[10], m[14], m[15]);
- N.p3.transform(t);
- }
-
- cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight());
- print_messages_pop();
- progress_report();
-
- return N;
-}
-
-#endif /* ENABLE_CGAL */
-
CSGTerm *TransformNode::render_csg_term(double c[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const
{
double x[20];
diff --git a/src/transformnode.h b/src/transformnode.h
index c262afd..a392107 100644
--- a/src/transformnode.h
+++ b/src/transformnode.h
@@ -3,9 +3,6 @@
#include "node.h"
#include "visitor.h"
-#ifdef ENABLE_CGAL
-# include "cgal.h"
-#endif
class TransformNode : public AbstractNode
{
@@ -17,9 +14,6 @@ public:
virtual std::string toString() const;
double m[20];
-#ifdef ENABLE_CGAL
- virtual CGAL_Nef_polyhedron renderCSGMesh() const;
-#endif
virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;
#ifndef REMOVE_DUMP
virtual QString dump(QString indent) const;
diff --git a/test-code/cgaltest.cc b/test-code/cgaltest.cc
index 837f442..8529da8 100644
--- a/test-code/cgaltest.cc
+++ b/test-code/cgaltest.cc
@@ -32,6 +32,7 @@
#include "builtin.h"
#include "nodedumper.h"
#include "CGALRenderer.h"
+#include "PolySetCGALRenderer.h"
#include <QApplication>
#include <QFile>
@@ -145,17 +146,25 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
-
NodeDumper dumper;
Traverser trav(dumper, *root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
std::string dumpstdstr = dumper.getDump() + "\n";
std::cout << dumpstdstr << "\n";
- CGALRenderer renderer(dumper.getCache());
- Traverser render(renderer, *root_node, Traverser::PRE_AND_POSTFIX);
- render.execute();
- std::cout << renderer.getCGALMesh() << "\n";
+ CGALRenderer cgalrenderer(dumper.getCache());
+ PolySetCGALRenderer psrenderer(cgalrenderer);
+ PolySetRenderer::setRenderer(&psrenderer);
+
+// This is done in renderCGALMesh() for convenience, but can be overridden here
+// Traverser render(cgalrenderer, *root_node, Traverser::PRE_AND_POSTFIX);
+// render.execute();
+ CGAL_Nef_polyhedron N = cgalrenderer.renderCGALMesh(*root_node);
+
+ QDir::setCurrent(original_path.absolutePath());
+ export_stl(&N, fileInfo.baseName() + ".stl", NULL);
+
+ PolySetRenderer::setRenderer(NULL);
destroy_builtin_functions();
destroy_builtin_modules();
diff --git a/test-code/cgaltest.pro b/test-code/cgaltest.pro
index 94942da..27eeb1a 100644
--- a/test-code/cgaltest.pro
+++ b/test-code/cgaltest.pro
@@ -7,7 +7,7 @@ UI_DIR = objects
RCC_DIR = objects
INCLUDEPATH += ../src
-DEFINES += REMOVE_DUMP
+DEFINES += REMOVE_DUMP REMOVE_RENDERCGAL
macx {
CONFIG -= app_bundle
LIBS += -framework Carbon
@@ -15,20 +15,14 @@ macx {
CONFIG += qt
QT += opengl
+CONFIG += cgal
-# Optionally specify location of Eigen2 using the
-# EIGEN2DIR env. variable
-EIGEN2_DIR = $$(EIGEN2DIR)
-!isEmpty(EIGEN2_DIR) {
- INCLUDEPATH += $$EIGEN2_DIR
-}
-else {
- macx {
- INCLUDEPATH += /opt/local/include/eigen2
- }
- else {
- INCLUDEPATH += /usr/include/eigen2
- }
+include(../cgal.pri)
+include(../eigen2.pri)
+
+# Standard include path for misc external libs
+macx {
+ INCLUDEPATH += /opt/local/include
}
LEXSOURCES += ../src/lexer.l
@@ -66,7 +60,9 @@ HEADERS += ../src/builtin.h \
../src/CGALRenderer.h \
../src/nodecache.h \
../src/importnode.h \
- ../src/state.h
+ ../src/state.h \
+ ../src/PolySetRenderer.h \
+ ../src/PolySetCGALRenderer.h
SOURCES += cgaltest.cc \
../src/export.cc \
@@ -83,11 +79,14 @@ SOURCES += cgaltest.cc \
../src/primitives.cc \
../src/projection.cc \
../src/cgaladv.cc \
+ ../src/cgaladv_minkowski2.cc \
+ ../src/cgaladv_minkowski3.cc \
../src/surface.cc \
../src/control.cc \
../src/render.cc \
../src/import.cc \
../src/dxfdata.cc \
+ ../src/nef2dxf.cc \
../src/dxftess.cc \
../src/dxftess-glu.cc \
../src/dxftess-cgal.cc \
@@ -98,4 +97,6 @@ SOURCES += cgaltest.cc \
../src/progress.cc \
../src/nodedumper.cc \
../src/CGALRenderer.cc \
- ../src/traverser.cc
+ ../src/traverser.cc \
+ ../src/PolySetRenderer.cc \
+ ../src/PolySetCGALRenderer.cc
contact: Jan Huwald // Impressum