diff options
Diffstat (limited to 'src')
98 files changed, 5318 insertions, 3604 deletions
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc new file mode 100644 index 0000000..440637a --- /dev/null +++ b/src/CGALEvaluator.cc @@ -0,0 +1,611 @@ +#include "CGALEvaluator.h" +#include "visitor.h" +#include "state.h" +#include "module.h" // FIXME: Temporarily for ModuleInstantiation +#include "printutils.h" + +#include "csgnode.h" +#include "cgaladvnode.h" +#include "transformnode.h" +#include "polyset.h" +#include "dxfdata.h" +#include "dxftess.h" + +#include "cgal.h" +#include "cgalutils.h" +#include <CGAL/assertions_behaviour.h> +#include <CGAL/exceptions.h> + +#include <string> +#include <map> +#include <list> +#include <sstream> +#include <iostream> +#include <assert.h> +#include <QRegExp> + +CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractNode &node) +{ + if (!isCached(node)) { + Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX); + evaluate.execute(); + assert(isCached(node)); + } + return this->cache[this->tree.getString(node)]; +} + +bool CGALEvaluator::isCached(const AbstractNode &node) const +{ + return this->cache.contains(this->tree.getString(node)); +} + +/*! + Modifies target by applying op to target and src: + target = target [op] src + */ +void CGALEvaluator::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op) +{ + if (target.dim != 2 && target.dim != 3) { + assert(false && "Dimension of Nef polyhedron must be 2 or 3"); + } + if (src.empty()) return; // Empty polyhedron. This can happen for e.g. square([0,0]) + if (target.dim != src.dim) return; // If someone tries to e.g. union 2d and 3d objects + + switch (op) { + case CGE_UNION: + target += src; + break; + case CGE_INTERSECTION: + target *= src; + break; + case CGE_DIFFERENCE: + target -= src; + break; + case CGE_MINKOWSKI: + target.minkowski(src); + break; + } +} + +/*! + FIXME: Let caller insert into the cache since caller might modify the result + (e.g. transform) +*/ +void CGALEvaluator::applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op) +{ + CGAL_Nef_polyhedron N; + if (this->visitedchildren[node.index()].size() > 0) { + for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); + iter != this->visitedchildren[node.index()].end(); + iter++) { + const AbstractNode *chnode = iter->first; + const string &chcacheid = iter->second; + // FIXME: Don't use deep access to modinst members + if (chnode->modinst->tag_background) continue; + assert(isCached(*chnode)); + if (N.empty()) { + N = this->cache[chcacheid].copy(); + } else { + process(N, this->cache[chcacheid], op); + } + chnode->progress_report(); + } + } + const std::string &cacheid = this->tree.getString(node); + this->cache.insert(cacheid, N); +} + +extern CGAL_Nef_polyhedron2 *convexhull2(std::list<CGAL_Nef_polyhedron2*> a); + +void CGALEvaluator::applyHull(const CgaladvNode &node) +{ + if (this->visitedchildren[node.index()].size() > 0) { + std::list<CGAL_Nef_polyhedron2*> polys; + bool all2d = true; + for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); + iter != this->visitedchildren[node.index()].end(); + iter++) { + const AbstractNode *chnode = iter->first; + const string &chcacheid = iter->second; + // FIXME: Don't use deep access to modinst members + if (chnode->modinst->tag_background) continue; + assert(isCached(*chnode)); + const CGAL_Nef_polyhedron &ch = this->cache[chcacheid]; + if (ch.dim == 2) { + polys.push_back(ch.p2); + } + else if (ch.dim == 3) { + PRINT("WARNING: hull() is not implemented yet for 3D objects!"); + all2d = false; + } + chnode->progress_report(); + } + + if (all2d) { + CGAL_Nef_polyhedron N(convexhull2(polys)); + const std::string &cacheid = this->tree.getString(node); + this->cache.insert(cacheid, N); + } + } +} + +/* + Typical visitor behavior: + o In prefix: Check if we're cached -> prune + o In postfix: Check if we're cached -> don't apply operator to children + o In postfix: addToParent() + */ + +Response CGALEvaluator::visit(State &state, const AbstractNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) applyToChildren(node, CGE_UNION); + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CGALEvaluator::visit(State &state, const AbstractIntersectionNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) applyToChildren(node, CGE_INTERSECTION); + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CGALEvaluator::visit(State &state, const CsgNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) { + CGALEvaluator::CsgOp op; + switch (node.type) { + case CSG_TYPE_UNION: + op = CGE_UNION; + break; + case CSG_TYPE_DIFFERENCE: + op = CGE_DIFFERENCE; + break; + case CSG_TYPE_INTERSECTION: + op = CGE_INTERSECTION; + break; + default: + assert(false); + } + applyToChildren(node, op); + } + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CGALEvaluator::visit(State &state, const TransformNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) { + // First union all children + applyToChildren(node, CGE_UNION); + + // Then apply transform + CGAL_Nef_polyhedron N = this->cache[this->tree.getString(node)]; + // If there is no geometry under the transform, N will be empty and of dim 0, + // just just silently ignore such nodes + 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.matrix[0], node.matrix[4], node.matrix[12], + node.matrix[1], node.matrix[5], node.matrix[13], node.matrix[15]); + + DxfData *dd = N.convertToDxfData(); + for (size_t i=0; i < dd->points.size(); i++) { + CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]); + p = t.transform(p); + dd->points[i][0] = to_double(p.x()); + dd->points[i][1] = to_double(p.y()); + } + + PolySet ps; + ps.is2d = true; + dxf_tesselate(&ps, *dd, 0, true, false, 0); + + N = evaluateCGALMesh(ps); + ps.refcount = 0; + delete dd; + } + else if (N.dim == 3) { + CGAL_Aff_transformation t( + node.matrix[0], node.matrix[4], node.matrix[ 8], node.matrix[12], + node.matrix[1], node.matrix[5], node.matrix[ 9], node.matrix[13], + node.matrix[2], node.matrix[6], node.matrix[10], node.matrix[14], node.matrix[15]); + N.p3->transform(t); + } + const std::string &cacheid = this->tree.getString(node); + this->cache.insert(cacheid, N); + } + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) { + // Apply polyset operation + PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_CGAL, &this->psevaluator); + CGAL_Nef_polyhedron N; + if (ps) { + try { + N = evaluateCGALMesh(*ps); +// print_messages_pop(); + node.progress_report(); + + ps->unlink(); + } + catch (...) { // Don't leak the PolySet on ProgressCancelException + ps->unlink(); + throw; + } + } + const std::string &cacheid = this->tree.getString(node); + this->cache.insert(cacheid, N); + } + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CGALEvaluator::visit(State &state, const CgaladvNode &node) +{ + if (state.isPrefix() && isCached(node)) return PruneTraversal; + if (state.isPostfix()) { + if (!isCached(node)) { + CGALEvaluator::CsgOp op; + switch (node.type) { + case MINKOWSKI: + op = CGE_MINKOWSKI; + applyToChildren(node, op); + break; + case GLIDE: + PRINT("WARNING: glide() is not implemented yet!"); + return PruneTraversal; + break; + case SUBDIV: + PRINT("WARNING: subdiv() is not implemented yet!"); + return PruneTraversal; + break; + case HULL: + applyHull(node); + break; + } + } + addToParent(state, node); + } + return ContinueTraversal; +} + +/*! + Adds ourself to out parent's list of traversed children. + Call this for _every_ node which affects output during the postfix traversal. +*/ +void CGALEvaluator::addToParent(const State &state, const AbstractNode &node) +{ + assert(state.isPostfix()); + this->visitedchildren.erase(node.index()); + if (state.parent()) { + this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, this->tree.getString(node))); + } +} + +#if 0 +/*! + Static function to evaluate CGAL meshes. + NB! This is just a support function used for development and debugging +*/ +CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractPolyNode &node) +{ + // FIXME: Lookup Nef polyhedron in cache. + + // print_messages_push(); + + PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_CGAL); + if (ps) { + try { + CGAL_Nef_polyhedron N = ps->evaluateCSGMesh(); + // 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 + +CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const PolySet &ps) +{ + if (ps.empty()) return CGAL_Nef_polyhedron(); + + 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 (size_t i = 0; i < ps.polygons.size(); i++) { + for (size_t j = 0; j < ps.polygons[i].size(); j++) { + double x = ps.polygons[i][j][0]; + double y = ps.polygons[i][j][1]; + if (this->grid.has(x, y)) { + int idx = this->grid.data(x, y); + // Filter away two vertices with the same index (due to grid) + // This could be done in a more general way, but we'd rather redo the entire + // grid concept instead. + if (this->polygons[this->poly_n].indexOf(idx) == -1) { + 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++; + } + } + if (this->polygons[this->poly_n].size() >= 3) { + add_edges(this->poly_n); + this->poly_n++; + } + else { + this->polygons.remove(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 = new CGAL_Nef_polyhedron2; + + 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 // not (this->is2d) + { + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + CGAL_Polyhedron *P = createPolyhedronFromPolySet(ps); + if (P) { + CGAL_Nef_polyhedron3 *N = new CGAL_Nef_polyhedron3(*P); + return CGAL_Nef_polyhedron(N); + } + } + catch (CGAL::Assertion_exception e) { + PRINTF("CGAL error: %s", e.what()); + CGAL::set_error_behaviour(old_behaviour); + return CGAL_Nef_polyhedron(); + } + } + return CGAL_Nef_polyhedron(); +} diff --git a/src/CGALEvaluator.h b/src/CGALEvaluator.h new file mode 100644 index 0000000..2453c25 --- /dev/null +++ b/src/CGALEvaluator.h @@ -0,0 +1,55 @@ +#ifndef CGALEVALUATOR_H_ +#define CGALEVALUATOR_H_ + +#include "myqhash.h" +#include "visitor.h" +#include "Tree.h" +#include "CGAL_Nef_polyhedron.h" +#include "PolySetCGALEvaluator.h" + +#include <string> +#include <map> +#include <list> + +using std::string; +using std::map; +using std::list; +using std::pair; + +class CGALEvaluator : public Visitor +{ +public: + enum CsgOp {CGE_UNION, CGE_INTERSECTION, CGE_DIFFERENCE, CGE_MINKOWSKI}; + // FIXME: If a cache is not given, we need to fix this ourselves + CGALEvaluator(QHash<string, CGAL_Nef_polyhedron> &cache, const Tree &tree) : cache(cache), tree(tree), psevaluator(*this) {} + virtual ~CGALEvaluator() {} + + virtual Response visit(State &state, const AbstractNode &node); + virtual Response visit(State &state, const AbstractIntersectionNode &node); + virtual Response visit(State &state, const CsgNode &node); + virtual Response visit(State &state, const TransformNode &node); + virtual Response visit(State &state, const AbstractPolyNode &node); + virtual Response visit(State &state, const CgaladvNode &node); + + CGAL_Nef_polyhedron evaluateCGALMesh(const AbstractNode &node); + CGAL_Nef_polyhedron evaluateCGALMesh(const PolySet &polyset); + + const Tree &getTree() const { return this->tree; } + +private: + void addToParent(const State &state, const AbstractNode &node); + bool isCached(const AbstractNode &node) const; + void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op); + void applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op); + void applyHull(const CgaladvNode &node); + + string currindent; + typedef list<pair<const AbstractNode *, string> > ChildList; + map<int, ChildList> visitedchildren; + + QHash<string, CGAL_Nef_polyhedron> &cache; + const Tree &tree; + PolySetCGALEvaluator psevaluator; +}; + +#endif diff --git a/src/cgalrenderer.cc b/src/CGALRenderer.cc index 8a9ac21..f8e914b 100644 --- a/src/cgalrenderer.cc +++ b/src/CGALRenderer.cc @@ -24,37 +24,40 @@ * */ -#include "cgalrenderer.h" +#include "CGALRenderer.h" #include "polyset.h" #include "CGAL_renderer.h" #include "dxfdata.h" #include "dxftess.h" +#include "CGAL_Nef_polyhedron.h" +#include "cgal.h" -#include "Preferences.h" +//#include "Preferences.h" CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root) { if (root.dim == 2) { - DxfData dd(root); + DxfData *dd = root.convertToDxfData(); this->polyhedron = NULL; this->polyset = new PolySet(); this->polyset->is2d = true; - dxf_tesselate(this->polyset, &dd, 0, true, false, 0); + dxf_tesselate(this->polyset, *dd, 0, true, false, 0); + delete dd; } else if (root.dim == 3) { this->polyset = NULL; this->polyhedron = new Polyhedron(); // FIXME: Make independent of Preferences - this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR, - Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(), - Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(), - Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue()); - this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR, - Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(), - Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(), - Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue()); + // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR, + // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(), + // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(), + // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue()); + // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR, + // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(), + // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(), + // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue()); - CGAL::OGL::Nef3_Converter<CGAL_Nef_polyhedron3>::convert_to_OGLPolyhedron(this->root.p3, this->polyhedron); + CGAL::OGL::Nef3_Converter<CGAL_Nef_polyhedron3>::convert_to_OGLPolyhedron(*this->root.p3, this->polyhedron); this->polyhedron->init(); } else { @@ -74,14 +77,15 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const if (this->root.dim == 2) { // Draw 2D polygons glDisable(GL_LIGHTING); - const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR); +// FIXME: const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR); + const QColor &col = QColor(0x00, 0xbf, 0x99); glColor3f(col.redF(), col.greenF(), col.blueF()); - for (int i=0; i < this->polyset->polygons.size(); i++) { + for (size_t i=0; i < this->polyset->polygons.size(); i++) { glBegin(GL_POLYGON); - for (int j=0; j < this->polyset->polygons[i].size(); j++) { - PolySet::Point p = this->polyset->polygons[i][j]; - glVertex3d(p.x, p.y, -0.1); + for (size_t j=0; j < this->polyset->polygons[i].size(); j++) { + const Vector3d &p = this->polyset->polygons[i][j]; + glVertex3d(p[0], p[1], -0.1); } glEnd(); } @@ -90,13 +94,14 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const typedef Explorer::Face_const_iterator fci_t; typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; typedef Explorer::Point Point; - Explorer E = this->root.p2.explorer(); + Explorer E = this->root.p2->explorer(); // Draw 2D edges glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glLineWidth(2); - const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR); +// FIXME: const QColor &col2 = Preferences::inst()->color(Preferences::CGAL_EDGE_2D_COLOR); + const QColor &col2 = QColor(0xff, 0x00, 0x00); glColor3f(col2.redF(), col2.greenF(), col2.blueF()); // Extract the boundary, including inner boundaries of the polygons diff --git a/src/cgalrenderer.h b/src/CGALRenderer.h index b3c1638..5f854ea 100644 --- a/src/cgalrenderer.h +++ b/src/CGALRenderer.h @@ -2,7 +2,7 @@ #define CGALRENDERER_H_ #include "renderer.h" -#include "cgal.h" +#include "CGAL_Nef_polyhedron.h" class CGALRenderer : public Renderer { @@ -11,7 +11,7 @@ public: ~CGALRenderer(); void draw(bool showfaces, bool showedges) const; -private: +public: const CGAL_Nef_polyhedron &root; class Polyhedron *polyhedron; class PolySet *polyset; diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc new file mode 100644 index 0000000..6dcbdea --- /dev/null +++ b/src/CGAL_Nef_polyhedron.cc @@ -0,0 +1,81 @@ +#include "CGAL_Nef_polyhedron.h" +#include "cgal.h" +#include "cgalutils.h" +#include "polyset.h" +#include "dxfdata.h" +#include "dxftess.h" +#include <CGAL/minkowski_sum_3.h> + +CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator+=(const CGAL_Nef_polyhedron &other) +{ + if (this->dim == 2) (*this->p2) += (*other.p2); + else if (this->dim == 3) (*this->p3) += (*other.p3); + return *this; +} + +CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator*=(const CGAL_Nef_polyhedron &other) +{ + if (this->dim == 2) (*this->p2) *= (*other.p2); + else if (this->dim == 3) (*this->p3) *= (*other.p3); + return *this; +} + +CGAL_Nef_polyhedron& CGAL_Nef_polyhedron::operator-=(const CGAL_Nef_polyhedron &other) +{ + if (this->dim == 2) (*this->p2) -= (*other.p2); + else if (this->dim == 3) (*this->p3) -= (*other.p3); + return *this; +} + +extern CGAL_Nef_polyhedron2 minkowski2(const CGAL_Nef_polyhedron2 &a, const CGAL_Nef_polyhedron2 &b); + +CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &other) +{ + if (this->dim == 2) (*this->p2) = minkowski2(*this->p2, *other.p2); + else if (this->dim == 3) (*this->p3) = CGAL::minkowski_sum_3(*this->p3, *other.p3); + return *this; +} + +int CGAL_Nef_polyhedron::weight() const +{ + if (this->dim == 2) return this->p2->explorer().number_of_vertices(); + if (this->dim == 3) return this->p3->number_of_vertices(); + return 0; +} + +/*! + Creates a new PolySet and initializes it with the data from this polyhedron + + This method is not const since convert_to_Polyhedron() wasn't const + in earlier versions of CGAL. +*/ +PolySet *CGAL_Nef_polyhedron::convertToPolyset() +{ + assert(!this->empty()); + PolySet *ps = NULL; + if (this->dim == 2) { + ps = new PolySet(); + DxfData *dd = this->convertToDxfData(); + ps->is2d = true; + dxf_tesselate(ps, *dd, 0, true, false, 0); + dxf_border_to_ps(ps, *dd); + delete dd; + } + else if (this->dim == 3) { + CGAL_Polyhedron P; + this->p3->convert_to_Polyhedron(P); + ps = createPolySetFromPolyhedron(P); + } + return ps; +} + +/*! + Deep copy +*/ +CGAL_Nef_polyhedron CGAL_Nef_polyhedron::copy() const +{ + CGAL_Nef_polyhedron copy = *this; + if (copy.p2) copy.p2 = new CGAL_Nef_polyhedron2(*copy.p2); + else if (copy.p3) copy.p3 = new CGAL_Nef_polyhedron3(*copy.p3); + return copy; +} diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h new file mode 100644 index 0000000..616ba23 --- /dev/null +++ b/src/CGAL_Nef_polyhedron.h @@ -0,0 +1,34 @@ +#ifndef CGAL_NEF_POLYHEDRON_H_ +#define CGAL_NEF_POLYHEDRON_H_ + +#ifdef ENABLE_CGAL + +#include "cgalfwd.h" + +class CGAL_Nef_polyhedron +{ +public: + CGAL_Nef_polyhedron() : dim(0), p2(0), p3(0) {} + CGAL_Nef_polyhedron(CGAL_Nef_polyhedron2 *p) : dim(2), p2(p), p3(0) {} + CGAL_Nef_polyhedron(CGAL_Nef_polyhedron3 *p) : dim(3), p2(0), p3(p) {} + ~CGAL_Nef_polyhedron() {} + + bool empty() const { return (dim == 0 || !p2 && !p3); } + CGAL_Nef_polyhedron &operator+=(const CGAL_Nef_polyhedron &other); + CGAL_Nef_polyhedron &operator*=(const CGAL_Nef_polyhedron &other); + CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other); + CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other); + CGAL_Nef_polyhedron copy() const; + int weight() const; + class PolySet *convertToPolyset(); + class DxfData *convertToDxfData() const; + + int dim; + // FIXME: Define ownership of the CGAL objects, e.g. use reference-counted smart pointers + CGAL_Nef_polyhedron2 *p2; + CGAL_Nef_polyhedron3 *p3; +}; + +#endif /* ENABLE_CGAL */ + +#endif diff --git a/src/nef2dxf.cc b/src/CGAL_Nef_polyhedron_DxfData.cc index 320d6b8..fe58636 100644 --- a/src/nef2dxf.cc +++ b/src/CGAL_Nef_polyhedron_DxfData.cc @@ -26,18 +26,21 @@ #include "dxfdata.h" #include "grid.h" +#include "CGAL_Nef_polyhedron.h" #include "cgal.h" #ifdef ENABLE_CGAL -DxfData::DxfData(const struct CGAL_Nef_polyhedron &N) +DxfData *CGAL_Nef_polyhedron::convertToDxfData() const { + assert(this->dim == 2); + DxfData *dxfdata = new DxfData(); Grid2d<int> grid(GRID_COARSE); typedef CGAL_Nef_polyhedron2::Explorer Explorer; typedef Explorer::Face_const_iterator fci_t; typedef Explorer::Halfedge_around_face_const_circulator heafcc_t; - Explorer E = N.p2.explorer(); + Explorer E = this->p2->explorer(); for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) { @@ -51,26 +54,27 @@ DxfData::DxfData(const struct CGAL_Nef_polyhedron &N) if (grid.has(x, y)) { this_point = grid.align(x, y); } else { - this_point = grid.align(x, y) = points.size(); - points.append(Point(x, y)); + this_point = grid.align(x, y) = dxfdata->points.size(); + dxfdata->points.push_back(Vector2d(x, y)); } if (first_point < 0) { - paths.append(Path()); + dxfdata->paths.push_back(DxfData::Path()); first_point = this_point; } if (this_point != last_point) { - paths.last().points.append(&points[this_point]); + dxfdata->paths.back().indices.push_back(this_point); last_point = this_point; } } } if (first_point >= 0) { - paths.last().is_closed = 1; - paths.last().points.append(&points[first_point]); + dxfdata->paths.back().is_closed = 1; + dxfdata->paths.back().indices.push_back(first_point); } } - fixup_path_direction(); + dxfdata->fixup_path_direction(); + return dxfdata; } #endif // ENABLE_CGAL diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc new file mode 100644 index 0000000..dafa6a6 --- /dev/null +++ b/src/CSGTermEvaluator.cc @@ -0,0 +1,230 @@ +#include "CSGTermEvaluator.h" +#include "visitor.h" +#include "state.h" +#include "csgterm.h" +#include "module.h" +#include "csgnode.h" +#include "transformnode.h" +#include "colornode.h" +#include "rendernode.h" +#include "cgaladvnode.h" +#include "printutils.h" +#include "PolySetEvaluator.h" + +#include <string> +#include <map> +#include <list> +#include <sstream> +#include <iostream> +#include <assert.h> + +/*! + \class CSGTermEvaluator + + A visitor responsible for creating a tree of CSGTerm nodes used for rendering + with OpenCSG. +*/ + +CSGTerm *CSGTermEvaluator::evaluateCSGTerm(const AbstractNode &node, + std::vector<CSGTerm*> &highlights, + std::vector<CSGTerm*> &background) +{ + Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX); + evaluate.execute(); + highlights = this->highlights; + background = this->background; + return this->stored_term[node.index()]; +} + +void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluator::CsgOp op) +{ + CSGTerm *t1 = NULL; + for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); + iter != this->visitedchildren[node.index()].end(); + iter++) { + const AbstractNode *chnode = *iter; + CSGTerm *t2 = this->stored_term[chnode->index()]; + this->stored_term.erase(chnode->index()); + if (t2 && !t1) { + t1 = t2; + } else if (t2 && t1) { + if (op == CSGT_UNION) { + t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); + } else if (op == CSGT_DIFFERENCE) { + t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2); + } else if (op == CSGT_INTERSECTION) { + t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); + } + } + } + if (t1 && node.modinst->tag_highlight) { + this->highlights.push_back(t1->link()); + } + if (t1 && node.modinst->tag_background) { + this->background.push_back(t1); + t1 = NULL; // don't propagate background tagged nodes + } + this->stored_term[node.index()] = t1; +} + +Response CSGTermEvaluator::visit(State &state, const AbstractNode &node) +{ + if (state.isPostfix()) { + applyToChildren(node, CSGT_UNION); + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const AbstractIntersectionNode &node) +{ + if (state.isPostfix()) { + applyToChildren(node, CSGT_INTERSECTION); + addToParent(state, node); + } + return ContinueTraversal; +} + +static CSGTerm *evaluate_csg_term_from_ps(const State &state, + vector<CSGTerm*> &highlights, + vector<CSGTerm*> &background, + PolySet *ps, + const ModuleInstantiation *modinst, + const AbstractNode &node) +{ + std::stringstream stream; + stream << node.name() << node.index(); + CSGTerm *t = new CSGTerm(ps, state.matrix(), state.color(), stream.str()); + if (modinst->tag_highlight) + highlights.push_back(t->link()); + if (modinst->tag_background) { + background.push_back(t); + return NULL; + } + return t; +} + +Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node) +{ + if (state.isPostfix()) { + CSGTerm *t1 = NULL; + PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_OPENCSG, this->psevaluator); + if (ps) { + t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, + ps, node.modinst, node); + } + this->stored_term[node.index()] = t1; + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const CsgNode &node) +{ + if (state.isPostfix()) { + CsgOp op; + switch (node.type) { + case CSG_TYPE_UNION: + op = CSGT_UNION; + break; + case CSG_TYPE_DIFFERENCE: + op = CSGT_DIFFERENCE; + break; + case CSG_TYPE_INTERSECTION: + op = CSGT_INTERSECTION; + break; + default: + assert(false); + } + applyToChildren(node, op); + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const TransformNode &node) +{ + if (state.isPrefix()) { + double m[16]; + + for (int i = 0; i < 16; i++) + { + int c_row = i%4; + int m_col = i/4; + m[i] = 0; + for (int j = 0; j < 4; j++) { + m[i] += state.matrix()[c_row + j*4] * node.matrix[m_col*4 + j]; + } + } + state.setMatrix(m); + } + if (state.isPostfix()) { + applyToChildren(node, CSGT_UNION); + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const ColorNode &node) +{ + if (state.isPrefix()) { + state.setColor(node.color); + } + if (state.isPostfix()) { + applyToChildren(node, CSGT_UNION); + addToParent(state, node); + } + return ContinueTraversal; +} + +// FIXME: If we've got CGAL support, render this node as a CGAL union into a PolySet +Response CSGTermEvaluator::visit(State &state, const RenderNode &node) +{ + if (state.isPostfix()) { + CSGTerm *t1 = NULL; + // FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this. + PolySet *ps = NULL; + if (this->psevaluator) { + ps = this->psevaluator->evaluatePolySet(node, AbstractPolyNode::RENDER_OPENCSG); + } + if (ps) { + t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, + ps, node.modinst, node); + } + this->stored_term[node.index()] = t1; + addToParent(state, node); + } + return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node) +{ + if (state.isPostfix()) { + CSGTerm *t1 = NULL; + // FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this. + PolySet *ps = NULL; + if (this->psevaluator) { + ps = this->psevaluator->evaluatePolySet(node, AbstractPolyNode::RENDER_OPENCSG); + } + if (ps) { + t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, + ps, node.modinst, node); + } + this->stored_term[node.index()] = t1; + addToParent(state, node); + } + return ContinueTraversal; +} + +/*! + Adds ourself to out parent's list of traversed children. + Call this for _every_ node which affects output during the postfix traversal. +*/ +void CSGTermEvaluator::addToParent(const State &state, const AbstractNode &node) +{ + assert(state.isPostfix()); + this->visitedchildren.erase(node.index()); + if (state.parent()) { + this->visitedchildren[state.parent()->index()].push_back(&node); + } +} diff --git a/src/CSGTermEvaluator.h b/src/CSGTermEvaluator.h new file mode 100644 index 0000000..ac22906 --- /dev/null +++ b/src/CSGTermEvaluator.h @@ -0,0 +1,56 @@ +#ifndef CSGTERMEVALUATOR_H_ +#define CSGTERMEVALUATOR_H_ + +#include <string> +#include <map> +#include <list> +#include <vector> +#include "Tree.h" +#include "visitor.h" +#include "node.h" + +using std::string; +using std::map; +using std::list; +using std::vector; + +class CSGTermEvaluator : public Visitor +{ +public: + CSGTermEvaluator(const Tree &tree, class PolySetEvaluator *psevaluator = NULL) + : tree(tree), psevaluator(psevaluator) { + } + virtual ~CSGTermEvaluator() {} + + virtual Response visit(State &state, const AbstractNode &node); + virtual Response visit(State &state, const AbstractIntersectionNode &node); + virtual Response visit(State &state, const AbstractPolyNode &node); + virtual Response visit(State &state, const CsgNode &node); + virtual Response visit(State &state, const TransformNode &node); + virtual Response visit(State &state, const ColorNode &node); + virtual Response visit(State &state, const RenderNode &node); + virtual Response visit(State &state, const CgaladvNode &node); + + class CSGTerm *evaluateCSGTerm(const AbstractNode &node, + vector<CSGTerm*> &highlights, + vector<CSGTerm*> &background); + +private: + enum CsgOp {CSGT_UNION, CSGT_INTERSECTION, CSGT_DIFFERENCE, CSGT_MINKOWSKI}; + void addToParent(const State &state, const AbstractNode &node); + void applyToChildren(const AbstractNode &node, CSGTermEvaluator::CsgOp op); + + const AbstractNode *root; + typedef list<const AbstractNode *> ChildList; + map<int, ChildList> visitedchildren; + +public: + map<int, class CSGTerm*> stored_term; // The term evaluated from each node index + + vector<CSGTerm*> highlights; + vector<CSGTerm*> background; + const Tree &tree; + class PolySetEvaluator *psevaluator; +}; + +#endif diff --git a/src/GLView.h b/src/GLView.h index 764b23b..1001754 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -20,6 +20,7 @@ class GLView : public QGLWidget public: GLView(QWidget *parent = NULL); + GLView(const QGLFormat & format, QWidget *parent = NULL); void setRenderer(class Renderer* r); #ifdef ENABLE_OPENCSG bool hasOpenCSGSupport() { return this->opencsg_support; } @@ -52,6 +53,7 @@ public: #endif private: + void init(); Renderer *renderer; bool showfaces; diff --git a/src/MainWindow.h b/src/MainWindow.h index 243a5ad..0745935 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -7,7 +7,9 @@ #include "context.h" #include "module.h" #include "polyset.h" +#include "Tree.h" #include <QPointer> +#include <vector> class MainWindow : public QMainWindow, public Ui::MainWindow { @@ -32,6 +34,7 @@ public: ModuleInstantiation root_inst; // Top level instance AbstractNode *absolute_root_node; // Result of tree evaluation AbstractNode *root_node; // Root if the root modifier (!) is used + Tree tree; class CSGTerm *root_raw_term; // Result of CSG term rendering CSGTerm *root_norm_term; // Normalized CSG products @@ -45,9 +48,9 @@ public: #endif class ThrownTogetherRenderer *thrownTogetherRenderer; - QVector<CSGTerm*> highlight_terms; + std::vector<CSGTerm*> highlight_terms; CSGChain *highlights_chain; - QVector<CSGTerm*> background_terms; + std::vector<CSGTerm*> background_terms; CSGChain *background_chain; QString last_compiled_doc; @@ -77,6 +80,7 @@ private: void compileCSG(bool procevents); bool maybeSave(); bool checkModified(); + QString dumpCSGTree(AbstractNode *root); static void consoleOutput(const QString &msg, void *userdata) { static_cast<MainWindow*>(userdata)->console->append(msg); } @@ -157,4 +161,19 @@ public slots: void autoReloadSet(bool); }; +class GuiLocker +{ +public: + GuiLocker() { + gui_locked++; + } + ~GuiLocker() { + gui_locked--; + } + static bool isLocked() { return gui_locked > 0; } + +private: + static unsigned int gui_locked; +}; + #endif diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 6548c8e..1741557 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -38,7 +38,7 @@ <property name="orientation"> <enum>Qt::Vertical</enum> </property> - <widget class="GLView" name="screen" native="true"/> + <widget class="GLView" name="glview" native="true"/> <widget class="QTextEdit" name="console"/> </widget> </item> diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc index 69013e9..f07b900 100644 --- a/src/OpenCSGRenderer.cc +++ b/src/OpenCSGRenderer.cc @@ -24,9 +24,13 @@ * */ +#include <GL/glew.h> #include "OpenCSGRenderer.h" #include "polyset.h" #include "csgterm.h" +#ifdef ENABLE_OPENCSG +# include <opencsg.h> +#endif class OpenCSGPrim : public OpenCSG::Primitive { @@ -53,11 +57,6 @@ OpenCSGRenderer::OpenCSGRenderer(CSGChain *root_chain, CSGChain *highlights_chai void OpenCSGRenderer::draw(bool showfaces, bool showedges) const { - static int glew_initialized = 0; - if (!glew_initialized) { - glew_initialized = 1; - glewInit(); - } if (this->root_chain) { GLint *shaderinfo = this->shaderinfo; if (!shaderinfo[0]) shaderinfo = NULL; @@ -76,20 +75,17 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, { std::vector<OpenCSG::Primitive*> primitives; int j = 0; - for (int i = 0;; i++) - { + for (int i = 0;; i++) { bool last = i == chain->polysets.size(); - - if (last || chain->types[i] == CSGTerm::TYPE_UNION) - { + if (last || chain->types[i] == CSGTerm::TYPE_UNION) { if (j+1 != i) { - OpenCSG::render(primitives); + OpenCSG::render(primitives); glDepthFunc(GL_EQUAL); } - if (shaderinfo) - glUseProgram(shaderinfo[0]); + if (shaderinfo) glUseProgram(shaderinfo[0]); for (; j < i; j++) { double *m = chain->matrices[j]; + double *c = chain->colors[j]; glPushMatrix(); glMultMatrixd(m); int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; @@ -97,12 +93,12 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); } else if (background) { chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); - } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0) { + } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) { // User-defined color from source - glColor4d(m[16], m[17], m[18], m[19]); + glColor4dv(c); if (shaderinfo) { - glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]); - glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); + glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]); + glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); } chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { @@ -112,8 +108,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, } glPopMatrix(); } - if (shaderinfo) - glUseProgram(0); + if (shaderinfo) glUseProgram(0); for (unsigned int k = 0; k < primitives.size(); k++) { delete primitives[k]; } @@ -121,18 +116,15 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, primitives.clear(); } - if (last) - break; + if (last) break; OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); prim->p = chain->polysets[i]; prim->m = chain->matrices[i]; prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) - prim->csgmode += 20; - else if (background) - prim->csgmode += 10; + if (highlight) prim->csgmode += 20; + else if (background) prim->csgmode += 10; primitives.push_back(prim); } } diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc new file mode 100644 index 0000000..a648587 --- /dev/null +++ b/src/PolySetCGALEvaluator.cc @@ -0,0 +1,484 @@ +#include "PolySetCGALEvaluator.h" +#include "cgal.h" +#include "polyset.h" +#include "CGALEvaluator.h" +#include "projectionnode.h" +#include "dxflinextrudenode.h" +#include "dxfrotextrudenode.h" +#include "cgaladvnode.h" +#include "rendernode.h" +#include "dxfdata.h" +#include "dxftess.h" +#include "module.h" + +#include "printutils.h" +#include "openscad.h" // get_fragments_from_r() +#include <boost/foreach.hpp> + +PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e) +{ + const string &cacheid = this->cgalevaluator.getTree().getString(node); + if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + + // Before projecting, union all children + CGAL_Nef_polyhedron sum; + BOOST_FOREACH (AbstractNode * v, node.getChildren()) { + if (v->modinst->tag_background) continue; + CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); + if (N.dim == 3) { + if (sum.empty()) sum = N.copy(); + else sum += N; + } + } + if (sum.empty()) return NULL; + + 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->cgalevaluator.evaluateCGALMesh(*cube); + cube->unlink(); + + // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED); + sum *= Ncube; + if (!sum.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 = sum.convertToPolyset(); + Grid2d<int> conversion_grid(GRID_COARSE); + for (size_t i = 0; i < ps3->polygons.size(); i++) { + for (size_t j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j][0]; + double y = ps3->polygons[i][j][1]; + double z = ps3->polygons[i][j][2]; + 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 (size_t j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j][0]; + double y = ps3->polygons[i][j][1]; + conversion_grid.align(x, y); + ps->insert_vertex(x, y); + } + next_ps3_polygon_cut_mode:; + } + ps3->unlink(); + } + else + { + if (!sum.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 = sum.convertToPolyset(); + CGAL_Nef_polyhedron np; + for (size_t i = 0; i < ps3->polygons.size(); i++) + { + int min_x_p = -1; + double min_x_val = 0; + for (size_t j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j][0]; + 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][0] - ps3->polygons[i][min_x_p][0]; + double ay = ps3->polygons[i][min_x_p1][1] - ps3->polygons[i][min_x_p][1]; + double at = atan2(ay, ax); + double bx = ps3->polygons[i][min_x_p2][0] - ps3->polygons[i][min_x_p][0]; + double by = ps3->polygons[i][min_x_p2][1] - ps3->polygons[i][min_x_p][1]; + 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 (size_t j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j][0]; + double y = ps3->polygons[i][j][1]; + CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y); + if (at > bt) + plist.push_front(p); + else + plist.push_back(p); + } + // FIXME: Should the CGAL_Nef_polyhedron2 be cached? + if (np.empty()) { + np.dim = 2; + np.p2 = new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); + } + else { + (*np.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); + } + } + DxfData *dxf = np.convertToDxfData(); + dxf_tesselate(ps, *dxf, 0, true, false, 0); + dxf_border_to_ps(ps, *dxf); + ps3->unlink(); + delete dxf; + } + +cant_project_non_simple_polyhedron: + + this->cache.insert(cacheid, new cache_entry(ps->link())); + return ps; +} + +static void add_slice(PolySet *ps, const DxfData &dxf, DxfData::Path &path, double rot1, double rot2, double h1, double h2) +{ + for (size_t j = 1; j < path.indices.size(); j++) + { + int k = j - 1; + + double jx1 = dxf.points[path.indices[j]][0] * cos(rot1*M_PI/180) + dxf.points[path.indices[j]][1] * sin(rot1*M_PI/180); + double jy1 = dxf.points[path.indices[j]][0] * -sin(rot1*M_PI/180) + dxf.points[path.indices[j]][1] * cos(rot1*M_PI/180); + + double jx2 = dxf.points[path.indices[j]][0] * cos(rot2*M_PI/180) + dxf.points[path.indices[j]][1] * sin(rot2*M_PI/180); + double jy2 = dxf.points[path.indices[j]][0] * -sin(rot2*M_PI/180) + dxf.points[path.indices[j]][1] * cos(rot2*M_PI/180); + + double kx1 = dxf.points[path.indices[k]][0] * cos(rot1*M_PI/180) + dxf.points[path.indices[k]][1] * sin(rot1*M_PI/180); + double ky1 = dxf.points[path.indices[k]][0] * -sin(rot1*M_PI/180) + dxf.points[path.indices[k]][1] * cos(rot1*M_PI/180); + + double kx2 = dxf.points[path.indices[k]][0] * cos(rot2*M_PI/180) + dxf.points[path.indices[k]][1] * sin(rot2*M_PI/180); + double ky2 = dxf.points[path.indices[k]][0] * -sin(rot2*M_PI/180) + dxf.points[path.indices[k]][1] * 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 (path.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 (path.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 (path.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 (path.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 *PolySetCGALEvaluator::evaluatePolySet(const DxfLinearExtrudeNode &node, + AbstractPolyNode::render_mode_e) +{ + const string &cacheid = this->cgalevaluator.getTree().getString(node); + if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + + DxfData *dxf; + + if (node.filename.empty()) + { + // Before extruding, union all (2D) children nodes + // to a single DxfData, then tesselate this into a PolySet + CGAL_Nef_polyhedron sum; + BOOST_FOREACH (AbstractNode * v, node.getChildren()) { + if (v->modinst->tag_background) continue; + CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); + if (N.dim != 2) { + PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!"); + } + else { + if (sum.empty()) sum = N.copy(); + else sum += N; + } + } + + if (sum.empty()) return NULL; + dxf = sum.convertToDxfData();; + } else { + dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); + } + + PolySet *ps = extrudeDxfData(node, *dxf); + this->cache.insert(cacheid, new cache_entry(ps->link())); + delete dxf; + return ps; +} + +PolySet *PolySetCGALEvaluator::extrudeDxfData(const DxfLinearExtrudeNode &node, DxfData &dxf) +{ + 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 (size_t i = 0; i < dxf.paths.size(); 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.c_str(), node.layername.c_str()); + first_open_path = false; + } + PRINTF(" %9.5f %10.5f ... %10.5f %10.5f", + dxf.points[dxf.paths[i].indices.front()][0] / node.scale + node.origin_x, + dxf.points[dxf.paths[i].indices.front()][1] / node.scale + node.origin_y, + dxf.points[dxf.paths[i].indices.back()][0] / node.scale + node.origin_x, + dxf.points[dxf.paths[i].indices.back()][1] / 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 (size_t i = 0; i < dxf.paths.size(); i++) + { + if (!dxf.paths[i].is_closed) + continue; + add_slice(ps, dxf, 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 (size_t i = 0; i < dxf.paths.size(); i++) + { + if (!dxf.paths[i].is_closed) + continue; + add_slice(ps, dxf, dxf.paths[i], 0, 0, h1, h2); + } + } + + return ps; +} + +PolySet *PolySetCGALEvaluator::evaluatePolySet(const DxfRotateExtrudeNode &node, + AbstractPolyNode::render_mode_e) +{ + const string &cacheid = this->cgalevaluator.getTree().getString(node); + if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + + DxfData *dxf; + + if (node.filename.empty()) + { + // Before extruding, union all (2D) children nodes + // to a single DxfData, then tesselate this into a PolySet + CGAL_Nef_polyhedron sum; + BOOST_FOREACH (AbstractNode * v, node.getChildren()) { + if (v->modinst->tag_background) continue; + CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); + if (N.dim != 2) { + PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!"); + } + else { + if (sum.empty()) sum = N.copy(); + else sum += N; + } + } + + if (sum.empty()) return NULL; + dxf = sum.convertToDxfData(); + } else { + dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); + } + + PolySet *ps = rotateDxfData(node, *dxf); + this->cache.insert(cacheid, new cache_entry(ps->link())); + delete dxf; + return ps; +} + +PolySet *PolySetCGALEvaluator::evaluatePolySet(const CgaladvNode &node, AbstractPolyNode::render_mode_e) +{ + CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node); + PolySet *ps = NULL; + if (!N.empty()) ps = N.convertToPolyset(); + return ps; +} + +PolySet *PolySetCGALEvaluator::evaluatePolySet(const RenderNode &node, AbstractPolyNode::render_mode_e) +{ + CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node); + PolySet *ps = NULL; + if (!N.empty()) ps = N.convertToPolyset(); + return ps; +} + +PolySet *PolySetCGALEvaluator::rotateDxfData(const DxfRotateExtrudeNode &node, DxfData &dxf) +{ + PolySet *ps = new PolySet(); + ps->convexity = node.convexity; + + for (size_t i = 0; i < dxf.paths.size(); i++) + { + double max_x = 0; + for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) { + max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]); + } + + int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa); + + double ***points; + points = new double**[fragments]; + for (int j=0; j < fragments; j++) { + points[j] = new double*[dxf.paths[i].indices.size()]; + for (size_t k=0; k < dxf.paths[i].indices.size(); k++) + points[j][k] = new double[3]; + } + + for (int j = 0; j < fragments; j++) { + double a = (j*2*M_PI) / fragments; + for (size_t k = 0; k < dxf.paths[i].indices.size(); k++) { + if (dxf.points[dxf.paths[i].indices[k]][0] == 0) { + points[j][k][0] = 0; + points[j][k][1] = 0; + } else { + points[j][k][0] = dxf.points[dxf.paths[i].indices[k]][0] * sin(a); + points[j][k][1] = dxf.points[dxf.paths[i].indices[k]][0] * cos(a); + } + points[j][k][2] = dxf.points[dxf.paths[i].indices[k]][1]; + } + } + + for (int j = 0; j < fragments; j++) { + int j1 = j + 1 < fragments ? j + 1 : 0; + for (size_t k = 0; k < dxf.paths[i].indices.size(); k++) { + int k1 = k + 1 < dxf.paths[i].indices.size() ? 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]); + } + } + } + + for (int j=0; j < fragments; j++) { + for (size_t k=0; k < dxf.paths[i].indices.size(); k++) + delete[] points[j][k]; + delete[] points[j]; + } + delete[] points; + } + + return ps; +} diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h new file mode 100644 index 0000000..f5b8665 --- /dev/null +++ b/src/PolySetCGALEvaluator.h @@ -0,0 +1,29 @@ +#ifndef POLYSETCGALEVALUATOR_H_ +#define POLYSETCGALEVALUATOR_H_ + +#include "PolySetEvaluator.h" + +/*! + This is a PolySet evaluator which uses the CGALEvaluator to support building + polysets. +*/ +class PolySetCGALEvaluator : public PolySetEvaluator +{ +public: + PolySetCGALEvaluator(class CGALEvaluator &CGALEvaluator) : + PolySetEvaluator(), cgalevaluator(CGALEvaluator) { } + virtual ~PolySetCGALEvaluator() { } + virtual PolySet *evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e); + virtual PolySet *evaluatePolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e); + virtual PolySet *evaluatePolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e); + virtual PolySet *evaluatePolySet(const CgaladvNode &node, AbstractPolyNode::render_mode_e); + virtual PolySet *evaluatePolySet(const RenderNode &node, AbstractPolyNode::render_mode_e); + +protected: + PolySet *extrudeDxfData(const DxfLinearExtrudeNode &node, class DxfData &dxf); + PolySet *rotateDxfData(const DxfRotateExtrudeNode &node, class DxfData &dxf); + + CGALEvaluator &cgalevaluator; +}; + +#endif diff --git a/src/PolySetEvaluator.cc b/src/PolySetEvaluator.cc new file mode 100644 index 0000000..56acb1d --- /dev/null +++ b/src/PolySetEvaluator.cc @@ -0,0 +1,15 @@ +#include "PolySetEvaluator.h" +#include "printutils.h" +#include "polyset.h" + +PolySetEvaluator *PolySetEvaluator::global_evaluator = NULL; + +PolySetEvaluator::cache_entry::cache_entry(PolySet *ps) : + ps(ps), msg(print_messages_stack.last()) +{ +} + +PolySetEvaluator::cache_entry::~cache_entry() +{ + ps->unlink(); +} diff --git a/src/PolySetEvaluator.h b/src/PolySetEvaluator.h new file mode 100644 index 0000000..70ec7ed --- /dev/null +++ b/src/PolySetEvaluator.h @@ -0,0 +1,41 @@ +#ifndef POLYSETEVALUATOR_H_ +#define POLYSETEVALUATOR_H_ + +#include "myqhash.h" +#include "node.h" +#include <QCache> + +class PolySetEvaluator +{ +public: + enum EvaluateMode { EVALUATE_CGAL, EVALUATE_OPENCSG }; + PolySetEvaluator() : cache(100) {} + + virtual ~PolySetEvaluator() {} + + virtual PolySet *evaluatePolySet(const class ProjectionNode &, AbstractPolyNode::render_mode_e) = 0; + virtual PolySet *evaluatePolySet(const class DxfLinearExtrudeNode &, AbstractPolyNode::render_mode_e) = 0; + virtual PolySet *evaluatePolySet(const class DxfRotateExtrudeNode &, AbstractPolyNode::render_mode_e) = 0; + virtual PolySet *evaluatePolySet(const class CgaladvNode &, AbstractPolyNode::render_mode_e) = 0; + virtual PolySet *evaluatePolySet(const class RenderNode &, AbstractPolyNode::render_mode_e) = 0; + + void clearCache() { + this->cache.clear(); + } + +protected: + + struct cache_entry { + class PolySet *ps; + QString msg; + cache_entry(PolySet *ps); + ~cache_entry(); + }; + + QCache<std::string, cache_entry> cache; + +private: + static PolySetEvaluator *global_evaluator; +}; + +#endif diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index 6e0325e..598d542 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -66,6 +66,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, if (polySetVisitMark[QPair<PolySet*,double*>(chain->polysets[i], chain->matrices[i])]++ > 0) continue; double *m = chain->matrices[i]; + double *c = chain->colors[i]; glPushMatrix(); glMultMatrixd(m); int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; @@ -91,12 +92,12 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, } else { chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); } - } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0) { - glColor4d(m[16], m[17], m[18], m[19]); + } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0) { + glColor4dv(c); chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); if (showedges) { glDisable(GL_LIGHTING); - glColor4d((m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); + glColor4d((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode)); glEnable(GL_LIGHTING); } diff --git a/src/Tree.cc b/src/Tree.cc new file mode 100644 index 0000000..a451f24 --- /dev/null +++ b/src/Tree.cc @@ -0,0 +1,30 @@ +#include "Tree.h" +#include "nodedumper.h" + +#include <assert.h> + +/*! + Returns the cached string representation of the subtree rootet by \a node. + If node is not cached, the cache will be rebuilt. +*/ +const std::string &Tree::getString(const AbstractNode &node) const +{ + assert(this->root_node); + if (!this->nodecache.contains(node)) { + NodeDumper dumper(this->nodecache, false); + Traverser trav(dumper, *this->root_node, Traverser::PRE_AND_POSTFIX); + trav.execute(); + assert(this->nodecache.contains(*this->root_node) && + "NodeDumper failed to create a cache"); + } + return this->nodecache[node]; +} + +/*! + Sets a new root. Will clear the existing cache. + */ +void Tree::setRoot(const AbstractNode *root) +{ + this->root_node = root; + this->nodecache.clear(); +} diff --git a/src/Tree.h b/src/Tree.h new file mode 100644 index 0000000..2c3f0b8 --- /dev/null +++ b/src/Tree.h @@ -0,0 +1,31 @@ +#ifndef TREE_H_ +#define TREE_H_ + +#include "nodecache.h" + +using std::string; + +/*! + For now, just an abstraction of the node tree which keeps a dump + cache based on node indices around. + + Note that since node trees don't survive a recompilation, the tree cannot either. + */ +class Tree +{ +public: + Tree(const AbstractNode *root = NULL) : root_node(root) {} + ~Tree() {} + + void setRoot(const AbstractNode *root); + const AbstractNode *root() const { return this->root_node; } + + // FIXME: Really return a reference? + const string &getString(const AbstractNode &node) const; + +private: + const AbstractNode *root_node; + mutable NodeCache nodecache; +}; + +#endif diff --git a/src/builtin.h b/src/builtin.h index d501a19..ae526f2 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -1,18 +1,20 @@ #ifndef BUILTIN_H_ #define BUILTIN_H_ -#include <QHash> +#include <string> +#include <boost/unordered_map.hpp> -extern QHash<QString, class AbstractFunction*> builtin_functions; +extern boost::unordered_map<std::string, class AbstractFunction*> builtin_functions; extern void initialize_builtin_functions(); extern void destroy_builtin_functions(); -extern QHash<QString, class AbstractModule*> builtin_modules; +extern boost::unordered_map<std::string, class AbstractModule*> builtin_modules; extern void initialize_builtin_modules(); extern void destroy_builtin_modules(); extern void register_builtin_csgops(); extern void register_builtin_transform(); +extern void register_builtin_color(); extern void register_builtin_primitives(); extern void register_builtin_surface(); extern void register_builtin_control(); @@ -14,87 +14,22 @@ #include <CGAL/Polygon_2.h> #include <CGAL/Polygon_with_holes_2.h> -typedef CGAL::Extended_cartesian<CGAL::Gmpq> CGAL_Kernel2; +typedef CGAL::Gmpq NT; +typedef CGAL::Extended_cartesian<NT> CGAL_Kernel2; typedef CGAL::Nef_polyhedron_2<CGAL_Kernel2> CGAL_Nef_polyhedron2; typedef CGAL_Kernel2::Aff_transformation_2 CGAL_Aff_transformation2; -typedef CGAL::Cartesian<CGAL::Gmpq> CGAL_Kernel3; -typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron; -typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS; -typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder; -typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3; -typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; -typedef CGAL_Nef_polyhedron3::Vector_3 CGAL_Vector; -typedef CGAL_Nef_polyhedron3::Plane_3 CGAL_Plane; -typedef CGAL_Nef_polyhedron3::Point_3 CGAL_Point; typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2; typedef CGAL::Polygon_2<CGAL_ExactKernel2> CGAL_Poly2; typedef CGAL::Polygon_with_holes_2<CGAL_ExactKernel2> CGAL_Poly2h; -struct CGAL_Nef_polyhedron -{ - int dim; - CGAL_Nef_polyhedron2 p2; - CGAL_Nef_polyhedron3 p3; - - CGAL_Nef_polyhedron() { - dim = 0; - } - - CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron2 &p) { - dim = 2; - p2 = p; - } - - CGAL_Nef_polyhedron(const CGAL_Nef_polyhedron3 &p) { - dim = 3; - p3 = p; - } - - CGAL_Nef_polyhedron& operator+=(const CGAL_Nef_polyhedron &other) { - if (other.dim == 2) { - this->p2 += other.p2; - this->dim = 2; - } - if (other.dim == 3) { - this->p3 += other.p3; - this->dim = 3; - } - return *this; - } - - CGAL_Nef_polyhedron& operator*=(const CGAL_Nef_polyhedron &other) { - if (other.dim == 2) { - this->p2 *= other.p2; - this->dim = 2; - } - if (other.dim == 3) { - this->p3 *= other.p3; - this->dim = 3; - } - return *this; - } - - CGAL_Nef_polyhedron& operator-=(const CGAL_Nef_polyhedron &other) { - if (other.dim == 2) { - this->p2 -= other.p2; - this->dim = 2; - } - if (other.dim == 3) { - this->p3 -= other.p3; - this->dim = 3; - } - return *this; - } +typedef CGAL::Cartesian<NT> CGAL_Kernel3; +typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3> CGAL_Nef_polyhedron3; +typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation; - int weight() { - if (dim == 2) - return p2.explorer().number_of_vertices(); - if (dim == 3) - return p3.number_of_vertices(); - return 0; - } -}; +typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron; +typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS; +typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder; #endif /* ENABLE_CGAL */ diff --git a/src/cgaladv.cc b/src/cgaladv.cc index d754c1b..8ffd626 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -24,25 +24,15 @@ * */ +#include "cgaladvnode.h" #include "module.h" -#include "node.h" #include "context.h" #include "builtin.h" #include "printutils.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); -extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a); -#endif - -enum cgaladv_type_e { - MINKOWSKI, - GLIDE, - SUBDIV, - HULL -}; +#include <sstream> +#include <assert.h> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope class CgaladvModule : public AbstractModule { @@ -52,38 +42,21 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class CgaladvNode : public AbstractNode -{ -public: - Value path; - QString subdiv_type; - int convexity, level; - cgaladv_type_e type; - CgaladvNode(const ModuleInstantiation *mi, cgaladv_type_e type) : AbstractNode(mi), type(type) { - convexity = 1; - } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { CgaladvNode *node = new CgaladvNode(inst, type); - QVector<QString> argnames; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; if (type == MINKOWSKI) - argnames = QVector<QString>() << "convexity"; + argnames += "convexity"; if (type == GLIDE) - argnames = QVector<QString>() << "path" << "convexity"; + argnames += "path", "convexity"; if (type == SUBDIV) - argnames = QVector<QString>() << "type" << "level" << "convexity"; + argnames += "type", "level", "convexity"; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -113,11 +86,8 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat if (node->level <= 1) node->level = 1; - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); return node; } @@ -130,129 +100,47 @@ void register_builtin_cgaladv() builtin_modules["hull"] = new CgaladvModule(HULL); } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CgaladvNode::render_cgal_nef_polyhedron() const +std::string CgaladvNode::name() 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; + switch (this->type) { + case MINKOWSKI: + return "minkowski"; + break; + case GLIDE: + return "glide"; + break; + case SUBDIV: + return "subdiv"; + break; + case HULL: + return "hull"; + break; + default: + assert(false); } - - 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->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else { - CGAL_Nef_polyhedron tmp = v->render_cgal_nef_polyhedron(); - 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: glide() is not implemented yet!"); - } - - if (type == SUBDIV) - { - PRINT("WARNING: subdiv() is not implemented yet!"); - } - - if (type == HULL) - { - std::list<CGAL_Nef_polyhedron2> polys; - bool all2d = true; - foreach(AbstractNode * v, children) { - if (v->modinst->tag_background) - continue; - N = v->render_cgal_nef_polyhedron(); - if (N.dim == 3) { - //polys.push_back(tmp.p3); - PRINT("WARNING: hull() is not implemented yet for 3D objects!"); - all2d=false; - } - if (N.dim == 2) { - polys.push_back(N.p2); - } - v->progress_report(); - } - - if (all2d) - N.p2 = convexhull2(polys); - } - - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - print_messages_pop(); - progress_report(); - - return N; } -CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +std::string CgaladvNode::toString() const { - if (type == MINKOWSKI) - return render_csg_term_from_nef(m, highlights, background, "minkowski", this->convexity); - - if (type == GLIDE) - return render_csg_term_from_nef(m, highlights, background, "glide", this->convexity); - - if (type == SUBDIV) - return render_csg_term_from_nef(m, highlights, background, "subdiv", this->convexity); - - if (type == HULL) - return render_csg_term_from_nef(m, highlights, background, "hull", this->convexity); - - return NULL; -} - -#else // ENABLE_CGAL - -CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ - PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!"); - return NULL; -} - -#endif // ENABLE_CGAL - -QString CgaladvNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - if (type == MINKOWSKI) - text.sprintf("minkowski(convexity = %d) {\n", this->convexity); - if (type == GLIDE) { - text.sprintf(", convexity = %d) {\n", this->convexity); - text = QString("glide(path = ") + this->path.dump() + text; - } - if (type == SUBDIV) - text.sprintf("subdiv(level = %d, convexity = %d) {\n", this->level, this->convexity); - if (type == HULL) - text.sprintf("hull() {\n"); - foreach (AbstractNode *v, this->children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + std::stringstream stream; + + stream << this->name(); + switch (type) { + case MINKOWSKI: + stream << "(convexity = " << this->convexity << ")"; + break; + case GLIDE: + stream << "(path = " << this->path << ", convexity = " << this->convexity << ")"; + break; + case SUBDIV: + stream << "(level = " << this->level << ", convexity = " << this->convexity << ")"; + break; + case HULL: + stream << "()"; + break; + default: + assert(false); } - return dump_cache; -} + return stream.str(); +} diff --git a/src/cgaladv_convexhull2.cc b/src/cgaladv_convexhull2.cc index 448dd4b..492df3c 100644 --- a/src/cgaladv_convexhull2.cc +++ b/src/cgaladv_convexhull2.cc @@ -29,16 +29,15 @@ #include "cgal.h" #include <CGAL/convex_hull_2.h> -extern CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a); extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p); -CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a) +CGAL_Nef_polyhedron2 *convexhull2(std::list<CGAL_Nef_polyhedron2*> a) { std::list<CGAL_Nef_polyhedron2::Point> points; - std::list<CGAL_Nef_polyhedron2>::iterator i; + std::list<CGAL_Nef_polyhedron2*>::iterator i; for (i=a.begin(); i!=a.end(); i++) { - CGAL_Poly2 ap=nef2p2(*i); + CGAL_Poly2 ap=nef2p2(**i); for (size_t j=0;j<ap.size();j++) { double x=to_double(ap[j].x()),y=to_double(ap[j].y()); CGAL_Nef_polyhedron2::Point p=CGAL_Nef_polyhedron2::Point(x,y); @@ -49,7 +48,7 @@ CGAL_Nef_polyhedron2 convexhull2(std::list<CGAL_Nef_polyhedron2> a) std::list<CGAL_Nef_polyhedron2::Point> result; CGAL::convex_hull_2(points.begin(),points.end(),std::back_inserter(result)); - return CGAL_Nef_polyhedron2(result.begin(),result.end(),CGAL_Nef_polyhedron2::INCLUDED); + return new CGAL_Nef_polyhedron2(result.begin(),result.end(),CGAL_Nef_polyhedron2::INCLUDED); } #endif diff --git a/src/cgaladv_minkowski2.cc b/src/cgaladv_minkowski2.cc index 2020ab9..f08e7d6 100644 --- a/src/cgaladv_minkowski2.cc +++ b/src/cgaladv_minkowski2.cc @@ -34,7 +34,6 @@ #include <CGAL/minkowski_sum_2.h> -extern CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b); extern CGAL_Poly2 nef2p2(CGAL_Nef_polyhedron2 p); //----------------------------------------------------------------------------- @@ -120,7 +119,7 @@ static CGAL_Nef_polyhedron2 p2nef2(CGAL_Poly2 p2) { return CGAL_Nef_polyhedron2(points.begin(), points.end(), CGAL_Nef_polyhedron2::INCLUDED); } -CGAL_Nef_polyhedron2 minkowski2(CGAL_Nef_polyhedron2 a, CGAL_Nef_polyhedron2 b) +CGAL_Nef_polyhedron2 minkowski2(const CGAL_Nef_polyhedron2 &a, const CGAL_Nef_polyhedron2 &b) { CGAL_Poly2 ap = nef2p2(a), bp = nef2p2(b); diff --git a/src/cgaladv_minkowski3.cc b/src/cgaladv_minkowski3.cc deleted file mode 100644 index f270de2..0000000 --- a/src/cgaladv_minkowski3.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - * OpenSCAD (www.openscad.org) - * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and - * Marius Kintel <marius@kintel.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * As a special exception, you have permission to link this program - * with the CGAL library and distribute executables, as long as you - * follow the requirements of the GNU GPL in regard to all of the - * software in the executable aside from CGAL. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifdef ENABLE_CGAL - -#include "node.h" -#include "printutils.h" -#include "cgal.h" - -#include <CGAL/minkowski_sum_3.h> - -extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b); - -CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b) -{ - return CGAL::minkowski_sum_3(a, b); -} - -#endif diff --git a/src/cgaladvnode.h b/src/cgaladvnode.h new file mode 100644 index 0000000..a3f8bea --- /dev/null +++ b/src/cgaladvnode.h @@ -0,0 +1,34 @@ +#ifndef CGALADVNODE_H_ +#define CGALADVNODE_H_ + +#include "node.h" +#include "visitor.h" +#include "value.h" + +enum cgaladv_type_e { + MINKOWSKI, + GLIDE, + SUBDIV, + HULL +}; + +class CgaladvNode : public AbstractNode +{ +public: + CgaladvNode(const ModuleInstantiation *mi, cgaladv_type_e type) : AbstractNode(mi), type(type) { + convexity = 1; + } + virtual ~CgaladvNode() { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const; + + Value path; + std::string subdiv_type; + int convexity, level; + cgaladv_type_e type; +}; + +#endif diff --git a/src/cgalfwd.h b/src/cgalfwd.h new file mode 100644 index 0000000..3fad608 --- /dev/null +++ b/src/cgalfwd.h @@ -0,0 +1,45 @@ +#ifndef CGALFWD_H_ +#define CGALFWD_H_ + +#ifndef CGAL_FORWARD +#include "cgal.h" +#else +#ifdef ENABLE_CGAL + +#include <memory> + +namespace CGAL { + class Gmpq; + template <class T> class Extended_cartesian; + class HDS_items; + template <class A, typename Items_, typename Mark_> class Nef_polyhedron_2; +} +typedef CGAL::Gmpq NT; +typedef CGAL::Extended_cartesian<NT> CGAL_Kernel2; +typedef CGAL::Nef_polyhedron_2<CGAL_Kernel2, CGAL::HDS_items, bool> CGAL_Nef_polyhedron2; + +namespace CGAL { + template <class T> class Cartesian; + template<class T> struct Default_items; + class SNC_indexed_items; + template <typename Kernel_, typename Items_, typename Mark_> class Nef_polyhedron_3; +} +typedef CGAL::Cartesian<NT> CGAL_Kernel3; +typedef CGAL::Nef_polyhedron_3<CGAL_Kernel3, CGAL::SNC_indexed_items, bool> CGAL_Nef_polyhedron3; + +namespace CGAL { +#ifndef CGAL_ALLOCATOR +# define CGAL_ALLOCATOR(T) std::allocator< T > +#endif + class HalfedgeDS_items_2; + template <class Traits_, class HalfedgeDSItems, class Alloc> class HalfedgeDS_default; + class Polyhedron_items_3; + template <class PolyhedronTraits_3, class PolyhedronItems_3, class T_HDS, class Alloc> class Polyhedron_3; +} +typedef CGAL::Polyhedron_3<CGAL_Kernel3, CGAL::Polyhedron_items_3, CGAL::HalfedgeDS_default<CGAL_Kernel3, CGAL::HalfedgeDS_items_2, CGAL_ALLOCATOR(int)>, CGAL_ALLOCATOR(int)> CGAL_Polyhedron; + +#endif /* ENABLE_CGAL */ + +#endif + +#endif diff --git a/src/cgalutils.cc b/src/cgalutils.cc new file mode 100644 index 0000000..79e9f1f --- /dev/null +++ b/src/cgalutils.cc @@ -0,0 +1,147 @@ +#ifdef ENABLE_CGAL + +#include "cgalutils.h" +#include "polyset.h" +#include "printutils.h" + +#include "cgal.h" +#include <CGAL/assertions_behaviour.h> +#include <CGAL/exceptions.h> + +PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p) +{ + PolySet *ps = new PolySet(); + + typedef CGAL_Polyhedron::Vertex Vertex; + typedef CGAL_Polyhedron::Vertex_const_iterator VCI; + typedef CGAL_Polyhedron::Facet_const_iterator FCI; + typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; + + for (FCI fi = p.facets_begin(); fi != p.facets_end(); ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + Vertex v1, v2, v3; + v1 = *VCI((hc++)->vertex()); + v3 = *VCI((hc++)->vertex()); + do { + v2 = v3; + v3 = *VCI((hc++)->vertex()); + double x1 = CGAL::to_double(v1.point().x()); + double y1 = CGAL::to_double(v1.point().y()); + double z1 = CGAL::to_double(v1.point().z()); + double x2 = CGAL::to_double(v2.point().x()); + double y2 = CGAL::to_double(v2.point().y()); + double z2 = CGAL::to_double(v2.point().z()); + double x3 = CGAL::to_double(v3.point().x()); + double y3 = CGAL::to_double(v3.point().y()); + double z3 = CGAL::to_double(v3.point().z()); + ps->append_poly(); + ps->append_vertex(x1, y1, z1); + ps->append_vertex(x2, y2, z2); + ps->append_vertex(x3, y3, z3); + } while (hc != hc_end); + } + return ps; +} + +#undef GEN_SURFACE_DEBUG + +class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS> +{ +public: + typedef CGAL_HDS::Vertex::Point CGALPoint; + + const PolySet &ps; + CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } + + void operator()(CGAL_HDS& hds) + { + CGAL_Polybuilder B(hds, true); + + std::vector<CGALPoint> vertices; + Grid3d<int> vertices_idx(GRID_FINE); + + for (size_t i = 0; i < ps.polygons.size(); i++) { + const PolySet::Polygon *poly = &ps.polygons[i]; + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); + if (!vertices_idx.has(p[0], p[1], p[2])) { + vertices_idx.data(p[0], p[1], p[2]) = vertices.size(); + vertices.push_back(CGALPoint(p[0], p[1], p[2])); + } + } + } + + B.begin_surface(vertices.size(), ps.polygons.size()); +#ifdef GEN_SURFACE_DEBUG + printf("=== CGAL Surface ===\n"); +#endif + + for (size_t i = 0; i < vertices.size(); i++) { + const CGALPoint &p = vertices[i]; + B.add_vertex(p); +#ifdef GEN_SURFACE_DEBUG + printf("%d: %f %f %f\n", i, p[0], p[1], p[2]); +#endif + } + + for (size_t i = 0; i < ps.polygons.size(); i++) { + const PolySet::Polygon *poly = &ps.polygons[i]; + QHash<int,int> fc; + bool facet_is_degenerated = false; + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); + int v = vertices_idx.data(p[0], p[1], p[2]); + if (fc[v]++ > 0) + facet_is_degenerated = true; + } + + if (!facet_is_degenerated) + B.begin_facet(); +#ifdef GEN_SURFACE_DEBUG + printf("F:"); +#endif + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); +#ifdef GEN_SURFACE_DEBUG + printf(" %d (%f,%f,%f)", vertices_idx.data(p[0], p[1], p[2]), p[0], p[1], p[2]); +#endif + if (!facet_is_degenerated) + B.add_vertex_to_facet(vertices_idx.data(p[0], p[1], p[2])); + } +#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_Polyhedron *createPolyhedronFromPolySet(const PolySet &ps) +{ + CGAL_Polyhedron *P = NULL; + CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); + try { + P = new CGAL_Polyhedron; + CGAL_Build_PolySet builder(ps); + P->delegate(builder); + } + catch (CGAL::Assertion_exception e) { + PRINTF("CGAL error: %s", e.what()); + CGAL::set_error_behaviour(old_behaviour); + } + return P; +} + +#endif /* ENABLE_CGAL */ + diff --git a/src/cgalutils.h b/src/cgalutils.h new file mode 100644 index 0000000..a249697 --- /dev/null +++ b/src/cgalutils.h @@ -0,0 +1,9 @@ +#ifndef CGALUTILS_H_ +#define CGALUTILS_H_ + +#include <cgalfwd.h> + +class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p); +CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps); + +#endif diff --git a/src/color.cc b/src/color.cc new file mode 100644 index 0000000..a65a8e4 --- /dev/null +++ b/src/color.cc @@ -0,0 +1,108 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and + * Marius Kintel <marius@kintel.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "colornode.h" +#include "module.h" +#include "context.h" +#include "builtin.h" +#include "printutils.h" +#include "visitor.h" +#include <sstream> +#include <assert.h> +#include <QColor> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope + +class ColorModule : public AbstractModule +{ +public: + ColorModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +using std::string; + +AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + ColorNode *node = new ColorNode(inst); + + node->color[0] = node->color[1] = node->color[2] = -1.0; + node->color[3] = 1.0; + + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; + + argnames += "c", "alpha"; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + Value v = c.lookup_variable("c"); + if (v.type == Value::VECTOR) { + for (size_t i = 0; i < 4; i++) + node->color[i] = i < v.vec.size() ? v.vec[i]->num : 1.0; + } else if (v.type == Value::STRING) { + std::string colorname = v.text; + QColor color; + color.setNamedColor(QString::fromStdString(colorname)); + if (color.isValid()) { + node->color[0] = color.redF(); + node->color[1] = color.greenF(); + node->color[2] = color.blueF(); + } else { + PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see", colorname.c_str()); + PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); + } + } + Value alpha = c.lookup_variable("alpha"); + if (alpha.type == Value::NUMBER) { + node->color[3] = alpha.num; + } + + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + + return node; +} + +string ColorNode::toString() const +{ + std::stringstream stream; + + stream << "color([" << this->color[0] << ", " << this->color[1] << ", " << this->color[2] << ", " << this->color[3] << "])"; + + return stream.str(); +} + +string ColorNode::name() const +{ + return "color"; +} + +void register_builtin_color() +{ + builtin_modules["color"] = new ColorModule(); +} diff --git a/src/colornode.h b/src/colornode.h new file mode 100644 index 0000000..b41e2a9 --- /dev/null +++ b/src/colornode.h @@ -0,0 +1,20 @@ +#ifndef COLORNODE_H_ +#define COLORNODE_H_ + +#include "node.h" +#include "visitor.h" + +class ColorNode : public AbstractNode +{ +public: + ColorNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const; + + double color[4]; +}; + +#endif diff --git a/src/context.cc b/src/context.cc index bfe9eb6..b4983f6 100644 --- a/src/context.cc +++ b/src/context.cc @@ -28,19 +28,36 @@ #include "expression.h" #include "function.h" #include "module.h" +#include "builtin.h" #include "printutils.h" #include <QFileInfo> #include <QDir> +#include <boost/foreach.hpp> -Context::Context(const Context *parent) +std::vector<const Context*> Context::ctx_stack; + +/*! + Initializes this context. Optionally initializes a context for an external library +*/ +Context::Context(const Context *parent, const Module *library) + : parent(parent), inst_p(NULL) { - this->parent = parent; - functions_p = NULL; - modules_p = NULL; - usedlibs_p = NULL; - inst_p = NULL; + ctx_stack.push_back(this); if (parent) document_path = parent->document_path; - ctx_stack.append(this); + if (library) { + this->functions_p = &library->functions; + this->modules_p = &library->modules; + this->usedlibs_p = &library->usedlibs; + for (size_t j = 0; j < library->assignments_var.size(); j++) { + this->set_variable(library->assignments_var[j], + library->assignments_expr[j]->evaluate(this)); + } + } + else { + functions_p = NULL; + modules_p = NULL; + usedlibs_p = NULL; + } } Context::~Context() @@ -48,16 +65,22 @@ Context::~Context() ctx_stack.pop_back(); } -void Context::args(const QVector<QString> &argnames, const QVector<Expression*> &argexpr, - const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) +/*! + Initialize context from argument lists (function call/module instantiation) + */ +void Context::args(const std::vector<std::string> &argnames, + const std::vector<Expression*> &argexpr, + const std::vector<std::string> &call_argnames, + const std::vector<Value> &call_argvalues) { - for (int i=0; i<argnames.size(); i++) { - set_variable(argnames[i], i < argexpr.size() && argexpr[i] ? argexpr[i]->evaluate(this->parent) : Value()); + for (size_t i=0; i<argnames.size(); i++) { + set_variable(argnames[i], i < argexpr.size() && argexpr[i] ? + argexpr[i]->evaluate(this->parent) : Value()); } - int posarg = 0; - for (int i=0; i<call_argnames.size(); i++) { - if (call_argnames[i].isEmpty()) { + size_t posarg = 0; + for (size_t i=0; i<call_argnames.size(); i++) { + if (call_argnames[i].empty()) { if (posarg < argnames.size()) set_variable(argnames[posarg++], call_argvalues[i]); } else { @@ -66,90 +89,107 @@ void Context::args(const QVector<QString> &argnames, const QVector<Expression*> } } -QVector<const Context*> Context::ctx_stack; +void Context::set_variable(const std::string &name, const Value &value) +{ + if (name[0] == '$') + this->config_variables[name] = value; + else + this->variables[name] = value; +} -void Context::set_variable(QString name, Value value) +void Context::set_constant(const std::string &name, const Value &value) { - if (name.startsWith("$")) - config_variables[name] = value; + if (this->constants.find(name) != this->constants.end()) + PRINTF("WARNING: Attempt to modify constant '%s'.",name.c_str()); else - variables[name] = value; + this->constants[name] = value; } -Value Context::lookup_variable(QString name, bool silent) const +Value Context::lookup_variable(const std::string &name, bool silent) const { - if (name.startsWith("$")) { + if (name[0] == '$') { for (int i = ctx_stack.size()-1; i >= 0; i--) { - if (ctx_stack[i]->config_variables.contains(name)) - return ctx_stack[i]->config_variables[name]; + const ValueMap &confvars = ctx_stack[i]->config_variables; + if (confvars.find(name) != confvars.end()) + return confvars.find(name)->second; } return Value(); } - if (variables.contains(name)) - return variables[name]; - if (parent) - return parent->lookup_variable(name, silent); + if (!this->parent && this->constants.find(name) != this->constants.end()) + return this->constants.find(name)->second; + if (this->variables.find(name) != this->variables.end()) + return this->variables.find(name)->second; + if (this->parent) + return this->parent->lookup_variable(name, silent); if (!silent) - PRINTA("WARNING: Ignoring unknown variable '%1'.", name); + PRINTF("WARNING: Ignoring unknown variable '%s'.", name.c_str()); return Value(); } -Value Context::evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const +Value Context::evaluate_function(const std::string &name, + const std::vector<std::string> &argnames, + const std::vector<Value> &argvalues) const { - if (functions_p && functions_p->contains(name)) - return functions_p->value(name)->evaluate(this, argnames, argvalues); - if (usedlibs_p) { - QHashIterator<QString, Module*> i(*usedlibs_p); - while (i.hasNext()) { - i.next(); - if (i.value()->functions.contains(name)) { - Module *lib = i.value(); - Context ctx(parent); - ctx.functions_p = &lib->functions; - ctx.modules_p = &lib->modules; - ctx.usedlibs_p = &lib->usedlibs; - for (int j = 0; j < lib->assignments_var.size(); j++) { - ctx.set_variable(lib->assignments_var[j], lib->assignments_expr[j]->evaluate(&ctx)); - } - return i.value()->functions.value(name)->evaluate(&ctx, argnames, argvalues); + if (this->functions_p && this->functions_p->find(name) != this->functions_p->end()) + return this->functions_p->find(name)->second->evaluate(this, argnames, argvalues); + if (this->usedlibs_p) { + BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) { + if (m.second->functions.find(name) != m.second->functions.end()) { + Context ctx(this->parent, m.second); + return m.second->functions[name]->evaluate(&ctx, argnames, argvalues); } } } - if (parent) - return parent->evaluate_function(name, argnames, argvalues); - PRINTA("WARNING: Ignoring unkown function '%1'.", name); + if (this->parent) + return this->parent->evaluate_function(name, argnames, argvalues); + PRINTF("WARNING: Ignoring unknown function '%s'.", name.c_str()); return Value(); } -AbstractNode *Context::evaluate_module(const ModuleInstantiation *inst) const +AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const { - if (modules_p && modules_p->contains(inst->modname)) - return modules_p->value(inst->modname)->evaluate(this, inst); - if (usedlibs_p) { - QHashIterator<QString, Module*> i(*usedlibs_p); - while (i.hasNext()) { - i.next(); - if (i.value()->modules.contains(inst->modname)) { - Module *lib = i.value(); - Context ctx(parent); - ctx.functions_p = &lib->functions; - ctx.modules_p = &lib->modules; - ctx.usedlibs_p = &lib->usedlibs; - for (int j = 0; j < lib->assignments_var.size(); j++) { - ctx.set_variable(lib->assignments_var[j], lib->assignments_expr[j]->evaluate(&ctx)); - } - return i.value()->modules.value(inst->modname)->evaluate(&ctx, inst); + if (this->modules_p && this->modules_p->find(inst.modname) != this->modules_p->end()) { + AbstractModule *m = this->modules_p->find(inst.modname)->second; + if (m == builtin_modules["dxf_linear_extrude"]) { + PRINTF("DEPRECATED: The dxf_linear_extrude() module will be removed in future releases. Use a linear_extrude() instead."); + } + else if (m == builtin_modules["dxf_rotate_extrude"]) { + PRINTF("DEPRECATED: The dxf_rotate_extrude() module will be removed in future releases. Use a rotate_extrude() instead."); + } + else if (m == builtin_modules["import_stl"]) { + PRINTF("DEPRECATED: The import_stl() module will be removed in future releases. Use import() instead."); + } + else if (m == builtin_modules["import_dxf"]) { + PRINTF("DEPRECATED: The import_dxf() module will be removed in future releases. Use import() instead."); + } + else if (m == builtin_modules["import_off"]) { + PRINTF("DEPRECATED: The import_off() module will be removed in future releases. Use import() instead."); + } + return m->evaluate(this, &inst); + } + if (this->usedlibs_p) { + BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) { + if (m.second->modules.find(inst.modname) != m.second->modules.end()) { + Context ctx(this->parent, m.second); + return m.second->modules[inst.modname]->evaluate(&ctx, &inst); } } } - if (parent) - return parent->evaluate_module(inst); - PRINTA("WARNING: Ignoring unkown module '%1'.", inst->modname); + if (this->parent) return this->parent->evaluate_module(inst); + PRINTF("WARNING: Ignoring unknown module '%s'.", inst.modname.c_str()); return NULL; } -QString Context::get_absolute_path(const QString &filename) const +/*! + Returns the absolute path to the given filename, unless it's empty. + */ +std::string Context::getAbsolutePath(const std::string &filename) const { - return QFileInfo(QDir(this->document_path), filename).absoluteFilePath(); + if (!filename.empty()) { + return QFileInfo(QDir(QString::fromStdString(this->document_path)), + QString::fromStdString(filename)).absoluteFilePath().toStdString(); + } + else { + return filename; + } } - diff --git a/src/context.h b/src/context.h index d5be745..f085e01 100644 --- a/src/context.h +++ b/src/context.h @@ -1,36 +1,52 @@ #ifndef CONTEXT_H_ #define CONTEXT_H_ -#include <QHash> -#include <QString> +#include <string> +#include <vector> +#include <boost/unordered_map.hpp> #include "value.h" +using boost::unordered_map; + class Context { public: - const Context *parent; - QHash<QString, Value> variables; - QHash<QString, Value> config_variables; - const QHash<QString, class AbstractFunction*> *functions_p; - const QHash<QString, class AbstractModule*> *modules_p; - const QHash<QString, class Module*> *usedlibs_p; - const class ModuleInstantiation *inst_p; - QString document_path; - - static QVector<const Context*> ctx_stack; - - Context(const Context *parent = NULL); + Context(const Context *parent = NULL, const class Module *library = NULL); ~Context(); - void args(const QVector<QString> &argnames, const QVector<class Expression*> &argexpr, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues); + void args(const std::vector<std::string> &argnames, + const std::vector<class Expression*> &argexpr, + const std::vector<std::string> &call_argnames, + const std::vector<Value> &call_argvalues); - void set_variable(QString name, Value value); - Value lookup_variable(QString name, bool silent = false) const; + void set_variable(const std::string &name, const Value &value); + void set_constant(const std::string &name, const Value &value); - QString get_absolute_path(const QString &filename) const; + Value lookup_variable(const std::string &name, bool silent = false) const; + Value evaluate_function(const std::string &name, + const std::vector<std::string> &argnames, + const std::vector<Value> &argvalues) const; + class AbstractNode *evaluate_module(const class ModuleInstantiation &inst) const; - Value evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const; - class AbstractNode *evaluate_module(const ModuleInstantiation *inst) const; + void setDocumentPath(const std::string &path) { this->document_path = path; } + std::string getAbsolutePath(const std::string &filename) const; + +public: + const Context *parent; + const unordered_map<std::string, class AbstractFunction*> *functions_p; + const unordered_map<std::string, class AbstractModule*> *modules_p; + typedef unordered_map<std::string, class Module*> ModuleContainer; + const ModuleContainer *usedlibs_p; + const ModuleInstantiation *inst_p; + + static std::vector<const Context*> ctx_stack; + +private: + typedef unordered_map<std::string, Value> ValueMap; + ValueMap constants; + ValueMap variables; + ValueMap config_variables; + std::string document_path; }; #endif diff --git a/src/control.cc b/src/control.cc index ae1d654..c39b74a 100644 --- a/src/control.cc +++ b/src/control.cc @@ -29,6 +29,7 @@ #include "context.h" #include "builtin.h" #include "printutils.h" +#include <sstream> enum control_type_e { CHILD, @@ -47,11 +48,14 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -void for_eval(AbstractNode *node, int l, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues, const QVector<ModuleInstantiation*> arg_children, const Context *arg_context) +void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l, + const std::vector<std::string> &call_argnames, + const std::vector<Value> &call_argvalues, + const Context *arg_context) { if (call_argnames.size() > l) { - QString it_name = call_argnames[l]; - Value it_values = call_argvalues[l]; + const std::string &it_name = call_argnames[l]; + const Value &it_values = call_argvalues[l]; Context c(arg_context); if (it_values.type == Value::RANGE) { double range_begin = it_values.range_begin; @@ -65,25 +69,23 @@ void for_eval(AbstractNode *node, int l, const QVector<QString> &call_argnames, if (range_step > 0 && (range_begin-range_end)/range_step < 10000) { for (double i = range_begin; i <= range_end; i += range_step) { c.set_variable(it_name, Value(i)); - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); + for_eval(node, inst, l+1, call_argnames, call_argvalues, &c); } } } else if (it_values.type == Value::VECTOR) { - for (int i = 0; i < it_values.vec.size(); i++) { + for (size_t i = 0; i < it_values.vec.size(); i++) { c.set_variable(it_name, *it_values.vec[i]); - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); + for_eval(node, inst, l+1, call_argnames, call_argvalues, &c); } } - else { - for_eval(node, l+1, call_argnames, call_argvalues, arg_children, &c); - } - } else { - foreach (ModuleInstantiation *v, arg_children) { - AbstractNode *n = v->evaluate(arg_context); - if (n != NULL) - node->children.append(n); + else if (it_values.type != Value::UNDEFINED) { + c.set_variable(it_name, it_values); + for_eval(node, inst, l+1, call_argnames, call_argvalues, &c); } + } else if (l > 0) { + std::vector<AbstractNode *> evaluatednodes = inst.evaluateChildren(arg_context); + node.children.insert(node.children.end(), evaluatednodes.begin(), evaluatednodes.end()); } } @@ -91,7 +93,7 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation { if (type == CHILD) { - int n = 0; + size_t n = 0; if (inst->argvalues.size() > 0) { double v; if (inst->argvalues[0].getnum(v)) @@ -118,52 +120,42 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation if (type == ECHO) { - QString msg = QString("ECHO: "); - for (int i = 0; i < inst->argnames.size(); i++) { - if (i > 0) - msg += QString(", "); - if (!inst->argnames[i].isEmpty()) - msg += inst->argnames[i] + QString(" = "); - msg += inst->argvalues[i].dump(); + std::stringstream msg; + msg << "ECHO: "; + for (size_t i = 0; i < inst->argnames.size(); i++) { + if (i > 0) msg << ", "; + if (!inst->argnames[i].empty()) msg << inst->argnames[i] << " = "; + msg << inst->argvalues[i]; } - PRINT(msg); + PRINTF("%s", msg.str().c_str()); } if (type == ASSIGN) { Context c(inst->ctx); - for (int i = 0; i < inst->argnames.size(); i++) { - if (!inst->argnames[i].isEmpty()) + for (size_t i = 0; i < inst->argnames.size(); i++) { + if (!inst->argnames[i].empty()) c.set_variable(inst->argnames[i], inst->argvalues[i]); } - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(&c); - if (n != NULL) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(&c); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); } if (type == FOR || type == INT_FOR) { - for_eval(node, 0, inst->argnames, inst->argvalues, inst->children, inst->ctx); + for_eval(*node, *inst, 0, inst->argnames, inst->argvalues, inst->ctx); } if (type == IF) { const IfElseModuleInstantiation *ifelse = dynamic_cast<const IfElseModuleInstantiation*>(inst); if (ifelse->argvalues.size() > 0 && ifelse->argvalues[0].type == Value::BOOL && ifelse->argvalues[0].b) { - foreach (ModuleInstantiation *v, ifelse->children) { - AbstractNode *n = v->evaluate(ifelse->ctx); - if (n != NULL) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); } else { - foreach (ModuleInstantiation *v, ifelse->else_children) { - AbstractNode *n = v->evaluate(ifelse->ctx); - if (n != NULL) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateElseChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); } } diff --git a/src/csgnode.h b/src/csgnode.h new file mode 100644 index 0000000..2e1d9fb --- /dev/null +++ b/src/csgnode.h @@ -0,0 +1,25 @@ +#ifndef CSGNODE_H_ +#define CSGNODE_H_ + +#include "node.h" +#include "visitor.h" + +enum csg_type_e { + CSG_TYPE_UNION, + CSG_TYPE_DIFFERENCE, + CSG_TYPE_INTERSECTION +}; + +class CsgNode : public AbstractNode +{ +public: + csg_type_e type; + CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const; +}; + +#endif diff --git a/src/csgops.cc b/src/csgops.cc index 8c295f9..98d68c7 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -24,22 +24,14 @@ * */ +#include "csgnode.h" + #include "module.h" -#include "node.h" #include "csgterm.h" #include "builtin.h" #include "printutils.h" -#ifdef ENABLE_CGAL -# include "cgal.h" -# include <CGAL/assertions_behaviour.h> -# include <CGAL/exceptions.h> -#endif - -enum csg_type_e { - CSG_TYPE_UNION, - CSG_TYPE_DIFFERENCE, - CSG_TYPE_INTERSECTION -}; +#include <sstream> +#include <assert.h> class CsgModule : public AbstractModule { @@ -49,128 +41,34 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class CsgNode : public AbstractNode -{ -public: - csg_type_e type; - CsgNode(const ModuleInstantiation *mi, csg_type_e type) : AbstractNode(mi), type(type) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst) const { CsgNode *node = new CsgNode(inst, type); - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); return node; } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CsgNode::render_cgal_nef_polyhedron() 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->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - if (type == CSG_TYPE_UNION) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (type == CSG_TYPE_DIFFERENCE) { - N.p2 -= v->render_cgal_nef_polyhedron().p2; - } else if (type == CSG_TYPE_INTERSECTION) { - N.p2 *= v->render_cgal_nef_polyhedron().p2; - } - } else if (N.dim == 3) { - if (type == CSG_TYPE_UNION) { - N.p3 += v->render_cgal_nef_polyhedron().p3; - } else if (type == CSG_TYPE_DIFFERENCE) { - N.p3 -= v->render_cgal_nef_polyhedron().p3; - } else if (type == CSG_TYPE_INTERSECTION) { - N.p3 *= v->render_cgal_nef_polyhedron().p3; - } - } - v->progress_report(); - } - cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); - } - catch (CGAL::Assertion_exception e) { - PRINTF("CGAL error: %s", e.what()); - } - 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 +std::string CsgNode::toString() const { - CSGTerm *t1 = NULL; - foreach (AbstractNode *v, children) { - CSGTerm *t2 = v->render_csg_term(m, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - if (type == CSG_TYPE_UNION) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } else if (type == CSG_TYPE_DIFFERENCE) { - t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2); - } else if (type == CSG_TYPE_INTERSECTION) { - t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); - } - } - } - if (t1 && modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; + return this->name() + "()"; } -QString CsgNode::dump(QString indent) const +std::string CsgNode::name() const { - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: ").arg(idx); - if (type == CSG_TYPE_UNION) - text += "union() {\n"; - if (type == CSG_TYPE_DIFFERENCE) - text += "difference() {\n"; - if (type == CSG_TYPE_INTERSECTION) - text += "intersection() {\n"; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; + switch (this->type) { + case CSG_TYPE_UNION: + return "union"; + break; + case CSG_TYPE_DIFFERENCE: + return "difference"; + break; + case CSG_TYPE_INTERSECTION: + return "intersection"; + break; + default: + assert(false); } - return dump_cache; } void register_builtin_csgops() diff --git a/src/csgterm.cc b/src/csgterm.cc index 6dd3bd5..f24a41a 100644 --- a/src/csgterm.cc +++ b/src/csgterm.cc @@ -26,16 +26,36 @@ #include "csgterm.h" #include "polyset.h" +#include <sstream> -CSGTerm::CSGTerm(PolySet *polyset, double m[20], QString label) +/*! + \class CSGTerm + + A CSGTerm is either a "primitive" or a CSG operation with two + children terms. A primitive in this context is any PolySet, which + may or may not have a subtree which is already evaluated (e.g. using + the render() module). + + */ + +/*! + \class CSGChain + + A CSGChain is just a vector of primitives, each having a CSG type associated with it. + It's created by importing a CSGTerm tree. + + */ + + +CSGTerm::CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], const std::string &label) { this->type = TYPE_PRIMITIVE; this->polyset = polyset; this->label = label; this->left = NULL; this->right = NULL; - for (int i = 0; i < 20; i++) - this->m[i] = m[i]; + for (int i = 0; i < 16; i++) this->m[i] = matrix[i]; + for (int i = 0; i < 4; i++) this->color[i] = color[i]; refcounter = 1; } @@ -157,56 +177,83 @@ void CSGTerm::unlink() } } -QString CSGTerm::dump() +std::string CSGTerm::dump() { + std::stringstream dump; + if (type == TYPE_UNION) - return QString("(%1 + %2)").arg(left->dump(), right->dump()); - if (type == TYPE_INTERSECTION) - return QString("(%1 * %2)").arg(left->dump(), right->dump()); - if (type == TYPE_DIFFERENCE) - return QString("(%1 - %2)").arg(left->dump(), right->dump()); - return label; + dump << "(" << left->dump() << " + " << right->dump() << ")"; + else if (type == TYPE_INTERSECTION) + dump << "(" << left->dump() << " * " << right->dump() << ")"; + else if (type == TYPE_DIFFERENCE) + dump << "(" << left->dump() << " - " << right->dump() << ")"; + else + dump << this->label; + + return dump.str(); } CSGChain::CSGChain() { } -void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label) +void CSGChain::add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, std::string label) { - polysets.append(polyset); - matrices.append(m); - types.append(type); - labels.append(label); + polysets.push_back(polyset); + matrices.push_back(m); + colors.push_back(color); + types.push_back(type); + labels.push_back(label); } void CSGChain::import(CSGTerm *term, CSGTerm::type_e type) { if (term->type == CSGTerm::TYPE_PRIMITIVE) { - add(term->polyset, term->m, type, term->label); + add(term->polyset, term->m, term->color, type, term->label); } else { import(term->left, type); import(term->right, term->type); } } -QString CSGChain::dump() +std::string CSGChain::dump() { - QString text; - for (int i = 0; i < types.size(); i++) + std::stringstream dump; + + for (size_t i = 0; i < types.size(); i++) { if (types[i] == CSGTerm::TYPE_UNION) { - if (i != 0) - text += "\n"; - text += "+"; + if (i != 0) dump << "\n"; + dump << "+"; } - if (types[i] == CSGTerm::TYPE_DIFFERENCE) - text += " -"; - if (types[i] == CSGTerm::TYPE_INTERSECTION) - text += " *"; - text += labels[i]; + else if (types[i] == CSGTerm::TYPE_DIFFERENCE) + dump << " -"; + else if (types[i] == CSGTerm::TYPE_INTERSECTION) + dump << " *"; + dump << labels[i]; } - text += "\n"; - return text; + dump << "\n"; + return dump.str(); } +BoundingBox CSGChain::getBoundingBox() const +{ + BoundingBox bbox; + for (size_t i=0;i<polysets.size();i++) { + if (types[i] != CSGTerm::TYPE_DIFFERENCE) { + BoundingBox psbox = polysets[i]->getBoundingBox(); + if (!psbox.isNull()) { + Eigen::Transform3d t; + // Column-major vs. Row-major + t.matrix() << + matrices[i][0], matrices[i][4], matrices[i][8], matrices[i][12], + matrices[i][1], matrices[i][5], matrices[i][9], matrices[i][13], + matrices[i][2], matrices[i][6], matrices[i][10], matrices[i][14], + matrices[i][3], matrices[i][7], matrices[i][11], matrices[i][15]; + bbox.extend(t * psbox.min()); + bbox.extend(t * psbox.max()); + } + } + } + return bbox; +} diff --git a/src/csgterm.h b/src/csgterm.h index 35d071d..2a89ef1 100644 --- a/src/csgterm.h +++ b/src/csgterm.h @@ -1,8 +1,9 @@ #ifndef CSGTERM_H_ #define CSGTERM_H_ -#include <QString> -#include <QVector> +#include <string> +#include <vector> +#include "polyset.h" class CSGTerm { @@ -15,14 +16,15 @@ public: }; type_e type; - class PolySet *polyset; - QString label; + PolySet *polyset; + std::string label; CSGTerm *left; CSGTerm *right; - double m[20]; + double m[16]; + double color[4]; int refcounter; - CSGTerm(PolySet *polyset, double m[20], QString label); + CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], const std::string &label); CSGTerm(type_e type, CSGTerm *left, CSGTerm *right); CSGTerm *normalize(); @@ -30,22 +32,25 @@ public: CSGTerm *link(); void unlink(); - QString dump(); + std::string dump(); }; class CSGChain { public: - QVector<PolySet*> polysets; - QVector<double*> matrices; - QVector<CSGTerm::type_e> types; - QVector<QString> labels; + std::vector<PolySet*> polysets; + std::vector<double*> matrices; + std::vector<double*> colors; + std::vector<CSGTerm::type_e> types; + std::vector<std::string> labels; CSGChain(); - void add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label); + void add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, std::string label); void import(CSGTerm *term, CSGTerm::type_e type = CSGTerm::TYPE_UNION); - QString dump(); + std::string dump(); + + BoundingBox getBoundingBox() const; }; #endif diff --git a/src/dxfdata.cc b/src/dxfdata.cc index 55d00b4..10f3b0c 100644 --- a/src/dxfdata.cc +++ b/src/dxfdata.cc @@ -24,24 +24,25 @@ * */ +#include "myqhash.h" #include "dxfdata.h" #include "grid.h" #include "printutils.h" -#include "openscad.h" // handle_dep() +#include "handle_dep.h" +#include "openscad.h" // get_fragments_from_r() #include <QFile> #include <QTextStream> -#include <QHash> -#include <QVector> #include "mathc99.h" #include <assert.h> +#include <boost/unordered_map.hpp> +#include <boost/foreach.hpp> +#include <algorithm> struct Line { - typedef DxfData::Point Point; - Point *p[2]; + int idx[2]; // indices into DxfData::points bool disabled; - Line(Point *p1, Point *p2) { p[0] = p1; p[1] = p2; disabled = false; } - Line() { p[0] = NULL; p[1] = NULL; disabled = false; } + Line(int i1 = -1, int i2 = -1) { idx[0] = i1; idx[1] = i2; disabled = false; } }; DxfData::DxfData() @@ -49,51 +50,53 @@ DxfData::DxfData() } /*! - Reads a layer from the given file, or all layers if layename.isNull() + Reads a layer from the given file, or all layers if layername.empty() */ -DxfData::DxfData(double fn, double fs, double fa, QString filename, QString layername, double xorigin, double yorigin, double scale) +DxfData::DxfData(double fn, double fs, double fa, + const std::string &filename, const std::string &layername, + double xorigin, double yorigin, double scale) { handle_dep(filename); // Register ourselves as a dependency - QFile f(filename); + QFile f(QString::fromStdString(filename)); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTF("WARNING: Can't open DXF file `%s'.", filename.toUtf8().data()); + PRINTF("WARNING: Can't open DXF file `%s'.", filename.c_str()); return; } QTextStream stream(&f); - Grid2d< QVector<int> > grid(GRID_COARSE); - QList<Line> lines; // Global lines - QHash< QString, QList<Line> > blockdata; // Lines in blocks + Grid2d< std::vector<int> > grid(GRID_COARSE); + std::vector<Line> lines; // Global lines + boost::unordered_map< std::string, std::vector<Line> > blockdata; // Lines in blocks bool in_entities_section = false; bool in_blocks_section = false; - QString current_block; + std::string current_block; #define ADD_LINE(_x1, _y1, _x2, _y2) do { \ double _p1x = _x1, _p1y = _y1, _p2x = _x2, _p2y = _y2; \ if (!in_entities_section && !in_blocks_section) \ break; \ if (in_entities_section && \ - !(layername.isNull() || layername == layer)) \ + !(layername.empty() || layername == layer)) \ break; \ grid.align(_p1x, _p1y); \ grid.align(_p2x, _p2y); \ - grid.data(_p1x, _p1y).append(lines.count()); \ - grid.data(_p2x, _p2y).append(lines.count()); \ + grid.data(_p1x, _p1y).push_back(lines.size()); \ + grid.data(_p2x, _p2y).push_back(lines.size()); \ if (in_entities_section) \ - lines.append( \ + lines.push_back( \ Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ - if (in_blocks_section && !current_block.isNull()) \ - blockdata[current_block].append( \ + if (in_blocks_section && !current_block.empty()) \ + blockdata[current_block].push_back( \ Line(addPoint(_p1x, _p1y), addPoint(_p2x, _p2y))); \ } while (0) - QString mode, layer, name, iddata; + std::string mode, layer, name, iddata; int dimtype = 0; double coords[7][2]; // Used by DIMENSION entities - QVector<double> xverts; - QVector<double> yverts; + std::vector<double> xverts; + std::vector<double> yverts; double radius = 0; double arc_start_angle = 0, arc_stop_angle = 0; double ellipse_start_angle = 0, ellipse_stop_angle = 0; @@ -102,8 +105,8 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye for (int j = 0; j < 2; j++) coords[i][j] = 0; - QHash<QString, int> unsupported_entities_list; - + typedef boost::unordered_map<std::string, int> EntityList; + EntityList unsupported_entities_list; // // Parse DXF file. Will populate this->points, this->dims, lines and blockdata @@ -117,7 +120,7 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye int id = id_str.toInt(&status); if (!status) { - PRINTA("WARNING: Illegal ID `%1' in `%3'.", id_str, filename); + PRINTF("WARNING: Illegal ID `%s' in `%s'.", id_str.toUtf8().data(), filename.c_str()); break; } @@ -162,16 +165,16 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye } else if (mode == "CIRCLE") { int n = get_fragments_from_r(radius, fn, fs, fa); - Point center(xverts[0], yverts[0]); + Vector2d center(xverts[0], yverts[0]); for (int i = 0; i < n; i++) { double a1 = (2*M_PI*i)/n; double a2 = (2*M_PI*(i+1))/n; - ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y, - cos(a2)*radius + center.x, sin(a2)*radius + center.y); + ADD_LINE(cos(a1)*radius + center[0], sin(a1)*radius + center[1], + cos(a2)*radius + center[0], sin(a2)*radius + center[1]); } } else if (mode == "ARC") { - Point center(xverts[0], yverts[0]); + Vector2d center(xverts[0], yverts[0]); int n = get_fragments_from_r(radius, fn, fs, fa); while (arc_start_angle > arc_stop_angle) arc_stop_angle += 360.0; @@ -181,29 +184,29 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye double a2 = ((arc_stop_angle-arc_start_angle)*(i+1))/n; a1 = (arc_start_angle + a1) * M_PI / 180.0; a2 = (arc_start_angle + a2) * M_PI / 180.0; - ADD_LINE(cos(a1)*radius + center.x, sin(a1)*radius + center.y, - cos(a2)*radius + center.x, sin(a2)*radius + center.y); + ADD_LINE(cos(a1)*radius + center[0], sin(a1)*radius + center[1], + cos(a2)*radius + center[0], sin(a2)*radius + center[1]); } } else if (mode == "ELLIPSE") { // Commented code is meant as documentation of vector math while (ellipse_start_angle > ellipse_stop_angle) ellipse_stop_angle += 2 * M_PI; // Vector2d center(xverts[0], yverts[0]); - Point center(xverts[0], yverts[0]); + Vector2d center(xverts[0], yverts[0]); // Vector2d ce(xverts[1], yverts[1]); - Point ce(xverts[1], yverts[1]); + Vector2d ce(xverts[1], yverts[1]); // double r_major = ce.length(); - double r_major = sqrt(ce.x*ce.x + ce.y*ce.y); + double r_major = sqrt(ce[0]*ce[0] + ce[1]*ce[1]); // double rot_angle = ce.angle(); double rot_angle; { // double dot = ce.dot(Vector2d(1.0, 0.0)); - double dot = ce.x; + double dot = ce[0]; double cosval = dot / r_major; if (cosval > 1.0) cosval = 1.0; if (cosval < -1.0) cosval = -1.0; rot_angle = acos(cosval); - if (ce.y < 0.0) rot_angle = 2 * M_PI - rot_angle; + if (ce[1] < 0.0) rot_angle = 2 * M_PI - rot_angle; } // the ratio stored in 'radius; due to the parser code not checking entity type @@ -212,24 +215,24 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye int n = get_fragments_from_r(r_major, fn, fs, fa); n = (int)ceil(n * sweep_angle / (2 * M_PI)); // Vector2d p1; - Point p1; + Vector2d p1; for (int i=0;i<=n;i++) { double a = (ellipse_start_angle + sweep_angle*i/n); // Vector2d p2(cos(a)*r_major, sin(a)*r_minor); - Point p2(cos(a)*r_major, sin(a)*r_minor); + Vector2d p2(cos(a)*r_major, sin(a)*r_minor); // p2.rotate(rot_angle); - Point p2_rot(cos(rot_angle)*p2.x - sin(rot_angle)*p2.y, - sin(rot_angle)*p2.x + cos(rot_angle)*p2.y); + Vector2d p2_rot(cos(rot_angle)*p2[0] - sin(rot_angle)*p2[1], + sin(rot_angle)*p2[0] + cos(rot_angle)*p2[1]); // p2 += center; - p2_rot.x += center.x; - p2_rot.y += center.y; + p2_rot[0] += center[0]; + p2_rot[1] += center[1]; if (i > 0) { // ADD_LINE(p1[0], p1[1], p2[0], p2[1]); - ADD_LINE(p1.x, p1.y, p2_rot.x, p2_rot.y); + ADD_LINE(p1[0], p1[1], p2_rot[0], p2_rot[1]); } // p1 = p2; - p1.x = p2_rot.x; - p1.y = p2_rot.y; + p1[0] = p2_rot[0]; + p1[1] = p2_rot[1]; } } else if (mode == "INSERT") { @@ -238,10 +241,10 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye int n = blockdata[iddata].size(); for (int i = 0; i < n; i++) { double a = arc_start_angle * M_PI / 180.0; - double lx1 = blockdata[iddata][i].p[0]->x * ellipse_start_angle; - double ly1 = blockdata[iddata][i].p[0]->y * ellipse_stop_angle; - double lx2 = blockdata[iddata][i].p[1]->x * ellipse_start_angle; - double ly2 = blockdata[iddata][i].p[1]->y * ellipse_stop_angle; + double lx1 = this->points[blockdata[iddata][i].idx[0]][0] * ellipse_start_angle; + double ly1 = this->points[blockdata[iddata][i].idx[0]][1] * ellipse_stop_angle; + double lx2 = this->points[blockdata[iddata][i].idx[1]][0] * ellipse_start_angle; + double ly2 = this->points[blockdata[iddata][i].idx[1]][1] * ellipse_stop_angle; double px1 = (cos(a)*lx1 - sin(a)*ly1) * scale + xverts[0]; double py1 = (sin(a)*lx1 + cos(a)*ly1) * scale + yverts[0]; double px2 = (cos(a)*lx2 - sin(a)*ly2) * scale + xverts[0]; @@ -250,32 +253,32 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye } } else if (mode == "DIMENSION" && - (layername.isNull() || layername == layer)) { - this->dims.append(Dim()); - this->dims.last().type = dimtype; + (layername.empty() || layername == layer)) { + this->dims.push_back(Dim()); + this->dims.back().type = dimtype; for (int i = 0; i < 7; i++) for (int j = 0; j < 2; j++) - this->dims.last().coords[i][j] = coords[i][j]; - this->dims.last().angle = arc_start_angle; - this->dims.last().length = radius; - this->dims.last().name = name; + this->dims.back().coords[i][j] = coords[i][j]; + this->dims.back().angle = arc_start_angle; + this->dims.back().length = radius; + this->dims.back().name = name; } else if (mode == "BLOCK") { current_block = iddata; } else if (mode == "ENDBLK") { - current_block = QString(); + current_block.erase(); } else if (mode == "ENDSEC") { } else if (in_blocks_section || (in_entities_section && - (layername.isNull() || layername == layer))) { + (layername.empty() || layername == layer))) { unsupported_entities_list[mode]++; } - mode = data; - layer = QString(); - name = QString(); - iddata = QString(); + mode = data.toStdString(); + layer.erase(); + name.erase(); + iddata.erase(); dimtype = 0; for (int i = 0; i < 7; i++) for (int j = 0; j < 2; j++) @@ -289,37 +292,37 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye } break; case 1: - name = data; + name = data.toStdString(); break; case 2: - iddata = data; + iddata = data.toStdString(); break; case 8: - layer = data; + layer = data.toStdString(); break; case 10: if (in_blocks_section) - xverts.append((data.toDouble())); + xverts.push_back((data.toDouble())); else - xverts.append((data.toDouble() - xorigin) * scale); + xverts.push_back((data.toDouble() - xorigin) * scale); break; case 11: if (in_blocks_section) - xverts.append((data.toDouble())); + xverts.push_back((data.toDouble())); else - xverts.append((data.toDouble() - xorigin) * scale); + xverts.push_back((data.toDouble() - xorigin) * scale); break; case 20: if (in_blocks_section) - yverts.append((data.toDouble())); + yverts.push_back((data.toDouble())); else - yverts.append((data.toDouble() - yorigin) * scale); + yverts.push_back((data.toDouble() - yorigin) * scale); break; case 21: if (in_blocks_section) - yverts.append((data.toDouble())); + yverts.push_back((data.toDouble())); else - yverts.append((data.toDouble() - yorigin) * scale); + yverts.push_back((data.toDouble() - yorigin) * scale); break; case 40: // CIRCLE, ARC: radius @@ -355,40 +358,40 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye } } - QHashIterator<QString, int> i(unsupported_entities_list); - while (i.hasNext()) { - i.next(); - if (layername.isNull()) { - PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in `%3'.", - i.key(), QString::number(i.value()), filename); + BOOST_FOREACH(const EntityList::value_type &i, unsupported_entities_list) { + if (layername.empty()) { + PRINTF("WARNING: Unsupported DXF Entity `%s' (%x) in `%s'.", + i.first.c_str(), i.second, filename.c_str()); } else { - PRINTA("WARNING: Unsupported DXF Entity `%1' (%2x) in layer `%3' of `%4'.", - i.key(), QString::number(i.value()), layername, filename); + PRINTF("WARNING: Unsupported DXF Entity `%s' (%x) in layer `%s' of `%s'.", + i.first.c_str(), i.second, layername.c_str(), filename.c_str()); } } // Extract paths from parsed data - QHash<int, int> enabled_lines; - for (int i = 0; i < lines.count(); i++) { + typedef boost::unordered_map<int, int> LineMap; + LineMap enabled_lines; + for (size_t i = 0; i < lines.size(); i++) { enabled_lines[i] = i; } // extract all open paths - while (enabled_lines.count() > 0) + while (enabled_lines.size() > 0) { int current_line, current_point; - foreach (int i, enabled_lines) { + BOOST_FOREACH(const LineMap::value_type &l, enabled_lines) { + int idx = l.second; for (int j = 0; j < 2; j++) { - QVector<int> *lv = &grid.data(lines[i].p[j]->x, lines[i].p[j]->y); - for (int ki = 0; ki < lv->count(); ki++) { + std::vector<int> *lv = &grid.data(this->points[lines[idx].idx[j]][0], this->points[lines[idx].idx[j]][1]); + for (size_t ki = 0; ki < lv->size(); ki++) { int k = lv->at(ki); - if (k == i || lines[k].disabled) + if (k == idx || lines[k].disabled) continue; goto next_open_path_j; } - current_line = i; + current_line = idx; current_point = j; goto create_open_path; next_open_path_j:; @@ -398,26 +401,26 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye break; create_open_path: - this->paths.append(Path()); - Path *this_path = &this->paths.last(); + this->paths.push_back(Path()); + Path *this_path = &this->paths.back(); - this_path->points.append(lines[current_line].p[current_point]); + this_path->indices.push_back(lines[current_line].idx[current_point]); while (1) { - this_path->points.append(lines[current_line].p[!current_point]); - Point *ref_point = lines[current_line].p[!current_point]; + this_path->indices.push_back(lines[current_line].idx[!current_point]); + const Vector2d &ref_point = this->points[lines[current_line].idx[!current_point]]; lines[current_line].disabled = true; - enabled_lines.remove(current_line); - QVector<int> *lv = &grid.data(ref_point->x, ref_point->y); - for (int ki = 0; ki < lv->count(); ki++) { + enabled_lines.erase(current_line); + std::vector<int> *lv = &grid.data(ref_point[0], ref_point[1]); + for (size_t ki = 0; ki < lv->size(); ki++) { int k = lv->at(ki); if (lines[k].disabled) continue; - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { + if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[0]][0], this->points[lines[k].idx[0]][1])) { current_line = k; current_point = 0; goto found_next_line_in_open_path; } - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { + if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[1]][0], this->points[lines[k].idx[1]][1])) { current_line = k; current_point = 1; goto found_next_line_in_open_path; @@ -429,31 +432,31 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye } // extract all closed paths - while (enabled_lines.count() > 0) + while (enabled_lines.size() > 0) { - int current_line = enabled_lines.begin().value(), current_point = 0; + int current_line = enabled_lines.begin()->second, current_point = 0; - this->paths.append(Path()); - Path *this_path = &this->paths.last(); + this->paths.push_back(Path()); + Path *this_path = &this->paths.back(); this_path->is_closed = true; - this_path->points.append(lines[current_line].p[current_point]); + this_path->indices.push_back(lines[current_line].idx[current_point]); while (1) { - this_path->points.append(lines[current_line].p[!current_point]); - Point *ref_point = lines[current_line].p[!current_point]; + this_path->indices.push_back(lines[current_line].idx[!current_point]); + const Vector2d &ref_point = this->points[lines[current_line].idx[!current_point]]; lines[current_line].disabled = true; - enabled_lines.remove(current_line); - QVector<int> *lv = &grid.data(ref_point->x, ref_point->y); - for (int ki = 0; ki < lv->count(); ki++) { + enabled_lines.erase(current_line); + std::vector<int> *lv = &grid.data(ref_point[0], ref_point[1]); + for (size_t ki = 0; ki < lv->size(); ki++) { int k = lv->at(ki); if (lines[k].disabled) continue; - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[0]->x, lines[k].p[0]->y)) { + if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[0]][0], this->points[lines[k].idx[0]][1])) { current_line = k; current_point = 0; goto found_next_line_in_closed_path; } - if (grid.eq(ref_point->x, ref_point->y, lines[k].p[1]->x, lines[k].p[1]->y)) { + if (grid.eq(ref_point[0], ref_point[1], this->points[lines[k].idx[1]][0], this->points[lines[k].idx[1]][1])) { current_line = k; current_point = 1; goto found_next_line_in_closed_path; @@ -468,10 +471,10 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye #if 0 printf("----- DXF Data -----\n"); - for (int i = 0; i < this->paths.count(); i++) { + for (int i = 0; i < this->paths.size(); i++) { printf("Path %d (%s):\n", i, this->paths[i].is_closed ? "closed" : "open"); - for (int j = 0; j < this->paths[i].points.count(); j++) - printf(" %f %f\n", this->paths[i].points[j]->x, this->paths[i].points[j]->y); + for (int j = 0; j < this->paths[i].points.size(); j++) + printf(" %f %f\n", (*this->paths[i].points[j])[0], (*this->paths[i].points[j])[1]); } printf("--------------------\n"); fflush(stdout); @@ -484,26 +487,26 @@ DxfData::DxfData(double fn, double fs, double fa, QString filename, QString laye */ void DxfData::fixup_path_direction() { - for (int i = 0; i < this->paths.count(); i++) { + for (size_t i = 0; i < this->paths.size(); i++) { if (!this->paths[i].is_closed) break; this->paths[i].is_inner = true; - double min_x = this->paths[i].points[0]->x; + double min_x = this->points[this->paths[i].indices[0]][0]; int min_x_point = 0; - for (int j = 1; j < this->paths[i].points.count(); j++) { - if (this->paths[i].points[j]->x < min_x) { - min_x = this->paths[i].points[j]->x; + for (size_t j = 1; j < this->paths[i].indices.size(); j++) { + if (this->points[this->paths[i].indices[j]][0] < min_x) { + min_x = this->points[this->paths[i].indices[j]][0]; min_x_point = j; } } // rotate points if the path is in non-standard rotation int b = min_x_point; - int a = b == 0 ? this->paths[i].points.count() - 2 : b - 1; - int c = b == this->paths[i].points.count() - 1 ? 1 : b + 1; - double ax = this->paths[i].points[a]->x - this->paths[i].points[b]->x; - double ay = this->paths[i].points[a]->y - this->paths[i].points[b]->y; - double cx = this->paths[i].points[c]->x - this->paths[i].points[b]->x; - double cy = this->paths[i].points[c]->y - this->paths[i].points[b]->y; + int a = b == 0 ? this->paths[i].indices.size() - 2 : b - 1; + int c = b == this->paths[i].indices.size() - 1 ? 1 : b + 1; + double ax = this->points[this->paths[i].indices[a]][0] - this->points[this->paths[i].indices[b]][0]; + double ay = this->points[this->paths[i].indices[a]][1] - this->points[this->paths[i].indices[b]][1]; + double cx = this->points[this->paths[i].indices[c]][0] - this->points[this->paths[i].indices[b]][0]; + double cy = this->points[this->paths[i].indices[c]][1] - this->points[this->paths[i].indices[b]][1]; #if 0 printf("Rotate check:\n"); printf(" a/b/c indices = %d %d %d\n", a, b, c); @@ -512,15 +515,17 @@ void DxfData::fixup_path_direction() #endif // FIXME: atan2() usually takes y,x. This variant probably makes the path clockwise.. if (atan2(ax, ay) < atan2(cx, cy)) { - for (int j = 0; j < this->paths[i].points.count()/2; j++) - this->paths[i].points.swap(j, this->paths[i].points.count()-1-j); + std::reverse(this->paths[i].indices.begin(), this->paths[i].indices.end()); } } } -DxfData::Point *DxfData::addPoint(double x, double y) +/*! + Adds a vertex and returns the index into DxfData::points + */ +int DxfData::addPoint(double x, double y) { - this->points.append(Point(x, y)); - return &this->points.last(); + this->points.push_back(Vector2d(x, y)); + return this->points.size()-1; } diff --git a/src/dxfdata.h b/src/dxfdata.h index d59ff5a..bada031 100644 --- a/src/dxfdata.h +++ b/src/dxfdata.h @@ -1,19 +1,16 @@ #ifndef DXFDATA_H_ #define DXFDATA_H_ -#include <QList> -#include <QString> +#include <vector> +#include <Eigen/Dense> + +using Eigen::Vector2d; class DxfData { public: - struct Point { - double x, y; - Point() : x(0), y(0) { } - Point(double x, double y) : x(x), y(y) { } - }; struct Path { - QList<Point*> points; + std::vector<int> indices; // indices into DxfData::points bool is_closed, is_inner; Path() : is_closed(false), is_inner(false) { } }; @@ -22,7 +19,7 @@ public: double coords[7][2]; double angle; double length; - QString name; + std::string name; Dim() { for (int i = 0; i < 7; i++) for (int j = 0; j < 2; j++) @@ -33,19 +30,17 @@ public: } }; - QList<Point> points; - QList<Path> paths; - QList<Dim> dims; + std::vector<Vector2d> points; + std::vector<Path> paths; + std::vector<Dim> dims; DxfData(); - DxfData(double fn, double fs, double fa, QString filename, QString layername = QString(), double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0); -#ifdef ENABLE_CGAL - DxfData(const struct CGAL_Nef_polyhedron &N); -#endif + DxfData(double fn, double fs, double fa, + const std::string &filename, const std::string &layername = "", + double xorigin = 0.0, double yorigin = 0.0, double scale = 1.0); - Point *addPoint(double x, double y); + int addPoint(double x, double y); -private: void fixup_path_direction(); }; diff --git a/src/dxfdim.cc b/src/dxfdim.cc index 4c53d86..7b016f1 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -33,25 +33,25 @@ #include "context.h" #include "mathc99.h" -#include <QHash> #include <QDateTime> #include <QFileInfo> +#include <sstream> -QHash<QString,Value> dxf_dim_cache; -QHash<QString,Value> dxf_cross_cache; +boost::unordered_map<std::string,Value> dxf_dim_cache; +boost::unordered_map<std::string,Value> dxf_cross_cache; -Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args) +Value builtin_dxf_dim(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args) { - QString filename; - QString layername; - QString name; + std::string filename; + std::string layername; + std::string name; double xorigin = 0; double yorigin = 0; double scale = 1; - for (int i = 0; i < argnames.count() && i < args.count(); i++) { + for (size_t i = 0; i < argnames.size() && i < args.size(); i++) { if (argnames[i] == "file") - filename = ctx->get_absolute_path(args[i].text); + filename = ctx->getAbsolutePath(args[i].text); if (argnames[i] == "layer") layername = args[i].text; if (argnames[i] == "origin") @@ -62,19 +62,21 @@ Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, cons name = args[i].text; } - QFileInfo fileInfo(filename); + QFileInfo fileInfo(QString::fromStdString(filename)); - QString key = filename + "|" + layername + "|" + name + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + - "|" + QString::number(scale) + "|" + QString::number(fileInfo.lastModified().toTime_t()) + "|" + QString::number(fileInfo.size()); - - if (dxf_dim_cache.contains(key)) - return dxf_dim_cache[key]; + std::stringstream keystream; + keystream << filename << "|" << layername << "|" << name << "|" << xorigin + << "|" << yorigin <<"|" << scale << "|" << fileInfo.lastModified().toTime_t() + << "|" << fileInfo.size(); + std::string key = keystream.str(); + if (dxf_dim_cache.find(key) != dxf_dim_cache.end()) + return dxf_dim_cache.find(key)->second; DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); - for (int i = 0; i < dxf.dims.count(); i++) + for (size_t i = 0; i < dxf.dims.size(); i++) { - if (!name.isNull() && dxf.dims[i].name != name) + if (!name.empty() && dxf.dims[i].name != name) continue; DxfData::Dim *d = &dxf.dims[i]; @@ -114,26 +116,26 @@ Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, cons return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); } - PRINTA("WARNING: Dimension `%1' in `%2', layer `%3' has unsupported type!", name, filename, layername); + PRINTF("WARNING: Dimension `%s' in `%s', layer `%s' has unsupported type!", name.c_str(), filename.c_str(), layername.c_str()); return Value(); } - PRINTA("WARNING: Can't find dimension `%1' in `%2', layer `%3'!", name, filename, layername); + PRINTF("WARNING: Can't find dimension `%s' in `%s', layer `%s'!", name.c_str(), filename.c_str(), layername.c_str()); return Value(); } -Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args) +Value builtin_dxf_cross(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args) { - QString filename; - QString layername; + std::string filename; + std::string layername; double xorigin = 0; double yorigin = 0; double scale = 1; - for (int i = 0; i < argnames.count() && i < args.count(); i++) { + for (size_t i = 0; i < argnames.size() && i < args.size(); i++) { if (argnames[i] == "file") - filename = ctx->get_absolute_path(args[i].text); + filename = ctx->getAbsolutePath(args[i].text); if (argnames[i] == "layer") layername = args[i].text; if (argnames[i] == "origin") @@ -142,25 +144,28 @@ Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, co args[i].getnum(scale); } - QFileInfo fileInfo(filename); + QFileInfo fileInfo(QString::fromStdString(filename)); - QString key = filename + "|" + layername + "|" + QString::number(xorigin) + "|" + QString::number(yorigin) + - "|" + QString::number(scale) + "|" + QString::number(fileInfo.lastModified().toTime_t()) + "|" + QString::number(fileInfo.size()); + std::stringstream keystream; + keystream << filename << "|" << layername << "|" << xorigin << "|" << yorigin + << "|" << scale << "|" << fileInfo.lastModified().toTime_t() + << "|" << fileInfo.size(); + std::string key = keystream.str(); - if (dxf_cross_cache.contains(key)) - return dxf_cross_cache[key]; + if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) + return dxf_cross_cache.find(key)->second; DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); double coords[4][2]; - for (int i = 0, j = 0; i < dxf.paths.count(); i++) { - if (dxf.paths[i].points.count() != 2) + for (size_t i = 0, j = 0; i < dxf.paths.size(); i++) { + if (dxf.paths[i].indices.size() != 2) continue; - coords[j][0] = dxf.paths[i].points[0]->x; - coords[j++][1] = dxf.paths[i].points[0]->y; - coords[j][0] = dxf.paths[i].points[1]->x; - coords[j++][1] = dxf.paths[i].points[1]->y; + coords[j][0] = dxf.points[dxf.paths[i].indices[0]][0]; + coords[j++][1] = dxf.points[dxf.paths[i].indices[0]][1]; + coords[j][0] = dxf.points[dxf.paths[i].indices[1]][0]; + coords[j++][1] = dxf.points[dxf.paths[i].indices[1]][1]; if (j == 4) { double x1 = coords[0][0], y1 = coords[0][1]; @@ -176,13 +181,13 @@ Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, co double y = y1 + ua*(y2 - y1); Value ret; ret.type = Value::VECTOR; - ret.vec.append(new Value(x)); - ret.vec.append(new Value(y)); + ret.append(new Value(x)); + ret.append(new Value(y)); return dxf_cross_cache[key] = ret; } } - PRINTA("WARNING: Can't find cross in `%1', layer `%2'!", filename, layername); + PRINTF("WARNING: Can't find cross in `%s', layer `%s'!", filename.c_str(), layername.c_str()); return Value(); } diff --git a/src/dxfdim.h b/src/dxfdim.h index 9686760..bd42109 100644 --- a/src/dxfdim.h +++ b/src/dxfdim.h @@ -1,10 +1,10 @@ #ifndef DXFDIM_H_ #define DXFDIM_H_ -#include <QHash> +#include <boost/unordered_map.hpp> #include "value.h" -extern QHash<QString,Value> dxf_cross_cache; -extern QHash<QString,Value> dxf_dim_cache; +extern boost::unordered_map<std::string,Value> dxf_dim_cache; +extern boost::unordered_map<std::string,Value> dxf_cross_cache; #endif diff --git a/src/dxflinextrude.cc b/src/dxflinextrude.cc index 9661066..fd37ffa 100644 --- a/src/dxflinextrude.cc +++ b/src/dxflinextrude.cc @@ -24,8 +24,9 @@ * */ +#include "dxflinextrudenode.h" + #include "module.h" -#include "node.h" #include "context.h" #include "printutils.h" #include "builtin.h" @@ -33,12 +34,14 @@ #include "dxftess.h" #include "polyset.h" #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h" #include "openscad.h" // get_fragments_from_r() -#include <QApplication> -#include <QTime> -#include <QProgressDialog> -#include <QDateTime> +#include <sstream> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope + #include <QFileInfo> class DxfLinearExtrudeModule : public AbstractModule @@ -48,30 +51,13 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class DxfLinearExtrudeNode : public AbstractPolyNode -{ -public: - int convexity, slices; - double fn, fs, fa, height, twist; - double origin_x, origin_y, scale; - bool center, has_twist; - QString filename, layername; - DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { - convexity = slices = 0; - fn = fs = fa = height = twist = 0; - origin_x = origin_y = scale = 0; - center = has_twist = false; - } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { DxfLinearExtrudeNode *node = new DxfLinearExtrudeNode(inst); - QVector<QString> argnames = QVector<QString>() << "file" << "layer" << "height" << "origin" << "scale" << "center" << "twist" << "slices"; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "file", "layer", "height", "origin", "scale", "center", "twist", "slices"; + std::vector<Expression*> argexpr; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -90,10 +76,10 @@ AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleI Value twist = c.lookup_variable("twist", true); Value slices = c.lookup_variable("slices", true); - if(!file.text.isNull()) - node->filename = c.get_absolute_path(file.text); - else - node->filename = file.text; + if (!file.text.empty()) { + PRINTF("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead."); + node->filename = c.getAbsolutePath(file.text); + } node->layername = layer.text; node->height = height.num; @@ -124,12 +110,9 @@ AbstractNode *DxfLinearExtrudeModule::evaluate(const Context *ctx, const ModuleI node->has_twist = true; } - if (node->filename.isEmpty()) { - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } + if (node->filename.empty()) { + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); } return node; @@ -141,203 +124,43 @@ 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) -{ - 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 *DxfLinearExtrudeNode::render_polyset(render_mode_e) const +PolySet *DxfLinearExtrudeNode::evaluate_polyset(render_mode_e mode, + PolySetEvaluator *evaluator) const { - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return PolySet::ps_cache[key]->ps->link(); + if (!evaluator) { + PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); + PolySet *ps = new PolySet(); + ps->is2d = true; + return ps; } 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->render_cgal_nef_polyhedron().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 = evaluator->evaluatePolySet(*this, mode); - 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_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); print_messages_pop(); - delete dxf; return ps; } -QString DxfLinearExtrudeNode::dump(QString indent) const +std::string DxfLinearExtrudeNode::toString() const { - if (dump_cache.isEmpty()) { - QString text; - QFileInfo fileInfo(filename); - text.sprintf("linear_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "height = %g, origin = [ %g %g ], scale = %g, center = %s, convexity = %d", - filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(), - (int)fileInfo.size(), layername.toAscii().data(), height, origin_x, - origin_y, scale, center ? "true" : "false", convexity); - if (has_twist) { - QString t2; - t2.sprintf(", twist = %g, slices = %d", twist, slices); - text += t2; - } - QString t3; - t3.sprintf(", $fn = %g, $fa = %g, $fs = %g) {\n", fn, fa, fs); - text += t3; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + std::stringstream stream; + + stream << this->name() << "(" + "file = \"" << this->filename << "\", " + "cache = \"" << QFileInfo(QString::fromStdString(this->filename)) << "\", " + "layer = \"" << this->layername << "\", " + "height = " << std::dec << this->height << ", " + "origin = [ " << this->origin_x << " " << this->origin_y << " ], " + "scale = " << this->scale << ", " + "center = " << (this->center?"true":"false") << ", " + "convexity = " << this->convexity; + + if (this->has_twist) { + stream << ", twist = " << this->twist << ", slices = " << this->slices; } - return dump_cache; + stream << ", $fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; + + return stream.str(); } - diff --git a/src/dxflinextrudenode.h b/src/dxflinextrudenode.h new file mode 100644 index 0000000..3c3beca --- /dev/null +++ b/src/dxflinextrudenode.h @@ -0,0 +1,30 @@ +#ifndef DXFLINEXTRUDENODE_H_ +#define DXFLINEXTRUDENODE_H_ + +#include "node.h" +#include "visitor.h" + +class DxfLinearExtrudeNode : public AbstractPolyNode +{ +public: + DxfLinearExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + convexity = slices = 0; + fn = fs = fa = height = twist = 0; + origin_x = origin_y = scale = 0; + center = has_twist = false; + } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { return "linear_extrude"; } + + int convexity, slices; + double fn, fs, fa, height, twist; + double origin_x, origin_y, scale; + bool center, has_twist; + std::string filename, layername; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc index 1be2265..5889cee 100644 --- a/src/dxfrotextrude.cc +++ b/src/dxfrotextrude.cc @@ -24,20 +24,22 @@ * */ +#include "dxfrotextrudenode.h" #include "module.h" -#include "node.h" #include "context.h" #include "printutils.h" #include "builtin.h" #include "polyset.h" #include "dxfdata.h" #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h" #include "openscad.h" // get_fragments_from_r() -#include <QTime> -#include <QApplication> -#include <QProgressDialog> -#include <QDateTime> +#include <sstream> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope + #include <QFileInfo> class DxfRotateExtrudeModule : public AbstractModule @@ -47,28 +49,13 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class DxfRotateExtrudeNode : public AbstractPolyNode -{ -public: - int convexity; - double fn, fs, fa; - double origin_x, origin_y, scale; - QString filename, layername; - DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { - convexity = 0; - fn = fs = fa = 0; - origin_x = origin_y = scale = 0; - } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { DxfRotateExtrudeNode *node = new DxfRotateExtrudeNode(inst); - QVector<QString> argnames = QVector<QString>() << "file" << "layer" << "origin" << "scale"; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "file", "layer", "origin", "scale"; + std::vector<Expression*> argexpr; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -83,10 +70,10 @@ AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleI Value origin = c.lookup_variable("origin", true); Value scale = c.lookup_variable("scale", true); - if(!file.text.isNull()) - node->filename = c.get_absolute_path(file.text); - else - node->filename = file.text; + if (!file.text.empty()) { + PRINTF("DEPRECATED: Support for reading files in rotate_extrude will be removed in future releases. Use a child import() instead."); + node->filename = c.getAbsolutePath(file.text); + } node->layername = layer.text; node->convexity = (int)convexity.num; @@ -99,12 +86,9 @@ AbstractNode *DxfRotateExtrudeModule::evaluate(const Context *ctx, const ModuleI if (node->scale <= 0) node->scale = 1; - if (node->filename.isEmpty()) { - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } + if (node->filename.empty()) { + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); } return node; @@ -116,131 +100,37 @@ void register_builtin_dxf_rotate_extrude() builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule(); } -PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const +PolySet *DxfRotateExtrudeNode::evaluate_polyset(render_mode_e mode, + PolySetEvaluator *evaluator) const { - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return PolySet::ps_cache[key]->ps->link(); + if (!evaluator) { + PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); + PolySet *ps = new PolySet(); + ps->is2d = true; + return ps; } 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->render_cgal_nef_polyhedron().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; - points = new double**[fragments]; - for (int j=0; j < fragments; j++) { - points[j] = new double*[dxf->paths[i].points.count()]; - for (int k=0; k < dxf->paths[i].points.count(); k++) - points[j][k] = new double[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]); - } - } - } - - for (int j=0; j < fragments; j++) { - for (int k=0; k < dxf->paths[i].points.count(); k++) - delete[] points[j][k]; - delete[] points[j]; - } - delete[] points; - } - PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); + PolySet *ps = evaluator->evaluatePolySet(*this, mode); + print_messages_pop(); - delete dxf; return ps; } -QString DxfRotateExtrudeNode::dump(QString indent) const +std::string DxfRotateExtrudeNode::toString() const { - if (dump_cache.isEmpty()) { - QString text; - QFileInfo fileInfo(filename); - text.sprintf("rotate_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "origin = [ %g %g ], scale = %g, convexity = %d, " - "$fn = %g, $fa = %g, $fs = %g) {\n", - filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(), - (int)fileInfo.size(),layername.toAscii().data(), origin_x, origin_y, - scale, convexity, fn, fa, fs); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; + std::stringstream stream; + + stream << this->name() << "(" + "file = \"" << this->filename << "\", " + "cache = \"" << QFileInfo(QString::fromStdString(this->filename)) << "\", " + "layer = \"" << this->layername << "\", " + "origin = [ " << std::dec << this->origin_x << " " << this->origin_y << " ], " + "scale = " << this->scale << ", " + "convexity = " << this->convexity << ", " + "$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; + + return stream.str(); } - diff --git a/src/dxfrotextrudenode.h b/src/dxfrotextrudenode.h new file mode 100644 index 0000000..797b53a --- /dev/null +++ b/src/dxfrotextrudenode.h @@ -0,0 +1,28 @@ +#ifndef DXFROTEXTRUDENODE_H_ +#define DXFROTEXTRUDENODE_H_ + +#include "node.h" +#include "visitor.h" + +class DxfRotateExtrudeNode : public AbstractPolyNode +{ +public: + DxfRotateExtrudeNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + convexity = 0; + fn = fs = fa = 0; + origin_x = origin_y = scale = 0; + } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { return "rotate_extrude"; } + + int convexity; + double fn, fs, fa; + double origin_x, origin_y, scale; + std::string filename, layername; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/dxftess-cgal.cc b/src/dxftess-cgal.cc index adb7e9a..e4a4908 100644 --- a/src/dxftess-cgal.cc +++ b/src/dxftess-cgal.cc @@ -93,7 +93,7 @@ void mark_inner_outer(QList<struct triangle> &tri, Grid2d<point_info_t> &point_i } } -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_triangle_splitting */, double h) +void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool /* do_triangle_splitting */, double h) { CDT cdt; @@ -106,17 +106,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr try { // read path data and copy all relevant infos - for (int i = 0; i < dxf->paths.count(); i++) + for (size_t i = 0; i < dxf.paths.size(); i++) { - if (!dxf->paths[i].is_closed) + if (!dxf.paths[i].is_closed) continue; Vertex_handle first, prev; struct point_info_t *first_pi = NULL, *prev_pi = NULL; - for (int j = 1; j < dxf->paths[i].points.count(); j++) + for (size_t j = 1; j < dxf.paths[i].indices.size(); j++) { - double x = dxf->paths[i].points[j]->x; - double y = dxf->paths[i].points[j]->y; + double x = dxf.points[dxf.paths[i].indices[j]][0]; + double y = dxf.points[dxf.paths[i].indices[j]][1]; if (point_info.has(x, y)) { // FIXME: How can the same path set contain the same point twice? @@ -127,7 +127,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr } struct point_info_t *pi = &point_info.align(x, y); - *pi = point_info_t(x, y, i, j, dxf->paths[i].points.count()-1); + *pi = point_info_t(x, y, i, j, dxf.paths[i].indices.size()-1); Vertex_handle vh = cdt.insert(CDTPoint(x, y)); if (first_pi == NULL) { @@ -306,17 +306,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool /* do_tr } if (path[0] == path[1] && point[0] == 1 && point[1] == 2) - dxf->paths[path[0]].is_inner = up; + dxf.paths[path[0]].is_inner = up; if (path[0] == path[1] && point[0] == 2 && point[1] == 1) - dxf->paths[path[0]].is_inner = !up; + dxf.paths[path[0]].is_inner = !up; if (path[1] == path[2] && point[1] == 1 && point[2] == 2) - dxf->paths[path[1]].is_inner = up; + dxf.paths[path[1]].is_inner = up; if (path[1] == path[2] && point[1] == 2 && point[2] == 1) - dxf->paths[path[1]].is_inner = !up; + dxf.paths[path[1]].is_inner = !up; if (path[2] == path[0] && point[2] == 1 && point[0] == 2) - dxf->paths[path[2]].is_inner = up; + dxf.paths[path[2]].is_inner = up; if (path[2] == path[0] && point[2] == 2 && point[0] == 1) - dxf->paths[path[2]].is_inner = !up; + dxf.paths[path[2]].is_inner = !up; } } diff --git a/src/dxftess-glu.cc b/src/dxftess-glu.cc index 63fa2e5..23d8a45 100644 --- a/src/dxftess-glu.cc +++ b/src/dxftess-glu.cc @@ -193,7 +193,7 @@ static bool point_on_line(double *p1, double *p2, double *p3) rot: CLOCKWISE rotation around positive Z axis */ -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h) +void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool do_triangle_splitting, double h) { GLUtesselator *tobj = gluNewTess(); @@ -227,17 +227,17 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian Grid3d< QPair<int,int> > point_to_path(GRID_COARSE); - for (int i = 0; i < dxf->paths.count(); i++) { - if (!dxf->paths[i].is_closed) + for (int i = 0; i < dxf.paths.size(); i++) { + if (!dxf.paths[i].is_closed) continue; gluTessBeginContour(tobj); - for (int j = 1; j < dxf->paths[i].points.count(); j++) { - point_to_path.data(dxf->paths[i].points[j]->x, - dxf->paths[i].points[j]->y, - h) = QPair<int,int>(i, j); + for (int j = 1; j < dxf.paths[i].indices.size(); j++) { + point_to_path.data(dxf.points[dxf.paths[i].indices[j]][0], + dxf.points[dxf.paths[i].indices[j]][1], + h) = QPair<int,int>(i, j); vl.append(tess_vdata()); - vl.last().v[0] = dxf->paths[i].points[j]->x; - vl.last().v[1] = dxf->paths[i].points[j]->y; + vl.last().v[0] = dxf.points[dxf.paths[i].indices[j]][0]; + vl.last().v[1] = dxf.points[dxf.paths[i].indices[j]][1]; vl.last().v[2] = h; gluTessVertex(tobj, vl.last().v, vl.last().v); } @@ -248,7 +248,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian gluDeleteTess(tobj); #if 0 - for (int i = 0; i < tess_tri.count(); i++) { + for (int i = 0; i < tess_tri.size(); i++) { printf("~~~\n"); printf(" %f %f %f\n", tess_tri[i].p[0][0], tess_tri[i].p[0][1], tess_tri[i].p[0][2]); printf(" %f %f %f\n", tess_tri[i].p[1][0], tess_tri[i].p[1][1], tess_tri[i].p[1][2]); @@ -258,7 +258,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian // GLU tessing sometimes generates degenerated triangles. We must find and remove // them so we can use the triangle array with CGAL.. - for (int i = 0; i < tess_tri.count(); i++) { + for (int i = 0; i < tess_tri.size(); i++) { if (point_on_line(tess_tri[i].p[0], tess_tri[i].p[1], tess_tri[i].p[2]) || point_on_line(tess_tri[i].p[1], tess_tri[i].p[2], tess_tri[i].p[0]) || point_on_line(tess_tri[i].p[2], tess_tri[i].p[0], tess_tri[i].p[1])) { @@ -279,7 +279,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian bool added_triangles = true; typedef QPair<int,int> QPair_ii; QHash<int, QPair_ii> tri_by_atan2; - for (int i = 0; i < tess_tri.count(); i++) + for (int i = 0; i < tess_tri.size(); i++) for (int j = 0; j < 3; j++) { int ai = (int)round(atan2(fabs(tess_tri[i].p[(j+1)%3][0] - tess_tri[i].p[j][0]), fabs(tess_tri[i].p[(j+1)%3][1] - tess_tri[i].p[j][1])) / 0.001); @@ -289,9 +289,9 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian { added_triangles = false; #ifdef DEBUG_TRIANGLE_SPLITTING - printf("*** Triangle splitting (%d) ***\n", tess_tri.count()+1); + printf("*** Triangle splitting (%d) ***\n", tess_tri.size()+1); #endif - for (int i = 0; i < tess_tri.count(); i++) + for (int i = 0; i < tess_tri.size(); i++) for (int k = 0; k < 3; k++) { QHash<QPair_ii, QPair_ii> possible_neigh; @@ -303,7 +303,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian possible_neigh[jl] = jl; } #ifdef DEBUG_TRIANGLE_SPLITTING - printf("%d/%d: %d\n", i, k, possible_neigh.count()); + printf("%d/%d: %d\n", i, k, possible_neigh.size()); #endif foreach (const QPair_ii &jl, possible_neigh) { int j = jl.first; @@ -321,7 +321,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian for (int m = 0; m < 2; m++) { int ai = (int)round(atan2(fabs(tess_tri.last().p[(m+1)%3][0] - tess_tri.last().p[m][0]), fabs(tess_tri.last().p[(m+1)%3][1] - tess_tri.last().p[m][1])) / 0.001 ); - tri_by_atan2.insertMulti(ai, QPair<int,int>(tess_tri.count()-1, m)); + tri_by_atan2.insertMulti(ai, QPair<int,int>(tess_tri.size()-1, m)); } tess_tri[i].p[(k+1)%3] = tess_tri[j].p[l]; for (int m = 0; m < 2; m++) { @@ -337,7 +337,7 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian } #endif - for (int i = 0; i < tess_tri.count(); i++) + for (int i = 0; i < tess_tri.size(); i++) { #if 0 printf("---\n"); @@ -370,19 +370,19 @@ void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_trian int j2 = point_to_path.data(tess_tri[i].p[2][0], tess_tri[i].p[2][1], tess_tri[i].p[2][2]).second; if (i0 == i1 && j0 == 1 && j1 == 2) - dxf->paths[i0].is_inner = !up; + dxf.paths[i0].is_inner = !up; if (i0 == i1 && j0 == 2 && j1 == 1) - dxf->paths[i0].is_inner = up; + dxf.paths[i0].is_inner = up; if (i1 == i2 && j1 == 1 && j2 == 2) - dxf->paths[i1].is_inner = !up; + dxf.paths[i1].is_inner = !up; if (i1 == i2 && j1 == 2 && j2 == 1) - dxf->paths[i1].is_inner = up; + dxf.paths[i1].is_inner = up; if (i2 == i0 && j2 == 1 && j0 == 2) - dxf->paths[i2].is_inner = !up; + dxf.paths[i2].is_inner = !up; if (i2 == i0 && j2 == 2 && j0 == 1) - dxf->paths[i2].is_inner = up; + dxf.paths[i2].is_inner = up; } tess_tri.clear(); diff --git a/src/dxftess.cc b/src/dxftess.cc index 03ed244..d2cb172 100644 --- a/src/dxftess.cc +++ b/src/dxftess.cc @@ -37,20 +37,20 @@ without tesselating. Vertex ordering of the resulting polygons will follow the paths' is_inner flag. */ -void dxf_border_to_ps(PolySet *ps, DxfData *dxf) +void dxf_border_to_ps(PolySet *ps, const DxfData &dxf) { - for (int i = 0; i < dxf->paths.count(); i++) { - const DxfData::Path &pt = dxf->paths[i]; - if (!pt.is_closed) + for (size_t i = 0; i < dxf.paths.size(); i++) { + const DxfData::Path &path = dxf.paths[i]; + if (!path.is_closed) continue; - ps->borders.append(PolySet::Polygon()); - for (int j = 1; j < pt.points.count(); j++) { - double x = pt.points[j]->x, y = pt.points[j]->y, z = 0.0; + ps->borders.push_back(PolySet::Polygon()); + for (size_t j = 1; j < path.indices.size(); j++) { + double x = dxf.points[path.indices[j]][0], y = dxf.points[path.indices[j]][1], z = 0.0; ps->grid.align(x, y, z); - if (pt.is_inner) { - ps->borders.last().append(PolySet::Point(x, y, z)); + if (path.is_inner) { + ps->borders.back().push_back(Vector3d(x, y, z)); } else { - ps->borders.last().insert(0, PolySet::Point(x, y, z)); + ps->borders.back().insert(ps->borders.back().begin(), Vector3d(x, y, z)); } } } diff --git a/src/dxftess.h b/src/dxftess.h index 19fca7d..d3af36e 100644 --- a/src/dxftess.h +++ b/src/dxftess.h @@ -3,7 +3,7 @@ class DxfData; class PolySet; -void dxf_tesselate(PolySet *ps, DxfData *dxf, double rot, bool up, bool do_triangle_splitting, double h); -void dxf_border_to_ps(PolySet *ps, DxfData *dxf); +void dxf_tesselate(PolySet *ps, DxfData &dxf, double rot, bool up, bool do_triangle_splitting, double h); +void dxf_border_to_ps(PolySet *ps, const DxfData &dxf); #endif diff --git a/src/export.cc b/src/export.cc index c207f2b..862e82b 100644 --- a/src/export.cc +++ b/src/export.cc @@ -24,6 +24,7 @@ * */ +#include "export.h" #include "printutils.h" #include "polyset.h" #include "dxfdata.h" @@ -34,51 +35,17 @@ #include <fstream> #ifdef ENABLE_CGAL +#include "CGAL_Nef_polyhedron.h" #include "cgal.h" -void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N) -{ - CGAL_Polyhedron P; - root_N->p3.convert_to_Polyhedron(P); - - typedef CGAL_Polyhedron::Vertex Vertex; - typedef CGAL_Polyhedron::Vertex_const_iterator VCI; - typedef CGAL_Polyhedron::Facet_const_iterator FCI; - typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; - - for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - Vertex v1, v2, v3; - v1 = *VCI((hc++)->vertex()); - v3 = *VCI((hc++)->vertex()); - do { - v2 = v3; - v3 = *VCI((hc++)->vertex()); - double x1 = CGAL::to_double(v1.point().x()); - double y1 = CGAL::to_double(v1.point().y()); - double z1 = CGAL::to_double(v1.point().z()); - double x2 = CGAL::to_double(v2.point().x()); - double y2 = CGAL::to_double(v2.point().y()); - double z2 = CGAL::to_double(v2.point().z()); - double x3 = CGAL::to_double(v3.point().x()); - double y3 = CGAL::to_double(v3.point().y()); - double z3 = CGAL::to_double(v3.point().z()); - ps->append_poly(); - ps->append_vertex(x1, y1, z1); - ps->append_vertex(x2, y2, z2); - ps->append_vertex(x3, y3, z3); - } while (hc != hc_end); - } -} - /*! - Saves the current 3D CGAL Nef polyhedron as STL to the given absolute filename. + Saves the current 3D CGAL Nef polyhedron as STL to the given file. + The file must be open. */ -void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd) +void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd) { CGAL_Polyhedron P; - root_N->p3.convert_to_Polyhedron(P); + root_N->p3->convert_to_Polyhedron(P); typedef CGAL_Polyhedron::Vertex Vertex; typedef CGAL_Polyhedron::Vertex_const_iterator VCI; @@ -87,6 +54,8 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog * setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output +<<<<<<< HEAD +======= std::ofstream output(filename.toUtf8()); if (!output.is_open()) { PRINTA("Can't open STL file \"%1\" for STL export: %2", @@ -95,6 +64,7 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog * return; } +>>>>>>> master output << "solid OpenSCAD_Model\n"; int facet_count = 0; @@ -153,83 +123,81 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog * } output << "endsolid OpenSCAD_Model\n"; +<<<<<<< HEAD +======= output.close(); +>>>>>>> master setlocale(LC_NUMERIC, ""); // Set default locale } -void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*) +void export_off(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog*) { - PRINTF("WARNING: OFF import is not implemented yet."); + CGAL_Polyhedron P; + root_N->p3->convert_to_Polyhedron(P); + output << P; } /*! Saves the current 2D CGAL Nef polyhedron as DXF to the given absolute filename. */ -void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *) +void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *) { - FILE *f = fopen(filename.toUtf8().data(), "w"); - if (!f) { - PRINTA("Can't open DXF file \"%1\" for DXF export: %2", - filename, QString(strerror(errno))); - set_output_handler(NULL, NULL); - return; - } - setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output // Some importers (e.g. Inkscape) needs a BLOCKS section to be present - fprintf(f, " 0\n" - "SECTION\n" - " 2\n" - "BLOCKS\n" - " 0\n" - "ENDSEC\n"); - - fprintf(f, " 0\n" - "SECTION\n" - " 2\n" - "ENTITIES\n"); - - DxfData dd(*root_N); - for (int i=0; i<dd.paths.size(); i++) + output << " 0\n" + << "SECTION\n" + << " 2\n" + << "BLOCKS\n" + << " 0\n" + << "ENDSEC\n" + << " 0\n" + << "SECTION\n" + << " 2\n" + << "ENTITIES\n"; + + DxfData *dd =root_N->convertToDxfData(); + for (size_t i=0; i<dd->paths.size(); i++) { - for (int j=1; j<dd.paths[i].points.size(); j++) { - DxfData::Point *p1 = dd.paths[i].points[j-1]; - DxfData::Point *p2 = dd.paths[i].points[j]; - double x1 = p1->x; - double y1 = p1->y; - double x2 = p2->x; - double y2 = p2->y; - fprintf(f, " 0\n"); - fprintf(f, "LINE\n"); + for (size_t j=1; j<dd->paths[i].indices.size(); j++) { + const Vector2d &p1 = dd->points[dd->paths[i].indices[j-1]]; + const Vector2d &p2 = dd->points[dd->paths[i].indices[j]]; + double x1 = p1[0]; + double y1 = p1[1]; + double x2 = p2[0]; + double y2 = p2[1]; + output << " 0\n" + << "LINE\n"; // Some importers (e.g. Inkscape) needs a layer to be specified - fprintf(f, " 8\n"); - fprintf(f, "0\n"); - fprintf(f, " 10\n"); - fprintf(f, "%f\n", x1); - fprintf(f, " 11\n"); - fprintf(f, "%f\n", x2); - fprintf(f, " 20\n"); - fprintf(f, "%f\n", y1); - fprintf(f, " 21\n"); - fprintf(f, "%f\n", y2); + output << " 8\n" + << "0\n" + << " 10\n" + << x1 << "\n" + << " 11\n" + << x2 << "\n" + << " 20\n" + << y1 << "\n" + << " 21\n" + << y2 << "\n"; } } + + output << " 0\n" + << "ENDSEC\n"; - fprintf(f, " 0\n"); - fprintf(f, "ENDSEC\n"); // Some importers (e.g. Inkscape) needs an OBJECTS section with a DICTIONARY entry - fprintf(f, " 0\n" - "SECTION\n" - " 2\n" - "OBJECTS\n" - " 0\n" - "DICTIONARY\n" - " 0\n" - "ENDSEC\n"); - fprintf(f, " 0\n"); - fprintf(f, "EOF\n"); - - fclose(f); + output << " 0\n" + << "SECTION\n" + << " 2\n" + << "OBJECTS\n" + << " 0\n" + << "DICTIONARY\n" + << " 0\n" + << "ENDSEC\n"; + + output << " 0\n" + <<"EOF\n"; + + delete dd; setlocale(LC_NUMERIC, ""); // Set default locale } diff --git a/src/export.h b/src/export.h index 619d9e0..cba0b23 100644 --- a/src/export.h +++ b/src/export.h @@ -2,10 +2,13 @@ #define EXPORT_H_ #ifdef ENABLE_CGAL -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); -void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); + +#include <iostream> + +void cgal_nef3_to_polyset(class PolySet *ps, class CGAL_Nef_polyhedron *root_N); +void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, class QProgressDialog *pd); +void export_off(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd); +void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDialog *pd); #endif #endif diff --git a/src/expr.cc b/src/expr.cc index 50950d0..c9eda4e 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -27,74 +27,76 @@ #include "expression.h" #include "value.h" #include "context.h" +#include <assert.h> +#include <sstream> +#include <algorithm> +#include "stl-utils.h" Expression::Expression() { - const_value = NULL; + this->const_value = NULL; } Expression::~Expression() { - for (int i=0; i < children.size(); i++) - delete children[i]; - if (const_value) - delete const_value; + std::for_each(this->children.begin(), this->children.end(), del_fun<Expression>()); + delete this->const_value; } Value Expression::evaluate(const Context *context) const { - if (type == "!") - return ! children[0]->evaluate(context); - if (type == "&&") - return children[0]->evaluate(context) && children[1]->evaluate(context); - if (type == "||") - return children[0]->evaluate(context) || children[1]->evaluate(context); - if (type == "*") - return children[0]->evaluate(context) * children[1]->evaluate(context); - if (type == "/") - return children[0]->evaluate(context) / children[1]->evaluate(context); - if (type == "%") - return children[0]->evaluate(context) % children[1]->evaluate(context); - if (type == "+") - return children[0]->evaluate(context) + children[1]->evaluate(context); - if (type == "-") - return children[0]->evaluate(context) - children[1]->evaluate(context); - if (type == "<") - return children[0]->evaluate(context) < children[1]->evaluate(context); - if (type == "<=") - return children[0]->evaluate(context) <= children[1]->evaluate(context); - if (type == "==") - return children[0]->evaluate(context) == children[1]->evaluate(context); - if (type == "!=") - return children[0]->evaluate(context) != children[1]->evaluate(context); - if (type == ">=") - return children[0]->evaluate(context) >= children[1]->evaluate(context); - if (type == ">") - return children[0]->evaluate(context) > children[1]->evaluate(context); - if (type == "?:") { - Value v = children[0]->evaluate(context); + if (this->type == "!") + return ! this->children[0]->evaluate(context); + if (this->type == "&&") + return this->children[0]->evaluate(context) && this->children[1]->evaluate(context); + if (this->type == "||") + return this->children[0]->evaluate(context) || this->children[1]->evaluate(context); + if (this->type == "*") + return this->children[0]->evaluate(context) * this->children[1]->evaluate(context); + if (this->type == "/") + return this->children[0]->evaluate(context) / this->children[1]->evaluate(context); + if (this->type == "%") + return this->children[0]->evaluate(context) % this->children[1]->evaluate(context); + if (this->type == "+") + return this->children[0]->evaluate(context) + this->children[1]->evaluate(context); + if (this->type == "-") + return this->children[0]->evaluate(context) - this->children[1]->evaluate(context); + if (this->type == "<") + return this->children[0]->evaluate(context) < this->children[1]->evaluate(context); + if (this->type == "<=") + return this->children[0]->evaluate(context) <= this->children[1]->evaluate(context); + if (this->type == "==") + return this->children[0]->evaluate(context) == this->children[1]->evaluate(context); + if (this->type == "!=") + return this->children[0]->evaluate(context) != this->children[1]->evaluate(context); + if (this->type == ">=") + return this->children[0]->evaluate(context) >= this->children[1]->evaluate(context); + if (this->type == ">") + return this->children[0]->evaluate(context) > this->children[1]->evaluate(context); + if (this->type == "?:") { + Value v = this->children[0]->evaluate(context); if (v.type == Value::BOOL) - return children[v.b ? 1 : 2]->evaluate(context); + return this->children[v.b ? 1 : 2]->evaluate(context); return Value(); } - if (type == "[]") { - Value v1 = children[0]->evaluate(context); - Value v2 = children[1]->evaluate(context); + if (this->type == "[]") { + Value v1 = this->children[0]->evaluate(context); + Value v2 = this->children[1]->evaluate(context); if (v1.type == Value::VECTOR && v2.type == Value::NUMBER) { - int i = (int)(v2.num); - if (i >= 0 && i < v1.vec.size()) + int i = int(v2.num); + if (i >= 0 && i < int(v1.vec.size())) return *v1.vec[i]; } return Value(); } - if (type == "I") - return children[0]->evaluate(context).inv(); - if (type == "C") - return *const_value; - if (type == "R") { - Value v1 = children[0]->evaluate(context); - Value v2 = children[1]->evaluate(context); - Value v3 = children[2]->evaluate(context); + if (this->type == "I") + return this->children[0]->evaluate(context).inv(); + if (this->type == "C") + return *this->const_value; + if (this->type == "R") { + Value v1 = this->children[0]->evaluate(context); + Value v2 = this->children[1]->evaluate(context); + Value v3 = this->children[2]->evaluate(context); if (v1.type == Value::NUMBER && v2.type == Value::NUMBER && v3.type == Value::NUMBER) { Value r = Value(); r.type = Value::RANGE; @@ -105,83 +107,100 @@ Value Expression::evaluate(const Context *context) const } return Value(); } - if (type == "V") { + if (this->type == "V") { Value v; v.type = Value::VECTOR; - for (int i = 0; i < children.size(); i++) - v.vec.append(new Value(children[i]->evaluate(context))); + for (size_t i = 0; i < this->children.size(); i++) + v.append(new Value(this->children[i]->evaluate(context))); return v; } - if (type == "L") - return context->lookup_variable(var_name); - if (type == "N") + if (this->type == "L") + return context->lookup_variable(this->var_name); + if (this->type == "N") { - Value v = children[0]->evaluate(context); + Value v = this->children[0]->evaluate(context); - if (v.type == Value::VECTOR && var_name == QString("x")) + if (v.type == Value::VECTOR && this->var_name == "x") return *v.vec[0]; - if (v.type == Value::VECTOR && var_name == QString("y")) + if (v.type == Value::VECTOR && this->var_name == "y") return *v.vec[1]; - if (v.type == Value::VECTOR && var_name == QString("z")) + if (v.type == Value::VECTOR && this->var_name == "z") return *v.vec[2]; - if (v.type == Value::RANGE && var_name == QString("begin")) + if (v.type == Value::RANGE && this->var_name == "begin") return Value(v.range_begin); - if (v.type == Value::RANGE && var_name == QString("step")) + if (v.type == Value::RANGE && this->var_name == "step") return Value(v.range_step); - if (v.type == Value::RANGE && var_name == QString("end")) + if (v.type == Value::RANGE && this->var_name == "end") return Value(v.range_end); return Value(); } - if (type == "F") { - QVector<Value> argvalues; - for (int i=0; i < children.size(); i++) - argvalues.append(children[i]->evaluate(context)); - return context->evaluate_function(call_funcname, call_argnames, argvalues); + if (this->type == "F") { + std::vector<Value> argvalues; + for (size_t i=0; i < this->children.size(); i++) + argvalues.push_back(this->children[i]->evaluate(context)); + return context->evaluate_function(this->call_funcname, this->call_argnames, argvalues); } abort(); } -QString Expression::dump() const +std::string Expression::toString() const { - if (type == "*" || type == "/" || type == "%" || type == "+" || type == "-" || - type == "<" || type == "<=" || type == "==" || type == "!=" || type == ">=" || type == ">") - return QString("(%1 %2 %3)").arg(children[0]->dump(), QString(type), children[1]->dump()); - if (type == "?:") - return QString("(%1 ? %2 : %3)").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); - if (type == "[]") - return QString("(%1[%2])").arg(children[0]->dump(), children[1]->dump()); - if (type == "I") - return QString("(-%1)").arg(children[0]->dump()); - if (type == "C") - return const_value->dump(); - if (type == "R") - return QString("[%1 : %2 : %3]").arg(children[0]->dump(), children[1]->dump(), children[2]->dump()); - if (type == "V") { - QString text = QString("["); - for (int i=0; i < children.size(); i++) { - if (i > 0) - text += QString(", "); - text += children[i]->dump(); + std::stringstream stream; + + if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" || + this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" || + this->type == "!=" || this->type == ">=" || this->type == ">") { + stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")"; + } + else if (this->type == "?:") { + stream << "(" << *this->children[0] << " ? " << this->type << " : " << *this->children[1] << ")"; + } + else if (this->type == "[]") { + stream << "(" << *this->children[0] << "[" << *this->children[1] << "])"; + } + else if (this->type == "I") { + stream << "(-" << *this->children[0] << ")"; + } + else if (this->type == "C") { + stream << *this->const_value; + } + else if (this->type == "R") { + stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << this->children[2] << "]"; + } + else if (this->type == "V") { + stream << "["; + for (size_t i=0; i < this->children.size(); i++) { + if (i > 0) stream << ", "; + stream << *this->children[i]; } - return text + QString("]"); - } - if (type == "L") - return var_name; - if (type == "N") - return QString("(%1.%2)").arg(children[0]->dump(), var_name); - if (type == "F") { - QString text = call_funcname + QString("("); - for (int i=0; i < children.size(); i++) { - if (i > 0) - text += QString(", "); - if (!call_argnames[i].isEmpty()) - text += call_argnames[i] + QString(" = "); - text += children[i]->dump(); + stream << "]"; + } + else if (this->type == "L") { + stream << this->var_name; + } + else if (this->type == "N") { + stream << "(" << *this->children[0] << "." << this->var_name << ")"; + } + else if (this->type == "F") { + stream << this->call_funcname << "("; + for (size_t i=0; i < this->children.size(); i++) { + if (i > 0) stream << ", "; + if (!this->call_argnames[i].empty()) stream << this->call_argnames[i] << " = "; + stream << *this->children[i]; } - return text + QString(")"); + stream << ")"; } - abort(); + else { + assert(false && "Illegal expression type"); + } + + return stream.str(); } +std::ostream &operator<<(std::ostream &stream, const Expression &expr) +{ + stream << expr.toString(); + return stream; +} diff --git a/src/expression.h b/src/expression.h index dadcea0..acbd6aa 100644 --- a/src/expression.h +++ b/src/expression.h @@ -1,19 +1,19 @@ #ifndef EXPRESSION_H_ #define EXPRESSION_H_ -#include <QVector> -#include <QString> +#include <string> +#include <vector> class Expression { public: - QVector<Expression*> children; + std::vector<Expression*> children; class Value *const_value; - QString var_name; + std::string var_name; - QString call_funcname; - QVector<QString> call_argnames; + std::string call_funcname; + std::vector<std::string> call_argnames; // Boolean: ! && || // Operators: * / % + - @@ -28,13 +28,15 @@ public: // Lookup Variable: L // Lookup member per name: N // Function call: F - QString type; + std::string type; Expression(); ~Expression(); Value evaluate(const class Context *context) const; - QString dump() const; + std::string toString() const; }; +std::ostream &operator<<(std::ostream &stream, const Expression &expr); + #endif diff --git a/src/func.cc b/src/func.cc index 2608960..52f04dd 100644 --- a/src/func.cc +++ b/src/func.cc @@ -29,31 +29,38 @@ #include "context.h" #include "dxfdim.h" #include "builtin.h" +#include <sstream> +#include <ctime> #include "mathc99.h" -#include <time.h> +#include <algorithm> +#include "stl-utils.h" +#include <boost/foreach.hpp> AbstractFunction::~AbstractFunction() { } -Value AbstractFunction::evaluate(const Context*, const QVector<QString>&, const QVector<Value>&) const +Value AbstractFunction::evaluate(const Context*, const std::vector<std::string>&, const std::vector<Value>&) const { return Value(); } -QString AbstractFunction::dump(QString indent, QString name) const +std::string AbstractFunction::dump(const std::string &indent, const std::string &name) const { - return QString("%1abstract function %2();\n").arg(indent, name); + std::stringstream dump; + dump << indent << "abstract function " << name << "();\n"; + return dump.str(); } Function::~Function() { - for (int i=0; i < argexpr.size(); i++) - delete argexpr[i]; + std::for_each(this->argexpr.begin(), this->argexpr.end(), del_fun<Expression>()); delete expr; } -Value Function::evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const +Value Function::evaluate(const Context *ctx, + const std::vector<std::string> &call_argnames, + const std::vector<Value> &call_argvalues) const { Context c(ctx); c.args(argnames, argexpr, call_argnames, call_argvalues); @@ -62,34 +69,36 @@ Value Function::evaluate(const Context *ctx, const QVector<QString> &call_argnam return Value(); } -QString Function::dump(QString indent, QString name) const +std::string Function::dump(const std::string &indent, const std::string &name) const { - QString text = QString("%1function %2(").arg(indent, name); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - text += argnames[i]; - if (argexpr[i]) - text += QString(" = ") + argexpr[i]->dump(); + std::stringstream dump; + dump << indent << "function " << name << "("; + for (size_t i=0; i < argnames.size(); i++) { + if (i > 0) dump << ", "; + dump << argnames[i]; + if (argexpr[i]) dump << " = " << *argexpr[i]; } - text += QString(") = %1;\n").arg(expr->dump()); - return text; + dump << ") = " << *expr << ";\n"; + return dump.str(); } -QHash<QString, AbstractFunction*> builtin_functions; +typedef boost::unordered_map<std::string, AbstractFunction*> BuiltinContainer; +BuiltinContainer builtin_functions; BuiltinFunction::~BuiltinFunction() { } -Value BuiltinFunction::evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const +Value BuiltinFunction::evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const { return eval_func(ctx, call_argnames, call_argvalues); } -QString BuiltinFunction::dump(QString indent, QString name) const +std::string BuiltinFunction::dump(const std::string &indent, const std::string &name) const { - return QString("%1builtin function %2();\n").arg(indent, name); + std::stringstream dump; + dump << indent << "builtin function " << name << "();\n"; + return dump.str(); } static double deg2rad(double x) @@ -112,14 +121,14 @@ static double rad2deg(double x) return x; } -Value builtin_abs(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_abs(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(fabs(args[0].num)); return Value(); } -Value builtin_sign(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_sign(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value((args[0].num<0) ? -1.0 : ((args[0].num>0) ? 1.0 : 0.0)); @@ -136,7 +145,7 @@ double frand(double min, double max) return (min>max) ? frand()*(min-max)+max : frand()*(max-min)+min; } -Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_rands(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 3 && args[0].type == Value::NUMBER && @@ -164,18 +173,18 @@ Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Valu for (int i=0; i<args[2].num; i++) { Value * r = new Value(frand(args[0].num, args[1].num)); - v.vec.append(r); + v.vec.push_back(r); } return v; } -Value builtin_min(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_min(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() >= 1 && args[0].type == Value::NUMBER) { double val = args[0].num; - for (int i = 1; i < args.size(); i++) + for (size_t i = 1; i < args.size(); i++) if (args[1].type == Value::NUMBER) val = fmin(val, args[i].num); return Value(val); @@ -183,11 +192,11 @@ Value builtin_min(const Context *, const QVector<QString>&, const QVector<Value> return Value(); } -Value builtin_max(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_max(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() >= 1 && args[0].type == Value::NUMBER) { double val = args[0].num; - for (int i = 1; i < args.size(); i++) + for (size_t i = 1; i < args.size(); i++) if (args[1].type == Value::NUMBER) val = fmax(val, args[i].num); return Value(val); @@ -195,98 +204,98 @@ Value builtin_max(const Context *, const QVector<QString>&, const QVector<Value> return Value(); } -Value builtin_sin(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_sin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(sin(deg2rad(args[0].num))); return Value(); } -Value builtin_cos(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_cos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(cos(deg2rad(args[0].num))); return Value(); } -Value builtin_asin(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_asin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(rad2deg(asin(args[0].num))); return Value(); } -Value builtin_acos(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_acos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(rad2deg(acos(args[0].num))); return Value(); } -Value builtin_tan(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_tan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(tan(deg2rad(args[0].num))); return Value(); } -Value builtin_atan(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_atan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(rad2deg(atan(args[0].num))); return Value(); } -Value builtin_atan2(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_atan2(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) return Value(rad2deg(atan2(args[0].num, args[1].num))); return Value(); } -Value builtin_pow(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_pow(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) return Value(pow(args[0].num, args[1].num)); return Value(); } -Value builtin_round(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_round(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(round(args[0].num)); return Value(); } -Value builtin_ceil(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_ceil(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(ceil(args[0].num)); return Value(); } -Value builtin_floor(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_floor(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(floor(args[0].num)); return Value(); } -Value builtin_sqrt(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_sqrt(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(sqrt(args[0].num)); return Value(); } -Value builtin_exp(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_exp(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(exp(args[0].num)); return Value(); } -Value builtin_log(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_log(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 2 && args[0].type == Value::NUMBER && args[1].type == Value::NUMBER) return Value(log(args[1].num) / log(args[0].num)); @@ -295,34 +304,31 @@ Value builtin_log(const Context *, const QVector<QString>&, const QVector<Value> return Value(); } -Value builtin_ln(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_ln(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { if (args.size() == 1 && args[0].type == Value::NUMBER) return Value(log(args[0].num)); return Value(); } -Value builtin_str(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_str(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { - QString str; - for (int i = 0; i < args.size(); i++) - { - if (args[i].type == Value::STRING) - str += args[i].text; - else - str += args[i].dump(); + std::stringstream stream; + + for (size_t i = 0; i < args.size(); i++) { + stream << args[i]; } - return Value(str); + return Value(stream.str()); } -Value builtin_lookup(const Context *, const QVector<QString>&, const QVector<Value> &args) +Value builtin_lookup(const Context *, const std::vector<std::string>&, const std::vector<Value> &args) { double p, low_p, low_v, high_p, high_v; if (args.size() < 2 || !args[0].getnum(p) || args[1].vec.size() < 2 || args[1].vec[0]->vec.size() < 2) return Value(); if (!args[1].vec[0]->getv2(low_p, low_v) || !args[1].vec[0]->getv2(high_p, high_v)) return Value(); - for (int i = 1; i < args[1].vec.size(); i++) { + for (size_t i = 1; i < args[1].vec.size(); i++) { double this_p, this_v; if (args[1].vec[i]->getv2(this_p, this_v)) { if (this_p <= p && (this_p > low_p || low_p > p)) { @@ -372,8 +378,7 @@ void initialize_builtin_functions() void destroy_builtin_functions() { - foreach (AbstractFunction *v, builtin_functions) - delete v; + BOOST_FOREACH(BuiltinContainer::value_type &f, builtin_functions) delete f.second; +//std::for_each(builtin_functions.begin(), builtin_functions.end(), del_fun<AbstractFunction>()); builtin_functions.clear(); } - diff --git a/src/function.h b/src/function.h index 3bb89c2..623ef7e 100644 --- a/src/function.h +++ b/src/function.h @@ -2,42 +2,43 @@ #define FUNCTION_H_ #include "value.h" -#include <QString> +#include <string> +#include <vector> class AbstractFunction { public: virtual ~AbstractFunction(); - virtual Value evaluate(const class Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; + virtual Value evaluate(const class Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const; + virtual std::string dump(const std::string &indent, const std::string &name) const; }; class BuiltinFunction : public AbstractFunction { public: - typedef Value (*eval_func_t)(const Context *ctx, const QVector<QString> &argnames, const QVector<Value> &args); + typedef Value (*eval_func_t)(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args); eval_func_t eval_func; BuiltinFunction(eval_func_t f) : eval_func(f) { } virtual ~BuiltinFunction(); - virtual Value evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; + virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const; + virtual std::string dump(const std::string &indent, const std::string &name) const; }; class Function : public AbstractFunction { public: - QVector<QString> argnames; - QVector<class Expression*> argexpr; + std::vector<std::string> argnames; + std::vector<class Expression*> argexpr; Expression *expr; Function() { } virtual ~Function(); - virtual Value evaluate(const Context *ctx, const QVector<QString> &call_argnames, const QVector<Value> &call_argvalues) const; - virtual QString dump(QString indent, QString name) const; + virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const; + virtual std::string dump(const std::string &indent, const std::string &name) const; }; #endif diff --git a/src/glview.cc b/src/glview.cc index 9d82443..896ff0a 100644 --- a/src/glview.cc +++ b/src/glview.cc @@ -50,32 +50,42 @@ GLView::GLView(QWidget *parent) : QGLWidget(parent), renderer(NULL) { - viewer_distance = 500; - object_rot_x = 35; - object_rot_y = 0; - object_rot_z = 25; - object_trans_x = 0; - object_trans_y = 0; - object_trans_z = 0; + init(); +} - mouse_drag_active = false; +GLView::GLView(const QGLFormat & format, QWidget *parent) : QGLWidget(format, parent) +{ + init(); +} - orthomode = false; - showaxes = false; - showcrosshairs = false; - showedges = false; - showfaces = true; +void GLView::init() +{ + this->viewer_distance = 500; + this->object_rot_x = 35; + this->object_rot_y = 0; + this->object_rot_z = 25; + this->object_trans_x = 0; + this->object_trans_y = 0; + this->object_trans_z = 0; + + this->mouse_drag_active = false; + + this->showedges = false; + this->showfaces = true; + this->orthomode = false; + this->showaxes = false; + this->showcrosshairs = false; for (int i = 0; i < 10; i++) - shaderinfo[i] = 0; + this->shaderinfo[i] = 0; - statusLabel = NULL; + this->statusLabel = NULL; setMouseTracking(true); #ifdef ENABLE_OPENCSG - opencsg_support = true; + this->opencsg_support = true; static int sId = 0; - opencsg_id = sId++; + this->opencsg_id = sId++; #endif } @@ -527,4 +537,3 @@ void GLView::mouseReleaseEvent(QMouseEvent*) mouse_drag_active = false; releaseMouse(); } - @@ -1,6 +1,7 @@ #ifndef GRID_H_ #define GRID_H_ +#include <QHash> #include "mathc99.h" #ifdef WIN32 typedef __int64 int64_t; @@ -8,7 +9,6 @@ typedef __int64 int64_t; #include <stdint.h> #endif #include <stdlib.h> -#include <QHash> const double GRID_COARSE = 0.001; const double GRID_FINE = 0.000001; diff --git a/src/handle_dep.cc b/src/handle_dep.cc new file mode 100644 index 0000000..2a05b4a --- /dev/null +++ b/src/handle_dep.cc @@ -0,0 +1,42 @@ +#include "handle_dep.h" +#include "myqhash.h" +#include <string> +#include <sstream> +#include <QString> +#include <QDir> +#include <QSet> +#include <stdlib.h> // for system() + +QSet<std::string> dependencies; +const char *make_command = NULL; + +void handle_dep(const std::string &filename) +{ + if (filename[0] == '/') + dependencies.insert(filename); + else { + QString dep = QDir::currentPath() + QString("/") + QString::fromStdString(filename); + dependencies.insert(dep.toStdString()); + } + if (!QFile(QString::fromStdString(filename)).exists() && make_command) { + std::stringstream buf; + buf << make_command << " '" << QString::fromStdString(filename).replace("'", "'\\''").toUtf8().data() << "'"; + system(buf.str().c_str()); // FIXME: Handle error + } +} + +bool write_deps(const std::string &filename, const std::string &output_file) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (!fp) { + fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", filename.c_str()); + return false; + } + fprintf(fp, "%s:", output_file.c_str()); + QSetIterator<std::string> i(dependencies); + while (i.hasNext()) + fprintf(fp, " \\\n\t%s", i.next().c_str()); + fprintf(fp, "\n"); + fclose(fp); + return true; +} diff --git a/src/handle_dep.h b/src/handle_dep.h new file mode 100644 index 0000000..1074a64 --- /dev/null +++ b/src/handle_dep.h @@ -0,0 +1,10 @@ +#ifndef HANDLE_DEP_H_ +#define HANDLE_DEP_H_ + +#include <string> + +extern const char *make_command; +void handle_dep(const std::string &filename); +bool write_deps(const std::string &filename, const std::string &output_file); + +#endif diff --git a/src/import.cc b/src/import.cc index a924e24..0fdc156 100644 --- a/src/import.cc +++ b/src/import.cc @@ -24,63 +24,49 @@ * */ +#include "importnode.h" + #include "module.h" -#include "node.h" #include "polyset.h" #include "context.h" #include "builtin.h" #include "dxfdata.h" #include "dxftess.h" #include "printutils.h" -#include "openscad.h" // handle_dep() +#include "handle_dep.h" // handle_dep() + +#ifdef ENABLE_CGAL +#include "cgalutils.h" +#endif #include <QFile> +#include <QRegExp> +#include <QStringList> #include <sys/types.h> #include <sys/stat.h> - -enum import_type_e { - TYPE_STL, - TYPE_OFF, - TYPE_DXF -}; +#include <fstream> +#include <sstream> +#include <assert.h> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope class ImportModule : public AbstractModule { public: import_type_e type; - ImportModule(import_type_e type) : type(type) { } + ImportModule(import_type_e type = TYPE_UNKNOWN) : type(type) { } virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class ImportNode : public AbstractPolyNode -{ -public: - import_type_e type; - QString filename; - QString layername; - int convexity; - double fn, fs, fa; - double origin_x, origin_y, scale; - ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { - ImportNode *node = new ImportNode(inst, type); - - QVector<QString> argnames; - if (type == TYPE_DXF) { - argnames = QVector<QString>() << "file" << "layer" << "convexity" << "origin" << "scale"; - } else { - argnames = QVector<QString>() << "file" << "convexity"; - } - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "file", "layer", "convexity", "origin", "scale"; + std::vector<Expression*> argexpr; // Map old argnames to new argnames for compatibility - QVector<QString> inst_argnames = inst->argnames; - for (int i=0; i<inst_argnames.size(); i++) { + std::vector<std::string> inst_argnames = inst->argnames; + for (size_t i=0; i<inst_argnames.size(); i++) { if (inst_argnames[i] == "filename") inst_argnames[i] = "file"; if (inst_argnames[i] == "layername") @@ -90,11 +76,23 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati Context c(ctx); c.args(argnames, argexpr, inst_argnames, inst->argvalues); + Value v = c.lookup_variable("file"); + std::string filename = c.getAbsolutePath(v.text); + import_type_e actualtype = this->type; + if (actualtype == TYPE_UNKNOWN) { + QFileInfo fi(QString::fromStdString(filename)); + if (fi.suffix().toLower() == "stl") actualtype = TYPE_STL; + else if (fi.suffix().toLower() == "off") actualtype = TYPE_OFF; + else if (fi.suffix().toLower() == "dxf") actualtype = TYPE_DXF; + } + + ImportNode *node = new ImportNode(inst, actualtype); + node->fn = c.lookup_variable("$fn").num; node->fs = c.lookup_variable("$fs").num; node->fa = c.lookup_variable("$fa").num; - node->filename = c.get_absolute_path(c.lookup_variable("file").text); + node->filename = filename; node->layername = c.lookup_variable("layer", true).text; node->convexity = c.lookup_variable("convexity", true).num; @@ -113,27 +111,20 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati return node; } -void register_builtin_import() +PolySet *ImportNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const { - builtin_modules["import_stl"] = new ImportModule(TYPE_STL); - builtin_modules["import_off"] = new ImportModule(TYPE_OFF); - builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF); -} + PolySet *p = NULL; -PolySet *ImportNode::render_polyset(render_mode_e) const -{ - PolySet *p = new PolySet(); - p->convexity = convexity; - - if (type == TYPE_STL) + if (this->type == TYPE_STL) { - handle_dep(filename); - QFile f(filename); + handle_dep(this->filename); + QFile f(QString::fromStdString(this->filename)); if (!f.open(QIODevice::ReadOnly)) { - PRINTF("WARNING: Can't open import file `%s'.", filename.toAscii().data()); + PRINTF("WARNING: Can't open import file `%s'.", this->filename.c_str()); return p; } + p = new PolySet(); QByteArray data = f.read(5); if (data.size() == 5 && QString(data) == QString("solid")) { @@ -202,44 +193,68 @@ PolySet *ImportNode::render_polyset(render_mode_e) const } } - if (type == TYPE_OFF) + else if (this->type == TYPE_OFF) { - PRINTF("WARNING: OFF import is not implemented yet."); +#ifdef ENABLE_CGAL + CGAL_Polyhedron poly; + std::ifstream file(this->filename.c_str()); + file >> poly; + file.close(); + + p = createPolySetFromPolyhedron(poly); +#else + PRINTF("WARNING: OFF import requires CGAL."); +#endif } - if (type == TYPE_DXF) + else if (this->type == TYPE_DXF) { - DxfData dd(fn, fs, fa, filename, layername, origin_x, origin_y, scale); + p = new PolySet(); + DxfData dd(this->fn, this->fs, this->fa, this->filename, this->layername, this->origin_x, this->origin_y, this->scale); p->is2d = true; - dxf_tesselate(p, &dd, 0, true, false, 0); - dxf_border_to_ps(p, &dd); + dxf_tesselate(p, dd, 0, true, false, 0); + dxf_border_to_ps(p, dd); + } + else + { + PRINTF("ERROR: Unsupported file format while trying to import file '%s'", this->filename.c_str()); } + if (p) p->convexity = this->convexity; return p; } -QString ImportNode::dump(QString indent) const +std::string ImportNode::toString() const { - if (dump_cache.isEmpty()) { - QString text; - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); - if (type == TYPE_STL) - text.sprintf("import_stl(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); - if (type == TYPE_OFF) - text.sprintf("import_off(file = \"%s\", cache = \"%x.%x\", convexity = %d);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, convexity); - if (type == TYPE_DXF) - text.sprintf("import_dxf(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " - "origin = [ %g %g ], scale = %g, convexity = %d, " - "$fn = %g, $fa = %g, $fs = %g);\n", - filename.toAscii().data(), (int)st.st_mtime, (int)st.st_size, - layername.toAscii().data(), origin_x, origin_y, scale, convexity, - fn, fa, fs); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; + std::stringstream stream; + + QString text; + struct stat st; + memset(&st, 0, sizeof(struct stat)); + stat(this->filename.c_str(), &st); + + stream << this->name(); + stream << "(file = \"" << this->filename << "\", " + "cache = \"" << std::hex << (int)st.st_mtime << "." << (int)st.st_size << "\", " + "layer = \"" << this->layername << "\", " + "origin = [ " << std::dec << this->origin_x << " " << this->origin_y << " ], " + "scale = " << this->scale << ", " + "convexity = " << this->convexity << ", " + "$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; + + return stream.str(); +} + +std::string ImportNode::name() const +{ + return "import"; +} + +void register_builtin_import() +{ + builtin_modules["import_stl"] = new ImportModule(TYPE_STL); + builtin_modules["import_off"] = new ImportModule(TYPE_OFF); + builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF); + builtin_modules["import"] = new ImportModule(); } diff --git a/src/importnode.h b/src/importnode.h new file mode 100644 index 0000000..49e9f16 --- /dev/null +++ b/src/importnode.h @@ -0,0 +1,33 @@ +#ifndef IMPORTNODE_H_ +#define IMPORTNODE_H_ + +#include "node.h" +#include "visitor.h" + +enum import_type_e { + TYPE_UNKNOWN, + TYPE_STL, + TYPE_OFF, + TYPE_DXF +}; + +class ImportNode : public AbstractPolyNode +{ +public: + ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const; + + import_type_e type; + std::string filename; + std::string layername; + int convexity; + double fn, fs, fa; + double origin_x, origin_y, scale; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/lexer.l b/src/lexer.l index f919ee5..4c0ddea 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -26,7 +26,8 @@ %{ -#include "openscad.h" +#include "handle_dep.h" +#include "openscad.h" // librarydir #include "printutils.h" #include "parser_yacc.h" #include <QStack> @@ -88,7 +89,7 @@ DIGIT [0-9] include[ \t\r\n>]*"<" { BEGIN(include); } <include>{ -[^\t\r\n>]+"/" { filepath = yytext; } +[^\t\r\n>]*"/" { filepath = yytext; } [^\t\r\n>/]+ { filename = yytext; } ">" { BEGIN(INITIAL); includefile(); } } @@ -102,7 +103,7 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { if (!finfo.exists()) { finfo = QFileInfo(QDir(librarydir), filename); } - handle_dep(finfo.absoluteFilePath()); + handle_dep(finfo.absoluteFilePath().toStdString()); parserlval.text = strdup(finfo.absoluteFilePath().toLocal8Bit()); return TOK_USE; } @@ -116,7 +117,7 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { } PRINTF("DEPRECATED: Support for implicit include will be removed in future releases. Use `include <filename>' instead."); - handle_dep(finfo.absoluteFilePath()); + handle_dep(finfo.absoluteFilePath().toStdString()); yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r"); if (!yyin) { PRINTF("WARNING: Can't open input file `%s'.", filename); @@ -206,7 +207,7 @@ void includefile() finfo = QFileInfo(QDir(librarydir), filename); } - handle_dep(finfo.absoluteFilePath()); + handle_dep(finfo.absoluteFilePath().toStdString()); yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r"); if (!yyin) { PRINTA("WARNING: Can't open input file `%1'.", filename); diff --git a/src/mainwin.cc b/src/mainwin.cc index 44c5cff..c937279 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -40,6 +40,7 @@ #include "dxftess.h" #include "progress.h" #ifdef ENABLE_OPENCSG +#include "CSGTermEvaluator.h" #include "OpenCSGRenderer.h" #endif #ifdef USE_PROGRESSWIDGET @@ -74,19 +75,33 @@ #include "qlanguagefactory.h" #endif +#include <fstream> + +#include <algorithm> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +using namespace boost::lambda; + #ifdef ENABLE_CGAL -#include "cgalrenderer.h" +#include "CGALEvaluator.h" +#include "PolySetCGALEvaluator.h" +#include "CGALRenderer.h" +#include "CGAL_Nef_polyhedron.h" +#include "cgal.h" #endif // ENABLE_CGAL +// Global application state +unsigned int GuiLocker::gui_locked = 0; + #define QUOTE(x__) # x__ #define QUOTED(x__) QUOTE(x__) static char helptitle[] = - "OpenSCAD " - QUOTED(OPENSCAD_VERSION) - " (www.openscad.org)\n"; + "OpenSCAD " QUOTED(OPENSCAD_VERSION) " (www.openscad.org)\n" + "Visitor refactored version\n"; static char copyrighttext[] = "Copyright (C) 2009-2011 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n" "\n" @@ -137,11 +152,13 @@ MainWindow::MainWindow(const QString &filename) root_ctx.set_variable("$fa", Value(12.0)); root_ctx.set_variable("$t", Value(0.0)); + root_ctx.set_constant("PI",Value(M_PI)); + Value zero3; zero3.type = Value::VECTOR; - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); + zero3.append(new Value(0.0)); + zero3.append(new Value(0.0)); + zero3.append(new Value(0.0)); root_ctx.set_variable("$vpt", zero3); root_ctx.set_variable("$vpr", zero3); @@ -180,8 +197,8 @@ MainWindow::MainWindow(const QString &filename) editor->setLineWrapping(true); // Not designable setFont("", 0); // Init default font - screen->statusLabel = new QLabel(this); - statusBar()->addWidget(screen->statusLabel); + this->glview->statusLabel = new QLabel(this); + statusBar()->addWidget(this->glview->statusLabel); animate_timer = new QTimer(this); connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); @@ -276,7 +293,7 @@ MainWindow::MainWindow(const QString &filename) this->viewActionOpenCSG->setVisible(false); #else connect(this->viewActionOpenCSG, SIGNAL(triggered()), this, SLOT(viewModeOpenCSG())); - if (!screen->hasOpenCSGSupport()) { + if (!this->glview->hasOpenCSGSupport()) { this->viewActionOpenCSG->setEnabled(false); } #endif @@ -332,9 +349,9 @@ MainWindow::MainWindow(const QString &filename) connect(editor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool))); connect(editor->document(), SIGNAL(modificationChanged(bool)), fileActionSave, SLOT(setEnabled(bool))); #endif - connect(screen, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate())); + connect(this->glview, SIGNAL(doAnimateUpdate()), this, SLOT(animateUpdate())); - connect(Preferences::inst(), SIGNAL(requestRedraw()), this->screen, SLOT(updateGL())); + connect(Preferences::inst(), SIGNAL(requestRedraw()), this->glview, SLOT(updateGL())); connect(Preferences::inst(), SIGNAL(fontChanged(const QString&,uint)), this, SLOT(setFont(const QString&,uint))); Preferences::inst()->apply(); @@ -481,7 +498,7 @@ MainWindow::setFileName(const QString &filename) { if (filename.isEmpty()) { this->fileName.clear(); - this->root_ctx.document_path = currentdir; + this->root_ctx.setDocumentPath(currentdir.toStdString()); setWindowTitle("OpenSCAD - New Document[*]"); } else { @@ -504,7 +521,7 @@ MainWindow::setFileName(const QString &filename) this->fileName = fileinfo.fileName(); } - this->root_ctx.document_path = fileinfo.dir().absolutePath(); + this->root_ctx.setDocumentPath(fileinfo.dir().absolutePath().toStdString()); QDir::setCurrent(fileinfo.dir().absolutePath()); } @@ -564,7 +581,7 @@ void MainWindow::load() AbstractNode *MainWindow::find_root_tag(AbstractNode *n) { - foreach(AbstractNode *v, n->children) { + BOOST_FOREACH (AbstractNode *v, n->children) { if (v->modinst->tag_root) return v; if (AbstractNode *vroot = find_root_tag(v)) return vroot; } @@ -572,7 +589,7 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n) } /*! - Parse and evaluate the design -> this->root_node + Parse and evaluate the design => this->root_node */ void MainWindow::compile(bool procevents) { @@ -581,7 +598,7 @@ void MainWindow::compile(bool procevents) QApplication::processEvents(); // Invalidate renderers before we kill the CSG tree - screen->setRenderer(NULL); + this->glview->setRenderer(NULL); if (this->opencsgRenderer) { delete this->opencsgRenderer; this->opencsgRenderer = NULL; @@ -592,80 +609,83 @@ void MainWindow::compile(bool procevents) } // Remove previous CSG tree - if (root_module) { - delete root_module; - root_module = NULL; + if (this->root_module) { + delete this->root_module; + this->root_module = NULL; } - if (absolute_root_node) { - delete absolute_root_node; - absolute_root_node = NULL; + if (this->absolute_root_node) { + delete this->absolute_root_node; + this->absolute_root_node = NULL; } - if (root_raw_term) { - root_raw_term->unlink(); - root_raw_term = NULL; + if (this->root_raw_term) { + this->root_raw_term->unlink(); + this->root_raw_term = NULL; } - if (root_norm_term) { - root_norm_term->unlink(); - root_norm_term = NULL; + if (this->root_norm_term) { + this->root_norm_term->unlink(); + this->root_norm_term = NULL; } - if (root_chain) { - delete root_chain; - root_chain = NULL; + if (this->root_chain) { + delete this->root_chain; + this->root_chain = NULL; } - foreach(CSGTerm *v, highlight_terms) { - v->unlink(); - } - highlight_terms.clear(); - if (highlights_chain) { - delete highlights_chain; - highlights_chain = NULL; - } - foreach(CSGTerm *v, background_terms) { - v->unlink(); - } - background_terms.clear(); - if (background_chain) { - delete background_chain; - background_chain = NULL; - } - root_node = NULL; + std::for_each(this->highlight_terms.begin(), this->highlight_terms.end(), + bind(&CSGTerm::unlink, _1)); + + this->highlight_terms.clear(); + delete this->highlights_chain; + this->highlights_chain = NULL; + + std::for_each(this->background_terms.begin(), this->background_terms.end(), + bind(&CSGTerm::unlink, _1)); + this->background_terms.clear(); + delete this->background_chain; + this->background_chain = NULL; + + this->root_node = NULL; + this->tree.setRoot(NULL); // Initialize special variables - root_ctx.set_variable("$t", Value(e_tval->text().toDouble())); + this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble())); Value vpt; vpt.type = Value::VECTOR; - vpt.vec.append(new Value(-screen->object_trans_x)); - vpt.vec.append(new Value(-screen->object_trans_y)); - vpt.vec.append(new Value(-screen->object_trans_z)); - root_ctx.set_variable("$vpt", vpt); + vpt.append(new Value(-this->glview->object_trans_x)); + vpt.append(new Value(-this->glview->object_trans_y)); + vpt.append(new Value(-this->glview->object_trans_z)); + this->root_ctx.set_variable("$vpt", vpt); Value vpr; vpr.type = Value::VECTOR; - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_x + 90, 360))); - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_y, 360))); - vpr.vec.append(new Value(fmodf(360 - screen->object_rot_z, 360))); + vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360))); + vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360))); + vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360))); root_ctx.set_variable("$vpr", vpr); // Parse - last_compiled_doc = editor->toPlainText(); - root_module = parse((last_compiled_doc + "\n" + commandline_commands).toAscii().data(), this->fileName.isEmpty() ? "" : QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false); + this->last_compiled_doc = editor->toPlainText(); + this->root_module = parse((this->last_compiled_doc + "\n" + + QString::fromStdString(commandline_commands)).toAscii().data(), + this->fileName.isEmpty() ? + "" : + QFileInfo(this->fileName).absolutePath().toLocal8Bit(), + false); // Error highlighting - if (highlighter) { - delete highlighter; - highlighter = NULL; + if (this->highlighter) { + delete this->highlighter; + this->highlighter = NULL; } if (parser_error_pos >= 0) { - highlighter = new Highlighter(editor->document()); + this->highlighter = new Highlighter(editor->document()); } - if (!root_module) { + if (!this->root_module) { if (!animate_panel->isVisible()) { #ifdef _QCODE_EDIT_ QDocumentCursor cursor = editor->cursor(); @@ -685,19 +705,23 @@ void MainWindow::compile(bool procevents) QApplication::processEvents(); AbstractNode::resetIndexCounter(); - root_inst = ModuleInstantiation(); - absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + this->root_inst = ModuleInstantiation(); + this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst); - if (!absolute_root_node) + if (!this->absolute_root_node) goto fail; // Do we have an explicit root node (! modifier)? - if (!(this->root_node = find_root_tag(absolute_root_node))) { - this->root_node = absolute_root_node; + if (!(this->root_node = find_root_tag(this->absolute_root_node))) { + this->root_node = this->absolute_root_node; } - root_node->dump(""); + // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers + this->tree.setRoot(this->root_node); + // Dump the tree (to initialize caches). + // FIXME: We shouldn't really need to do this explicitly.. + this->tree.getString(*this->root_node); - if (1) { + if (1) { PRINT("Compilation finished."); if (procevents) QApplication::processEvents(); @@ -707,7 +731,7 @@ fail: PRINT("ERROR: Compilation failed! (no top level object found)"); } else { int line = 1; - QByteArray pb = last_compiled_doc.toAscii(); + QByteArray pb = this->last_compiled_doc.toAscii(); char *p = pb.data(); for (int i = 0; i < parser_error_pos; i++) { if (p[i] == '\n') @@ -735,13 +759,6 @@ void MainWindow::compileCSG(bool procevents) if (procevents) QApplication::processEvents(); - double m[20]; - - for (int i = 0; i < 16; i++) - m[i] = i % 5 == 0 ? 1.0 : 0.0; - for (int i = 16; i < 20; i++) - m[i] = -1; - // Main CSG evaluation QTime t; t.start(); @@ -762,7 +779,12 @@ void MainWindow::compileCSG(bool procevents) progress_report_prep(root_node, report_func, pd); try { - root_raw_term = root_node->render_csg_term(m, &highlight_terms, &background_terms); + // FIXME: put cache somewhere else as it's pretty useless now + QHash<std::string, CGAL_Nef_polyhedron> cache; + CGALEvaluator cgalevaluator(cache, this->tree); + PolySetCGALEvaluator psevaluator(cgalevaluator); + CSGTermEvaluator csgrenderer(this->tree, &psevaluator); + root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); if (!root_raw_term) { PRINT("ERROR: CSG generation failed! (no top level object found)"); if (procevents) @@ -801,12 +823,12 @@ void MainWindow::compileCSG(bool procevents) if (highlight_terms.size() > 0) { - PRINTF("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); + PRINTF("Compiling highlights (%zu CSG Trees)...", highlight_terms.size()); if (procevents) QApplication::processEvents(); highlights_chain = new CSGChain(); - for (int i = 0; i < highlight_terms.size(); i++) { + for (unsigned int i = 0; i < highlight_terms.size(); i++) { while (1) { CSGTerm *n = highlight_terms[i]->normalize(); highlight_terms[i]->unlink(); @@ -820,12 +842,12 @@ void MainWindow::compileCSG(bool procevents) if (background_terms.size() > 0) { - PRINTF("Compiling background (%d CSG Trees)...", background_terms.size()); + PRINTF("Compiling background (%zu CSG Trees)...", background_terms.size()); if (procevents) QApplication::processEvents(); background_chain = new CSGChain(); - for (int i = 0; i < background_terms.size(); i++) { + for (unsigned int i = 0; i < background_terms.size(); i++) { while (1) { CSGTerm *n = background_terms[i]->normalize(); background_terms[i]->unlink(); @@ -842,10 +864,11 @@ void MainWindow::compileCSG(bool procevents) PRINTF("WARNING: OpenCSG rendering has been disabled."); } else { + PRINTF("Normalized CSG tree has %d elements", root_chain->polysets.size()); this->opencsgRenderer = new OpenCSGRenderer(this->root_chain, this->highlights_chain, this->background_chain, - this->screen->shaderinfo); + this->glview->shaderinfo); } this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain, this->highlights_chain, @@ -876,7 +899,9 @@ void MainWindow::actionOpen() { QString new_filename = QFileDialog::getOpenFileName(this, "Open File", "", "OpenSCAD Designs (*.scad)"); #ifdef ENABLE_MDI - new MainWindow(new_filename); + if (!new_filename.isEmpty()) { + new MainWindow(new_filename); + } #else if (!new_filename.isEmpty()) { if (!maybeSave()) @@ -1027,7 +1052,7 @@ void MainWindow::pasteViewportTranslation() QTextCursor cursor = editor->textCursor(); #endif QString txt; - txt.sprintf("[ %.2f, %.2f, %.2f ]", -screen->object_trans_x, -screen->object_trans_y, -screen->object_trans_z); + txt.sprintf("[ %.2f, %.2f, %.2f ]", -this->glview->object_trans_x, -this->glview->object_trans_y, -this->glview->object_trans_z); cursor.insertText(txt); } @@ -1040,7 +1065,7 @@ void MainWindow::pasteViewportRotation() #endif QString txt; txt.sprintf("[ %.2f, %.2f, %.2f ]", - fmodf(360 - screen->object_rot_x + 90, 360), fmodf(360 - screen->object_rot_y, 360), fmodf(360 - screen->object_rot_z, 360)); + fmodf(360 - this->glview->object_rot_x + 90, 360), fmodf(360 - this->glview->object_rot_y, 360), fmodf(360 - this->glview->object_rot_z, 360)); cursor.insertText(txt); } @@ -1084,6 +1109,9 @@ bool MainWindow::checkModified() void MainWindow::actionReloadCompile() { + if (GuiLocker::isLocked()) return; + GuiLocker lock; + if (!checkModified()) return; console->clear(); @@ -1111,6 +1139,9 @@ void MainWindow::actionReloadCompile() void MainWindow::actionCompile() { + if (GuiLocker::isLocked()) return; + GuiLocker lock; + setCurrentOutput(); console->clear(); @@ -1130,7 +1161,7 @@ void MainWindow::actionCompile() } if (viewActionAnimate->isChecked() && e_dump->isChecked()) { - QImage img = screen->grabFrameBuffer(); + QImage img = this->glview->grabFrameBuffer(); QString filename; double s = e_fsteps->text().toDouble(); double t = e_tval->text().toDouble(); @@ -1145,15 +1176,19 @@ void MainWindow::actionCompile() void MainWindow::actionRenderCGAL() { + if (GuiLocker::isLocked()) return; + GuiLocker lock; + setCurrentOutput(); console->clear(); compile(true); - if (!root_module || !root_node) + if (!this->root_module || !this->root_node) { return; + } - this->screen->setRenderer(NULL); + this->glview->setRenderer(NULL); delete this->cgalRenderer; this->cgalRenderer = NULL; if (this->root_N) { @@ -1183,9 +1218,12 @@ void MainWindow::actionRenderCGAL() QApplication::processEvents(); - progress_report_prep(root_node, report_func, pd); + progress_report_prep(this->root_node, report_func, pd); try { - this->root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); + // FIXME: put cache somewhere else as it's pretty useless now + QHash<std::string, CGAL_Nef_polyhedron> cache; + CGALEvaluator evaluator(cache, this->tree); + this->root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->root_node)); } catch (ProgressCancelException e) { PRINT("Rendering cancelled."); @@ -1194,64 +1232,70 @@ 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) { PRINTF(" Top level object is a 2D object:"); QApplication::processEvents(); - PRINTF(" Empty: %6s", this->root_N->p2.is_empty() ? "yes" : "no"); + PRINTF(" Empty: %6s", this->root_N->p2->is_empty() ? "yes" : "no"); QApplication::processEvents(); - PRINTF(" Plane: %6s", this->root_N->p2.is_plane() ? "yes" : "no"); + PRINTF(" Plane: %6s", this->root_N->p2->is_plane() ? "yes" : "no"); QApplication::processEvents(); - PRINTF(" Vertices: %6d", (int)this->root_N->p2.explorer().number_of_vertices()); + PRINTF(" Vertices: %6d", (int)this->root_N->p2->explorer().number_of_vertices()); QApplication::processEvents(); - PRINTF(" Halfedges: %6d", (int)this->root_N->p2.explorer().number_of_halfedges()); + PRINTF(" Halfedges: %6d", (int)this->root_N->p2->explorer().number_of_halfedges()); QApplication::processEvents(); - PRINTF(" Edges: %6d", (int)this->root_N->p2.explorer().number_of_edges()); + PRINTF(" Edges: %6d", (int)this->root_N->p2->explorer().number_of_edges()); QApplication::processEvents(); - PRINTF(" Faces: %6d", (int)this->root_N->p2.explorer().number_of_faces()); + PRINTF(" Faces: %6d", (int)this->root_N->p2->explorer().number_of_faces()); QApplication::processEvents(); - PRINTF(" FaceCycles: %6d", (int)this->root_N->p2.explorer().number_of_face_cycles()); + PRINTF(" FaceCycles: %6d", (int)this->root_N->p2->explorer().number_of_face_cycles()); QApplication::processEvents(); - PRINTF(" ConnComp: %6d", (int)this->root_N->p2.explorer().number_of_connected_components()); + PRINTF(" ConnComp: %6d", (int)this->root_N->p2->explorer().number_of_connected_components()); QApplication::processEvents(); } if (this->root_N->dim == 3) { PRINTF(" Top level object is a 3D object:"); - PRINTF(" Simple: %6s", this->root_N->p3.is_simple() ? "yes" : "no"); + PRINTF(" Simple: %6s", this->root_N->p3->is_simple() ? "yes" : "no"); QApplication::processEvents(); - PRINTF(" Valid: %6s", this->root_N->p3.is_valid() ? "yes" : "no"); + PRINTF(" Valid: %6s", this->root_N->p3->is_valid() ? "yes" : "no"); QApplication::processEvents(); - PRINTF(" Vertices: %6d", (int)this->root_N->p3.number_of_vertices()); + PRINTF(" Vertices: %6d", (int)this->root_N->p3->number_of_vertices()); QApplication::processEvents(); - PRINTF(" Halfedges: %6d", (int)this->root_N->p3.number_of_halfedges()); + PRINTF(" Halfedges: %6d", (int)this->root_N->p3->number_of_halfedges()); QApplication::processEvents(); - PRINTF(" Edges: %6d", (int)this->root_N->p3.number_of_edges()); + PRINTF(" Edges: %6d", (int)this->root_N->p3->number_of_edges()); QApplication::processEvents(); - PRINTF(" Halffacets: %6d", (int)this->root_N->p3.number_of_halffacets()); + PRINTF(" Halffacets: %6d", (int)this->root_N->p3->number_of_halffacets()); QApplication::processEvents(); - PRINTF(" Facets: %6d", (int)this->root_N->p3.number_of_facets()); + PRINTF(" Facets: %6d", (int)this->root_N->p3->number_of_facets()); QApplication::processEvents(); - PRINTF(" Volumes: %6d", (int)this->root_N->p3.number_of_volumes()); + PRINTF(" Volumes: %6d", (int)this->root_N->p3->number_of_volumes()); QApplication::processEvents(); } int s = t.elapsed() / 1000; PRINTF("Total rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - this->cgalRenderer = new CGALRenderer(*this->root_N); - // Go to CGAL view mode - if (viewActionCGALGrid->isChecked()) { - viewModeCGALGrid(); + if (!this->root_N->empty()) { + this->cgalRenderer = new CGALRenderer(*this->root_N); + // Go to CGAL view mode + if (viewActionCGALGrid->isChecked()) { + viewModeCGALGrid(); + } + else { + viewModeCGALSurface(); + } + + PRINT("Rendering finished."); } else { - viewModeCGALSurface(); + PRINT("WARNING: No top level geometry to render"); } - - PRINT("Rendering finished."); } #ifdef USE_PROGRESSWIDGET @@ -1272,7 +1316,7 @@ void MainWindow::actionDisplayAST() e->setWindowTitle("AST Dump"); e->setReadOnly(true); if (root_module) { - e->setPlainText(root_module->dump("", "")); + e->setPlainText(QString::fromStdString(root_module->dump("", ""))); } else { e->setPlainText("No AST to dump. Please try compiling first..."); } @@ -1289,8 +1333,8 @@ void MainWindow::actionDisplayCSGTree() e->setTabStopWidth(30); e->setWindowTitle("CSG Tree Dump"); e->setReadOnly(true); - if (root_node) { - e->setPlainText(root_node->dump("")); + if (this->root_node) { + e->setPlainText(QString::fromStdString(this->tree.getString(*this->root_node))); } else { e->setPlainText("No CSG to dump. Please try compiling first..."); } @@ -1307,7 +1351,7 @@ void MainWindow::actionDisplayCSGProducts() e->setTabStopWidth(30); e->setWindowTitle("CSG Products Dump"); e->setReadOnly(true); - e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? root_raw_term->dump() : "N/A", root_norm_term ? root_norm_term->dump() : "N/A", root_chain ? root_chain->dump() : "N/A", highlights_chain ? highlights_chain->dump() : "N/A", background_chain ? background_chain->dump() : "N/A")); + e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n").arg(root_raw_term ? QString::fromStdString(root_raw_term->dump()) : "N/A", root_norm_term ? QString::fromStdString(root_norm_term->dump()) : "N/A", root_chain ? QString::fromStdString(root_chain->dump()) : "N/A", highlights_chain ? QString::fromStdString(highlights_chain->dump()) : "N/A", background_chain ? QString::fromStdString(background_chain->dump()) : "N/A")); e->show(); e->resize(600, 400); clearCurrentOutput(); @@ -1319,6 +1363,8 @@ void MainWindow::actionExportSTLorOFF(bool stl_mode) void MainWindow::actionExportSTLorOFF(bool) #endif { + if (GuiLocker::isLocked()) return; + GuiLocker lock; #ifdef ENABLE_CGAL setCurrentOutput(); @@ -1334,14 +1380,16 @@ void MainWindow::actionExportSTLorOFF(bool) return; } - if (!this->root_N->p3.is_simple()) { + if (!this->root_N->p3->is_simple()) { PRINT("Object isn't a valid 2-manifold! Modify your design.."); clearCurrentOutput(); return; } + QString suffix = stl_mode ? ".stl" : ".off"; QString stl_filename = QFileDialog::getSaveFileName(this, - stl_mode ? "Export STL File" : "Export OFF File", "", + stl_mode ? "Export STL File" : "Export OFF File", + this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix, stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)"); if (stl_filename.isEmpty()) { PRINTF("No filename specified. %s export aborted.", stl_mode ? "STL" : "OFF"); @@ -1351,19 +1399,23 @@ void MainWindow::actionExportSTLorOFF(bool) QProgressDialog *pd = new QProgressDialog( stl_mode ? "Exporting object to STL file..." : "Exporting object to OFF file...", - QString(), 0, this->root_N->p3.number_of_facets() + 1); + QString(), 0, this->root_N->p3->number_of_facets() + 1); pd->setValue(0); pd->setAutoClose(false); pd->show(); QApplication::processEvents(); - if (stl_mode) - export_stl(this->root_N, stl_filename, pd); - else - export_off(this->root_N, stl_filename, pd); - - PRINTF("%s export finished.", stl_mode ? "STL" : "OFF"); + std::ofstream fstream(stl_filename.toUtf8()); + if (!fstream.is_open()) { + PRINTA("Can't open file \"%1\" for export", stl_filename); + } + else { + if (stl_mode) export_stl(this->root_N, fstream, pd); + else export_off(this->root_N, fstream, pd); + fstream.close(); + PRINTF("%s export finished.", stl_mode ? "STL" : "OFF"); + } delete pd; clearCurrentOutput(); @@ -1397,16 +1449,25 @@ void MainWindow::actionExportDXF() return; } - QString stl_filename = QFileDialog::getSaveFileName(this, - "Export DXF File", "", "DXF Files (*.dxf)"); - if (stl_filename.isEmpty()) { + QString dxf_filename = QFileDialog::getSaveFileName(this, + "Export DXF File", + this->fileName.isEmpty() ? "Untitled.dxf" : QFileInfo(this->fileName).baseName()+".dxf", + "DXF Files (*.dxf)"); + if (dxf_filename.isEmpty()) { PRINTF("No filename specified. DXF export aborted."); clearCurrentOutput(); return; } - export_dxf(this->root_N, stl_filename, NULL); - PRINTF("DXF export finished."); + std::ofstream fstream(dxf_filename.toUtf8()); + if (!fstream.is_open()) { + PRINTA("Can't open file \"%s\" for export", dxf_filename); + } + else { + export_dxf(this->root_N, fstream, NULL); + fstream.close(); + PRINTF("DXF export finished."); + } clearCurrentOutput(); #endif /* ENABLE_CGAL */ @@ -1414,7 +1475,7 @@ void MainWindow::actionExportDXF() void MainWindow::actionExportImage() { - QImage img = screen->grabFrameBuffer(); + QImage img = this->glview->grabFrameBuffer(); setCurrentOutput(); QString img_filename = QFileDialog::getSaveFileName(this, @@ -1432,9 +1493,12 @@ void MainWindow::actionExportImage() void MainWindow::actionFlushCaches() { - PolySet::ps_cache.clear(); +// FIXME: Polycache -> PolySetEvaluator +// FIXME: PolySetEvaluator->clearCache(); #ifdef ENABLE_CGAL - AbstractNode::cgal_nef_cache.clear(); +// FIXME: Flush caches through whatever channels we have + // CGALEvaluator::evaluator()->getCache().clear(); + // this->dumper->clearCache(); #endif dxf_dim_cache.clear(); dxf_cross_cache.clear(); @@ -1459,10 +1523,10 @@ void MainWindow::viewModeActionsUncheck() */ void MainWindow::viewModeOpenCSG() { - if (screen->hasOpenCSGSupport()) { + if (this->glview->hasOpenCSGSupport()) { viewModeActionsUncheck(); viewActionOpenCSG->setChecked(true); - screen->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer); + this->glview->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer); } else { viewModeThrownTogether(); } @@ -1476,17 +1540,17 @@ void MainWindow::viewModeCGALSurface() { viewModeActionsUncheck(); viewActionCGALSurfaces->setChecked(true); - screen->setShowFaces(true); - screen->setRenderer(this->cgalRenderer); - screen->updateGL(); + this->glview->setShowFaces(true); + this->glview->setRenderer(this->cgalRenderer); + this->glview->updateGL(); } void MainWindow::viewModeCGALGrid() { viewModeActionsUncheck(); viewActionCGALGrid->setChecked(true); - screen->setShowFaces(false); - screen->setRenderer(this->cgalRenderer); + this->glview->setShowFaces(false); + this->glview->setRenderer(this->cgalRenderer); } #endif /* ENABLE_CGAL */ @@ -1495,31 +1559,31 @@ void MainWindow::viewModeThrownTogether() { viewModeActionsUncheck(); viewActionThrownTogether->setChecked(true); - screen->setRenderer(this->thrownTogetherRenderer); + this->glview->setRenderer(this->thrownTogetherRenderer); } void MainWindow::viewModeShowEdges() { QSettings settings; settings.setValue("view/showEdges",viewActionShowEdges->isChecked()); - screen->setShowEdges(viewActionShowEdges->isChecked()); - screen->updateGL(); + this->glview->setShowEdges(viewActionShowEdges->isChecked()); + this->glview->updateGL(); } void MainWindow::viewModeShowAxes() { QSettings settings; settings.setValue("view/showAxes",viewActionShowAxes->isChecked()); - screen->setShowAxes(viewActionShowAxes->isChecked()); - screen->updateGL(); + this->glview->setShowAxes(viewActionShowAxes->isChecked()); + this->glview->updateGL(); } void MainWindow::viewModeShowCrosshairs() { QSettings settings; settings.setValue("view/showCrosshairs",viewActionShowCrosshairs->isChecked()); - screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); - screen->updateGL(); + this->glview->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); + this->glview->updateGL(); } void MainWindow::viewModeAnimate() @@ -1557,66 +1621,66 @@ void MainWindow::animateUpdate() void MainWindow::viewAngleTop() { - screen->object_rot_x = 90; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); + this->glview->object_rot_x = 90; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 0; + this->glview->updateGL(); } void MainWindow::viewAngleBottom() { - screen->object_rot_x = 270; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); + this->glview->object_rot_x = 270; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 0; + this->glview->updateGL(); } void MainWindow::viewAngleLeft() { - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 90; - screen->updateGL(); + this->glview->object_rot_x = 0; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 90; + this->glview->updateGL(); } void MainWindow::viewAngleRight() { - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 270; - screen->updateGL(); + this->glview->object_rot_x = 0; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 270; + this->glview->updateGL(); } void MainWindow::viewAngleFront() { - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 0; - screen->updateGL(); + this->glview->object_rot_x = 0; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 0; + this->glview->updateGL(); } void MainWindow::viewAngleBack() { - screen->object_rot_x = 0; - screen->object_rot_y = 0; - screen->object_rot_z = 180; - screen->updateGL(); + this->glview->object_rot_x = 0; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 180; + this->glview->updateGL(); } void MainWindow::viewAngleDiagonal() { - screen->object_rot_x = 35; - screen->object_rot_y = 0; - screen->object_rot_z = 25; - screen->updateGL(); + this->glview->object_rot_x = 35; + this->glview->object_rot_y = 0; + this->glview->object_rot_z = 25; + this->glview->updateGL(); } void MainWindow::viewCenter() { - screen->object_trans_x = 0; - screen->object_trans_y = 0; - screen->object_trans_z = 0; - screen->updateGL(); + this->glview->object_trans_x = 0; + this->glview->object_trans_y = 0; + this->glview->object_trans_z = 0; + this->glview->updateGL(); } void MainWindow::viewPerspective() @@ -1625,8 +1689,8 @@ void MainWindow::viewPerspective() settings.setValue("view/orthogonalProjection",false); viewActionPerspective->setChecked(true); viewActionOrthogonal->setChecked(false); - screen->setOrthoMode(false); - screen->updateGL(); + this->glview->setOrthoMode(false); + this->glview->updateGL(); } void MainWindow::viewOrthogonal() @@ -1635,8 +1699,8 @@ void MainWindow::viewOrthogonal() settings.setValue("view/orthogonalProjection",true); viewActionPerspective->setChecked(false); viewActionOrthogonal->setChecked(true); - screen->setOrthoMode(true); - screen->updateGL(); + this->glview->setOrthoMode(true); + this->glview->updateGL(); } void MainWindow::hideConsole() diff --git a/src/module.cc b/src/module.cc index 54b151c..0b17ff7 100644 --- a/src/module.cc +++ b/src/module.cc @@ -31,6 +31,8 @@ #include "function.h" #include "builtin.h" #include "printutils.h" +#include <boost/foreach.hpp> +#include <sstream> AbstractModule::~AbstractModule() { @@ -40,91 +42,102 @@ AbstractNode *AbstractModule::evaluate(const Context*, const ModuleInstantiation { AbstractNode *node = new AbstractNode(inst); - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } + node->children = inst->evaluateChildren(); return node; } -QString AbstractModule::dump(QString indent, QString name) const +std::string AbstractModule::dump(const std::string &indent, const std::string &name) const { - return QString("%1abstract module %2();\n").arg(indent, name); + std::stringstream dump; + dump << indent << "abstract module " << name << "();\n"; + return dump.str(); } ModuleInstantiation::~ModuleInstantiation() { - foreach (Expression *v, argexpr) - delete v; - foreach (ModuleInstantiation *v, children) - delete v; + BOOST_FOREACH (Expression *v, argexpr) delete v; + BOOST_FOREACH (ModuleInstantiation *v, children) delete v; } IfElseModuleInstantiation::~IfElseModuleInstantiation() { - foreach (ModuleInstantiation *v, else_children) - delete v; + BOOST_FOREACH (ModuleInstantiation *v, else_children) delete v; } -QString ModuleInstantiation::dump(QString indent) const +std::string ModuleInstantiation::dump(const std::string &indent) const { - QString text = indent; - if (!label.isEmpty()) - text += label + QString(": "); - text += modname + QString("("); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - if (!argnames[i].isEmpty()) - text += argnames[i] + QString(" = "); - text += argexpr[i]->dump(); + std::stringstream dump; + dump << indent; + if (!label.empty()) dump << label <<": "; + dump << modname + "("; + for (size_t i=0; i < argnames.size(); i++) { + if (i > 0) dump << ", "; + if (!argnames[i].empty()) dump << argnames[i] << " = "; + dump << *argexpr[i]; } if (children.size() == 0) { - text += QString(");\n"); + dump << ");\n"; } else if (children.size() == 1) { - text += QString(")\n"); - text += children[0]->dump(indent + QString("\t")); + dump << ")\n"; + dump << children[0]->dump(indent + "\t"); } else { - text += QString(") {\n"); - for (int i = 0; i < children.size(); i++) { - text += children[i]->dump(indent + QString("\t")); + dump << ") {\n"; + for (size_t i = 0; i < children.size(); i++) { + dump << children[i]->dump(indent + "\t"); } - text += QString("%1}\n").arg(indent); + dump << indent << "}\n"; } - return text; + return dump.str(); } AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const { AbstractNode *node = NULL; if (this->ctx) { - PRINTA("WARNING: Ignoring recursive module instanciation of '%1'.", modname); + PRINTF("WARNING: Ignoring recursive module instantiation of '%s'.", modname.c_str()); } else { ModuleInstantiation *that = (ModuleInstantiation*)this; that->argvalues.clear(); - foreach (Expression *v, that->argexpr) { - that->argvalues.append(v->evaluate(ctx)); + BOOST_FOREACH (Expression *v, that->argexpr) { + that->argvalues.push_back(v->evaluate(ctx)); } that->ctx = ctx; - node = ctx->evaluate_module(this); + node = ctx->evaluate_module(*this); that->ctx = NULL; that->argvalues.clear(); } return node; } +std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *ctx) const +{ + if (!ctx) ctx = this->ctx; + std::vector<AbstractNode*> childnodes; + BOOST_FOREACH (ModuleInstantiation *v, this->children) { + AbstractNode *n = v->evaluate(ctx); + if (n != NULL) childnodes.push_back(n); + } + return childnodes; +} + +std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const Context *ctx) const +{ + if (!ctx) ctx = this->ctx; + std::vector<AbstractNode*> childnodes; + BOOST_FOREACH (ModuleInstantiation *v, this->else_children) { + AbstractNode *n = v->evaluate(this->ctx); + if (n != NULL) childnodes.push_back(n); + } + return childnodes; +} + Module::~Module() { - foreach (Expression *v, assignments_expr) - delete v; - foreach (AbstractFunction *v, functions) - delete v; - foreach (AbstractModule *v, modules) - delete v; - foreach (ModuleInstantiation *v, children) - delete v; + BOOST_FOREACH (Expression *v, assignments_expr) delete v; + BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second; + BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second; + BOOST_FOREACH (ModuleInstantiation *v, children) delete v; } AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst) const @@ -143,62 +156,53 @@ AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *in else c.usedlibs_p = NULL; - for (int i = 0; i < assignments_var.size(); i++) { + for (size_t i = 0; i < assignments_var.size(); i++) { c.set_variable(assignments_var[i], assignments_expr[i]->evaluate(&c)); } AbstractNode *node = new AbstractNode(inst); - for (int i = 0; i < children.size(); i++) { + for (size_t i = 0; i < children.size(); i++) { AbstractNode *n = children[i]->evaluate(&c); if (n != NULL) - node->children.append(n); + node->children.push_back(n); } return node; } -QString Module::dump(QString indent, QString name) const +std::string Module::dump(const std::string &indent, const std::string &name) const { - QString text, tab; - if (!name.isEmpty()) { - text = QString("%1module %2(").arg(indent, name); - for (int i=0; i < argnames.size(); i++) { - if (i > 0) - text += QString(", "); - text += argnames[i]; - if (argexpr[i]) - text += QString(" = ") + argexpr[i]->dump(); + std::stringstream dump; + std::string tab; + if (!name.empty()) { + dump << indent << "module " << name << "("; + for (size_t i=0; i < argnames.size(); i++) { + if (i > 0) dump << ", "; + dump << argnames[i]; + if (argexpr[i]) dump << " = " << *argexpr[i]; } - text += QString(") {\n"); + dump << ") {\n"; tab = "\t"; } - { - QHashIterator<QString, AbstractFunction*> i(functions); - while (i.hasNext()) { - i.next(); - text += i.value()->dump(indent + tab, i.key()); - } + BOOST_FOREACH(const FunctionContainer::value_type &f, functions) { + dump << f.second->dump(indent + tab, f.first); } - { - QHashIterator<QString, AbstractModule*> i(modules); - while (i.hasNext()) { - i.next(); - text += i.value()->dump(indent + tab, i.key()); - } + BOOST_FOREACH(const AbstractModuleContainer::value_type &m, modules) { + dump << m.second->dump(indent + tab, m.first); } - for (int i = 0; i < assignments_var.size(); i++) { - text += QString("%1%2 = %3;\n").arg(indent + tab, assignments_var[i], assignments_expr[i]->dump()); + for (size_t i = 0; i < assignments_var.size(); i++) { + dump << indent << tab << assignments_var[i] << " = " << *assignments_expr[i] << ";\n"; } - for (int i = 0; i < children.size(); i++) { - text += children[i]->dump(indent + tab); + for (size_t i = 0; i < children.size(); i++) { + dump << children[i]->dump(indent + tab); } - if (!name.isEmpty()) { - text += QString("%1}\n").arg(indent); + if (!name.empty()) { + dump << indent << "}\n"; } - return text; + return dump.str(); } -QHash<QString, AbstractModule*> builtin_modules; +Module::AbstractModuleContainer builtin_modules; void initialize_builtin_modules() { @@ -206,6 +210,7 @@ void initialize_builtin_modules() register_builtin_csgops(); register_builtin_transform(); + register_builtin_color(); register_builtin_primitives(); register_builtin_surface(); register_builtin_control(); @@ -219,7 +224,8 @@ void initialize_builtin_modules() void destroy_builtin_modules() { - foreach (AbstractModule *v, builtin_modules) - delete v; + BOOST_FOREACH(Module::AbstractModuleContainer::value_type &m, builtin_modules) { + delete m.second; + } builtin_modules.clear(); } diff --git a/src/module.h b/src/module.h index b680ea1..c28ab34 100644 --- a/src/module.h +++ b/src/module.h @@ -1,20 +1,20 @@ #ifndef MODULE_H_ #define MODULE_H_ -#include <QString> -#include <QVector> -#include <QHash> +#include <string> +#include <vector> +#include <boost/unordered_map.hpp> #include "value.h" class ModuleInstantiation { public: - QString label; - QString modname; - QVector<QString> argnames; - QVector<class Expression*> argexpr; - QVector<Value> argvalues; - QVector<ModuleInstantiation*> children; + std::string label; + std::string modname; + std::vector<std::string> argnames; + std::vector<class Expression*> argexpr; + std::vector<Value> argvalues; + std::vector<ModuleInstantiation*> children; bool tag_root; bool tag_highlight; @@ -24,15 +24,17 @@ public: ModuleInstantiation() : tag_root(false), tag_highlight(false), tag_background(false), ctx(NULL) { } virtual ~ModuleInstantiation(); - QString dump(QString indent) const; + std::string dump(const std::string &indent) const; class AbstractNode *evaluate(const Context *ctx) const; + std::vector<AbstractNode*> evaluateChildren(const Context *ctx = NULL) const; }; class IfElseModuleInstantiation : public ModuleInstantiation { public: virtual ~IfElseModuleInstantiation(); + std::vector<AbstractNode*> evaluateElseChildren(const Context *ctx = NULL) const; - QVector<ModuleInstantiation*> else_children; + std::vector<ModuleInstantiation*> else_children; }; class AbstractModule @@ -40,37 +42,40 @@ class AbstractModule public: virtual ~AbstractModule(); virtual class AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; - virtual QString dump(QString indent, QString name) const; + virtual std::string dump(const std::string &indent, const std::string &name) const; }; class Module : public AbstractModule { public: - QHash< QString, Module*> usedlibs; + typedef boost::unordered_map<std::string, class Module*> ModuleContainer; + ModuleContainer usedlibs; struct libs_cache_ent { Module *mod; - QString cache_id, msg; + std::string cache_id, msg; }; - static QHash<QString, libs_cache_ent> libs_cache; - static Module *compile_library(QString filename); + static boost::unordered_map<std::string, libs_cache_ent> libs_cache; + static Module *compile_library(std::string filename); - QVector<QString> argnames; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; - QVector<QString> assignments_var; - QVector<Expression*> assignments_expr; + std::vector<std::string> assignments_var; + std::vector<Expression*> assignments_expr; - QHash<QString, class AbstractFunction*> functions; - QHash<QString, AbstractModule*> modules; + typedef boost::unordered_map<std::string, class AbstractFunction*> FunctionContainer; + FunctionContainer functions; + typedef boost::unordered_map<std::string, AbstractModule*> AbstractModuleContainer; + AbstractModuleContainer modules; - QVector<ModuleInstantiation*> children; + std::vector<ModuleInstantiation*> children; Module() { } virtual ~Module(); virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; - virtual QString dump(QString indent, QString name) const; + virtual std::string dump(const std::string &indent, const std::string &name) const; }; #endif diff --git a/src/myqhash.h b/src/myqhash.h new file mode 100644 index 0000000..9156ec2 --- /dev/null +++ b/src/myqhash.h @@ -0,0 +1,16 @@ +#ifndef OPENSCAD_QHASH_H_ +#define OPENSCAD_QHASH_H_ + +/*! + Defines a qHash for std::string. + + Note that this header must be included before Qt headers (at least + before qhash.h) to take effect. + */ + +#include <qglobal.h> +#include <string> +extern uint qHash(const std::string &); +#include <QHash> + +#endif diff --git a/src/namedcolors.cpp b/src/namedcolors.cpp new file mode 100644 index 0000000..3150a40 --- /dev/null +++ b/src/namedcolors.cpp @@ -0,0 +1,155 @@ +#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b) + +static const struct RGBData { + const char *name; + uint value; +} named_colors[] = { + { "aliceblue", rgb(240, 248, 255) }, + { "antiquewhite", rgb(250, 235, 215) }, + { "aqua", rgb( 0, 255, 255) }, + { "aquamarine", rgb(127, 255, 212) }, + { "azure", rgb(240, 255, 255) }, + { "beige", rgb(245, 245, 220) }, + { "bisque", rgb(255, 228, 196) }, + { "black", rgb( 0, 0, 0) }, + { "blanchedalmond", rgb(255, 235, 205) }, + { "blue", rgb( 0, 0, 255) }, + { "blueviolet", rgb(138, 43, 226) }, + { "brown", rgb(165, 42, 42) }, + { "burlywood", rgb(222, 184, 135) }, + { "cadetblue", rgb( 95, 158, 160) }, + { "chartreuse", rgb(127, 255, 0) }, + { "chocolate", rgb(210, 105, 30) }, + { "coral", rgb(255, 127, 80) }, + { "cornflowerblue", rgb(100, 149, 237) }, + { "cornsilk", rgb(255, 248, 220) }, + { "crimson", rgb(220, 20, 60) }, + { "cyan", rgb( 0, 255, 255) }, + { "darkblue", rgb( 0, 0, 139) }, + { "darkcyan", rgb( 0, 139, 139) }, + { "darkgoldenrod", rgb(184, 134, 11) }, + { "darkgray", rgb(169, 169, 169) }, + { "darkgreen", rgb( 0, 100, 0) }, + { "darkgrey", rgb(169, 169, 169) }, + { "darkkhaki", rgb(189, 183, 107) }, + { "darkmagenta", rgb(139, 0, 139) }, + { "darkolivegreen", rgb( 85, 107, 47) }, + { "darkorange", rgb(255, 140, 0) }, + { "darkorchid", rgb(153, 50, 204) }, + { "darkred", rgb(139, 0, 0) }, + { "darksalmon", rgb(233, 150, 122) }, + { "darkseagreen", rgb(143, 188, 143) }, + { "darkslateblue", rgb( 72, 61, 139) }, + { "darkslategray", rgb( 47, 79, 79) }, + { "darkslategrey", rgb( 47, 79, 79) }, + { "darkturquoise", rgb( 0, 206, 209) }, + { "darkviolet", rgb(148, 0, 211) }, + { "deeppink", rgb(255, 20, 147) }, + { "deepskyblue", rgb( 0, 191, 255) }, + { "dimgray", rgb(105, 105, 105) }, + { "dimgrey", rgb(105, 105, 105) }, + { "dodgerblue", rgb( 30, 144, 255) }, + { "firebrick", rgb(178, 34, 34) }, + { "floralwhite", rgb(255, 250, 240) }, + { "forestgreen", rgb( 34, 139, 34) }, + { "fuchsia", rgb(255, 0, 255) }, + { "gainsboro", rgb(220, 220, 220) }, + { "ghostwhite", rgb(248, 248, 255) }, + { "gold", rgb(255, 215, 0) }, + { "goldenrod", rgb(218, 165, 32) }, + { "gray", rgb(128, 128, 128) }, + { "green", rgb( 0, 128, 0) }, + { "greenyellow", rgb(173, 255, 47) }, + { "grey", rgb(128, 128, 128) }, + { "honeydew", rgb(240, 255, 240) }, + { "hotpink", rgb(255, 105, 180) }, + { "indianred", rgb(205, 92, 92) }, + { "indigo", rgb( 75, 0, 130) }, + { "ivory", rgb(255, 255, 240) }, + { "khaki", rgb(240, 230, 140) }, + { "lavender", rgb(230, 230, 250) }, + { "lavenderblush", rgb(255, 240, 245) }, + { "lawngreen", rgb(124, 252, 0) }, + { "lemonchiffon", rgb(255, 250, 205) }, + { "lightblue", rgb(173, 216, 230) }, + { "lightcoral", rgb(240, 128, 128) }, + { "lightcyan", rgb(224, 255, 255) }, + { "lightgoldenrodyellow", rgb(250, 250, 210) }, + { "lightgray", rgb(211, 211, 211) }, + { "lightgreen", rgb(144, 238, 144) }, + { "lightgrey", rgb(211, 211, 211) }, + { "lightpink", rgb(255, 182, 193) }, + { "lightsalmon", rgb(255, 160, 122) }, + { "lightseagreen", rgb( 32, 178, 170) }, + { "lightskyblue", rgb(135, 206, 250) }, + { "lightslategray", rgb(119, 136, 153) }, + { "lightslategrey", rgb(119, 136, 153) }, + { "lightsteelblue", rgb(176, 196, 222) }, + { "lightyellow", rgb(255, 255, 224) }, + { "lime", rgb( 0, 255, 0) }, + { "limegreen", rgb( 50, 205, 50) }, + { "linen", rgb(250, 240, 230) }, + { "magenta", rgb(255, 0, 255) }, + { "maroon", rgb(128, 0, 0) }, + { "mediumaquamarine", rgb(102, 205, 170) }, + { "mediumblue", rgb( 0, 0, 205) }, + { "mediumorchid", rgb(186, 85, 211) }, + { "mediumpurple", rgb(147, 112, 219) }, + { "mediumseagreen", rgb( 60, 179, 113) }, + { "mediumslateblue", rgb(123, 104, 238) }, + { "mediumspringgreen", rgb( 0, 250, 154) }, + { "mediumturquoise", rgb( 72, 209, 204) }, + { "mediumvioletred", rgb(199, 21, 133) }, + { "midnightblue", rgb( 25, 25, 112) }, + { "mintcream", rgb(245, 255, 250) }, + { "mistyrose", rgb(255, 228, 225) }, + { "moccasin", rgb(255, 228, 181) }, + { "navajowhite", rgb(255, 222, 173) }, + { "navy", rgb( 0, 0, 128) }, + { "oldlace", rgb(253, 245, 230) }, + { "olive", rgb(128, 128, 0) }, + { "olivedrab", rgb(107, 142, 35) }, + { "orange", rgb(255, 165, 0) }, + { "orangered", rgb(255, 69, 0) }, + { "orchid", rgb(218, 112, 214) }, + { "palegoldenrod", rgb(238, 232, 170) }, + { "palegreen", rgb(152, 251, 152) }, + { "paleturquoise", rgb(175, 238, 238) }, + { "palevioletred", rgb(219, 112, 147) }, + { "papayawhip", rgb(255, 239, 213) }, + { "peachpuff", rgb(255, 218, 185) }, + { "peru", rgb(205, 133, 63) }, + { "pink", rgb(255, 192, 203) }, + { "plum", rgb(221, 160, 221) }, + { "powderblue", rgb(176, 224, 230) }, + { "purple", rgb(128, 0, 128) }, + { "red", rgb(255, 0, 0) }, + { "rosybrown", rgb(188, 143, 143) }, + { "royalblue", rgb( 65, 105, 225) }, + { "saddlebrown", rgb(139, 69, 19) }, + { "salmon", rgb(250, 128, 114) }, + { "sandybrown", rgb(244, 164, 96) }, + { "seagreen", rgb( 46, 139, 87) }, + { "seashell", rgb(255, 245, 238) }, + { "sienna", rgb(160, 82, 45) }, + { "silver", rgb(192, 192, 192) }, + { "skyblue", rgb(135, 206, 235) }, + { "slateblue", rgb(106, 90, 205) }, + { "slategray", rgb(112, 128, 144) }, + { "slategrey", rgb(112, 128, 144) }, + { "snow", rgb(255, 250, 250) }, + { "springgreen", rgb( 0, 255, 127) }, + { "steelblue", rgb( 70, 130, 180) }, + { "tan", rgb(210, 180, 140) }, + { "teal", rgb( 0, 128, 128) }, + { "thistle", rgb(216, 191, 216) }, + { "tomato", rgb(255, 99, 71) }, + { "transparent", 0 }, + { "turquoise", rgb( 64, 224, 208) }, + { "violet", rgb(238, 130, 238) }, + { "wheat", rgb(245, 222, 179) }, + { "white", rgb(255, 255, 255) }, + { "whitesmoke", rgb(245, 245, 245) }, + { "yellow", rgb(255, 255, 0) }, + { "yellowgreen", rgb(154, 205, 50) } +}; diff --git a/src/node.cc b/src/node.cc index 87b3c2b..e2f3fa0 100644 --- a/src/node.cc +++ b/src/node.cc @@ -30,9 +30,14 @@ #include "csgterm.h" #include "progress.h" #include "polyset.h" -#include <QRegExp> +#include "visitor.h" +#include "nodedumper.h" +#include "stl-utils.h" -int AbstractNode::idx_counter; +#include <sstream> +#include <algorithm> + +size_t AbstractNode::idx_counter; AbstractNode::AbstractNode(const ModuleInstantiation *mi) { @@ -42,139 +47,47 @@ AbstractNode::AbstractNode(const ModuleInstantiation *mi) AbstractNode::~AbstractNode() { - foreach (AbstractNode *v, children) - delete v; -} - -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; -} - -#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->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - if (intersect) - N.p2 *= v->render_cgal_nef_polyhedron().p2; - else - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else { - if (intersect) - N.p3 *= v->render_cgal_nef_polyhedron().p3; - else - N.p3 += v->render_cgal_nef_polyhedron().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; + std::for_each(this->children.begin(), this->children.end(), del_fun<AbstractNode>()); } -CGAL_Nef_polyhedron AbstractNode::render_cgal_nef_polyhedron() const +Response AbstractNode::accept(class State &state, Visitor &visitor) const { - return render_cgal_nef_polyhedron_backend(this, false); + return visitor.visit(state, *this); } -CGAL_Nef_polyhedron AbstractIntersectionNode::render_cgal_nef_polyhedron() const +Response AbstractIntersectionNode::accept(class State &state, Visitor &visitor) const { - return render_cgal_nef_polyhedron_backend(this, true); + return visitor.visit(state, *this); } -#endif /* ENABLE_CGAL */ - -static CSGTerm *render_csg_term_backend(const AbstractNode *that, bool intersect, double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) +Response AbstractPolyNode::accept(class State &state, Visitor &visitor) const { - 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; + return visitor.visit(state, *this); } -CSGTerm *AbstractNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +std::string AbstractNode::toString() const { - return render_csg_term_backend(this, false, m, highlights, background); + return this->name() + "()"; } -CSGTerm *AbstractIntersectionNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +std::string AbstractNode::name() const { - return render_csg_term_backend(this, true, m, highlights, background); + return "group"; } -QString AbstractNode::dump(QString indent) const +std::string AbstractIntersectionNode::toString() const { - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: group() {\n").arg(idx); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; + return this->name() + "()"; } -QString AbstractIntersectionNode::dump(QString indent) const +std::string AbstractIntersectionNode::name() const { - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: intersection() {\n").arg(idx); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; + return "intersection_for"; } void AbstractNode::progress_prepare() { - foreach (AbstractNode *v, children) - v->progress_prepare(); + std::for_each(this->children.begin(), this->children.end(), std::mem_fun(&AbstractNode::progress_prepare)); this->progress_mark = ++progress_report_count; } @@ -183,52 +96,8 @@ void AbstractNode::progress_report() const progress_update(this, this->progress_mark); } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron AbstractPolyNode::render_cgal_nef_polyhedron() const +std::ostream &operator<<(std::ostream &stream, const AbstractNode &node) { - 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->render_cgal_nef_polyhedron(); - 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; - } + stream << node.toString(); + return stream; } - -#endif /* ENABLE_CGAL */ - -CSGTerm *AbstractPolyNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ - PolySet *ps = render_polyset(RENDER_OPENCSG); - return render_csg_term_from_ps(m, highlights, background, ps, modinst, idx); -} - -CSGTerm *AbstractPolyNode::render_csg_term_from_ps(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, PolySet *ps, const ModuleInstantiation *modinst, int idx) -{ - CSGTerm *t = new CSGTerm(ps, m, QString("n%1").arg(idx)); - if (modinst->tag_highlight && highlights) - highlights->append(t->link()); - if (modinst->tag_background && background) { - background->append(t); - return NULL; - } - return t; -} - @@ -1,12 +1,10 @@ #ifndef NODE_H_ #define NODE_H_ -#include <QCache> -#include <QVector> +#include <vector> +#include <string> -#ifdef ENABLE_CGAL -#include "cgal.h" -#endif +#include "traverser.h" extern int progress_report_count; extern void (*progress_report_f)(const class AbstractNode*, void*, int); @@ -15,64 +13,78 @@ extern void *progress_report_vp; void progress_report_prep(AbstractNode *root, void (*f)(const class AbstractNode *node, void *vp, int mark), void *vp); void progress_report_fin(); +/*! + + The node tree is the result of evaluation of a module instantiation + tree. Both the module tree and the node tree are regenerated from + scratch for each compile. + + */ class AbstractNode { - static int idx_counter; // Node instantiation index + // FIXME: the idx_counter/idx is mostly (only?) for debugging. + // We can hash on pointer value or smth. else. + // -> remove and + // use smth. else to display node identifier in CSG tree output? + static size_t idx_counter; // Node instantiation index public: + AbstractNode(const class ModuleInstantiation *mi); + virtual ~AbstractNode(); + virtual Response accept(class State &state, class Visitor &visitor) const; + virtual std::string toString() const; + /*! The 'OpenSCAD name' of this node, defaults to classname, but can be + overloaded to provide specialization for e.g. CSG nodes, primitive nodes etc. + Used for human-readable output. */ + virtual std::string name() const; + + // FIXME: Make return value a reference + const std::vector<AbstractNode*> &getChildren() const { + return this->children; + } + size_t index() const { return this->idx; } + static void resetIndexCounter() { idx_counter = 1; } - QVector<AbstractNode*> children; - const class ModuleInstantiation *modinst; + // FIXME: Make protected + std::vector<AbstractNode*> children; + const ModuleInstantiation *modinst; + // progress_mark is a running number used for progress indication + // FIXME: Make all progress handling external, put it in the traverser class? int progress_mark; void progress_prepare(); void progress_report() const; - int idx; - QString dump_cache; - - AbstractNode(const ModuleInstantiation *mi); - virtual ~AbstractNode(); - virtual QString mk_cache_id() const; -#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 render_cgal_nef_polyhedron() const; - class CSGTerm *render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const; -#endif - virtual class CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; + int idx; // Node index (unique per tree) }; class AbstractIntersectionNode : public AbstractNode { public: AbstractIntersectionNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; + virtual ~AbstractIntersectionNode() { }; + virtual Response accept(class State &state, class Visitor &visitor) const; + virtual std::string toString() const; + virtual std::string name() const; }; class AbstractPolyNode : public AbstractNode { public: + AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; + virtual ~AbstractPolyNode() { }; + virtual Response accept(class State &state, class Visitor &visitor) const; + enum render_mode_e { RENDER_CGAL, RENDER_OPENCSG }; - AbstractPolyNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }; - virtual class PolySet *render_polyset(render_mode_e mode) const = 0; -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() 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); + /*! Should return a PolySet of the given geometry. It's normal to return an + empty PolySet if smth. is wrong, but don't return NULL unless we change the calling + strategy for this method. */ + virtual class PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const = 0; }; +std::ostream &operator<<(std::ostream &stream, const AbstractNode &node); + #endif diff --git a/src/nodecache.h b/src/nodecache.h new file mode 100644 index 0000000..cc3355e --- /dev/null +++ b/src/nodecache.h @@ -0,0 +1,46 @@ +#ifndef NODECACHE_H_ +#define NODECACHE_H_ + +#include <vector> +#include <string> +#include "node.h" + +/*! + Caches string values per node based on the node.index(). + The node index guaranteed to be unique per node tree since the index is reset + every time a new tree is generated. +*/ +class NodeCache +{ +public: + NodeCache() { } + virtual ~NodeCache() { } + + bool contains(const AbstractNode &node) const { + return !(*this)[node].empty(); + } + + const std::string & operator[](const AbstractNode &node) const { + if (this->cache.size() > node.index()) return this->cache[node.index()]; + else return this->nullvalue; + } + + void insert(const class AbstractNode &node, const std::string & value) { + if (this->cache.size() <= node.index()) this->cache.resize(node.index() + 1); + this->cache[node.index()] = value; + } + + void remove(const class AbstractNode &node) { + if (this->cache.size() > node.index()) this->cache[node.index()] = std::string(); + } + + void clear() { + this->cache.clear(); + } + +private: + std::vector<std::string> cache; + std::string nullvalue; +}; + +#endif diff --git a/src/nodedumper.cc b/src/nodedumper.cc new file mode 100644 index 0000000..d75c703 --- /dev/null +++ b/src/nodedumper.cc @@ -0,0 +1,103 @@ +#include "nodedumper.h" +#include <string> +#include <map> +#include <list> +#include "visitor.h" +#include "state.h" +#include "nodecache.h" + +#include <sstream> +#include <iostream> +#include <assert.h> + +/*! + \class NodeDumper + + A visitor responsible for creating a text dump of a node tree. Also + contains a cache for fast retrieval of the text representation of + any node or subtree. +*/ + +bool NodeDumper::isCached(const AbstractNode &node) const +{ + return !this->cache[node].empty(); +} + +/*! + Indent or deindent. Must be called before we output any children. +*/ +void NodeDumper::handleIndent(const State &state) +{ + if (state.isPrefix()) { + this->currindent += "\t"; + } + else if (state.isPostfix()) { + this->currindent.erase((this->currindent.length() >= 1) ? + this->currindent.length() - 1 : 0); + } +} + +/*! + Dumps the block of children contained in this->visitedchildren, + including braces and indentation. + All children are assumed to be cached already. + */ +string NodeDumper::dumpChildren(const AbstractNode &node) +{ + std::stringstream dump; + if (!this->visitedchildren[node.index()].empty()) { + dump << " {\n"; + + for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); + iter != this->visitedchildren[node.index()].end(); + iter++) { + assert(isCached(**iter)); + dump << this->cache[**iter] << "\n"; + } + + dump << this->currindent << "}"; + } + else { + dump << ";"; + } + return dump.str(); +} + +/*! + Called for each node in the tree. + Will abort traversal if we're cached +*/ +Response NodeDumper::visit(State &state, const AbstractNode &node) +{ + if (isCached(node)) return PruneTraversal; + + handleIndent(state); + if (state.isPostfix()) { + std::stringstream dump; + dump << this->currindent; + if (this->idprefix) dump << "n" << node.index() << ":"; + dump << node; + dump << dumpChildren(node); + this->cache.insert(node, dump.str()); + } + + handleVisitedChildren(state, node); + return ContinueTraversal; +} + +/*! + Adds this given node to its parent's child list. + Should be called for all nodes, including leaf nodes. +*/ +void NodeDumper::handleVisitedChildren(const State &state, const AbstractNode &node) +{ + if (state.isPostfix()) { + this->visitedchildren.erase(node.index()); + if (!state.parent()) { + this->root = &node; + } + else { + this->visitedchildren[state.parent()->index()].push_back(&node); + } + } +} diff --git a/src/nodedumper.h b/src/nodedumper.h new file mode 100644 index 0000000..efaf4fa --- /dev/null +++ b/src/nodedumper.h @@ -0,0 +1,40 @@ +#ifndef NODEDUMPER_H_ +#define NODEDUMPER_H_ + +#include <string> +#include <map> +#include <list> +#include "visitor.h" +#include "nodecache.h" + +using std::string; +using std::map; +using std::list; + +class NodeDumper : public Visitor +{ +public: + /*! If idPrefix is true, we will output "n<id>:" in front of each node, + which is useful for debugging. */ + NodeDumper(NodeCache &cache, bool idPrefix = false) : + cache(cache), idprefix(idPrefix), root(NULL) { } + virtual ~NodeDumper() {} + + virtual Response visit(State &state, const AbstractNode &node); + +private: + void handleVisitedChildren(const State &state, const AbstractNode &node); + bool isCached(const AbstractNode &node) const; + void handleIndent(const State &state); + string dumpChildren(const AbstractNode &node); + + NodeCache &cache; + bool idprefix; + + string currindent; + const AbstractNode *root; + typedef list<const AbstractNode *> ChildList; + map<int, ChildList> visitedchildren; +}; + +#endif diff --git a/src/openscad.cc b/src/openscad.cc index 180dbdf..c4b0d5f 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -24,6 +24,7 @@ * */ +#include "myqhash.h" #include "openscad.h" #include "MainWindow.h" #include "node.h" @@ -32,13 +33,19 @@ #include "value.h" #include "export.h" #include "builtin.h" +#include "nodedumper.h" +#include "printutils.h" +#include "handle_dep.h" #include <string> #include <vector> +#include <fstream> #ifdef ENABLE_CGAL -#include "cgal.h" +#include "CGAL_Nef_polyhedron.h" #include <CGAL/assertions_behaviour.h> +#include "CGALEvaluator.h" +#include "PolySetCGALEvaluator.h" #endif #include <QApplication> @@ -46,7 +53,10 @@ #include <QDir> #include <QSet> #include <QSettings> +#include <QTextStream> #include <boost/program_options.hpp> +#include <sstream> + #ifdef Q_WS_MAC #include "EventFilter.h" #include "AppleEvents.h" @@ -74,9 +84,7 @@ static void version() exit(1); } -QString commandline_commands; -const char *make_command = NULL; -QSet<QString> dependencies; +std::string commandline_commands; QString currentdir; QString examplesdir; QString librarydir; @@ -84,19 +92,6 @@ QString librarydir; using std::string; using std::vector; -void handle_dep(QString filename) -{ - if (filename.startsWith("/")) - dependencies.insert(filename); - else - dependencies.insert(QDir::currentPath() + QString("/") + filename); - if (!QFile(filename).exists() && make_command) { - char buffer[4096]; - snprintf(buffer, 4096, "%s '%s'", make_command, filename.replace("'", "'\\''").toUtf8().data()); - system(buffer); // FIXME: Handle error - } -} - int main(int argc, char **argv) { int rc = 0; @@ -194,8 +189,8 @@ int main(int argc, char **argv) const vector<string> &commands = vm["D"].as<vector<string> >(); for (vector<string>::const_iterator i = commands.begin(); i != commands.end(); i++) { - commandline_commands.append(i->c_str()); - commandline_commands.append(";\n"); + commandline_commands += *i; + commandline_commands += ";\n"; } } @@ -249,6 +244,17 @@ int main(int argc, char **argv) librarydir = libdir.path(); } + // Initialize global visitors + NodeCache nodecache; + NodeDumper dumper(nodecache); + Tree tree; +#ifdef ENABLE_CGAL + // FIXME: enforce some maximum cache size (old version had 100K vertices as limit) + QHash<std::string, CGAL_Nef_polyhedron> cache; + CGALEvaluator cgalevaluator(cache, tree); + PolySetCGALEvaluator psevaluator(cgalevaluator); +#endif + if (stl_output_file || off_output_file || dxf_output_file) { if (!filename) @@ -265,9 +271,9 @@ int main(int argc, char **argv) Value zero3; zero3.type = Value::VECTOR; - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); - zero3.vec.append(new Value(0.0)); + zero3.append(new Value(0.0)); + zero3.append(new Value(0.0)); + zero3.append(new Value(0.0)); root_ctx.set_variable("$vpt", zero3); root_ctx.set_variable("$vpr", zero3); @@ -283,15 +289,16 @@ int main(int argc, char **argv) fprintf(stderr, "Can't open input file `%s'!\n", filename); exit(1); } else { - QString text; + std::stringstream text; char buffer[513]; int ret; while ((ret = fread(buffer, 1, 512, fp)) > 0) { buffer[ret] = 0; - text += buffer; + text << buffer; } fclose(fp); - root_module = parse((text+commandline_commands).toAscii().data(), fileInfo.absolutePath().toLocal8Bit(), false); + text << commandline_commands; + root_module = parse(text.str().c_str(), fileInfo.absolutePath().toLocal8Bit(), false); } QDir::setCurrent(fileInfo.absolutePath()); @@ -299,41 +306,60 @@ 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->render_cgal_nef_polyhedron()); + tree.setRoot(root_node); + CGAL_Nef_polyhedron root_N = cgalevaluator.evaluateCGALMesh(*tree.root()); QDir::setCurrent(original_path.absolutePath()); if (deps_output_file) { - fp = fopen(deps_output_file, "wt"); - if (!fp) { - fprintf(stderr, "Can't open dependencies file `%s' for writing!\n", deps_output_file); + if (!write_deps(deps_output_file, + stl_output_file ? stl_output_file : off_output_file)) { exit(1); } - fprintf(fp, "%s:", stl_output_file ? stl_output_file : off_output_file); - QSetIterator<QString> i(dependencies); - while (i.hasNext()) - fprintf(fp, " \\\n\t%s", i.next().toUtf8().data()); - fprintf(fp, "\n"); - fclose(fp); } - if (root_N->dim == 3 && !root_N->p3.is_simple()) { - fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); - exit(1); + if (stl_output_file) { + if (root_N->dim == 3 && !root_N->p3.is_simple()) { + fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); + exit(1); + } + std::ofstream fstream(stl_output_file); + if (!fstream.is_open()) { + PRINTF("Can't open file \"%s\" for export", stl_output_file); + } + else { + export_stl(&root_N, fstream, NULL); + fstream.close(); + } } - if (stl_output_file) - export_stl(root_N, stl_output_file, NULL); - - if (off_output_file) - export_off(root_N, off_output_file, NULL); + if (off_output_file) { + if (root_N->dim == 3 && !root_N->p3.is_simple()) { + fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); + exit(1); + } + std::ofstream fstream(stl_output_file); + if (!fstream.is_open()) { + PRINTF("Can't open file \"%s\" for export", stl_output_file); + } + else { + export_off(&root_N, fstream, NULL); + fstream.close(); + } + } - if (dxf_output_file) - export_dxf(root_N, dxf_output_file, NULL); + if (dxf_output_file) { + std::ofstream fstream(dxf_output_file); + if (!fstream.is_open()) { + PRINTF("Can't open file \"%s\" for export", dxf_output_file); + } + else { + export_dxf(&root_N, fstream, NULL); + fstream.close(); + } + } delete root_node; - delete root_N; #else fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); exit(1); diff --git a/src/openscad.h b/src/openscad.h index e022668..9d97bac 100644 --- a/src/openscad.h +++ b/src/openscad.h @@ -41,12 +41,11 @@ extern class AbstractModule *parse(const char *text, const char *path, int debug); extern int get_fragments_from_r(double r, double fn, double fs, double fa); -#include <QString> -extern QString commandline_commands; +#include <string> +extern std::string commandline_commands; extern int parser_error_pos; -extern void handle_dep(QString filename); - +#include <QString> // The CWD when application started. We shouldn't change CWD, but until we stop // doing this, use currentdir to get the original CWD. extern QString currentdir; diff --git a/src/parser.y b/src/parser.y index aad5ba0..2d78d1f 100644 --- a/src/parser.y +++ b/src/parser.y @@ -44,6 +44,8 @@ #include "value.h" #include "function.h" #include "printutils.h" +#include <sstream> +#include <boost/foreach.hpp> int parser_error_pos = -1; @@ -54,18 +56,18 @@ int lexerget_lineno(void); int lexerlex_destroy(void); int lexerlex(void); -QVector<Module*> module_stack; +std::vector<Module*> module_stack; Module *module; class ArgContainer { -public: - QString argname; +public: + std::string argname; Expression *argexpr; }; class ArgsContainer { public: - QVector<QString> argnames; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; }; %} @@ -144,29 +146,29 @@ statement: '{' inner_input '}' | module_instantiation { if ($1) { - module->children.append($1); + module->children.push_back($1); } else { delete $1; } } | TOK_ID '=' expr ';' { bool add_new_assignment = true; - for (int i = 0; i < module->assignments_var.size(); i++) { - if (module->assignments_var[i] != QString($1)) + for (size_t i = 0; i < module->assignments_var.size(); i++) { + if (module->assignments_var[i] != $1) continue; delete module->assignments_expr[i]; module->assignments_expr[i] = $3; add_new_assignment = false; } if (add_new_assignment) { - module->assignments_var.append($1); - module->assignments_expr.append($3); + module->assignments_var.push_back($1); + module->assignments_expr.push_back($3); free($1); } } | TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' { Module *p = module; - module_stack.append(module); + module_stack.push_back(module); module = new Module(); p->modules[$2] = module; module->argnames = $4->argnames; @@ -174,7 +176,7 @@ statement: free($2); delete $4; } statement { - module = module_stack.last(); + module = module_stack.back(); module_stack.pop_back(); } | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr { @@ -192,7 +194,7 @@ children_instantiation: module_instantiation { $$ = new ModuleInstantiation(); if ($1) { - $$->children.append($1); + $$->children.push_back($1); } else { delete $1; } @@ -205,13 +207,13 @@ if_statement: TOK_IF '(' expr ')' children_instantiation { $$ = new IfElseModuleInstantiation(); $$->modname = "if"; - $$->argnames.append(QString()); - $$->argexpr.append($3); + $$->argnames.push_back(""); + $$->argexpr.push_back($3); if ($$) { $$->children = $5->children; } else { - for (int i = 0; i < $5->children.count(); i++) + for (size_t i = 0; i < $5->children.size(); i++) delete $5->children[i]; } $5->children.clear(); @@ -227,7 +229,7 @@ ifelse_statement: if ($$) { $$->else_children = $3->children; } else { - for (int i = 0; i < $3->children.count(); i++) + for (size_t i = 0; i < $3->children.size(); i++) delete $3->children[i]; } $3->children.clear(); @@ -243,7 +245,7 @@ module_instantiation: if ($$) { $$->children = $2->children; } else { - for (int i = 0; i < $2->children.count(); i++) + for (size_t i = 0; i < $2->children.size(); i++) delete $2->children[i]; } $2->children.clear(); @@ -261,7 +263,7 @@ module_instantiation_list: $$ = $1; if ($$) { if ($2) - $$->children.append($2); + $$->children.push_back($2); } else { delete $2; } @@ -270,7 +272,7 @@ module_instantiation_list: single_module_instantiation: TOK_ID '(' arguments_call ')' { $$ = new ModuleInstantiation(); - $$->modname = QString($1); + $$->modname = $1; $$->argnames = $3->argnames; $$->argexpr = $3->argexpr; free($1); @@ -279,7 +281,7 @@ single_module_instantiation: TOK_ID ':' single_module_instantiation { $$ = $3; if ($$) - $$->label = QString($1); + $$->label = $1; free($1); } | '!' single_module_instantiation { @@ -321,20 +323,20 @@ expr: TOK_ID { $$ = new Expression(); $$->type = "L"; - $$->var_name = QString($1); + $$->var_name = $1; free($1); } | expr '.' TOK_ID { $$ = new Expression(); $$->type = "N"; - $$->children.append($1); - $$->var_name = QString($3); + $$->children.push_back($1); + $$->var_name = $3; free($3); } | TOK_STRING { $$ = new Expression(); $$->type = "C"; - $$->const_value = new Value(QString($1)); + $$->const_value = new Value(std::string($1)); free($1); } | TOK_NUMBER { @@ -348,16 +350,16 @@ expr: e_one->const_value = new Value(1.0); $$ = new Expression(); $$->type = "R"; - $$->children.append($2); - $$->children.append(e_one); - $$->children.append($4); + $$->children.push_back($2); + $$->children.push_back(e_one); + $$->children.push_back($4); } | '[' expr ':' expr ':' expr ']' { $$ = new Expression(); $$->type = "R"; - $$->children.append($2); - $$->children.append($4); - $$->children.append($6); + $$->children.push_back($2); + $$->children.push_back($4); + $$->children.push_back($6); } | '[' optional_commas ']' { $$ = new Expression(); @@ -371,80 +373,80 @@ expr: expr '*' expr { $$ = new Expression(); $$->type = "*"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '/' expr { $$ = new Expression(); $$->type = "/"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '%' expr { $$ = new Expression(); $$->type = "%"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '+' expr { $$ = new Expression(); $$->type = "+"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '-' expr { $$ = new Expression(); $$->type = "-"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '<' expr { $$ = new Expression(); $$->type = "<"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr LE expr { $$ = new Expression(); $$->type = "<="; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr EQ expr { $$ = new Expression(); $$->type = "=="; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr NE expr { $$ = new Expression(); $$->type = "!="; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr GE expr { $$ = new Expression(); $$->type = ">="; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr '>' expr { $$ = new Expression(); $$->type = ">"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr AND expr { $$ = new Expression(); $$->type = "&&"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | expr OR expr { $$ = new Expression(); $$->type = "||"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | '+' expr { $$ = $2; @@ -452,12 +454,12 @@ expr: '-' expr { $$ = new Expression(); $$->type = "I"; - $$->children.append($2); + $$->children.push_back($2); } | '!' expr { $$ = new Expression(); $$->type = "!"; - $$->children.append($2); + $$->children.push_back($2); } | '(' expr ')' { $$ = $2; @@ -465,20 +467,20 @@ expr: expr '?' expr ':' expr { $$ = new Expression(); $$->type = "?:"; - $$->children.append($1); - $$->children.append($3); - $$->children.append($5); + $$->children.push_back($1); + $$->children.push_back($3); + $$->children.push_back($5); } | expr '[' expr ']' { $$ = new Expression(); $$->type = "[]"; - $$->children.append($1); - $$->children.append($3); + $$->children.push_back($1); + $$->children.push_back($3); } | TOK_ID '(' arguments_call ')' { $$ = new Expression(); $$->type = "F"; - $$->call_funcname = QString($1); + $$->call_funcname = $1; $$->call_argnames = $3->argnames; $$->children = $3->argexpr; free($1); @@ -492,11 +494,11 @@ vector_expr: expr { $$ = new Expression(); $$->type = 'V'; - $$->children.append($1); + $$->children.push_back($1); } | vector_expr ',' optional_commas expr { $$ = $1; - $$->children.append($4); + $$->children.push_back($4); } ; arguments_decl: @@ -505,27 +507,27 @@ arguments_decl: } | argument_decl { $$ = new ArgsContainer(); - $$->argnames.append($1->argname); - $$->argexpr.append($1->argexpr); + $$->argnames.push_back($1->argname); + $$->argexpr.push_back($1->argexpr); delete $1; } | arguments_decl ',' optional_commas argument_decl { $$ = $1; - $$->argnames.append($4->argname); - $$->argexpr.append($4->argexpr); + $$->argnames.push_back($4->argname); + $$->argexpr.push_back($4->argexpr); delete $4; } ; argument_decl: TOK_ID { $$ = new ArgContainer(); - $$->argname = QString($1); + $$->argname = $1; $$->argexpr = NULL; free($1); } | TOK_ID '=' expr { $$ = new ArgContainer(); - $$->argname = QString($1); + $$->argname = $1; $$->argexpr = $3; free($1); } ; @@ -536,14 +538,14 @@ arguments_call: } | argument_call { $$ = new ArgsContainer(); - $$->argnames.append($1->argname); - $$->argexpr.append($1->argexpr); + $$->argnames.push_back($1->argname); + $$->argexpr.push_back($1->argexpr); delete $1; } | arguments_call ',' optional_commas argument_call { $$ = $1; - $$->argnames.append($4->argname); - $$->argexpr.append($4->argexpr); + $$->argnames.push_back($4->argname); + $$->argexpr.push_back($4->argexpr); delete $4; } ; @@ -554,7 +556,7 @@ argument_call: } | TOK_ID '=' expr { $$ = new ArgContainer(); - $$->argname = QString($1); + $$->argname = $1; $$->argexpr = $3; free($1); } ; @@ -596,13 +598,11 @@ AbstractModule *parse(const char *text, const char *path, int debug) if (!module) return NULL; - QHashIterator<QString, Module*> i(module->usedlibs); - while (i.hasNext()) { - i.next(); - module->usedlibs[i.key()] = Module::compile_library(i.key()); - if (!module->usedlibs[i.key()]) { - PRINTF("WARNING: Failed to compile library `%s'.", i.key().toUtf8().data()); - module->usedlibs.remove(i.key()); + BOOST_FOREACH(Module::ModuleContainer::value_type &m, module->usedlibs) { + module->usedlibs[m.first] = Module::compile_library(m.first); + if (!module->usedlibs[m.first]) { + PRINTF("WARNING: Failed to compile library `%s'.", m.first.c_str()); + module->usedlibs.erase(m.first); } } @@ -610,46 +610,47 @@ AbstractModule *parse(const char *text, const char *path, int debug) return module; } -QHash<QString, Module::libs_cache_ent> Module::libs_cache; +boost::unordered_map<std::string, Module::libs_cache_ent> Module::libs_cache; -Module *Module::compile_library(QString filename) +Module *Module::compile_library(std::string filename) { struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(filename.toAscii().data(), &st); + stat(filename.c_str(), &st); - QString cache_id; - cache_id.sprintf("%x.%x", (int)st.st_mtime, (int)st.st_size); + std::stringstream idstream; + idstream << std::hex << st.st_mtime << "." << st.st_size; + std::string cache_id = idstream.str(); - if (libs_cache.contains(filename) && libs_cache[filename].cache_id == cache_id) { - PRINT(libs_cache[filename].msg); - return &(*libs_cache[filename].mod); + if (libs_cache.find(filename) != libs_cache.end() && libs_cache[filename].cache_id == cache_id) { + PRINTF("%s", libs_cache[filename].msg.c_str()); + return &(*libs_cache[filename].mod); } - QFile f(filename); + QFile f(QString::fromStdString(filename)); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTF("WARNING: Can't open library file `%s'.", filename.toUtf8().data()); + PRINTF("WARNING: Can't open library file `%s'.", filename.c_str()); return NULL; } QString text = QTextStream(&f).readAll(); print_messages_push(); - PRINTF("Compiling library `%s'.", filename.toUtf8().data()); - libs_cache_ent e = { NULL, cache_id, QString("WARNING: Library `%1' tries to recursively use itself!").arg(filename) }; - if (libs_cache.contains(filename)) + PRINTF("Compiling library `%s'.", filename.c_str()); + libs_cache_ent e = { NULL, cache_id, std::string("WARNING: Library `") + filename + "' tries to recursively use itself!" }; + if (libs_cache.find(filename) != libs_cache.end()) delete libs_cache[filename].mod; libs_cache[filename] = e; Module *backup_mod = module; - Module *lib_mod = dynamic_cast<Module*>(parse(text.toLocal8Bit(), QFileInfo(filename).absoluteDir().absolutePath().toLocal8Bit(), 0)); + Module *lib_mod = dynamic_cast<Module*>(parse(text.toLocal8Bit(), QFileInfo(QString::fromStdString(filename)).absoluteDir().absolutePath().toLocal8Bit(), 0)); module = backup_mod; if (lib_mod) { libs_cache[filename].mod = lib_mod; - libs_cache[filename].msg = print_messages_stack.last(); + libs_cache[filename].msg = print_messages_stack.last().toStdString(); } else { - libs_cache.remove(filename); + libs_cache.erase(filename); } print_messages_pop(); diff --git a/src/polyset.cc b/src/polyset.cc index 9601fef..db37b49 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -26,22 +26,15 @@ #include "polyset.h" #include "printutils.h" -#include "Preferences.h" +// FIXME: Reenable/rewrite - don't be dependant on GUI +// #include "Preferences.h" #ifdef ENABLE_CGAL #include <CGAL/assertions_behaviour.h> #include <CGAL/exceptions.h> #endif #include <Eigen/Core> #include <Eigen/LU> - -QCache<QString,PolySet::ps_cache_entry> PolySet::ps_cache(100); - -PolySet::ps_cache_entry::ps_cache_entry(PolySet *ps) : - ps(ps), msg(print_messages_stack.last()) { } - -PolySet::ps_cache_entry::~ps_cache_entry() { - ps->unlink(); -} +#include <QColor> PolySet::PolySet() : grid(GRID_FINE) { @@ -69,26 +62,26 @@ void PolySet::unlink() void PolySet::append_poly() { - polygons.append(Polygon()); + polygons.push_back(Polygon()); } void PolySet::append_vertex(double x, double y, double z) { grid.align(x, y, z); - polygons.last().append(Point(x, y, z)); + polygons.back().push_back(Vector3d(x, y, z)); } void PolySet::insert_vertex(double x, double y, double z) { grid.align(x, y, z); - polygons.last().insert(0, Point(x, y, z)); + polygons.back().insert(polygons.back().begin(), Vector3d(x, y, z)); } -static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const PolySet::Point *p1, const PolySet::Point *p2, bool e0, bool e1, bool e2, double z, bool mirrored) +static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector3d &p1, const Vector3d &p2, bool e0, bool e1, bool e2, double z, bool mirrored) { - double ax = p1->x - p0->x, bx = p1->x - p2->x; - double ay = p1->y - p0->y, by = p1->y - p2->y; - double az = p1->z - p0->z, bz = p1->z - p2->z; + double ax = p1[0] - p0[0], bx = p1[0] - p2[0]; + double ay = p1[1] - p0[1], by = p1[1] - p2[1]; + double az = p1[2] - p0[2], bz = p1[2] - p2[2]; double nx = ay*bz - az*by; double ny = az*bx - ax*bz; double nz = ax*by - ay*bx; @@ -100,39 +93,39 @@ static void gl_draw_triangle(GLint *shaderinfo, const PolySet::Point *p0, const double e1f = e1 ? 2.0 : -1.0; double e2f = e2 ? 2.0 : -1.0; glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p1->x, p1->y, p1->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[4], p1[0], p1[1], p1[2] + z); + glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z); glVertexAttrib3d(shaderinfo[6], 0.0, 1.0, 0.0); - glVertex3d(p0->x, p0->y, p0->z + z); + glVertex3d(p0[0], p0[1], p0[2] + z); if (!mirrored) { glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z); + glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z); glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); - glVertex3d(p1->x, p1->y, p1->z + z); + glVertex3d(p1[0], p1[1], p1[2] + z); } glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p1->x, p1->y, p1->z + z); + glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z); + glVertexAttrib3d(shaderinfo[5], p1[0], p1[1], p1[2] + z); glVertexAttrib3d(shaderinfo[6], 1.0, 0.0, 0.0); - glVertex3d(p2->x, p2->y, p2->z + z); + glVertex3d(p2[0], p2[1], p2[2] + z); if (mirrored) { glVertexAttrib3d(shaderinfo[3], e0f, e1f, e2f); - glVertexAttrib3d(shaderinfo[4], p0->x, p0->y, p0->z + z); - glVertexAttrib3d(shaderinfo[5], p2->x, p2->y, p2->z + z); + glVertexAttrib3d(shaderinfo[4], p0[0], p0[1], p0[2] + z); + glVertexAttrib3d(shaderinfo[5], p2[0], p2[1], p2[2] + z); glVertexAttrib3d(shaderinfo[6], 0.0, 0.0, 1.0); - glVertex3d(p1->x, p1->y, p1->z + z); + glVertex3d(p1[0], p1[1], p1[2] + z); } } else #endif { - glVertex3d(p0->x, p0->y, p0->z + z); + glVertex3d(p0[0], p0[1], p0[2] + z); if (!mirrored) - glVertex3d(p1->x, p1->y, p1->z + z); - glVertex3d(p2->x, p2->y, p2->z + z); + glVertex3d(p1[0], p1[1], p1[2] + z); + glVertex3d(p2[0], p2[1], p2[2] + z); if (mirrored) - glVertex3d(p1->x, p1->y, p1->z + z); + glVertex3d(p1[0], p1[1], p1[2] + z); } } @@ -145,7 +138,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m bool mirrored = m3f.determinant() < 0; if (colormode == COLORMODE_MATERIAL) { - const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); +// FIXME: Reenable/rewrite - don't be dependant on GUI +// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); + const QColor &col = QColor(0xf9, 0xd7, 0x2c); glColor3f(col.redF(), col.greenF(), col.blueF()); #ifdef ENABLE_OPENCSG if (shaderinfo) { @@ -155,7 +150,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m #endif /* ENABLE_OPENCSG */ } if (colormode == COLORMODE_CUTOUT) { - const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR); +// FIXME: Reenable/rewrite - don't be dependant on GUI +// const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_BACK_COLOR); + const QColor &col = QColor(0x9d, 0xcb, 0x51); glColor3f(col.redF(), col.greenF(), col.blueF()); #ifdef ENABLE_OPENCSG if (shaderinfo) { @@ -193,82 +190,82 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m glBegin(GL_TRIANGLES); for (double z = -zbase/2; z < zbase; z += zbase) { - for (int i = 0; i < polygons.size(); i++) { + for (size_t i = 0; i < polygons.size(); i++) { const Polygon *poly = &polygons[i]; if (poly->size() == 3) { if (z < 0) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(2), &poly->at(1), true, true, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(2), poly->at(1), true, true, true, z, mirrored); } else { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(2), true, true, true, z, mirrored); } } else if (poly->size() == 4) { if (z < 0) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(3), poly->at(1), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(2), poly->at(1), poly->at(3), true, false, true, z, mirrored); } else { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, z, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(3), true, false, true, z, mirrored); + gl_draw_triangle(shaderinfo, poly->at(2), poly->at(3), poly->at(1), true, false, true, z, mirrored); } } else { - Point center; - for (int j = 0; j < poly->size(); j++) { - center.x += poly->at(j).x; - center.y += poly->at(j).y; + Vector3d center = Vector3d::Zero(); + for (size_t j = 0; j < poly->size(); j++) { + center[0] += poly->at(j)[0]; + center[1] += poly->at(j)[1]; } - center.x /= poly->size(); - center.y /= poly->size(); - for (int j = 1; j <= poly->size(); j++) { + center[0] /= poly->size(); + center[1] /= poly->size(); + for (size_t j = 1; j <= poly->size(); j++) { if (z < 0) { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j % poly->size()), &poly->at(j - 1), + gl_draw_triangle(shaderinfo, center, poly->at(j % poly->size()), poly->at(j - 1), false, true, false, z, mirrored); } else { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), + gl_draw_triangle(shaderinfo, center, poly->at(j - 1), poly->at(j % poly->size()), false, true, false, z, mirrored); } } } } } - const QVector<Polygon> *borders_p = &borders; + const std::vector<Polygon> *borders_p = &borders; if (borders_p->size() == 0) borders_p = &polygons; - for (int i = 0; i < borders_p->size(); i++) { + for (size_t i = 0; i < borders_p->size(); i++) { const Polygon *poly = &borders_p->at(i); - for (int j = 1; j <= poly->size(); j++) { - Point p1 = poly->at(j - 1), p2 = poly->at(j - 1); - Point p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size()); - p1.z -= zbase/2, p2.z += zbase/2; - p3.z -= zbase/2, p4.z += zbase/2; - gl_draw_triangle(shaderinfo, &p2, &p1, &p3, true, true, false, 0, mirrored); - gl_draw_triangle(shaderinfo, &p2, &p3, &p4, false, true, true, 0, mirrored); + for (size_t j = 1; j <= poly->size(); j++) { + Vector3d p1 = poly->at(j - 1), p2 = poly->at(j - 1); + Vector3d p3 = poly->at(j % poly->size()), p4 = poly->at(j % poly->size()); + p1[2] -= zbase/2, p2[2] += zbase/2; + p3[2] -= zbase/2, p4[2] += zbase/2; + gl_draw_triangle(shaderinfo, p2, p1, p3, true, true, false, 0, mirrored); + gl_draw_triangle(shaderinfo, p2, p3, p4, false, true, true, 0, mirrored); } } glEnd(); } else { - for (int i = 0; i < polygons.size(); i++) { + for (size_t i = 0; i < polygons.size(); i++) { const Polygon *poly = &polygons[i]; glBegin(GL_TRIANGLES); if (poly->size() == 3) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(2), true, true, true, 0, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(2), true, true, true, 0, mirrored); } else if (poly->size() == 4) { - gl_draw_triangle(shaderinfo, &poly->at(0), &poly->at(1), &poly->at(3), true, false, true, 0, mirrored); - gl_draw_triangle(shaderinfo, &poly->at(2), &poly->at(3), &poly->at(1), true, false, true, 0, mirrored); + gl_draw_triangle(shaderinfo, poly->at(0), poly->at(1), poly->at(3), true, false, true, 0, mirrored); + gl_draw_triangle(shaderinfo, poly->at(2), poly->at(3), poly->at(1), true, false, true, 0, mirrored); } else { - Point center; - for (int j = 0; j < poly->size(); j++) { - center.x += poly->at(j).x; - center.y += poly->at(j).y; - center.z += poly->at(j).z; + Vector3d center = Vector3d::Zero(); + for (size_t j = 0; j < poly->size(); j++) { + center[0] += poly->at(j)[0]; + center[1] += poly->at(j)[1]; + center[2] += poly->at(j)[2]; } - center.x /= poly->size(); - center.y /= poly->size(); - center.z /= poly->size(); - for (int j = 1; j <= poly->size(); j++) { - gl_draw_triangle(shaderinfo, ¢er, &poly->at(j - 1), &poly->at(j % poly->size()), false, true, false, 0, mirrored); + center[0] /= poly->size(); + center[1] /= poly->size(); + center[2] /= poly->size(); + for (size_t j = 1; j <= poly->size(); j++) { + gl_draw_triangle(shaderinfo, center, poly->at(j - 1), poly->at(j % poly->size()), false, true, false, 0, mirrored); } } glEnd(); @@ -290,400 +287,48 @@ void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const double zbase = csgmode; for (double z = -zbase/2; z < zbase; z += zbase) { - for (int i = 0; i < borders.size(); i++) { + for (size_t i = 0; i < borders.size(); i++) { const Polygon *poly = &borders[i]; glBegin(GL_LINE_LOOP); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, z); + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); + glVertex3d(p[0], p[1], z); } glEnd(); } } - for (int i = 0; i < borders.size(); i++) { + for (size_t i = 0; i < borders.size(); i++) { const Polygon *poly = &borders[i]; glBegin(GL_LINES); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, -zbase/2); - glVertex3d(p->x, p->y, +zbase/2); + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); + glVertex3d(p[0], p[1], -zbase/2); + glVertex3d(p[0], p[1], +zbase/2); } glEnd(); } } else { - for (int i = 0; i < polygons.size(); i++) { + for (size_t i = 0; i < polygons.size(); i++) { const Polygon *poly = &polygons[i]; glBegin(GL_LINE_LOOP); - for (int j = 0; j < poly->size(); j++) { - const Point *p = &poly->at(j); - glVertex3d(p->x, p->y, p->z); + for (size_t j = 0; j < poly->size(); j++) { + const Vector3d &p = poly->at(j); + glVertex3d(p[0], p[1], p[2]); } glEnd(); } } } -#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::render_cgal_nef_polyhedron() const +BoundingBox PolySet::getBoundingBox() 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)) { - int idx = this->grid.data(x, y); - // Filter away two vertices with the same index (due to grid) - // This could be done in a more general way, but we'd rather redo the entire - // grid concept instead. - if (this->polygons[this->poly_n].indexOf(idx) == -1) { - 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++; - } - } - if (this->polygons[this->poly_n].size() >= 3) { - add_edges(this->poly_n); - this->poly_n++; - } - else { - this->polygons.remove(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); + BoundingBox bbox; + for (size_t i = 0; i < polygons.size(); i++) { + const Polygon &poly = polygons[i]; + for (size_t j = 0; j < poly.size(); j++) { + const Vector3d &p = poly[j]; + bbox.extend(p); } - - 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("CGAL error: %s", e.what()); - CGAL::set_error_behaviour(old_behaviour); - return CGAL_Nef_polyhedron(); - } - CGAL::set_error_behaviour(old_behaviour); - } - return CGAL_Nef_polyhedron(); + return bbox; } - -#endif /* ENABLE_CGAL */ - diff --git a/src/polyset.h b/src/polyset.h index 8712ff2..e80d182 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -1,30 +1,21 @@ #ifndef POLYSET_H_ #define POLYSET_H_ -#include <GL/glew.h> // this must be included before the GL headers -#include <qgl.h> - +#include <GL/glew.h> #include "grid.h" -#ifdef ENABLE_OPENCSG -# include <opencsg.h> -#endif -#ifdef ENABLE_CGAL -# include "cgal.h" -#endif +#include <vector> +#include <Eigen/Core> +#include <Eigen/Geometry> -#include <QCache> +using Eigen::Vector3d; +typedef Eigen::AlignedBox<double, 3> BoundingBox; class PolySet { public: - struct Point { - double x, y, z; - Point() : x(0), y(0), z(0) { } - Point(double x, double y, double z) : x(x), y(y), z(z) { } - }; - typedef QList<Point> Polygon; - QVector<Polygon> polygons; - QVector<Polygon> borders; + typedef std::vector<Vector3d> Polygon; + std::vector<Polygon> polygons; + std::vector<Polygon> borders; Grid3d<void*> grid; bool is2d; @@ -33,16 +24,12 @@ public: PolySet(); ~PolySet(); + bool empty() const { return polygons.size() == 0; } void append_poly(); - void append_vertex(double x, double y, double z); - void insert_vertex(double x, double y, double z); + void append_vertex(double x, double y, double z = 0.0); + void insert_vertex(double x, double y, double z = 0.0); - void append_vertex(double x, double y) { - append_vertex(x, y, 0.0); - } - void insert_vertex(double x, double y) { - insert_vertex(x, y, 0.0); - } + BoundingBox getBoundingBox() const; enum colormode_e { COLORMODE_NONE, @@ -62,22 +49,9 @@ public: CSGMODE_HIGHLIGHT_DIFFERENCE = 22 }; - struct ps_cache_entry { - PolySet *ps; - QString msg; - ps_cache_entry(PolySet *ps); - ~ps_cache_entry(); - }; - - static QCache<QString,ps_cache_entry> ps_cache; - 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 render_cgal_nef_polyhedron() const; -#endif - int refcount; PolySet *link(); void unlink(); diff --git a/src/primitives.cc b/src/primitives.cc index 45b2da3..50a197d 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -33,6 +33,11 @@ #include "builtin.h" #include "printutils.h" #include <assert.h> +#include "visitor.h" +#include <sstream> +#include <assert.h> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope #define F_MINIMUM 0.01 @@ -57,47 +62,83 @@ public: class PrimitiveNode : public AbstractPolyNode { public: + PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { + switch (this->type) { + case CUBE: + return "cube"; + break; + case SPHERE: + return "sphere"; + break; + case CYLINDER: + return "cylinder"; + break; + case POLYHEDRON: + return "polyhedron"; + break; + case SQUARE: + return "square"; + break; + case CIRCLE: + return "circle"; + break; + case POLYGON: + return "polygon"; + break; + default: + assert(false && "PrimitiveNode::name(): Unknown primitive type"); + return AbstractPolyNode::name(); + } + } + bool center; double x, y, z, h, r1, r2; double fn, fs, fa; primitive_type_e type; int convexity; Value points, paths, triangles; - PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; }; AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { - PrimitiveNode *node = new PrimitiveNode(inst, type); + PrimitiveNode *node = new PrimitiveNode(inst, this->type); node->center = false; node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1; - QVector<QString> argnames; - QVector<Expression*> argexpr; - - if (type == CUBE) { - argnames = QVector<QString>() << "size" << "center"; - } - if (type == SPHERE) { - argnames = QVector<QString>() << "r"; - } - if (type == CYLINDER) { - argnames = QVector<QString>() << "h" << "r1" << "r2" << "center"; - } - if (type == POLYHEDRON) { - argnames = QVector<QString>() << "points" << "triangles" << "convexity"; - } - if (type == SQUARE) { - argnames = QVector<QString>() << "size" << "center"; - } - if (type == CIRCLE) { - argnames = QVector<QString>() << "r"; - } - if (type == POLYGON) { - argnames = QVector<QString>() << "points" << "paths" << "convexity"; + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; + + switch (this->type) { + case CUBE: + argnames += "size", "center"; + break; + case SPHERE: + argnames += "r"; + break; + case CYLINDER: + argnames += "h", "r1", "r2", "center"; + break; + case POLYHEDRON: + argnames += "points", "triangles", "convexity"; + break; + case SQUARE: + argnames += "size", "center"; + break; + case CIRCLE: + argnames += "r"; + break; + case POLYGON: + argnames += "points", "paths", "convexity"; + break; + default: + assert(false && "PrimitiveModule::evaluate(): Unknown node type"); } Context c(ctx); @@ -141,8 +182,7 @@ AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstanti Value r, r1, r2; r1 = c.lookup_variable("r1"); r2 = c.lookup_variable("r2"); - if (r1.type != Value::NUMBER && r2.type != Value::NUMBER) - r = c.lookup_variable("r", true); // silence warning since r has no default value + r = c.lookup_variable("r", true); // silence warning since r has no default value Value center = c.lookup_variable("center"); if (h.type == Value::NUMBER) { node->h = h.num; @@ -233,25 +273,25 @@ static void generate_circle(point2d *circle, double r, int fragments) } } -PolySet *PrimitiveNode::render_polyset(render_mode_e) const +PolySet *PrimitiveNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const { PolySet *p = new PolySet(); - if (type == CUBE && x > 0 && y > 0 && z > 0) + if (this->type == CUBE && this->x > 0 && this->y > 0 && this->z > 0) { double x1, x2, y1, y2, z1, z2; - if (center) { - x1 = -x/2; - x2 = +x/2; - y1 = -y/2; - y2 = +y/2; - z1 = -z/2; - z2 = +z/2; + if (this->center) { + x1 = -this->x/2; + x2 = +this->x/2; + y1 = -this->y/2; + y2 = +this->y/2; + z1 = -this->z/2; + z2 = +this->z/2; } else { x1 = y1 = z1 = 0; - x2 = x; - y2 = y; - z2 = z; + x2 = this->x; + y2 = this->y; + z2 = this->z; } p->append_poly(); // top @@ -291,7 +331,7 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const p->append_vertex(x1, y2, z2); } - if (type == SPHERE && r1 > 0) + if (this->type == SPHERE && this->r1 > 0) { struct ring_s { point2d *points; @@ -358,17 +398,18 @@ sphere_next_r2: delete[] ring; } - if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0)) + if (this->type == CYLINDER && + this->h > 0 && this->r1 >=0 && this->r2 >= 0 && (this->r1 > 0 || this->r2 > 0)) { - int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa); + int fragments = get_fragments_from_r(fmax(this->r1, this->r2), this->fn, this->fs, this->fa); double z1, z2; - if (center) { - z1 = -h/2; - z2 = +h/2; + if (this->center) { + z1 = -this->h/2; + z2 = +this->h/2; } else { z1 = 0; - z2 = h; + z2 = this->h; } point2d *circle1 = new point2d[fragments]; @@ -401,13 +442,13 @@ sphere_next_r2: } } - if (r1 > 0) { + if (this->r1 > 0) { p->append_poly(); for (int i=0; i<fragments; i++) p->insert_vertex(circle1[i].x, circle1[i].y, z1); } - if (r2 > 0) { + if (this->r2 > 0) { p->append_poly(); for (int i=0; i<fragments; i++) p->append_vertex(circle2[i].x, circle2[i].y, z2); @@ -417,35 +458,35 @@ sphere_next_r2: delete[] circle2; } - if (type == POLYHEDRON) + if (this->type == POLYHEDRON) { - p->convexity = convexity; - for (int i=0; i<triangles.vec.size(); i++) + p->convexity = this->convexity; + for (size_t i=0; i<this->triangles.vec.size(); i++) { p->append_poly(); - for (int j=0; j<triangles.vec[i]->vec.size(); j++) { - int pt = triangles.vec[i]->vec[j]->num; - if (pt < points.vec.size()) { + for (size_t j=0; j<this->triangles.vec[i]->vec.size(); j++) { + size_t pt = this->triangles.vec[i]->vec[j]->num; + if (pt < this->points.vec.size()) { double px, py, pz; - if (points.vec[pt]->getv3(px, py, pz)) + if (this->points.vec[pt]->getv3(px, py, pz)) p->insert_vertex(px, py, pz); } } } } - if (type == SQUARE && x > 0 && y > 0) + if (this->type == SQUARE && x > 0 && y > 0) { double x1, x2, y1, y2; - if (center) { - x1 = -x/2; - x2 = +x/2; - y1 = -y/2; - y2 = +y/2; + if (this->center) { + x1 = -this->x/2; + x2 = +this->x/2; + y1 = -this->y/2; + y2 = +this->y/2; } else { x1 = y1 = 0; - x2 = x; - y2 = y; + x2 = this->x; + y2 = this->y; } p->is2d = true; @@ -456,96 +497,113 @@ sphere_next_r2: p->append_vertex(x1, y2); } - if (type == CIRCLE) + if (this->type == CIRCLE) { - int fragments = get_fragments_from_r(r1, fn, fs, fa); + int fragments = get_fragments_from_r(this->r1, this->fn, this->fs, this->fa); p->is2d = true; p->append_poly(); for (int i=0; i < fragments; i++) { double phi = (M_PI*2*i) / fragments; - p->append_vertex(r1*cos(phi), r1*sin(phi)); + p->append_vertex(this->r1*cos(phi), this->r1*sin(phi)); } } - if (type == POLYGON) + if (this->type == POLYGON) { DxfData dd; - for (int i=0; i<points.vec.size(); i++) { + for (size_t i=0; i<this->points.vec.size(); i++) { double x,y; - if (!points.vec[i]->getv2(x, y)) { + if (!this->points.vec[i]->getv2(x, y)) { PRINTF("ERROR: Unable to convert point at index %d to a vec2 of numbers", i); - // FIXME: Return NULL and make sure this is checked by all callers? - return p; + p->unlink(); + return NULL; } - dd.points.append(DxfData::Point(x, y)); + dd.points.push_back(Vector2d(x, y)); } - if (paths.vec.size() == 0) + if (this->paths.vec.size() == 0) { - dd.paths.append(DxfData::Path()); - for (int i=0; i<points.vec.size(); i++) { + dd.paths.push_back(DxfData::Path()); + for (size_t i=0; i<this->points.vec.size(); i++) { assert(i < dd.points.size()); // FIXME: Not needed, but this used to be an 'if' - DxfData::Point *p = &dd.points[i]; - dd.paths.last().points.append(p); + dd.paths.back().indices.push_back(i); } - if (dd.paths.last().points.size() > 0) { - dd.paths.last().points.append(dd.paths.last().points.first()); - dd.paths.last().is_closed = true; + if (dd.paths.back().indices.size() > 0) { + dd.paths.back().indices.push_back(dd.paths.back().indices.front()); + dd.paths.back().is_closed = true; } } else { - for (int i=0; i<paths.vec.size(); i++) + for (size_t i=0; i<this->paths.vec.size(); i++) { - dd.paths.append(DxfData::Path()); - for (int j=0; j<paths.vec[i]->vec.size(); j++) { - int idx = paths.vec[i]->vec[j]->num; + dd.paths.push_back(DxfData::Path()); + for (size_t j=0; j<this->paths.vec[i]->vec.size(); j++) { + int idx = this->paths.vec[i]->vec[j]->num; if (idx < dd.points.size()) { - DxfData::Point *p = &dd.points[idx]; - dd.paths.last().points.append(p); + dd.paths.back().indices.push_back(idx); } } - if (dd.paths.last().points.isEmpty()) { - dd.paths.removeLast(); + if (dd.paths.back().indices.empty()) { + dd.paths.pop_back(); } else { - dd.paths.last().points.append(dd.paths.last().points.first()); - dd.paths.last().is_closed = true; + dd.paths.back().indices.push_back(dd.paths.back().indices.front()); + dd.paths.back().is_closed = true; } } } p->is2d = true; p->convexity = convexity; - dxf_tesselate(p, &dd, 0, true, false, 0); - dxf_border_to_ps(p, &dd); + dxf_tesselate(p, dd, 0, true, false, 0); + dxf_border_to_ps(p, dd); } return p; } -QString PrimitiveNode::dump(QString indent) const +std::string PrimitiveNode::toString() const { - if (dump_cache.isEmpty()) { - QString text; - if (type == CUBE) - text.sprintf("cube(size = [%g, %g, %g], center = %s);\n", x, y, z, center ? "true" : "false"); - if (type == SPHERE) - text.sprintf("sphere($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); - if (type == CYLINDER) - text.sprintf("cylinder($fn = %g, $fa = %g, $fs = %g, h = %g, r1 = %g, r2 = %g, center = %s);\n", fn, fa, fs, h, r1, r2, center ? "true" : "false"); - if (type == POLYHEDRON) - text.sprintf("polyhedron(points = %s, triangles = %s, convexity = %d);\n", points.dump().toAscii().data(), triangles.dump().toAscii().data(), convexity); - if (type == SQUARE) - text.sprintf("square(size = [%g, %g], center = %s);\n", x, y, center ? "true" : "false"); - if (type == CIRCLE) - text.sprintf("circle($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); - if (type == POLYGON) - text.sprintf("polygon(points = %s, paths = %s, convexity = %d);\n", points.dump().toAscii().data(), paths.dump().toAscii().data(), convexity); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + std::stringstream stream; + + stream << this->name(); + + switch (this->type) { + case CUBE: + stream << "(size = [" << this->x << ", " << this->y << ", " << this->z << "], " + << "center = " << (center ? "true" : "false") << ")"; + break; + case SPHERE: + stream << "($fn = " << this->fn << ", $fa = " << this->fa + << ", $fs = " << this->fs << ", r = " << this->r1 << ")"; + break; + case CYLINDER: + stream << "($fn = " << this->fn << ", $fa = " << this->fa + << ", $fs = " << this->fs << ", h = " << this->h << ", r1 = " << this->r1 + << ", r2 = " << this->r2 << ", center = " << (center ? "true" : "false") << ")"; + break; + case POLYHEDRON: + stream << "(points = " << this->points + << ", triangles = " << this->triangles + << ", convexity = " << this->convexity << ")"; + break; + case SQUARE: + stream << "(size = [" << this->x << ", " << this->y << "], " + << "center = " << (center ? "true" : "false") << ")"; + break; + case CIRCLE: + stream << "($fn = " << this->fn << ", $fa = " << this->fa + << ", $fs = " << this->fs << ", r = " << this->r1 << ")"; + break; + case POLYGON: + stream << "(points = " << this->points << ", paths = " << this->paths << ", convexity = " << this->convexity << ")"; + break; + default: + assert(false); } - return dump_cache; -} + return stream.str(); +} diff --git a/src/printutils.cc b/src/printutils.cc index 8830a8c..01fa06b 100644 --- a/src/printutils.cc +++ b/src/printutils.cc @@ -18,8 +18,7 @@ void print_messages_push() void print_messages_pop() { - QString msg = print_messages_stack.last(); - print_messages_stack.removeLast(); + QString msg = print_messages_stack.takeLast(); if (print_messages_stack.size() > 0 && !msg.isNull()) { if (!print_messages_stack.last().isEmpty()) print_messages_stack.last() += "\n"; @@ -49,3 +48,8 @@ void PRINT_NOCACHE(const QString &msg) outputhandler(msg, outputhandler_data); } } + +std::ostream &operator<<(std::ostream &os, const QFileInfo &fi) { + os << std::hex << (fi.exists()?fi.lastModified().toTime_t():0) << "." << fi.size(); + return os; +} diff --git a/src/printutils.h b/src/printutils.h index 7f2e828..0432622 100644 --- a/src/printutils.h +++ b/src/printutils.h @@ -3,6 +3,9 @@ #include <QString> #include <QList> +#include <iostream> +#include <QFileInfo> +#include <QDateTime> typedef void (OutputHandlerFunc)(const QString &msg, void *userdata); extern OutputHandlerFunc *outputhandler; @@ -22,4 +25,6 @@ void PRINT_NOCACHE(const QString &msg); #define PRINTF_NOCACHE(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) #define PRINTA_NOCACHE(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) +std::ostream &operator<<(std::ostream &os, const QFileInfo &fi); + #endif diff --git a/src/projection.cc b/src/projection.cc index d87b366..5a7ea6e 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -24,8 +24,8 @@ * */ +#include "projectionnode.h" #include "module.h" -#include "node.h" #include "context.h" #include "printutils.h" #include "builtin.h" @@ -34,6 +34,8 @@ #include "polyset.h" #include "export.h" #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h" #ifdef ENABLE_CGAL # include <CGAL/assertions_behaviour.h> @@ -41,10 +43,9 @@ #endif #include <assert.h> - -#include <QApplication> -#include <QTime> -#include <QProgressDialog> +#include <sstream> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope class ProjectionModule : public AbstractModule { @@ -53,24 +54,13 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class ProjectionNode : public AbstractPolyNode -{ -public: - int convexity; - bool cut_mode; - ProjectionNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { - cut_mode = false; - } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { ProjectionNode *node = new ProjectionNode(inst); - QVector<QString> argnames = QVector<QString>() << "cut"; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "cut"; + std::vector<Expression*> argexpr; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -83,216 +73,41 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant if (cut.type == Value::BOOL) node->cut_mode = cut.b; - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); return node; } -void register_builtin_projection() +PolySet *ProjectionNode::evaluate_polyset(render_mode_e mode, PolySetEvaluator *evaluator) const { - builtin_modules["projection"] = new ProjectionModule(); -} - -#ifdef ENABLE_CGAL - -PolySet *ProjectionNode::render_polyset(render_mode_e) const -{ - QString key = mk_cache_id(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return PolySet::ps_cache[key]->ps->link(); - } - - 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->render_cgal_nef_polyhedron().p3; - } - } - catch (CGAL::Assertion_exception e) { - PRINTF("CGAL error: %s", e.what()); - CGAL::set_error_behaviour(old_behaviour); + if (!evaluator) { + PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); + PolySet *ps = new PolySet(); + ps->is2d = true; 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->render_cgal_nef_polyhedron(); - 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); + print_messages_push(); - 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; - } + PolySet *ps = evaluator->evaluatePolySet(*this, mode); - 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_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); print_messages_pop(); return ps; } -#else // ENABLE_CGAL - -PolySet *ProjectionNode::render_polyset(render_mode_e) const +std::string ProjectionNode::toString() const { - PRINT("WARNING: Found projection() statement but compiled without CGAL support!"); - PolySet *ps = new PolySet(); - ps->is2d = true; - return ps; -} + std::stringstream stream; -#endif // ENABLE_CGAL + stream << "projection(cut = " << (this->cut_mode ? "true" : "false") + << ", convexity = " << this->convexity << ")"; -QString ProjectionNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text; - text.sprintf("projection(cut = %s, convexity = %d) {\n", - this->cut_mode ? "true" : "false", this->convexity); - foreach (AbstractNode *v, this->children) - text += v->dump(indent + QString("\t")); - text += indent + "}\n"; - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; + return stream.str(); } +void register_builtin_projection() +{ + builtin_modules["projection"] = new ProjectionModule(); +} diff --git a/src/projectionnode.h b/src/projectionnode.h new file mode 100644 index 0000000..4c29b7b --- /dev/null +++ b/src/projectionnode.h @@ -0,0 +1,25 @@ +#ifndef PROJECTIONNODE_H_ +#define PROJECTIONNODE_H_ + +#include "node.h" +#include "visitor.h" +#include <string> + +class ProjectionNode : public AbstractPolyNode +{ +public: + ProjectionNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + cut_mode = false; + } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { return "projection"; } + + int convexity; + bool cut_mode; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const; +}; + +#endif diff --git a/src/qhash.cc b/src/qhash.cc new file mode 100644 index 0000000..cec9adf --- /dev/null +++ b/src/qhash.cc @@ -0,0 +1,19 @@ +#include "myqhash.h" + +static uint hash(const uchar *p, int n) +{ + uint h = 0; + uint g; + + while (n--) { + h = (h << 4) + *p++; + if ((g = (h & 0xf0000000)) != 0) + h ^= g >> 23; + h &= ~g; + } + return h; +} + +uint qHash(const std::string &str) { + return hash(reinterpret_cast<const uchar *>(str.c_str()), str.length()); +} diff --git a/src/render-opencsg.cc.org b/src/render-opencsg.cc.org new file mode 100644 index 0000000..fe0fc60 --- /dev/null +++ b/src/render-opencsg.cc.org @@ -0,0 +1,87 @@ +#include "render-opencsg.h" +#include "polyset.h" +#include "csgterm.h" +#ifdef ENABLE_OPENCSG +# include <opencsg.h> +#endif + +class OpenCSGPrim : public OpenCSG::Primitive +{ +public: + OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) : + OpenCSG::Primitive(operation, convexity) { } + PolySet *p; + double *m; + int csgmode; + virtual void render() { + glPushMatrix(); + glMultMatrixd(m); + p->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); + glPopMatrix(); + } +}; + +void renderCSGChainviaOpenCSG(CSGChain *chain, GLint *shaderinfo, bool highlight, bool background) +{ + std::vector<OpenCSG::Primitive*> primitives; + int j = 0; + for (int i = 0;; i++) + { + bool last = i == chain->polysets.size(); + + if (last || chain->types[i] == CSGTerm::TYPE_UNION) + { + if (j+1 != i) { + OpenCSG::render(primitives); + glDepthFunc(GL_EQUAL); + } + if (shaderinfo) + glUseProgram(shaderinfo[0]); + for (; j < i; j++) { + double *m = chain->matrices[j]; + glPushMatrix(); + glMultMatrixd(m); + int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + if (highlight) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); + } else if (background) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); + } else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { + // User-defined color from source + glColor4d(m[16], m[17], m[18], m[19]); + if (shaderinfo) { + glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]); + glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); + } + chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); + } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { + chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo); + } else { + chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo); + } + glPopMatrix(); + } + if (shaderinfo) + glUseProgram(0); + for (unsigned int k = 0; k < primitives.size(); k++) { + delete primitives[k]; + } + glDepthFunc(GL_LEQUAL); + primitives.clear(); + } + + if (last) + break; + + OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? + OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); + prim->p = chain->polysets[i]; + prim->m = chain->matrices[i]; + prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + if (highlight) + prim->csgmode += 20; + else if (background) + prim->csgmode += 10; + primitives.push_back(prim); + } +} diff --git a/src/render.cc b/src/render.cc index 9fa7ab6..48a8535 100644 --- a/src/render.cc +++ b/src/render.cc @@ -24,8 +24,8 @@ * */ +#include "rendernode.h" #include "module.h" -#include "node.h" #include "polyset.h" #include "context.h" #include "dxfdata.h" @@ -34,13 +34,11 @@ #include "builtin.h" #include "printutils.h" #include "progress.h" -#ifdef ENABLE_CGAL -# include "cgal.h" -#endif +#include "visitor.h" -#include <QProgressDialog> -#include <QApplication> -#include <QTime> +#include <sstream> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope class RenderModule : public AbstractModule { @@ -49,24 +47,13 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class RenderNode : public AbstractNode -{ -public: - int convexity; - RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; -}; - AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { RenderNode *node = new RenderNode(inst); - QVector<QString> argnames = QVector<QString>() << "convexity"; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "convexity"; + std::vector<Expression*> argexpr; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -75,11 +62,8 @@ AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiati if (v.type == Value::NUMBER) node->convexity = (int)v.num; - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } + std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); return node; } @@ -89,176 +73,11 @@ void register_builtin_render() builtin_modules["render"] = new RenderModule(); } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron RenderNode::render_cgal_nef_polyhedron() 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->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (N.dim == 3) { - N.p3 += v->render_cgal_nef_polyhedron().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; -} - -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(); - if (PolySet::ps_cache.contains(key)) { - PRINT(PolySet::ps_cache[key]->msg); - return AbstractPolyNode::render_csg_term_from_ps(m, highlights, background, - PolySet::ps_cache[key]->ps->link(), modinst, idx); - } - - print_messages_push(); - CGAL_Nef_polyhedron N; - - QString cache_id = mk_cache_id(); - if (cgal_nef_cache.contains(cache_id)) - { - PRINT(cgal_nef_cache[cache_id]->msg); - N = cgal_nef_cache[cache_id]->N; - } - else - { - PRINTF_NOCACHE("Processing uncached %s statement...", statement); - // PRINTA("Cache ID: %1", cache_id); - QApplication::processEvents(); - - QTime t; - t.start(); - - N = this->render_cgal_nef_polyhedron(); - - int s = t.elapsed() / 1000; - PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); - } - - PolySet *ps = NULL; - - if (N.dim == 2) - { - DxfData dd(N); - ps = new PolySet(); - ps->is2d = true; - dxf_tesselate(ps, &dd, 0, true, false, 0); - dxf_border_to_ps(ps, &dd); - } - - if (N.dim == 3) - { - if (!N.p3.is_simple()) { - PRINTF("WARNING: Result of %s() isn't valid 2-manifold! Modify your design..", statement); - return NULL; - } - - ps = new PolySet(); - - CGAL_Polyhedron P; - N.p3.convert_to_Polyhedron(P); - - typedef CGAL_Polyhedron::Vertex Vertex; - typedef CGAL_Polyhedron::Vertex_const_iterator VCI; - typedef CGAL_Polyhedron::Facet_const_iterator FCI; - typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; - - for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { - HFCC hc = fi->facet_begin(); - HFCC hc_end = hc; - ps->append_poly(); - do { - Vertex v = *VCI((hc++)->vertex()); - double x = CGAL::to_double(v.point().x()); - double y = CGAL::to_double(v.point().y()); - double z = CGAL::to_double(v.point().z()); - ps->append_vertex(x, y, z); - } while (hc != hc_end); - } - } - - if (ps) - { - ps->convexity = convexity; - PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); - - CSGTerm *term = new CSGTerm(ps, m, QString("n%1").arg(idx)); - if (modinst->tag_highlight && highlights) - highlights->append(term->link()); - if (modinst->tag_background && background) { - background->append(term); - return NULL; - } - return term; - } - print_messages_pop(); - - return NULL; -} - -CSGTerm *RenderNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +std::string RenderNode::toString() const { - return render_csg_term_from_nef(m, highlights, background, "render", this->convexity); -} + std::stringstream stream; -#else + stream << this->name() << "(convexity = " << convexity << ")"; -CSGTerm *RenderNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ - CSGTerm *t1 = NULL; - PRINT("WARNING: Found render() statement but compiled without CGAL support!"); - foreach(AbstractNode * v, children) { - CSGTerm *t2 = v->render_csg_term(m, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } - } - if (modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; + return stream.str(); } - -#endif - -QString RenderNode::dump(QString indent) const -{ - if (dump_cache.isEmpty()) { - QString text = indent + QString("n%1: ").arg(idx) + QString("render(convexity = %1) {\n").arg(QString::number(convexity)); - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; -} - diff --git a/src/rendernode.h b/src/rendernode.h new file mode 100644 index 0000000..9138c29 --- /dev/null +++ b/src/rendernode.h @@ -0,0 +1,21 @@ +#ifndef RENDERNODE_H_ +#define RENDERNODE_H_ + +#include "node.h" +#include "visitor.h" +#include <string> + +class RenderNode : public AbstractNode +{ +public: + RenderNode(const ModuleInstantiation *mi) : AbstractNode(mi), convexity(1) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { return "render"; } + + int convexity; +}; + +#endif diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..69aee87 --- /dev/null +++ b/src/state.h @@ -0,0 +1,41 @@ +#ifndef STATE_H_ +#define STATE_H_ + +#include <cstring> + +class State +{ +public: + State(const class AbstractNode *parent) + : parentnode(parent), isprefix(false), ispostfix(false), numchildren(0) { + for (int i=0;i<16;i++) this->m[i] = i % 5 == 0 ? 1.0 : 0.0; + for (int i=0;i<4;i++) this->c[i] = -1.0; + } + virtual ~State() {} + + void setPrefix(bool on) { this->isprefix = on; } + void setPostfix(bool on) { this->ispostfix = on; } + void setNumChildren(unsigned int numc) { this->numchildren = numc; } + void setParent(const AbstractNode *parent) { this->parentnode = parent; } + void setMatrix(const double m[16]) { memcpy(this->m, m, 16*sizeof(double)); } + void setColor(const double c[4]) { memcpy(this->c, c, 4*sizeof(double)); } + + bool isPrefix() const { return this->isprefix; } + bool isPostfix() const { return this->ispostfix; } + unsigned int numChildren() const { return this->numchildren; } + const AbstractNode *parent() const { return this->parentnode; } + const double *matrix() const { return this->m; } + const double *color() const { return this->c; } + +private: + const AbstractNode * parentnode; + bool isprefix; + bool ispostfix; + unsigned int numchildren; + + // Transformation matrix and color. FIXME: Generalize such state variables? + double m[16]; + double c[4]; +}; + +#endif diff --git a/src/stl-utils.h b/src/stl-utils.h new file mode 100644 index 0000000..a48b7d5 --- /dev/null +++ b/src/stl-utils.h @@ -0,0 +1,19 @@ +#ifndef STLUTILS_H_ +#define STLUTILS_H_ + +template<class T> +struct del_fun_t +{ + del_fun_t& operator()(T* p) { + delete p; + return *this; + } +}; + +template<class T> +del_fun_t<T> del_fun() +{ + return del_fun_t<T>(); +} + +#endif diff --git a/src/surface.cc b/src/surface.cc index 92b661f..22598bf 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -31,9 +31,16 @@ #include "builtin.h" #include "dxftess.h" #include "printutils.h" -#include "openscad.h" // handle_dep() +#include "handle_dep.h" // handle_dep() +#include "visitor.h" #include <QFile> +#include <QRegExp> +#include <QStringList> +#include <sstream> +#include <boost/unordered_map.hpp> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope class SurfaceModule : public AbstractModule { @@ -45,12 +52,17 @@ public: class SurfaceNode : public AbstractPolyNode { public: - QString filename; + SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const { return "surface"; } + + std::string filename; bool center; int convexity; - SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { } - virtual PolySet *render_polyset(render_mode_e mode) const; - virtual QString dump(QString indent) const; + virtual PolySet *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; }; AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const @@ -59,13 +71,14 @@ AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiat node->center = false; node->convexity = 1; - QVector<QString> argnames = QVector<QString>() << "file" << "center" << "convexity"; - QVector<Expression*> argexpr; + std::vector<std::string> argnames; + argnames += "file", "center", "convexity"; + std::vector<Expression*> argexpr; Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); - node->filename = c.get_absolute_path(c.lookup_variable("file").text); + node->filename = c.getAbsolutePath(c.lookup_variable("file").text); Value center = c.lookup_variable("center", true); if (center.type == Value::BOOL) { @@ -85,18 +98,19 @@ void register_builtin_surface() builtin_modules["surface"] = new SurfaceModule(); } -PolySet *SurfaceNode::render_polyset(render_mode_e) const +PolySet *SurfaceNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const { + PolySet *p = new PolySet(); handle_dep(filename); - QFile f(filename); + QFile f(QString::fromStdString(filename)); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - PRINTF("WARNING: Can't open DXF file `%s'.", filename.toAscii().data()); - return NULL; + PRINTF("WARNING: Can't open DAT file `%s'.", filename.c_str()); + return p; } int lines = 0, columns = 0; - QHash<QPair<int,int>,double> data; + boost::unordered_map<std::pair<int,int>,double> data; double min_val = 0; while (!f.atEnd()) @@ -113,13 +127,12 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const if (i >= columns) columns = i + 1; double v = fields[i].toDouble(); - data[QPair<int,int>(lines, i)] = v; + data[std::make_pair(lines, i)] = v; min_val = fmin(v-1, min_val); } lines++; } - PolySet *p = new PolySet(); p->convexity = convexity; double ox = center ? -columns/2.0 : 0; @@ -128,10 +141,10 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const for (int i = 1; i < lines; i++) for (int j = 1; j < columns; j++) { - double v1 = data[QPair<int,int>(i-1, j-1)]; - double v2 = data[QPair<int,int>(i-1, j)]; - double v3 = data[QPair<int,int>(i, j-1)]; - double v4 = data[QPair<int,int>(i, j)]; + double v1 = data[std::make_pair(i-1, j-1)]; + double v2 = data[std::make_pair(i-1, j)]; + double v3 = data[std::make_pair(i, j-1)]; + double v4 = data[std::make_pair(i, j)]; double vx = (v1 + v2 + v3 + v4) / 4; p->append_poly(); @@ -159,14 +172,14 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const { p->append_poly(); p->append_vertex(ox + 0, oy + i-1, min_val); - p->append_vertex(ox + 0, oy + i-1, data[QPair<int,int>(i-1, 0)]); - p->append_vertex(ox + 0, oy + i, data[QPair<int,int>(i, 0)]); + p->append_vertex(ox + 0, oy + i-1, data[std::make_pair(i-1, 0)]); + p->append_vertex(ox + 0, oy + i, data[std::make_pair(i, 0)]); p->append_vertex(ox + 0, oy + i, min_val); p->append_poly(); p->insert_vertex(ox + columns-1, oy + i-1, min_val); - p->insert_vertex(ox + columns-1, oy + i-1, data[QPair<int,int>(i-1, columns-1)]); - p->insert_vertex(ox + columns-1, oy + i, data[QPair<int,int>(i, columns-1)]); + p->insert_vertex(ox + columns-1, oy + i-1, data[std::make_pair(i-1, columns-1)]); + p->insert_vertex(ox + columns-1, oy + i, data[std::make_pair(i, columns-1)]); p->insert_vertex(ox + columns-1, oy + i, min_val); } @@ -174,14 +187,14 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const { p->append_poly(); p->insert_vertex(ox + i-1, oy + 0, min_val); - p->insert_vertex(ox + i-1, oy + 0, data[QPair<int,int>(0, i-1)]); - p->insert_vertex(ox + i, oy + 0, data[QPair<int,int>(0, i)]); + p->insert_vertex(ox + i-1, oy + 0, data[std::make_pair(0, i-1)]); + p->insert_vertex(ox + i, oy + 0, data[std::make_pair(0, i)]); p->insert_vertex(ox + i, oy + 0, min_val); p->append_poly(); p->append_vertex(ox + i-1, oy + lines-1, min_val); - p->append_vertex(ox + i-1, oy + lines-1, data[QPair<int,int>(lines-1, i-1)]); - p->append_vertex(ox + i, oy + lines-1, data[QPair<int,int>(lines-1, i)]); + p->append_vertex(ox + i-1, oy + lines-1, data[std::make_pair(lines-1, i-1)]); + p->append_vertex(ox + i, oy + lines-1, data[std::make_pair(lines-1, i)]); p->append_vertex(ox + i, oy + lines-1, min_val); } @@ -198,14 +211,12 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const return p; } -QString SurfaceNode::dump(QString indent) const +std::string SurfaceNode::toString() const { - if (dump_cache.isEmpty()) { - QString text; - text.sprintf("surface(file = \"%s\", center = %s);\n", - filename.toAscii().data(), center ? "true" : "false"); - ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; - } - return dump_cache; -} + std::stringstream stream; + stream << this->name() << "(file = \"" << this->filename + << "\", center = " << (this->center ? "true" : "false") << ")"; + + return stream.str(); +} diff --git a/src/transform.cc b/src/transform.cc index 9df9ade..3308e0c 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -24,8 +24,8 @@ * */ +#include "transformnode.h" #include "module.h" -#include "node.h" #include "context.h" #include "dxfdata.h" #include "csgterm.h" @@ -33,14 +33,19 @@ #include "dxftess.h" #include "builtin.h" #include "printutils.h" +#include "visitor.h" +#include <sstream> +#include <vector> +#include <assert.h> +#include <boost/assign/std/vector.hpp> +using namespace boost::assign; // bring 'operator+=()' into scope enum transform_type_e { SCALE, ROTATE, MIRROR, TRANSLATE, - MULTMATRIX, - COLOR + MULTMATRIX }; class TransformModule : public AbstractModule @@ -51,69 +56,80 @@ public: virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; }; -class TransformNode : public AbstractNode +using std::string; +using std::vector; + +static vector<string> split(const string &str, const string &delim) { -public: - double m[20]; - TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } -#ifdef ENABLE_CGAL - virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif - virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; - virtual QString dump(QString indent) const; -}; + assert(delim.size() > 0); + + vector<string> strvec; + size_t start = 0, end = 0; + while (end != string::npos) { + end = str.find(delim, start); + // If at end, use length=maxLength. Else use length=end-start. + strvec.push_back(str.substr(start, (end == string::npos) ? string::npos : end - start)); + // If at end, use start=maxSize. Else use start=end+delimiter. + start = ((end > (string::npos - delim.size())) ? string::npos : end + delim.size()); + } + return strvec; +} + +template <class T> static bool from_string(T &t, const string &s) +{ + std::istringstream iss(s); + return !(iss >> t).fail(); +} AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const { TransformNode *node = new TransformNode(inst); for (int i = 0; i < 16; i++) - node->m[i] = i % 5 == 0 ? 1.0 : 0.0; - for (int i = 16; i < 19; i++) - node->m[i] = -1; - node->m[19] = 1; - - QVector<QString> argnames; - QVector<Expression*> argexpr; - - if (type == SCALE) { - argnames = QVector<QString>() << "v"; - } - if (type == ROTATE) { - argnames = QVector<QString>() << "a" << "v"; - } - if (type == MIRROR) { - argnames = QVector<QString>() << "v"; - } - if (type == TRANSLATE) { - argnames = QVector<QString>() << "v"; - } - if (type == MULTMATRIX) { - argnames = QVector<QString>() << "m"; - } - if (type == COLOR) { - argnames = QVector<QString>() << "c" << "alpha"; + node->matrix[i] = i % 5 == 0 ? 1.0 : 0.0; + + std::vector<std::string> argnames; + std::vector<Expression*> argexpr; + + switch (this->type) { + case SCALE: + argnames += "v"; + break; + case ROTATE: + argnames += "a", "v"; + break; + case MIRROR: + argnames += "v"; + break; + case TRANSLATE: + argnames += "v"; + break; + case MULTMATRIX: + argnames += "m"; + break; + default: + assert(false); } Context c(ctx); c.args(argnames, argexpr, inst->argnames, inst->argvalues); - if (type == SCALE) + if (this->type == SCALE) { Value v = c.lookup_variable("v"); - v.getnum(node->m[0]); - v.getnum(node->m[5]); - v.getnum(node->m[10]); - v.getv3(node->m[0], node->m[5], node->m[10]); - if (node->m[10] <= 0) - node->m[10] = 1; + v.getnum(node->matrix[0]); + v.getnum(node->matrix[5]); + v.getnum(node->matrix[10]); + v.getv3(node->matrix[0], node->matrix[5], node->matrix[10]); + if (node->matrix[10] <= 0) + node->matrix[10] = 1; } - if (type == ROTATE) + else if (this->type == ROTATE) { Value val_a = c.lookup_variable("a"); if (val_a.type == Value::VECTOR) { - for (int i = 0; i < 3 && i < val_a.vec.size(); i++) { + for (size_t i = 0; i < 3 && i < val_a.vec.size(); i++) { double a; val_a.vec[i]->getnum(a); double c = cos(a*M_PI/180.0); @@ -140,10 +156,10 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti { m[x+y*4] = 0; for (int i = 0; i < 4; i++) - m[x+y*4] += node->m[i+y*4] * mr[x+i*4]; + m[x+y*4] += node->matrix[i+y*4] * mr[x+i*4]; } for (int i = 0; i < 16; i++) - node->m[i] = m[i]; + node->matrix[i] = m[i]; } } else @@ -165,21 +181,21 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti double c = cos(a*M_PI/180.0); double s = sin(a*M_PI/180.0); - node->m[ 0] = x*x*(1-c)+c; - node->m[ 1] = y*x*(1-c)+z*s; - node->m[ 2] = z*x*(1-c)-y*s; + node->matrix[ 0] = x*x*(1-c)+c; + node->matrix[ 1] = y*x*(1-c)+z*s; + node->matrix[ 2] = z*x*(1-c)-y*s; - node->m[ 4] = x*y*(1-c)-z*s; - node->m[ 5] = y*y*(1-c)+c; - node->m[ 6] = z*y*(1-c)+x*s; + node->matrix[ 4] = x*y*(1-c)-z*s; + node->matrix[ 5] = y*y*(1-c)+c; + node->matrix[ 6] = z*y*(1-c)+x*s; - node->m[ 8] = x*z*(1-c)+y*s; - node->m[ 9] = y*z*(1-c)-x*s; - node->m[10] = z*z*(1-c)+c; + node->matrix[ 8] = x*z*(1-c)+y*s; + node->matrix[ 9] = y*z*(1-c)-x*s; + node->matrix[10] = z*z*(1-c)+c; } } } - if (type == MIRROR) + else if (this->type == MIRROR) { Value val_v = c.lookup_variable("v"); double x = 1, y = 0, z = 0; @@ -193,197 +209,65 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti if (x != 0.0 || y != 0.0 || z != 0.0) { - node->m[ 0] = 1-2*x*x; - node->m[ 1] = -2*y*x; - node->m[ 2] = -2*z*x; + node->matrix[ 0] = 1-2*x*x; + node->matrix[ 1] = -2*y*x; + node->matrix[ 2] = -2*z*x; - node->m[ 4] = -2*x*y; - node->m[ 5] = 1-2*y*y; - node->m[ 6] = -2*z*y; + node->matrix[ 4] = -2*x*y; + node->matrix[ 5] = 1-2*y*y; + node->matrix[ 6] = -2*z*y; - node->m[ 8] = -2*x*z; - node->m[ 9] = -2*y*z; - node->m[10] = 1-2*z*z; + node->matrix[ 8] = -2*x*z; + node->matrix[ 9] = -2*y*z; + node->matrix[10] = 1-2*z*z; } } - if (type == TRANSLATE) + else if (this->type == TRANSLATE) { Value v = c.lookup_variable("v"); - v.getv3(node->m[12], node->m[13], node->m[14]); + v.getv3(node->matrix[12], node->matrix[13], node->matrix[14]); } - if (type == MULTMATRIX) + else if (this->type == MULTMATRIX) { Value v = c.lookup_variable("m"); if (v.type == Value::VECTOR) { for (int i = 0; i < 16; i++) { - int x = i / 4, y = i % 4; + size_t x = i / 4, y = i % 4; if (y < v.vec.size() && v.vec[y]->type == Value::VECTOR && x < v.vec[y]->vec.size()) - v.vec[y]->vec[x]->getnum(node->m[i]); + v.vec[y]->vec[x]->getnum(node->matrix[i]); } } } - if (type == COLOR) - { - Value v = c.lookup_variable("c"); - if (v.type == Value::VECTOR) { - for (int i = 0; i < 4; i++) - node->m[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0; - } else if (v.type == Value::STRING) { - QString colorname = v.text; - QColor color; - color.setNamedColor(colorname); - if (color.isValid()) { - node->m[16+0] = color.redF(); - node->m[16+1] = color.greenF(); - node->m[16+2] = color.blueF(); - } else { - PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data()); - PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); - } - } - Value alpha = c.lookup_variable("alpha"); - if (alpha.type == Value::NUMBER) { - node->m[16+3] = alpha.num; - } - } - foreach (ModuleInstantiation *v, inst->children) { - AbstractNode *n = v->evaluate(inst->ctx); - if (n != NULL) - node->children.append(n); - } + vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(); + node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); return node; } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const +string TransformNode::toString() 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->render_cgal_nef_polyhedron(); - if (N.dim != 0) - first = false; - } else if (N.dim == 2) { - N.p2 += v->render_cgal_nef_polyhedron().p2; - } else if (N.dim == 3) { - N.p3 += v->render_cgal_nef_polyhedron().p3; + std::stringstream stream; + + stream << "multmatrix(["; + for (int j=0;j<4;j++) { + stream << "["; + for (int i=0;i<4;i++) { + // FIXME: The 0 test is to avoid a leading minus before a single 0 (cosmetics) + stream << ((this->matrix[i*4+j]==0)?0:this->matrix[i*4+j]); + if (i != 3) stream << ", "; } - v->progress_report(); + stream << "]"; + if (j != 3) stream << ", "; } + stream << "])"; - 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.render_cgal_nef_polyhedron(); - 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]; - - for (int i = 0; i < 16; i++) - { - int c_row = i%4; - int m_col = i/4; - x[i] = 0; - for (int j = 0; j < 4; j++) - x[i] += c[c_row + j*4] * m[m_col*4 + j]; - } - - for (int i = 16; i < 20; i++) - x[i] = m[i] < 0 ? c[i] : m[i]; - - CSGTerm *t1 = NULL; - foreach(AbstractNode *v, children) - { - CSGTerm *t2 = v->render_csg_term(x, highlights, background); - if (t2 && !t1) { - t1 = t2; - } else if (t2 && t1) { - t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); - } - } - if (t1 && modinst->tag_highlight && highlights) - highlights->append(t1->link()); - if (t1 && modinst->tag_background && background) { - background->append(t1); - return NULL; - } - return t1; + return stream.str(); } -QString TransformNode::dump(QString indent) const +string TransformNode::name() const { - if (dump_cache.isEmpty()) { - QString text; - if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0) - text.sprintf("n%d: color([%g, %g, %g, %g])", idx, - m[16], m[17], m[18], m[19]); - else - text.sprintf("n%d: multmatrix([[%g, %g, %g, %g], [%g, %g, %g, %g], " - "[%g, %g, %g, %g], [%g, %g, %g, %g]])", idx, - 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[3], m[7], m[11], m[15]); - text = indent + text + " {\n"; - foreach (AbstractNode *v, children) - text += v->dump(indent + QString("\t")); - ((AbstractNode*)this)->dump_cache = text + indent + "}\n"; - } - return dump_cache; + return "transform"; } void register_builtin_transform() @@ -393,6 +277,4 @@ void register_builtin_transform() builtin_modules["mirror"] = new TransformModule(MIRROR); builtin_modules["translate"] = new TransformModule(TRANSLATE); builtin_modules["multmatrix"] = new TransformModule(MULTMATRIX); - builtin_modules["color"] = new TransformModule(COLOR); } - diff --git a/src/transformnode.h b/src/transformnode.h new file mode 100644 index 0000000..9d822cb --- /dev/null +++ b/src/transformnode.h @@ -0,0 +1,20 @@ +#ifndef TRANSFORMNODE_H_ +#define TRANSFORMNODE_H_ + +#include "node.h" +#include "visitor.h" + +class TransformNode : public AbstractNode +{ +public: + TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } + virtual Response accept(class State &state, Visitor &visitor) const { + return visitor.visit(state, *this); + } + virtual std::string toString() const; + virtual std::string name() const; + + double matrix[16]; +}; + +#endif diff --git a/src/traverser.cc b/src/traverser.cc new file mode 100644 index 0000000..a87449b --- /dev/null +++ b/src/traverser.cc @@ -0,0 +1,44 @@ +#include "traverser.h" +#include "visitor.h" +#include "node.h" +#include "state.h" +#include <algorithm> + +void Traverser::execute() +{ + State state(NULL); + traverse(this->root, state); +} + +struct TraverseNode +{ + Traverser *traverser; + const State &state; + TraverseNode(Traverser *traverser, const State &state) : + traverser(traverser), state(state) {} + void operator()(const AbstractNode *node) { traverser->traverse(*node, state); } +}; + +void Traverser::traverse(const AbstractNode &node, const State &state) +{ + // FIXME: Handle abort + + State newstate = state; + newstate.setNumChildren(node.getChildren().size()); + + if (traversaltype == PREFIX || traversaltype == PRE_AND_POSTFIX) { + newstate.setPrefix(true); + newstate.setParent(state.parent()); + node.accept(newstate, this->visitor); + } + + newstate.setParent(&node); + std::for_each(node.getChildren().begin(), node.getChildren().end(), TraverseNode(this, newstate)); + + if (traversaltype == POSTFIX || traversaltype == PRE_AND_POSTFIX) { + newstate.setParent(state.parent()); + newstate.setPrefix(false); + newstate.setPostfix(true); + node.accept(newstate, this->visitor); + } +} diff --git a/src/traverser.h b/src/traverser.h new file mode 100644 index 0000000..85373cc --- /dev/null +++ b/src/traverser.h @@ -0,0 +1,26 @@ +#ifndef TRAVERSER_H_ +#define TRAVERSER_H_ + +enum Response {ContinueTraversal, AbortTraversal, PruneTraversal}; + +class Traverser +{ +public: + enum TraversalType {PREFIX, POSTFIX, PRE_AND_POSTFIX}; + + Traverser(class Visitor &visitor, const class AbstractNode &root, TraversalType travtype) + : visitor(visitor), root(root), traversaltype(travtype) { + } + virtual ~Traverser() { } + + void execute(); + // FIXME: reverse parameters + void traverse(const AbstractNode &node, const class State &state); +private: + + Visitor &visitor; + const AbstractNode &root; + TraversalType traversaltype; +}; + +#endif diff --git a/src/value.cc b/src/value.cc index b0a79a4..34566bd 100644 --- a/src/value.cc +++ b/src/value.cc @@ -26,6 +26,8 @@ #include "value.h" #include "mathc99.h" +#include <assert.h> +#include <sstream> Value::Value() { @@ -34,8 +36,7 @@ Value::Value() Value::~Value() { - for (int i = 0; i < this->vec.size(); i++) - delete this->vec[i]; + for (size_t i = 0; i < this->vec.size(); i++) delete this->vec[i]; this->vec.clear(); } @@ -53,7 +54,7 @@ Value::Value(double v) this->num = v; } -Value::Value(const QString &t) +Value::Value(const std::string &t) { reset_undef(); this->type = STRING; @@ -71,8 +72,9 @@ Value& Value::operator = (const Value &v) this->type = v.type; this->b = v.b; this->num = v.num; - for (int i = 0; i < v.vec.size(); i++) - this->vec.append(new Value(*v.vec[i])); + for (size_t i = 0; i < v.vec.size(); i++) { + this->vec.push_back(new Value(*v.vec[i])); + } this->range_begin = v.range_begin; this->range_step = v.range_step; this->range_end = v.range_end; @@ -109,8 +111,8 @@ Value Value::operator + (const Value &v) const if (this->type == VECTOR && v.type == VECTOR) { Value r; r.type = VECTOR; - for (int i = 0; i < this->vec.size() && i < v.vec.size(); i++) - r.vec.append(new Value(*this->vec[i] + *v.vec[i])); + for (size_t i = 0; i < this->vec.size() && i < v.vec.size(); i++) + r.vec.push_back(new Value(*this->vec[i] + *v.vec[i])); return r; } if (this->type == NUMBER && v.type == NUMBER) { @@ -124,8 +126,8 @@ Value Value::operator - (const Value &v) const if (this->type == VECTOR && v.type == VECTOR) { Value r; r.type = VECTOR; - for (int i = 0; i < this->vec.size() && i < v.vec.size(); i++) - r.vec.append(new Value(*this->vec[i] - *v.vec[i])); + for (size_t i = 0; i < this->vec.size() && i < v.vec.size(); i++) + r.vec.push_back(new Value(*this->vec[i] - *v.vec[i])); return r; } if (this->type == NUMBER && v.type == NUMBER) { @@ -139,15 +141,15 @@ Value Value::operator * (const Value &v) const if (this->type == VECTOR && v.type == NUMBER) { Value r; r.type = VECTOR; - for (int i = 0; i < this->vec.size(); i++) - r.vec.append(new Value(*this->vec[i] * v)); + for (size_t i = 0; i < this->vec.size(); i++) + r.vec.push_back(new Value(*this->vec[i] * v)); return r; } if (this->type == NUMBER && v.type == VECTOR) { Value r; r.type = VECTOR; - for (int i = 0; i < v.vec.size(); i++) - r.vec.append(new Value(*this * *v.vec[i])); + for (size_t i = 0; i < v.vec.size(); i++) + r.vec.push_back(new Value(*this * *v.vec[i])); return r; } if (this->type == NUMBER && v.type == NUMBER) { @@ -161,15 +163,15 @@ Value Value::operator / (const Value &v) const if (this->type == VECTOR && v.type == NUMBER) { Value r; r.type = VECTOR; - for (int i = 0; i < this->vec.size(); i++) - r.vec.append(new Value(*this->vec[i] / v)); + for (size_t i = 0; i < this->vec.size(); i++) + r.vec.push_back(new Value(*this->vec[i] / v)); return r; } if (this->type == NUMBER && v.type == VECTOR) { Value r; r.type = VECTOR; - for (int i = 0; i < v.vec.size(); i++) - r.vec.append(new Value(v / *v.vec[i])); + for (size_t i = 0; i < v.vec.size(); i++) + r.vec.push_back(new Value(v / *v.vec[i])); return r; } if (this->type == NUMBER && v.type == NUMBER) { @@ -216,7 +218,7 @@ Value Value::operator == (const Value &v) const if (this->type == VECTOR && v.type == VECTOR) { if (this->vec.size() != v.vec.size()) return Value(false); - for (int i=0; i<this->vec.size(); i++) + for (size_t i=0; i<this->vec.size(); i++) if (!(*this->vec[i] == *v.vec[i]).b) return Value(false); return Value(true); @@ -254,8 +256,8 @@ Value Value::inv() const if (this->type == VECTOR) { Value r; r.type = VECTOR; - for (int i = 0; i < this->vec.size(); i++) - r.vec.append(new Value(this->vec[i]->inv())); + for (size_t i = 0; i < this->vec.size(); i++) + r.vec.push_back(new Value(this->vec[i]->inv())); return r; } if (this->type == NUMBER) @@ -307,46 +309,71 @@ bool Value::getv3(double &x, double &y, double &z) const return true; } -QString Value::dump() const -{ - if (this->type == STRING) { - return QString("\"") + this->text + QString("\""); - } - if (this->type == VECTOR) { - QString text = "["; - for (int i = 0; i < this->vec.size(); i++) { - if (i > 0) - text += ", "; - text += this->vec[i]->dump(); - } - return text + "]"; - } - if (this->type == RANGE) { - QString text; - text.sprintf("[ %g : %g : %g ]", this->range_begin, this->range_step, this->range_end); - return text; - } - if (this->type == NUMBER) { - QString text; - text.sprintf("%g", this->num); - return text; - } - if (this->type == BOOL) { - return QString(this->b ? "true" : "false"); - } - return QString("undef"); -} - void Value::reset_undef() { this->type = UNDEFINED; this->b = false; this->num = 0; - for (int i = 0; i < this->vec.size(); i++) - delete this->vec[i]; + for (size_t i = 0; i < this->vec.size(); i++) delete this->vec[i]; this->vec.clear(); this->range_begin = 0; this->range_step = 0; this->range_end = 0; - this->text = QString(); + this->text = ""; +} + +std::string Value::toString() const +{ + std::stringstream stream; + stream.precision(16); + + switch (this->type) { + case STRING: + stream << '"' << this->text << '"'; + break; + case VECTOR: + stream << '['; + for (size_t i = 0; i < this->vec.size(); i++) { + if (i > 0) stream << ", "; + stream << *(this->vec[i]); + } + stream << ']'; + break; + case RANGE: + stream << "[ " + << this->range_begin + << " : " + << this->range_step + << " : " + << this->range_end + << " ]"; + break; + case NUMBER: + stream << this->num; + break; + case BOOL: + stream << this->b; + break; + default: + stream << "undef"; + } + + return stream.str(); } + +/*! + Append a value to this vector. + This must be of type VECTOR. +*/ +void Value::append(Value *val) +{ + assert(this->type == VECTOR); + this->vec.push_back(val); +} + +std::ostream &operator<<(std::ostream &stream, const Value &value) +{ + stream << value.toString(); + return stream; +} + diff --git a/src/value.h b/src/value.h index 3491cbb..2003460 100644 --- a/src/value.h +++ b/src/value.h @@ -1,8 +1,8 @@ #ifndef VALUE_H_ #define VALUE_H_ -#include <QVector> -#include <QString> +#include <vector> +#include <string> class Value { @@ -20,18 +20,18 @@ public: bool b; double num; - QVector<Value*> vec; + std::vector<Value*> vec; double range_begin; double range_step; double range_end; - QString text; + std::string text; Value(); ~Value(); Value(bool v); Value(double v); - Value(const QString &t); + Value(const std::string &t); Value(const Value &v); Value& operator = (const Value &v); @@ -59,10 +59,14 @@ public: bool getv2(double &x, double &y) const; bool getv3(double &x, double &y, double &z) const; - QString dump() const; + std::string toString() const; + + void append(Value *val); private: void reset_undef(); }; +std::ostream &operator<<(std::ostream &stream, const Value &value); + #endif diff --git a/src/visitor.h b/src/visitor.h new file mode 100644 index 0000000..f4846b8 --- /dev/null +++ b/src/visitor.h @@ -0,0 +1,55 @@ +#ifndef VISITOR_H_ +#define VISITOR_H_ + +#include "traverser.h" + +class Visitor +{ +public: + Visitor() {} + virtual ~Visitor() {} + + virtual Response visit(class State &state, const class AbstractNode &node) = 0; + virtual Response visit(class State &state, const class AbstractIntersectionNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class AbstractPolyNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class CgaladvNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class CsgNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class DxfLinearExtrudeNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class DxfRotateExtrudeNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class ImportNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class PrimitiveNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class ProjectionNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class RenderNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class SurfaceNode &node) { + return visit(state, (const class AbstractPolyNode &)node); + } + virtual Response visit(class State &state, const class TransformNode &node) { + return visit(state, (const class AbstractNode &)node); + } + virtual Response visit(class State &state, const class ColorNode &node) { + return visit(state, (const class AbstractNode &)node); + } + // Add visit() methods for new visitable subtypes of AbstractNode here +}; + +#endif |