diff options
-rw-r--r-- | openscad.pro | 8 | ||||
-rw-r--r-- | src/CGALRenderer.cc | 597 | ||||
-rw-r--r-- | src/CGALRenderer.h | 34 | ||||
-rw-r--r-- | src/PolySetCGALRenderer.cc | 384 | ||||
-rw-r--r-- | src/PolySetCGALRenderer.h | 26 | ||||
-rw-r--r-- | src/PolySetRenderer.cc | 5 | ||||
-rw-r--r-- | src/PolySetRenderer.h | 23 | ||||
-rw-r--r-- | src/cgaladv.cc | 59 | ||||
-rw-r--r-- | src/csgnode.h | 6 | ||||
-rw-r--r-- | src/csgops.cc | 58 | ||||
-rw-r--r-- | src/dxflinextrude.cc | 167 | ||||
-rw-r--r-- | src/dxfrotextrude.cc | 94 | ||||
-rw-r--r-- | src/export.h | 1 | ||||
-rw-r--r-- | src/mainwin.cc | 14 | ||||
-rw-r--r-- | src/node.cc | 170 | ||||
-rw-r--r-- | src/node.h | 13 | ||||
-rw-r--r-- | src/nodecache.h | 4 | ||||
-rw-r--r-- | src/nodedumper.cc | 1 | ||||
-rw-r--r-- | src/nodedumper.h | 7 | ||||
-rw-r--r-- | src/openscad.cc | 24 | ||||
-rw-r--r-- | src/polyset.cc | 354 | ||||
-rw-r--r-- | src/polyset.h | 7 | ||||
-rw-r--r-- | src/projection.cc | 182 | ||||
-rw-r--r-- | src/render.cc | 40 | ||||
-rw-r--r-- | src/rendernode.h | 6 | ||||
-rw-r--r-- | src/surface.cc | 2 | ||||
-rw-r--r-- | src/transform.cc | 73 | ||||
-rw-r--r-- | src/transformnode.h | 6 | ||||
-rw-r--r-- | test-code/cgaltest.cc | 19 | ||||
-rw-r--r-- | test-code/cgaltest.pro | 33 |
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; +} @@ -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 |