diff options
Diffstat (limited to 'src')
66 files changed, 3364 insertions, 2262 deletions
| diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc new file mode 100644 index 0000000..4963814 --- /dev/null +++ b/src/CGALRenderer.cc @@ -0,0 +1,647 @@ +#include "CGALRenderer.h" +#include "visitor.h" +#include "state.h" +#include "module.h" // FIXME: Temporarily for ModuleInstantiation +#include "printutils.h" + +#include "csgnode.h" +#include "transformnode.h" +#include "polyset.h" +#include "dxfdata.h" +#include "dxftess.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 CGALRenderer::renderCGALMesh(const AbstractNode &node) +{ +	if (!isCached(node)) { +		Traverser render(*this, node, Traverser::PRE_AND_POSTFIX); +		render.execute(); +		assert(isCached(node)); +	} +	return this->cache[this->tree.getString(node)]; +} + +bool CGALRenderer::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 CGALRenderer::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CsgOp op) +{ + 	if (target.dim != 2 && target.dim != 3) { + 		assert(false && "Dimension of Nef polyhedron must be 2 or 3"); + 	} + +	if (target.dim == 2) { +		switch (op) { +		case UNION: +			target.p2 += src.p2; +			break; +		case INTERSECTION: +			target.p2 *= src.p2; +			break; +		case DIFFERENCE: +			target.p2 -= src.p2; +			break; +		case MINKOWSKI: +			target.p2 = minkowski2(target.p2, src.p2); +			break; +		} +	} +	else if (target.dim == 3) { +		switch (op) { +		case UNION: +			target.p3 += src.p3; +			break; +		case INTERSECTION: +			target.p3 *= src.p3; +			break; +		case DIFFERENCE: +			target.p3 -= src.p3; +			break; +		case MINKOWSKI: +			target.p3 = minkowski3(target.p3, src.p3); +			break; +		} +	} +} + +/*! +	FIXME: Let caller insert into the cache since caller might modify the result +  (e.g. transform) +*/ +void CGALRenderer::applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp op) +{ +	CGAL_Nef_polyhedron N; +	if (this->visitedchildren[node.index()].size() > 0) { +		bool first = 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)); +			if (first) { +				N = this->cache[chcacheid]; +				// If the first child(ren) are empty (e.g. echo) nodes,  +				// ignore them (reset first flag) + 				if (N.dim != 0) first = false; +			} else { +				process(N, this->cache[chcacheid], op); +			} +			chnode->progress_report(); +		} +	} +	this->cache.insert(this->tree.getString(node), 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 CGALRenderer::visit(State &state, const AbstractNode &node) +{ +	if (state.isPrefix() && isCached(node)) return PruneTraversal; +	if (state.isPostfix()) { +		if (!isCached(node)) applyToChildren(node, UNION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CGALRenderer::visit(State &state, const AbstractIntersectionNode &node) +{ +	if (state.isPrefix() && isCached(node)) return PruneTraversal; +	if (state.isPostfix()) { +		if (!isCached(node)) applyToChildren(node, INTERSECTION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CGALRenderer::visit(State &state, const CsgNode &node) +{ +	if (state.isPrefix() && isCached(node)) return PruneTraversal; +	if (state.isPostfix()) { +		if (!isCached(node)) { +			CsgOp op; +			switch (node.type) { +			case CSG_TYPE_UNION: +				op = UNION; +				break; +			case CSG_TYPE_DIFFERENCE: +				op = DIFFERENCE; +				break; +			case CSG_TYPE_INTERSECTION: +				op = INTERSECTION; +				break; +			} +			applyToChildren(node, op); +		} +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CGALRenderer::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, 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); +				for (int i=0; i < dd.points.size(); i++) { +					CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd.points[i].x, dd.points[i].y); +					p = t.transform(p); +					dd.points[i].x = to_double(p.x()); +					dd.points[i].y = to_double(p.y()); +				} +				 +				PolySet ps; +				ps.is2d = true; +				dxf_tesselate(&ps, &dd, 0, true, false, 0); +				 +				N = renderCGALMesh(ps); +				ps.refcount = 0; +			} +			else if (N.dim == 3) { +				CGAL_Aff_transformation t( +					node.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); +			} +			this->cache.insert(this->tree.getString(node), N); +		} +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +// FIXME: RenderNode: Union over children + some magic +// FIXME: CgaladvNode: Iterate over children. Special operation + +// FIXME: Subtypes of AbstractPolyNode: +// ProjectionNode +// DxfLinearExtrudeNode +// DxfRotateExtrudeNode +// (SurfaceNode) +// (PrimitiveNode) +Response CGALRenderer::visit(State &state, const AbstractPolyNode &node) +{ +	if (state.isPrefix() && isCached(node)) return PruneTraversal; +	if (state.isPostfix()) { +		if (!isCached(node)) { +			// First union all children +			applyToChildren(node, UNION); + +			// Then apply polyset operation +			PolySet *ps = node.render_polyset(AbstractPolyNode::RENDER_CGAL, &this->psrenderer); +			if (ps) { +				try { +					CGAL_Nef_polyhedron N = renderCGALMesh(*ps); +//				print_messages_pop(); +					node.progress_report(); +					 +					ps->unlink(); +					this->cache.insert(this->tree.getString(node), N); +				} +				catch (...) { // Don't leak the PolySet on ProgressCancelException +					ps->unlink(); +					throw; +				} +			} +		} +		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 CGALRenderer::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 render CGAL meshes. +	NB! This is just a support function used for development and debugging +*/ +CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const AbstractPolyNode &node) +{ +	// FIXME: Lookup Nef polyhedron in cache. + +	// 	print_messages_push(); +	 +	PolySet *ps = node.render_polyset(AbstractPolyNode::RENDER_CGAL); +	if (ps) { +		try { +			CGAL_Nef_polyhedron N = ps->renderCSGMesh(); +			// FIXME: Insert into cache +			// print_messages_pop(); +			node.progress_report(); +			 +			ps->unlink(); +			return N; +		} +		catch (...) { // Don't leak the PolySet on ProgressCancelException +			ps->unlink(); +			throw; +		} +	} +} +#endif + +#ifdef ENABLE_CGAL + +#undef GEN_SURFACE_DEBUG + +class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS> +{ +public: +	typedef CGAL_HDS::Vertex::Point Point; + +	const PolySet &ps; +	CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { } + +	void operator()(CGAL_HDS& hds) +	{ +		CGAL_Polybuilder B(hds, true); + +		QList<PolySet::Point> vertices; +		Grid3d<int> vertices_idx(GRID_FINE); + +		for (int i = 0; i < ps.polygons.size(); i++) { +			const PolySet::Polygon *poly = &ps.polygons[i]; +			for (int j = 0; j < poly->size(); j++) { +				const PolySet::Point *p = &poly->at(j); +				if (!vertices_idx.has(p->x, p->y, p->z)) { +					vertices_idx.data(p->x, p->y, p->z) = vertices.size(); +					vertices.append(*p); +				} +			} +		} + +		B.begin_surface(vertices.size(), ps.polygons.size()); +#ifdef GEN_SURFACE_DEBUG +		printf("=== CGAL Surface ===\n"); +#endif + +		for (int i = 0; i < vertices.size(); i++) { +			const PolySet::Point *p = &vertices[i]; +			B.add_vertex(Point(p->x, p->y, p->z)); +#ifdef GEN_SURFACE_DEBUG +			printf("%d: %f %f %f\n", i, p->x, p->y, p->z); +#endif +		} + +		for (int i = 0; i < ps.polygons.size(); i++) { +			const PolySet::Polygon *poly = &ps.polygons[i]; +			QHash<int,int> fc; +			bool facet_is_degenerated = false; +			for (int j = 0; j < poly->size(); j++) { +				const PolySet::Point *p = &poly->at(j); +				int v = vertices_idx.data(p->x, p->y, p->z); +				if (fc[v]++ > 0) +					facet_is_degenerated = true; +			} +			 +			if (!facet_is_degenerated) +				B.begin_facet(); +#ifdef GEN_SURFACE_DEBUG +			printf("F:"); +#endif +			for (int j = 0; j < poly->size(); j++) { +				const PolySet::Point *p = &poly->at(j); +#ifdef GEN_SURFACE_DEBUG +				printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z); +#endif +				if (!facet_is_degenerated) +					B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z)); +			} +#ifdef GEN_SURFACE_DEBUG +			if (facet_is_degenerated) +				printf(" (degenerated)"); +			printf("\n"); +#endif +			if (!facet_is_degenerated) +				B.end_facet(); +		} + +#ifdef GEN_SURFACE_DEBUG +		printf("====================\n"); +#endif +		B.end_surface(); + +		#undef PointKey +	} +}; + +#endif /* ENABLE_CGAL */ + +CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const PolySet &ps) +{ +	if (ps.is2d) +	{ +#if 0 +		// This version of the code causes problems in some cases. +		// Example testcase: import_dxf("testdata/polygon8.dxf"); +		// +		typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t; +		typedef point_list_t::iterator point_list_it; +		std::list< point_list_t > pdata_point_lists; +		std::list < std::pair < point_list_it, point_list_it > > pdata; +		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); + +		for (int i = 0; i < ps.polygons.size(); i++) { +			pdata_point_lists.push_back(point_list_t()); +			for (int j = 0; j < ps.polygons[i].size(); j++) { +				double x = ps.polygons[i][j].x; +				double y = ps.polygons[i][j].y; +				CGAL_Nef_polyhedron2::Point p; +				if (grid.has(x, y)) { +					p = grid.data(x, y); +				} else { +					p = CGAL_Nef_polyhedron2::Point(x, y); +					grid.data(x, y) = p; +				} +				pdata_point_lists.back().push_back(p); +			} +			pdata.push_back(std::make_pair(pdata_point_lists.back().begin(), +					pdata_point_lists.back().end())); +		} + +		CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS); +		return CGAL_Nef_polyhedron(N); +#endif +#if 0 +		// This version of the code works fine but is pretty slow. +		// +		CGAL_Nef_polyhedron2 N; +		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); + +		for (int i = 0; i < ps.polygons.size(); i++) { +			std::list<CGAL_Nef_polyhedron2::Point> plist; +			for (int j = 0; j < ps.polygons[i].size(); j++) { +				double x = ps.polygons[i][j].x; +				double y = ps.polygons[i][j].y; +				CGAL_Nef_polyhedron2::Point p; +				if (grid.has(x, y)) { +					p = grid.data(x, y); +				} else { +					p = CGAL_Nef_polyhedron2::Point(x, y); +					grid.data(x, y) = p; +				} +				plist.push_back(p); +			} +			N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); +		} + +		return CGAL_Nef_polyhedron(N); +#endif +#if 1 +		// This version of the code does essentially the same thing as the 2nd +		// version but merges some triangles before sending them to CGAL. This adds +		// complexity but speeds up things.. +		// +		struct PolyReducer +		{ +			Grid2d<int> grid; +			QHash< QPair<int,int>, QPair<int,int> > egde_to_poly; +			QHash< int, CGAL_Nef_polyhedron2::Point > points; +			QHash< int, QList<int> > polygons; +			int poly_n; + +			void add_edges(int pn) +			{ +				for (int j = 1; j <= this->polygons[pn].size(); j++) { +					int a = this->polygons[pn][j-1]; +					int b = this->polygons[pn][j % this->polygons[pn].size()]; +					if (a > b) { a = a^b; b = a^b; a = a^b; } +					if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0) +						this->egde_to_poly[QPair<int,int>(a, b)].first = pn; +					else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0) +						this->egde_to_poly[QPair<int,int>(a, b)].second = pn; +					else +						abort(); +				} +			} + +			void del_poly(int pn) +			{ +				for (int j = 1; j <= this->polygons[pn].size(); j++) { +					int a = this->polygons[pn][j-1]; +					int b = this->polygons[pn][j % this->polygons[pn].size()]; +					if (a > b) { a = a^b; b = a^b; a = a^b; } +					if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn) +						this->egde_to_poly[QPair<int,int>(a, b)].first = 0; +					if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn) +						this->egde_to_poly[QPair<int,int>(a, b)].second = 0; +				} +				this->polygons.remove(pn); +			} + +			PolyReducer(const PolySet &ps) : grid(GRID_COARSE), poly_n(1) +			{ +				int point_n = 1; +				for (int i = 0; i < ps.polygons.size(); i++) { +					for (int j = 0; j < ps.polygons[i].size(); j++) { +						double x = ps.polygons[i][j].x; +						double y = ps.polygons[i][j].y; +						if (this->grid.has(x, y)) { +							this->polygons[this->poly_n].append(this->grid.data(x, y)); +						} else { +							this->grid.align(x, y) = point_n; +							this->polygons[this->poly_n].append(point_n); +							this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y); +							point_n++; +						} +					} +					add_edges(this->poly_n); +					this->poly_n++; +				} +			} + +			int merge(int p1, int p1e, int p2, int p2e) +			{ +				for (int i = 1; i < this->polygons[p1].size(); i++) { +					int j = (p1e + i) % this->polygons[p1].size(); +					this->polygons[this->poly_n].append(this->polygons[p1][j]); +				} +				for (int i = 1; i < this->polygons[p2].size(); i++) { +					int j = (p2e + i) % this->polygons[p2].size(); +					this->polygons[this->poly_n].append(this->polygons[p2][j]); +				} +				del_poly(p1); +				del_poly(p2); +				add_edges(this->poly_n); +				return this->poly_n++; +			} + +			void reduce() +			{ +				QList<int> work_queue; +				QHashIterator< int, QList<int> > it(polygons); +				while (it.hasNext()) { +					it.next(); +					work_queue.append(it.key()); +				} +				while (!work_queue.isEmpty()) { +					int poly1_n = work_queue.first(); +					work_queue.removeFirst(); +					if (!this->polygons.contains(poly1_n)) +						continue; +					for (int j = 1; j <= this->polygons[poly1_n].size(); j++) { +						int a = this->polygons[poly1_n][j-1]; +						int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()]; +						if (a > b) { a = a^b; b = a^b; a = a^b; } +						if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 && +								this->egde_to_poly[QPair<int,int>(a, b)].second != 0) { +							int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first + +									this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n; +							int poly2_edge = -1; +							for (int k = 1; k <= this->polygons[poly2_n].size(); k++) { +								int c = this->polygons[poly2_n][k-1]; +								int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()]; +								if (c > d) { c = c^d; d = c^d; c = c^d; } +								if (a == c && b == d) { +									poly2_edge = k-1; +									continue; +								} +								int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first + +										this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n; +								if (poly3_n < 0) +									continue; +								if (poly3_n == poly1_n) +									goto next_poly1_edge; +							} +							work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge)); +							goto next_poly1; +						} +					next_poly1_edge:; +					} +				next_poly1:; +				} +			} + +			CGAL_Nef_polyhedron2 toNef() +			{ +				CGAL_Nef_polyhedron2 N; + +				QHashIterator< int, QList<int> > it(polygons); +				while (it.hasNext()) { +					it.next(); +					std::list<CGAL_Nef_polyhedron2::Point> plist; +					for (int j = 0; j < it.value().size(); j++) { +						int p = it.value()[j]; +						plist.push_back(points[p]); +					} +					N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); +				} + +				return N; +			} +		}; + +		PolyReducer pr(ps); +		// printf("Number of polygons before reduction: %d\n", pr.polygons.size()); +		pr.reduce(); +		// printf("Number of polygons after reduction: %d\n", pr.polygons.size()); +		return CGAL_Nef_polyhedron(pr.toNef()); +#endif +#if 0 +		// This is another experimental version. I should run faster than the above, +		// is a lot simpler and has only one known weakness: Degenerate polygons, which +		// get repaired by GLUTess, might trigger a CGAL crash here. The only +		// known case for this is triangle-with-duplicate-vertex.dxf +		// FIXME: If we just did a projection, we need to recreate the border! +		if (ps.polygons.size() > 0) assert(ps.borders.size() > 0); +		CGAL_Nef_polyhedron2 N; +		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); + +		for (int i = 0; i < ps.borders.size(); i++) { +			std::list<CGAL_Nef_polyhedron2::Point> plist; +			for (int j = 0; j < ps.borders[i].size(); j++) { +				double x = ps.borders[i][j].x; +				double y = ps.borders[i][j].y; +				CGAL_Nef_polyhedron2::Point p; +				if (grid.has(x, y)) { +					p = grid.data(x, y); +				} else { +					p = CGAL_Nef_polyhedron2::Point(x, y); +					grid.data(x, y) = p; +				} +				plist.push_back(p);		 +			} +			// FIXME: If a border (path) has a duplicate vertex in dxf, +			// the CGAL_Nef_polyhedron2 constructor will crash. +			N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); +		} + +		return CGAL_Nef_polyhedron(N); + +#endif +	} +	else // not (this->is2d) +	{ +		CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); +		try { +		CGAL_Polyhedron P; +		CGAL_Build_PolySet builder(ps); +		P.delegate(builder); +#if 0 +		std::cout << P; +#endif +		CGAL_Nef_polyhedron3 N(P); +		return CGAL_Nef_polyhedron(N); +    } +		catch (CGAL::Assertion_exception e) { +			PRINTF("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object."); +			CGAL::set_error_behaviour(old_behaviour); +			return CGAL_Nef_polyhedron(); +		} +	} +	return CGAL_Nef_polyhedron(); +} diff --git a/src/CGALRenderer.h b/src/CGALRenderer.h new file mode 100644 index 0000000..def07a6 --- /dev/null +++ b/src/CGALRenderer.h @@ -0,0 +1,56 @@ +#ifndef CGALRENDERER_H_ +#define CGALRENDERER_H_ + +#include "myqhash.h" +#include "visitor.h" +#include "Tree.h" +#include "cgal.h" +#include "PolySetCGALRenderer.h" + +#include <string> +#include <map> +#include <list> + +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); + +using std::string; +using std::map; +using std::list; +using std::pair; + +class CGALRenderer : public Visitor +{ +public: +	enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI}; +	// FIXME: If a cache is not given, we need to fix this ourselves +	CGALRenderer(QHash<string, CGAL_Nef_polyhedron> &cache, const Tree &tree) : cache(cache), tree(tree), psrenderer(*this) {} +  virtual ~CGALRenderer() {} + +  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); + + 	CGAL_Nef_polyhedron renderCGALMesh(const AbstractNode &node); +	CGAL_Nef_polyhedron renderCGALMesh(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, CGALRenderer::CsgOp op); +	void applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp op); + +  string currindent; +  typedef list<pair<const AbstractNode *, string> > ChildList; +  map<int, ChildList> visitedchildren; + +	QHash<string, CGAL_Nef_polyhedron> &cache; +	const Tree &tree; +	PolySetCGALRenderer psrenderer; +}; + +#endif diff --git a/src/CSGTermRenderer.cc b/src/CSGTermRenderer.cc new file mode 100644 index 0000000..0f5910f --- /dev/null +++ b/src/CSGTermRenderer.cc @@ -0,0 +1,348 @@ +#include "CSGTermRenderer.h" +#include "visitor.h" +#include "state.h" +#include "csgterm.h" +#include "module.h" +#include "csgnode.h" +#include "transformnode.h" +#include "rendernode.h" +#include "printutils.h" + +#include <string> +#include <map> +#include <list> +#include <sstream> +#include <iostream> +#include <assert.h> + +/*! +	\class CSGTermRenderer + +	A visitor responsible for creating a tree of CSGTerm nodes used for rendering +	with OpenCSG. +*/ + +CSGTerm *CSGTermRenderer::renderCSGTerm(const AbstractNode &node, +																				vector<CSGTerm*> *highlights,  +																				vector<CSGTerm*> *background) +{ +	Traverser render(*this, node, Traverser::PRE_AND_POSTFIX); +	render.execute(); +	return this->stored_term[node.index()]; +} + +void CSGTermRenderer::applyToChildren(const AbstractNode &node, CSGTermRenderer::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 == UNION) { +				t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); +			} else if (op == DIFFERENCE) { +				t1 = new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2); +			} else if (op == INTERSECTION) { +				t1 = new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2); +			} +		} +	} +	if (t1 && node.modinst->tag_highlight && this->highlights) { +		this->highlights->push_back(t1->link()); +	} +	if (t1 && node.modinst->tag_background && this->background) { +		this->background->push_back(t1); +		t1 = NULL; // don't propagate background tagged nodes +	} +	this->stored_term[node.index()] = t1; +} + +Response CSGTermRenderer::visit(State &state, const AbstractNode &node) +{ +	if (state.isPostfix()) { +		applyToChildren(node, UNION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CSGTermRenderer::visit(State &state, const AbstractIntersectionNode &node) +{ +	if (state.isPostfix()) { +		applyToChildren(node, INTERSECTION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +static CSGTerm *render_csg_term_from_ps(const double m[20],  +																				vector<CSGTerm*> *highlights,  +																				vector<CSGTerm*> *background,  +																				PolySet *ps,  +																				const ModuleInstantiation *modinst,  +																				const AbstractPolyNode &node) +{ +	CSGTerm *t = new CSGTerm(ps, m, QString("%1%2").arg(node.name().c_str()).arg(node.index())); +	if (modinst->tag_highlight && highlights) +		highlights->push_back(t->link()); +	if (modinst->tag_background && background) { +		background->push_back(t); +		return NULL; +	} +	return t; +} + +Response CSGTermRenderer::visit(State &state, const AbstractPolyNode &node) +{ +	if (state.isPostfix()) { +		CSGTerm *t1 = NULL; +		PolySet *ps = node.render_polyset(AbstractPolyNode::RENDER_OPENCSG, this->psrenderer); +		if (ps) { +			t1 = render_csg_term_from_ps(state.matrix(), this->highlights, this->background,  +																	 ps, node.modinst, node); +		} +		this->stored_term[node.index()] = t1; +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CSGTermRenderer::visit(State &state, const CsgNode &node) +{ +	if (state.isPostfix()) { +		CsgOp op; +		switch (node.type) { +		case CSG_TYPE_UNION: +			op = UNION; +			break; +		case CSG_TYPE_DIFFERENCE: +			op = DIFFERENCE; +			break; +		case CSG_TYPE_INTERSECTION: +			op = INTERSECTION; +			break; +		} +		applyToChildren(node, op); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CSGTermRenderer::visit(State &state, const TransformNode &node) +{ +	if (state.isPrefix()) { +		double m[20]; +		 +		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]; +			} +		} +		 +		for (int i = 16; i < 20; i++) { +			m[i] = node.matrix[i] < 0 ? state.matrix()[i] : node.matrix[i]; +		} + +		state.setMatrix(m); +	} +	if (state.isPostfix()) { +		applyToChildren(node, UNION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +// FIXME: Find out how to best call into CGAL from this visitor +Response CSGTermRenderer::visit(State &state, const RenderNode &node) +{ +	PRINT("WARNING: Found render() statement but compiled without CGAL support!"); +	if (state.isPostfix()) { +		applyToChildren(node, UNION); +		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 CSGTermRenderer::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); +	} +} + + +#if 0 + +// FIXME: #ifdef ENABLE_CGAL +#if 0 +CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) 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); + +	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() or subdiv() statement but compiled without CGAL support!"); +	return NULL; +} + +#endif // ENABLE_CGAL + + + +// FIXME: #ifdef ENABLE_CGAL +#if 0 +CSGTerm *AbstractNode::render_csg_term_from_nef(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background, const char *statement, int convexity) const +{ +	QString key = mk_cache_id(); +	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->renderCSGMesh(); + +		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->push_back(term->link()); +		if (modinst->tag_background && background) { +			background->push_back(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 +{ +	return render_csg_term_from_nef(m, highlights, background, "render", this->convexity); +} + +#else + +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->push_back(t1->link()); +	if (t1 && modinst->tag_background && background) { +		background->push_back(t1); +		return NULL; +	} +	return t1; +} + +#endif + + + +#endif + diff --git a/src/CSGTermRenderer.h b/src/CSGTermRenderer.h new file mode 100644 index 0000000..ec3ee9c --- /dev/null +++ b/src/CSGTermRenderer.h @@ -0,0 +1,53 @@ +#ifndef CSGTERMRENDERER_H_ +#define CSGTERMRENDERER_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 CSGTermRenderer : public Visitor +{ +public: +	CSGTermRenderer(const Tree &tree, class PolySetRenderer *psrenderer = NULL) +		: highlights(NULL), background(NULL), tree(tree), psrenderer(psrenderer) { +	} +  virtual ~CSGTermRenderer() {} + +  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 RenderNode &node); + +	class CSGTerm *renderCSGTerm(const AbstractNode &node, +															 vector<CSGTerm*> *highlights, vector<CSGTerm*> *background); + +private: +	enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI}; +  void addToParent(const State &state, const AbstractNode &node); +	void applyToChildren(const AbstractNode &node, CSGTermRenderer::CsgOp op); + +  const AbstractNode *root; +  typedef list<const AbstractNode *> ChildList; +  map<int, ChildList> visitedchildren; + +public: +  map<int, class CSGTerm*> stored_term; // The term rendered from each node index + +	vector<CSGTerm*> *highlights; +	vector<CSGTerm*> *background; +	const Tree &tree; +	class PolySetRenderer *psrenderer; +}; + +#endif diff --git a/src/GLView.h b/src/GLView.h index 7516894..f729838 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -18,6 +18,7 @@ class GLView : public QGLWidget  public:  	GLView(QWidget *parent = NULL); +	GLView(const QGLFormat & format, QWidget *parent = NULL);  	void setRenderFunc(void (*func)(void*), void *userdata);  #ifdef ENABLE_OPENCSG  	bool hasOpenCSGSupport() { return this->opencsg_support; } @@ -45,6 +46,8 @@ public:  #endif  private: +	void init(); +  	void (*renderfunc)(void*);  	void *renderfunc_vp; diff --git a/src/MainWindow.h b/src/MainWindow.h index c0a9844..2c43856 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 @@ -43,9 +46,9 @@ public:  	PolySet *cgal_ogl_ps;  #endif -	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;  	bool enableOpenCSG; @@ -76,6 +79,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);  	} @@ -153,4 +157,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 f61d240..8d5bbb9 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/PolySetCGALRenderer.cc b/src/PolySetCGALRenderer.cc new file mode 100644 index 0000000..95e4806 --- /dev/null +++ b/src/PolySetCGALRenderer.cc @@ -0,0 +1,413 @@ +#include "PolySetCGALRenderer.h" +#include "polyset.h" +#include "CGALRenderer.h" +#include "projectionnode.h" +#include "dxflinextrudenode.h" +#include "dxfrotextrudenode.h" +#include "dxfdata.h" +#include "dxftess.h" +#include "module.h" + +#include "printutils.h" +#include "export.h" // void cgal_nef3_to_polyset() +#include "openscad.h" // get_fragments_from_r() + +PolySet *PolySetCGALRenderer::renderPolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalrenderer.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	CGAL_Nef_polyhedron N = this->cgalrenderer.renderCGALMesh(node); + +	PolySet *ps = new PolySet(); +	ps->convexity = node.convexity; +	ps->is2d = true; + +	if (node.cut_mode) +	{ +		PolySet *cube = new PolySet(); +		double infval = 1e8, eps = 0.1; +		double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps; + +		cube->append_poly(); // top +		cube->append_vertex(x1, y1, z2); +		cube->append_vertex(x2, y1, z2); +		cube->append_vertex(x2, y2, z2); +		cube->append_vertex(x1, y2, z2); + +		cube->append_poly(); // bottom +		cube->append_vertex(x1, y2, z1); +		cube->append_vertex(x2, y2, z1); +		cube->append_vertex(x2, y1, z1); +		cube->append_vertex(x1, y1, z1); + +		cube->append_poly(); // side1 +		cube->append_vertex(x1, y1, z1); +		cube->append_vertex(x2, y1, z1); +		cube->append_vertex(x2, y1, z2); +		cube->append_vertex(x1, y1, z2); + +		cube->append_poly(); // side2 +		cube->append_vertex(x2, y1, z1); +		cube->append_vertex(x2, y2, z1); +		cube->append_vertex(x2, y2, z2); +		cube->append_vertex(x2, y1, z2); + +		cube->append_poly(); // side3 +		cube->append_vertex(x2, y2, z1); +		cube->append_vertex(x1, y2, z1); +		cube->append_vertex(x1, y2, z2); +		cube->append_vertex(x2, y2, z2); + +		cube->append_poly(); // side4 +		cube->append_vertex(x1, y2, z1); +		cube->append_vertex(x1, y1, z1); +		cube->append_vertex(x1, y1, z2); +		cube->append_vertex(x1, y2, z2); +		CGAL_Nef_polyhedron Ncube = this->cgalrenderer.renderCGALMesh(*cube); +		cube->unlink(); + +		// N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED); +		N.p3 *= Ncube.p3; +		if (!N.p3.is_simple()) { +			PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design.."); +			goto cant_project_non_simple_polyhedron; +		} + +		PolySet *ps3 = new PolySet(); +		cgal_nef3_to_polyset(ps3, &N); +		Grid2d<int> conversion_grid(GRID_COARSE); +		for (int i = 0; i < ps3->polygons.size(); i++) { +			for (int j = 0; j < ps3->polygons[i].size(); j++) { +				double x = ps3->polygons[i][j].x; +				double y = ps3->polygons[i][j].y; +				double z = ps3->polygons[i][j].z; +				if (z != 0) +					goto next_ps3_polygon_cut_mode; +				if (conversion_grid.align(x, y) == i+1) +					goto next_ps3_polygon_cut_mode; +				conversion_grid.data(x, y) = i+1; +			} +			ps->append_poly(); +			for (int j = 0; j < ps3->polygons[i].size(); j++) { +				double x = ps3->polygons[i][j].x; +				double y = ps3->polygons[i][j].y; +				conversion_grid.align(x, y); +				ps->insert_vertex(x, y); +			} +		next_ps3_polygon_cut_mode:; +		} +		ps3->unlink(); +	} +	else +	{ +		if (!N.p3.is_simple()) { +			PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design.."); +			goto cant_project_non_simple_polyhedron; +		} + +		PolySet *ps3 = new PolySet(); +		cgal_nef3_to_polyset(ps3, &N); +		CGAL_Nef_polyhedron np; +		np.dim = 2; +		for (int i = 0; i < ps3->polygons.size(); i++) +		{ +			int min_x_p = -1; +			double min_x_val = 0; +			for (int j = 0; j < ps3->polygons[i].size(); j++) { +				double x = ps3->polygons[i][j].x; +				if (min_x_p < 0 || x < min_x_val) { +					min_x_p = j; +					min_x_val = x; +				} +			} +			int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size(); +			int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size(); +			double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x; +			double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y; +			double at = atan2(ay, ax); +			double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x; +			double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y; +			double bt = atan2(by, bx); + +			double eps = 0.000001; +			if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) || +					(fabs(bx) < eps && fabs(by) < eps)) { +				// this triangle is degenerated in projection +				continue; +			} + +			std::list<CGAL_Nef_polyhedron2::Point> plist; +			for (int j = 0; j < ps3->polygons[i].size(); j++) { +				double x = ps3->polygons[i][j].x; +				double y = ps3->polygons[i][j].y; +				CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y); +				if (at > bt) +					plist.push_front(p); +				else +					plist.push_back(p); +			} +			np.p2 += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), +					CGAL_Nef_polyhedron2::INCLUDED); +		} +		DxfData dxf(np); +		dxf_tesselate(ps, &dxf, 0, true, false, 0); +		dxf_border_to_ps(ps, &dxf); +		ps3->unlink(); +	} + +cant_project_non_simple_polyhedron: + +	this->cache.insert(cacheid, new cache_entry(ps->link())); +	return ps; +} + +static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2) +{ +	for (int j = 1; j < pt->points.count(); j++) +	{ +		int k = j - 1; + +		double jx1 = pt->points[j]->x *  cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180); +		double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180); + +		double jx2 = pt->points[j]->x *  cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180); +		double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180); + +		double kx1 = pt->points[k]->x *  cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180); +		double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180); + +		double kx2 = pt->points[k]->x *  cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180); +		double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180); + +		double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2); +		double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1); + +		if (dia1_len_sq > dia2_len_sq) +		{ +			ps->append_poly(); +			if (pt->is_inner) { +				ps->append_vertex(kx1, ky1, h1); +				ps->append_vertex(jx1, jy1, h1); +				ps->append_vertex(jx2, jy2, h2); +			} else { +				ps->insert_vertex(kx1, ky1, h1); +				ps->insert_vertex(jx1, jy1, h1); +				ps->insert_vertex(jx2, jy2, h2); +			} + +			ps->append_poly(); +			if (pt->is_inner) { +				ps->append_vertex(kx2, ky2, h2); +				ps->append_vertex(kx1, ky1, h1); +				ps->append_vertex(jx2, jy2, h2); +			} else { +				ps->insert_vertex(kx2, ky2, h2); +				ps->insert_vertex(kx1, ky1, h1); +				ps->insert_vertex(jx2, jy2, h2); +			} +		} +		else +		{ +			ps->append_poly(); +			if (pt->is_inner) { +				ps->append_vertex(kx1, ky1, h1); +				ps->append_vertex(jx1, jy1, h1); +				ps->append_vertex(kx2, ky2, h2); +			} else { +				ps->insert_vertex(kx1, ky1, h1); +				ps->insert_vertex(jx1, jy1, h1); +				ps->insert_vertex(kx2, ky2, h2); +			} + +			ps->append_poly(); +			if (pt->is_inner) { +				ps->append_vertex(jx2, jy2, h2); +				ps->append_vertex(kx2, ky2, h2); +				ps->append_vertex(jx1, jy1, h1); +			} else { +				ps->insert_vertex(jx2, jy2, h2); +				ps->insert_vertex(kx2, ky2, h2); +				ps->insert_vertex(jx1, jy1, h1); +			} +		} +	} +} + +PolySet *PolySetCGALRenderer::renderPolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalrenderer.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	DxfData *dxf; + +	if (node.filename.isEmpty()) +	{ +		// 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, node.getChildren()) { +			if (v->modinst->tag_background) continue; +			N.p2 += this->cgalrenderer.renderCGALMesh(*v).p2; +		} + +		dxf = new DxfData(N); +	} else { +		dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); +	} + +	PolySet *ps = new PolySet(); +	ps->convexity = node.convexity; + +	double h1, h2; + +	if (node.center) { +		h1 = -node.height/2.0; +		h2 = +node.height/2.0; +	} else { +		h1 = 0; +		h2 = node.height; +	} + +	bool first_open_path = true; +	for (int i = 0; i < dxf->paths.count(); i++) +	{ +		if (dxf->paths[i].is_closed) +			continue; +		if (first_open_path) { +			PRINTF("WARNING: Open paths in dxf_linear_extrude(file = \"%s\", layer = \"%s\"):", +					node.filename.toAscii().data(), node.layername.toAscii().data()); +			first_open_path = false; +		} +		PRINTF("   %9.5f %10.5f ... %10.5f %10.5f", +				dxf->paths[i].points.first()->x / node.scale + node.origin_x, +				dxf->paths[i].points.first()->y / node.scale + node.origin_y,  +				dxf->paths[i].points.last()->x / node.scale + node.origin_x, +				dxf->paths[i].points.last()->y / node.scale + node.origin_y); +	} + + +	if (node.has_twist) +	{ +		dxf_tesselate(ps, dxf, 0, false, true, h1); +		dxf_tesselate(ps, dxf, node.twist, true, true, h2); +		for (int j = 0; j < node.slices; j++) +		{ +			double t1 = node.twist*j / node.slices; +			double t2 = node.twist*(j+1) / node.slices; +			double g1 = h1 + (h2-h1)*j / node.slices; +			double g2 = h1 + (h2-h1)*(j+1) / node.slices; +			for (int i = 0; i < dxf->paths.count(); i++) +			{ +				if (!dxf->paths[i].is_closed) +					continue; +				add_slice(ps, &dxf->paths[i], t1, t2, g1, g2); +			} +		} +	} +	else +	{ +		dxf_tesselate(ps, dxf, 0, false, true, h1); +		dxf_tesselate(ps, dxf, 0, true, true, h2); +		for (int i = 0; i < dxf->paths.count(); i++) +		{ +			if (!dxf->paths[i].is_closed) +				continue; +			add_slice(ps, &dxf->paths[i], 0, 0, h1, h2); +		} +	} + +	delete dxf; + +	this->cache.insert(cacheid, new cache_entry(ps->link())); +	return ps; +} + +PolySet *PolySetCGALRenderer::renderPolySet(const DxfRotateExtrudeNode &node,  +																						AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalrenderer.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	DxfData *dxf; + +	if (node.filename.isEmpty()) +	{ +		// 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, node.getChildren()) { +			if (v->modinst->tag_background) continue; +			N.p2 += this->cgalrenderer.renderCGALMesh(*v).p2; +		} + +		dxf = new DxfData(N); +	} else { +		dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); +	} + +	PolySet *ps = new PolySet(); +	ps->convexity = node.convexity; + +	for (int i = 0; i < dxf->paths.count(); i++) +	{ +		double max_x = 0; +		for (int j = 0; j < dxf->paths[i].points.count(); j++) { +			max_x = fmax(max_x, dxf->paths[i].points[j]->x); +		} + +		int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa); + +		double points[fragments][dxf->paths[i].points.count()][3]; + +		for (int j = 0; j < fragments; j++) { +			double a = (j*2*M_PI) / fragments; +			for (int k = 0; k < dxf->paths[i].points.count(); k++) { +				if (dxf->paths[i].points[k]->x == 0) { +					points[j][k][0] = 0; +					points[j][k][1] = 0; +				} else { +					points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); +					points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); +				} +				points[j][k][2] = dxf->paths[i].points[k]->y; +			} +		} + +		for (int j = 0; j < fragments; j++) { +			int j1 = j + 1 < fragments ? j + 1 : 0; +			for (int k = 0; k < dxf->paths[i].points.count(); k++) { +				int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; +				if (points[j][k][0] != points[j1][k][0] || +						points[j][k][1] != points[j1][k][1] || +						points[j][k][2] != points[j1][k][2]) { +					ps->append_poly(); +					ps->append_vertex(points[j ][k ][0], +							points[j ][k ][1], points[j ][k ][2]); +					ps->append_vertex(points[j1][k ][0], +							points[j1][k ][1], points[j1][k ][2]); +					ps->append_vertex(points[j ][k1][0], +							points[j ][k1][1], points[j ][k1][2]); +				} +				if (points[j][k1][0] != points[j1][k1][0] || +						points[j][k1][1] != points[j1][k1][1] || +						points[j][k1][2] != points[j1][k1][2]) { +					ps->append_poly(); +					ps->append_vertex(points[j ][k1][0], +							points[j ][k1][1], points[j ][k1][2]); +					ps->append_vertex(points[j1][k ][0], +							points[j1][k ][1], points[j1][k ][2]); +					ps->append_vertex(points[j1][k1][0], +							points[j1][k1][1], points[j1][k1][2]); +				} +			} +		} +	} + +	delete dxf; + +	this->cache.insert(cacheid, new cache_entry(ps->link())); +	return ps; +} diff --git a/src/PolySetCGALRenderer.h b/src/PolySetCGALRenderer.h new file mode 100644 index 0000000..d6ef63c --- /dev/null +++ b/src/PolySetCGALRenderer.h @@ -0,0 +1,24 @@ +#ifndef POLYSETCGALRENDERER_H_ +#define POLYSETCGALRENDERER_H_ + +#include "PolySetRenderer.h" + +/*! +	This is a PolySet renderer which uses the CGALRenderer to support building +	polysets. +*/ +class PolySetCGALRenderer : public PolySetRenderer +{ +public: +	PolySetCGALRenderer(class CGALRenderer &cgalrenderer) :  +		PolySetRenderer(), cgalrenderer(cgalrenderer) { } +	virtual ~PolySetCGALRenderer() { } +	virtual PolySet *renderPolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e); +	virtual PolySet *renderPolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e); +	virtual PolySet *renderPolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e); + +private: +	CGALRenderer &cgalrenderer; +}; + +#endif diff --git a/src/PolySetRenderer.cc b/src/PolySetRenderer.cc new file mode 100644 index 0000000..c2fddf0 --- /dev/null +++ b/src/PolySetRenderer.cc @@ -0,0 +1,15 @@ +#include "PolySetRenderer.h" +#include "printutils.h" +#include "polyset.h" + +PolySetRenderer *PolySetRenderer::global_renderer = NULL; + +PolySetRenderer::cache_entry::cache_entry(PolySet *ps) : +		ps(ps), msg(print_messages_stack.last()) +{ +} + +PolySetRenderer::cache_entry::~cache_entry() +{ +	ps->unlink(); +} diff --git a/src/PolySetRenderer.h b/src/PolySetRenderer.h new file mode 100644 index 0000000..c175825 --- /dev/null +++ b/src/PolySetRenderer.h @@ -0,0 +1,39 @@ +#ifndef POLYSETRENDERER_H_ +#define POLYSETRENDERER_H_ + +#include "myqhash.h" +#include "node.h" +#include <QCache> + +class PolySetRenderer +{ +public: +	enum RenderMode { RENDER_CGAL, RENDER_OPENCSG }; +	PolySetRenderer() : cache(100) {} + +	virtual ~PolySetRenderer() {} + +	virtual PolySet *renderPolySet(const class ProjectionNode &, AbstractPolyNode::render_mode_e) = 0; +	virtual PolySet *renderPolySet(const class DxfLinearExtrudeNode &, AbstractPolyNode::render_mode_e) = 0; +	virtual PolySet *renderPolySet(const class DxfRotateExtrudeNode &, 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 PolySetRenderer *global_renderer; +}; + +#endif 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/cgaladv.cc b/src/cgaladv.cc index 829bc84..0e89ed5 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -29,6 +29,9 @@  #include "builtin.h"  #include "printutils.h"  #include "cgal.h" +#include "visitor.h" +#include <sstream> +#include <assert.h>  #ifdef ENABLE_CGAL  extern CGAL_Nef_polyhedron3 minkowski3(CGAL_Nef_polyhedron3 a, CGAL_Nef_polyhedron3 b); @@ -52,18 +55,34 @@ public:  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; +	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 { +		switch (this->type) { +		case MINKOWSKI: +			return "minkowski"; +			break; +		case GLIDE: +			return "glide"; +			break; +		case SUBDIV: +			return "subdiv"; +			break; +		default: +			assert(false); +		} +	} + +	Value path; +	std::string subdiv_type; +	int convexity, level; +	cgaladv_type_e type;  };  AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const @@ -126,101 +145,24 @@ void register_builtin_cgaladv()  	builtin_modules["subdiv"] = new CgaladvModule(SUBDIV);  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CgaladvNode::render_cgal_nef_polyhedron() const +std::string CgaladvNode::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(); -	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: subdiv() is not implemented yet!"); -	} - -	if (type == SUBDIV) -	{ -		PRINT("WARNING: subdiv() is not implemented yet!"); +	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; +	default: +		assert(false);  	} -	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 -{ -	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); - -	return NULL; +	return stream.str();  } - -#else // ENABLE_CGAL - -CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ -	PRINT("WARNING: Found minkowski(), glide() or subdiv() 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); -		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; -} - diff --git a/src/cgaladv_minkowski3.cc b/src/cgaladv_minkowski3.cc index cc9ef00..c8bf66b 100644 --- a/src/cgaladv_minkowski3.cc +++ b/src/cgaladv_minkowski3.cc @@ -25,10 +25,7 @@  #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); diff --git a/src/context.cc b/src/context.cc index 5aec712b..4bc6ed5 100644 --- a/src/context.cc +++ b/src/context.cc @@ -147,8 +147,16 @@ AbstractNode *Context::evaluate_module(const ModuleInstantiation *inst) const  	return NULL;  } +/*! +	Returns the absolute path to the given filename, unless it's empty. + */  QString Context::get_absolute_path(const QString &filename) const  { -	return QFileInfo(QDir(this->document_path), filename).absoluteFilePath(); +	if (!filename.isEmpty()) { +		return QFileInfo(QDir(this->document_path), filename).absoluteFilePath(); +	} +	else { +		return filename; +	}  } diff --git a/src/control.cc b/src/control.cc index 5b7e1b1..7b631f9 100644 --- a/src/control.cc +++ b/src/control.cc @@ -123,7 +123,7 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation  				msg += QString(", ");  			if (!inst->argnames[i].isEmpty())  				msg += inst->argnames[i] + QString(" = "); -			msg += inst->argvalues[i].dump(); +			msg += QString::fromStdString(inst->argvalues[i].toString());  		}  		PRINT(msg);  	} 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 ae97085..c7251f8 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -23,22 +23,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  { @@ -48,18 +40,6 @@ 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); @@ -71,105 +51,26 @@ AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *ins  	return node;  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CsgNode::render_cgal_nef_polyhedron() const +std::string CsgNode::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(); - -	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("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object."); -	} -	CGAL::set_error_behaviour(old_behaviour); - -	print_messages_pop(); -	progress_report(); - -	return N; -} - -#endif /* ENABLE_CGAL */ - -CSGTerm *CsgNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ -	CSGTerm *t1 = NULL; -	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 942aba6..0fd4196 100644 --- a/src/csgterm.cc +++ b/src/csgterm.cc @@ -26,7 +26,26 @@  #include "csgterm.h"  #include "polyset.h" -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 m[20], QString label)  {  	this->type = TYPE_PRIMITIVE;  	this->polyset = polyset; @@ -199,9 +218,9 @@ QString CSGChain::dump()  				text += "\n";  			text += "+";  		} -		if (types[i] == CSGTerm::TYPE_DIFFERENCE) +		else if (types[i] == CSGTerm::TYPE_DIFFERENCE)  			text += " -"; -		if (types[i] == CSGTerm::TYPE_INTERSECTION) +		else if (types[i] == CSGTerm::TYPE_INTERSECTION)  			text += " *";  		text += labels[i];  	} diff --git a/src/csgterm.h b/src/csgterm.h index 35d071d..4ee6faa 100644 --- a/src/csgterm.h +++ b/src/csgterm.h @@ -22,7 +22,7 @@ public:  	double m[20];  	int refcounter; -	CSGTerm(PolySet *polyset, double m[20], QString label); +	CSGTerm(PolySet *polyset, const double m[20], QString label);  	CSGTerm(type_e type, CSGTerm *left, CSGTerm *right);  	CSGTerm *normalize(); diff --git a/src/dxfdim.cc b/src/dxfdim.cc index 02a1274..e1444c7 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -51,15 +51,15 @@ Value builtin_dxf_dim(const Context *ctx, const QVector<QString> &argnames, cons  	for (int i = 0; i < argnames.count() && i < args.count(); i++) {  		if (argnames[i] == "file") -			filename = ctx->get_absolute_path(args[i].text); +			filename = ctx->get_absolute_path(QString::fromStdString(args[i].text));  		if (argnames[i] == "layer") -			layername = args[i].text; +			layername = QString::fromStdString(args[i].text);  		if (argnames[i] == "origin")  			args[i].getv2(xorigin, yorigin);  		if (argnames[i] == "scale")  			args[i].getnum(scale);  		if (argnames[i] == "name") -			name = args[i].text; +			name = QString::fromStdString(args[i].text);  	}  	struct stat st; @@ -135,9 +135,9 @@ Value builtin_dxf_cross(const Context *ctx, const QVector<QString> &argnames, co  	for (int i = 0; i < argnames.count() && i < args.count(); i++) {  		if (argnames[i] == "file") -			filename = ctx->get_absolute_path(args[i].text); +			filename = ctx->get_absolute_path(QString::fromStdString(args[i].text));  		if (argnames[i] == "layer") -			layername = args[i].text; +			layername = QString::fromStdString(args[i].text);  		if (argnames[i] == "origin")  			args[i].getv2(xorigin, yorigin);  		if (argnames[i] == "scale") @@ -180,8 +180,8 @@ 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;  		}  	} diff --git a/src/dxflinextrude.cc b/src/dxflinextrude.cc index 83c3d9c..aa45d79 100644 --- a/src/dxflinextrude.cc +++ b/src/dxflinextrude.cc @@ -23,8 +23,9 @@   *   */ +#include "dxflinextrudenode.h" +  #include "module.h" -#include "node.h"  #include "context.h"  #include "printutils.h"  #include "builtin.h" @@ -32,11 +33,14 @@  #include "dxftess.h"  #include "polyset.h"  #include "progress.h" +#include "visitor.h" +#include "PolySetRenderer.h"  #include "openscad.h" // get_fragments_from_r()  #include <sys/types.h>  #include <sys/stat.h>  #include <unistd.h> +#include <sstream>  #include <QApplication>  #include <QTime> @@ -49,24 +53,6 @@ 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); @@ -91,12 +77,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()) +		node->filename = c.get_absolute_path(QString::fromStdString(file.text)); -	node->layername = layer.text; +	node->layername = QString::fromStdString(layer.text);  	node->height = height.num;  	node->convexity = (int)convexity.num;  	origin.getv2(node->origin_x, node->origin_y); @@ -142,205 +126,48 @@ 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::render_polyset(render_mode_e mode,  +																							PolySetRenderer *renderer) 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 (!renderer) { +		PRINTF("WARNING: No suitable PolySetRenderer 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 +	PolySet *ps = renderer->renderPolySet(*this, mode); -		// 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 = 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; -		struct stat st; -		memset(&st, 0, sizeof(struct stat)); -		stat(filename.toAscii().data(), &st); -		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)st.st_mtime, (int)st.st_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; + +	QString text; +	struct stat st; +	memset(&st, 0, sizeof(struct stat)); +	stat(this->filename.toAscii().data(), &st); +	 +	stream << this->name() << "(" +		"file = \"" << this->filename << "\", " +		"cache = \"" << std::hex << (int)st.st_mtime << "." << (int)st.st_size << "\", " +		"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..8f829ea --- /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; +	QString filename, layername; +	virtual PolySet *render_polyset(render_mode_e mode, class PolySetRenderer *) const; +}; + +#endif diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc index ea603f0..b59270f 100644 --- a/src/dxfrotextrude.cc +++ b/src/dxfrotextrude.cc @@ -23,19 +23,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 "PolySetRenderer.h"  #include "openscad.h" // get_fragments_from_r()  #include <sys/types.h>  #include <sys/stat.h>  #include <unistd.h> +#include <sstream>  #include <QTime>  #include <QApplication> @@ -48,22 +51,6 @@ 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); @@ -84,12 +71,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()) +		node->filename = c.get_absolute_path(QString::fromStdString(file.text)); -	node->layername = layer.text; +	node->layername = QString::fromStdString(layer.text);  	node->convexity = (int)convexity.num;  	origin.getv2(node->origin_x, node->origin_y);  	node->scale = scale.num; @@ -117,120 +102,40 @@ void register_builtin_dxf_rotate_extrude()  	builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule();  } -PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const +PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e mode,  +																							PolySetRenderer *renderer) 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 (!renderer) { +		PRINTF("WARNING: No suitable PolySetRenderer 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[fragments][dxf->paths[i].points.count()][3]; - -		for (int j = 0; j < fragments; j++) { -			double a = (j*2*M_PI) / fragments; -			for (int k = 0; k < dxf->paths[i].points.count(); k++) { -				if (dxf->paths[i].points[k]->x == 0) { -					points[j][k][0] = 0; -					points[j][k][1] = 0; -				} else { -					points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); -					points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); -				} -				points[j][k][2] = dxf->paths[i].points[k]->y; -			} -		} - -		for (int j = 0; j < fragments; j++) { -			int j1 = j + 1 < fragments ? j + 1 : 0; -			for (int k = 0; k < dxf->paths[i].points.count(); k++) { -				int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; -				if (points[j][k][0] != points[j1][k][0] || -						points[j][k][1] != points[j1][k][1] || -						points[j][k][2] != points[j1][k][2]) { -					ps->append_poly(); -					ps->append_vertex(points[j ][k ][0], -							points[j ][k ][1], points[j ][k ][2]); -					ps->append_vertex(points[j1][k ][0], -							points[j1][k ][1], points[j1][k ][2]); -					ps->append_vertex(points[j ][k1][0], -							points[j ][k1][1], points[j ][k1][2]); -				} -				if (points[j][k1][0] != points[j1][k1][0] || -						points[j][k1][1] != points[j1][k1][1] || -						points[j][k1][2] != points[j1][k1][2]) { -					ps->append_poly(); -					ps->append_vertex(points[j ][k1][0], -							points[j ][k1][1], points[j ][k1][2]); -					ps->append_vertex(points[j1][k ][0], -							points[j1][k ][1], points[j1][k ][2]); -					ps->append_vertex(points[j1][k1][0], -							points[j1][k1][1], points[j1][k1][2]); -				} -			} -		} -	} - -	PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); +	PolySet *ps = renderer->renderPolySet(*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; -		struct stat st; -		memset(&st, 0, sizeof(struct stat)); -		stat(filename.toAscii().data(), &st); -		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)st.st_mtime, (int)st.st_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; + +	struct stat st; +	memset(&st, 0, sizeof(struct stat)); +	stat(filename.toAscii().data(), &st); +	stream << this->name() << "(" +		"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();  } - diff --git a/src/dxfrotextrudenode.h b/src/dxfrotextrudenode.h new file mode 100644 index 0000000..0372cb3 --- /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; +	QString filename, layername; +	virtual PolySet *render_polyset(render_mode_e mode, class PolySetRenderer *) const; +}; + +#endif diff --git a/src/export.cc b/src/export.cc index 40b16d5..71ebe8f 100644 --- a/src/export.cc +++ b/src/export.cc @@ -29,6 +29,7 @@  #include <QApplication>  #include <QProgressDialog> +#include <QTextStream>  #include <errno.h>  #ifdef ENABLE_CGAL @@ -71,9 +72,10 @@ void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N)  }  /*! -	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, QTextStream &output, QProgressDialog *pd)  {  	CGAL_Polyhedron P;  	root_N->p3.convert_to_Polyhedron(P); @@ -85,14 +87,7 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *  	setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output -	FILE *f = fopen(filename.toUtf8().data(), "w"); -	if (!f) { -		PRINTA("Can't open STL file \"%1\" for STL export: %2",  -					 filename, QString(strerror(errno))); -		set_output_handler(NULL, NULL); -		return; -	} -	fprintf(f, "solid OpenSCAD_Model\n"); +	output << "solid OpenSCAD_Model\n";  	int facet_count = 0;  	for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { @@ -126,14 +121,16 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *  				// Avoid generating normals for polygons with zero area  				double eps = 0.000001;  				if (nlength < eps) nlength = 1.0; -				fprintf(f, "  facet normal %f %f %f\n", -						nx / nlength, ny / nlength, nz  / nlength); -				fprintf(f, "    outer loop\n"); -				fprintf(f, "      vertex %s\n", vs1.toAscii().data()); -				fprintf(f, "      vertex %s\n", vs2.toAscii().data()); -				fprintf(f, "      vertex %s\n", vs3.toAscii().data()); -				fprintf(f, "    endloop\n"); -				fprintf(f, "  endfacet\n"); +				output << "  facet normal "  +							 << nx / nlength << " "  +							 << ny / nlength << " "  +							 << nz / nlength << "\n"; +				output << "    outer loop\n"; +				output << "      vertex " << vs1 << "\n"; +				output << "      vertex " << vs2 << "\n"; +				output << "      vertex " << vs3 << "\n"; +				output << "    endloop\n"; +				output << "  endfacet\n";  			}  		} while (hc != hc_end);  		if (pd) { @@ -142,12 +139,11 @@ void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *  		}  	} -	fprintf(f, "endsolid OpenSCAD_Model\n"); -	fclose(f); +	output << "endsolid OpenSCAD_Model\n";  	setlocale(LC_NUMERIC, "");      // Set default locale  } -void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*) +void export_off(CGAL_Nef_polyhedron*, QTextStream&, QProgressDialog*)  {  	PRINTF("WARNING: OFF import is not implemented yet.");  } @@ -155,29 +151,20 @@ void export_off(CGAL_Nef_polyhedron*, QString, QProgressDialog*)  /*!  	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, QTextStream &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"); +	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);  	for (int i=0; i<dd.paths.size(); i++) @@ -189,37 +176,38 @@ void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *  			double y1 = p1->y;  			double x2 = p2->x;  			double y2 = p2->y; -			fprintf(f, "  0\n"); -			fprintf(f, "LINE\n"); +			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"; +  	setlocale(LC_NUMERIC, "");      // Set default locale  } diff --git a/src/export.h b/src/export.h index 619d9e0..d35246c 100644 --- a/src/export.h +++ b/src/export.h @@ -2,10 +2,11 @@  #define EXPORT_H_  #ifdef ENABLE_CGAL +#include "cgal.h"  void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N); -void export_stl(class CGAL_Nef_polyhedron *root_N, QString filename, class QProgressDialog *pd); -void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); -void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); +void export_stl(class CGAL_Nef_polyhedron *root_N, class QTextStream &output, class QProgressDialog *pd); +void export_off(CGAL_Nef_polyhedron *root_N, class QTextStream &output, QProgressDialog *pd); +void export_dxf(CGAL_Nef_polyhedron *root_N, class QTextStream &output, QProgressDialog *pd);  #endif  #endif diff --git a/src/expr.cc b/src/expr.cc index 23bcc32..1cfe627 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -26,6 +26,8 @@  #include "expression.h"  #include "value.h"  #include "context.h" +#include <assert.h> +#include <sstream>  Expression::Expression()  { @@ -108,7 +110,7 @@ Value Expression::evaluate(const Context *context) const  		Value v;  		v.type = Value::VECTOR;  		for (int i = 0; i < children.size(); i++) -			v.vec.append(new Value(children[i]->evaluate(context))); +			v.append(new Value(children[i]->evaluate(context)));  		return v;  	}  	if (type == "L") @@ -142,45 +144,62 @@ Value Expression::evaluate(const Context *context) const  	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("["); +	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 << "(" << *children[0] << " " << this->type << " " << *children[1] << ")"; +	} +	else if (this->type == "?:") { +		stream << "(" << *children[0] << " ? " << this->type << " : " << *children[1] << ")"; +	} +	else if (this->type == "[]") { +		stream << "(" << *children[0] << "[" << *children[1] << "])"; +	} +	else if (this->type == "I") { +		stream << "(-" << *children[0] << ")"; +	} +	else if (this->type == "C") { +		stream << *const_value; +	} +	else if (this->type == "R") { +		stream << "[" << *children[0] << " : " << *children[1] << " : " << children[2] << "]"; +	} +	else if (this->type == "V") { +		stream << "[";  		for (int i=0; i < children.size(); i++) { -			if (i > 0) -				text += QString(", "); -			text += children[i]->dump(); +			if (i > 0) stream << ", "; +			stream << *children[i];  		} -		return text + QString("]"); +		stream << "]";  	} -	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("("); +	else if (this->type == "L") { +		stream << var_name; +	} +	else if (this->type == "N") { +		stream << "(" << *children[0] << "." << var_name << ")"; +	} +	else if (this->type == "F") { +		stream << call_funcname << "(";  		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(); +			if (i > 0) stream << ", "; +			if (!call_argnames[i].isEmpty()) stream << call_argnames[i]  << " = "; +			stream << *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..38043db 100644 --- a/src/expression.h +++ b/src/expression.h @@ -34,7 +34,9 @@ public:  	~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 e8ff54a..7627a94 100644 --- a/src/func.cc +++ b/src/func.cc @@ -29,6 +29,7 @@  #include "dxfdim.h"  #include "builtin.h"  #include <math.h> +#include <sstream>  AbstractFunction::~AbstractFunction()  { @@ -68,9 +69,9 @@ QString Function::dump(QString indent, QString name) const  			text += QString(", ");  		text += argnames[i];  		if (argexpr[i]) -			text += QString(" = ") + argexpr[i]->dump(); +			text += QString(" = ") + QString::fromStdString(argexpr[i]->toString());  	} -	text += QString(") = %1;\n").arg(expr->dump()); +	text += QString(") = %1;\n").arg(QString::fromStdString(expr->toString()));  	return text;  } @@ -302,15 +303,12 @@ Value builtin_ln(const Context *, const QVector<QString>&, const QVector<Value>  Value builtin_str(const Context *, const QVector<QString>&, const QVector<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 (int 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) diff --git a/src/function.h b/src/function.h index 3bb89c2..7b58e38 100644 --- a/src/function.h +++ b/src/function.h @@ -3,6 +3,7 @@  #include "value.h"  #include <QString> +#include <QVector>  class AbstractFunction  { diff --git a/src/glview.cc b/src/glview.cc index d51714b..f4b2ccf 100644 --- a/src/glview.cc +++ b/src/glview.cc @@ -24,7 +24,8 @@   */  #include "GLView.h" -#include "Preferences.h" +// FIXME: Reenable/rewrite - don't be dependant on GUI +//#include "Preferences.h"  #include <QApplication>  #include <QWheelEvent> @@ -38,35 +39,45 @@  GLView::GLView(QWidget *parent) : QGLWidget(parent)  { -	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; -	last_mouse_x = 0; -	last_mouse_y = 0; +GLView::GLView(const QGLFormat & format, QWidget *parent) : QGLWidget(format, parent) +{ +	init(); +} -	orthomode = false; -	showaxes = false; -	showcrosshairs = false; +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->last_mouse_x = 0; +	this->last_mouse_y = 0; -	renderfunc = NULL; -	renderfunc_vp = NULL; +	this->orthomode = false; +	this->showaxes = false; +	this->showcrosshairs = false; + +	this->renderfunc = NULL; +	this->renderfunc_vp = NULL;  	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  } @@ -215,7 +226,9 @@ void GLView::resizeGL(int w, int h)  void GLView::paintGL()  { -	const QColor &bgcol = Preferences::inst()->color(Preferences::BACKGROUND_COLOR); +// FIXME: Reenable/rewrite - don't be dependant on GUI +//	const QColor &bgcol = Preferences::inst()->color(Preferences::BACKGROUND_COLOR); +		const QColor &bgcol = QColor(0xff, 0xff, 0xe5);  	glClearColor(bgcol.redF(), bgcol.greenF(), bgcol.blueF(), 0.0);  	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -258,7 +271,8 @@ void GLView::paintGL()  	if (showcrosshairs)  	{  		glLineWidth(3); -		const QColor &col = Preferences::inst()->color(Preferences::CROSSHAIR_COLOR); +//		const QColor &col = Preferences::inst()->color(Preferences::CROSSHAIR_COLOR); +		const QColor &col = QColor(0x80, 0x00, 0x00);  		glColor3f(col.redF(), col.greenF(), col.blueF());  		glBegin(GL_LINES);  		for (double xf = -1; xf <= +1; xf += 2) @@ -509,4 +523,3 @@ void GLView::mouseReleaseEvent(QMouseEvent*)  	mouse_drag_active = false;  	releaseMouse();  } - @@ -1,10 +1,10 @@  #ifndef GRID_H_  #define GRID_H_ +#include <QHash>  #include <math.h>  #include <stdint.h>  #include <stdlib.h> -#include <QHash>  const double GRID_COARSE = 0.001;  const double GRID_FINE   = 0.000001; diff --git a/src/import.cc b/src/import.cc index bab13ae..ba65cbb 100644 --- a/src/import.cc +++ b/src/import.cc @@ -23,8 +23,9 @@   *   */ +#include "importnode.h" +  #include "module.h" -#include "node.h"  #include "polyset.h"  #include "context.h"  #include "builtin.h" @@ -36,12 +37,8 @@  #include <QFile>  #include <sys/types.h>  #include <sys/stat.h> - -enum import_type_e { -	TYPE_STL, -	TYPE_OFF, -	TYPE_DXF -}; +#include <sstream> +#include <assert.h>  class ImportModule : public AbstractModule  { @@ -51,26 +48,12 @@ public:  	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) { +	if (this->type == TYPE_DXF) {  		argnames = QVector<QString>() << "file" << "layer" << "convexity" << "origin" << "scale";  	} else {  		argnames = QVector<QString>() << "file" << "convexity"; @@ -93,8 +76,10 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati  	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->layername = c.lookup_variable("layer", true).text; +	Value v = c.lookup_variable("file"); +	node->filename = c.get_absolute_path(QString::fromStdString(v.text)); +//	node->filename = c.get_absolute_path(QString::fromStdString(c.lookup_variable("file").text)); +	node->layername = QString::fromStdString(c.lookup_variable("layer", true).text);  	node->convexity = c.lookup_variable("convexity", true).num;  	if (node->convexity <= 0) @@ -119,17 +104,17 @@ void register_builtin_import()  	builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF);  } -PolySet *ImportNode::render_polyset(render_mode_e) const +PolySet *ImportNode::render_polyset(render_mode_e, class PolySetRenderer *) const  {  	PolySet *p = new PolySet(); -	p->convexity = convexity; +	p->convexity = this->convexity; -	if (type == TYPE_STL) +	if (this->type == TYPE_STL)  	{ -		handle_dep(filename); -		QFile f(filename); +		handle_dep(this->filename); +		QFile f(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.toAscii().data());  			return p;  		} @@ -190,14 +175,14 @@ PolySet *ImportNode::render_polyset(render_mode_e) const  		}  	} -	if (type == TYPE_OFF) +	if (this->type == TYPE_OFF)  	{  		PRINTF("WARNING: OFF import is not implemented yet.");  	} -	if (type == TYPE_DXF) +	if (this->type == TYPE_DXF)  	{ -		DxfData dd(fn, fs, fa, filename, layername, origin_x, origin_y, scale); +		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); @@ -206,28 +191,56 @@ PolySet *ImportNode::render_polyset(render_mode_e) const  	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; +	std::stringstream stream; + +	QString text; +	struct stat st; +	memset(&st, 0, sizeof(struct stat)); +	stat(this->filename.toAscii().data(), &st); + +	stream << this->name(); +	switch (this->type) { +	case TYPE_STL: +		stream << "(file = \"" << this->filename << "\", " +			"cache = \"" << std::hex << (int)st.st_mtime << "." << (int)st.st_size << "\", " +			"convexity = " << std::dec << this->convexity << ")"; +		break; +	case TYPE_OFF: +		stream << "(file = \"" << this->filename << "\", " +			"cache = \"" << std::hex << (int)st.st_mtime << "." << (int)st.st_size << "\", " +			"convexity = " << std::dec << this->convexity << ")"; +		break; +	case TYPE_DXF: +		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 << ")"; +		break; +	default: +		assert(false);  	} -	return dump_cache; + +	return stream.str();  } +std::string ImportNode::name() const +{ +	switch (this->type) { +	case TYPE_STL: +		return "import_stl"; +		break; +	case TYPE_OFF: +		return "import_off"; +		break; +	case TYPE_DXF: +		return "import_dxf"; +		break; +	default: +		assert(false); +	} +} diff --git a/src/importnode.h b/src/importnode.h new file mode 100644 index 0000000..a010d4c --- /dev/null +++ b/src/importnode.h @@ -0,0 +1,32 @@ +#ifndef IMPORTNODE_H_ +#define IMPORTNODE_H_ + +#include "node.h" +#include "visitor.h" + +enum import_type_e { +	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; +	QString filename; +	QString layername; +	int convexity; +	double fn, fs, fa; +	double origin_x, origin_y, scale; +	virtual PolySet *render_polyset(render_mode_e mode, class PolySetRenderer *) const; +}; + +#endif diff --git a/src/mainwin.cc b/src/mainwin.cc index ef738fc..951c4f5 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -40,10 +40,13 @@  #include "progress.h"  #ifdef ENABLE_OPENCSG  #include "render-opencsg.h" +#include "CSGTermRenderer.h"  #endif  #ifdef USE_PROGRESSWIDGET  #include "ProgressWidget.h"  #endif +#include "CGALRenderer.h" +#include "PolySetCGALRenderer.h"  #include <QMenu>  #include <QTime> @@ -72,6 +75,11 @@  #include "qlanguagefactory.h"  #endif +#include <algorithm> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +using namespace boost::lambda; +  //for chdir  #include <unistd.h> @@ -109,13 +117,15 @@ using CGAL::OGL::Nef3_Converter;  #endif  #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";  static char copyrighttext[] =  	"Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>\n"  	"\n" @@ -137,9 +147,9 @@ MainWindow::MainWindow(const QString &filename)  	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); @@ -177,8 +187,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())); @@ -272,7 +282,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 @@ -333,9 +343,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(); @@ -536,7 +546,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)  { @@ -546,81 +556,84 @@ 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; -	enableOpenCSG = false; +	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); +	this->enableOpenCSG = false;  	// 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" +  +														 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(); @@ -640,19 +653,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(); @@ -662,7 +679,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') @@ -690,13 +707,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(); @@ -717,7 +727,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; +		CGALRenderer cgalrenderer(cache, this->tree); +		PolySetCGALRenderer psrenderer(cgalrenderer); +		CSGTermRenderer csgrenderer(this->tree, &psrenderer); +		root_raw_term = csgrenderer.renderCSGTerm(*root_node, &highlight_terms, &background_terms);  		if (!root_raw_term) {  			PRINT("ERROR: CSG generation failed! (no top level object found)");  			if (procevents) @@ -755,7 +770,7 @@ void MainWindow::compileCSG(bool procevents)  		root_chain->import(root_norm_term);  		if (root_chain->polysets.size() > 1000) { -			PRINTF("WARNING: Normalized tree has %d elements!", root_chain->polysets.size()); +			PRINTF("WARNING: Normalized tree has %u elements!", root_chain->polysets.size());  			PRINTF("WARNING: OpenCSG rendering has been disabled.");  		} else {  			enableOpenCSG = true; @@ -763,12 +778,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(); @@ -782,12 +797,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(); @@ -972,7 +987,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);  } @@ -985,7 +1000,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);  } @@ -1029,6 +1044,9 @@ bool MainWindow::checkModified()  void MainWindow::actionReloadCompile()  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock; +  	if (!checkModified()) return;  	console->clear(); @@ -1047,13 +1065,16 @@ void MainWindow::actionReloadCompile()  	else  #endif  	{ -		screen->updateGL(); +		this->glview->updateGL();  	}  	clearCurrentOutput();  }  void MainWindow::actionCompile()  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock; +  	setCurrentOutput();  	console->clear(); @@ -1069,11 +1090,11 @@ void MainWindow::actionCompile()  #endif  	}  	else { -		screen->updateGL(); +		this->glview->updateGL();  	}  	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(); @@ -1088,13 +1109,17 @@ 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; +	}  	if (this->root_N) {  		delete this->root_N; @@ -1124,9 +1149,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; +		CGALRenderer renderer(cache, this->tree); +		this->root_N = new CGAL_Nef_polyhedron(renderer.renderCGALMesh(*this->root_node));  	}  	catch (ProgressCancelException e) {  		PRINT("Rendering cancelled."); @@ -1135,8 +1163,9 @@ void MainWindow::actionRenderCGAL()  	if (this->root_N)  	{ -		PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost()); -		PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size()); +		// FIXME: Reenable cache cost info +// 		PRINTF("Number of vertices currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.totalCost()); +// 		PRINTF("Number of objects currently in CGAL cache: %d", AbstractNode::cgal_nef_cache.size());  		QApplication::processEvents();  		if (this->root_N->dim == 2) { @@ -1186,7 +1215,7 @@ void MainWindow::actionRenderCGAL()  		if (!viewActionCGALSurfaces->isChecked() && !viewActionCGALGrid->isChecked()) {  			viewModeCGALSurface();  		} else { -			screen->updateGL(); +			this->glview->updateGL();  		}  		PRINT("Rendering finished."); @@ -1227,8 +1256,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...");  	} @@ -1257,6 +1286,8 @@ void MainWindow::actionExportSTLorOFF(bool stl_mode)  void MainWindow::actionExportSTLorOFF(bool)  #endif  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock;  #ifdef ENABLE_CGAL  	setCurrentOutput(); @@ -1295,13 +1326,18 @@ void MainWindow::actionExportSTLorOFF(bool)  	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"); +	QFile file(stl_filename); +	if (!file.open(QIODevice::ReadWrite)) { +		PRINTA("Can't open file \"%1\" for export", stl_filename); +	} +	else { +		QTextStream fstream(&file); +		if (stl_mode) export_stl(this->root_N, fstream, pd); +		else export_off(this->root_N, fstream, pd); +		file.close(); +		PRINTF("%s export finished.", stl_mode ? "STL" : "OFF"); +	}  	delete pd;  	clearCurrentOutput(); @@ -1335,16 +1371,24 @@ void MainWindow::actionExportDXF()  		return;  	} -	QString stl_filename = QFileDialog::getSaveFileName(this, +	QString dxf_filename = QFileDialog::getSaveFileName(this,  			"Export DXF File", "", "DXF Files (*.dxf)"); -	if (stl_filename.isEmpty()) { +	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."); +	QFile file(dxf_filename); +	if (!file.open(QIODevice::ReadWrite)) { +		PRINTA("Can't open file \"%1\" for export", dxf_filename); +	} +	else { +		QTextStream fstream(&file); +		export_dxf(this->root_N, fstream, NULL); +		file.close(); +		PRINTF("DXF export finished."); +	}  	clearCurrentOutput();  #endif /* ENABLE_CGAL */ @@ -1352,9 +1396,12 @@ void MainWindow::actionExportDXF()  void MainWindow::actionFlushCaches()  { -	PolySet::ps_cache.clear(); +// FIXME: Polycache -> PolySetRenderer +// FIXME: PolySetRenderer->clearCache();  #ifdef ENABLE_CGAL -	AbstractNode::cgal_nef_cache.clear(); +// FIXME: Flush caches through whatever channels we have +	// CGALRenderer::renderer()->getCache().clear(); +	// this->dumper->clearCache();  #endif  	dxf_dim_cache.clear();  	dxf_cross_cache.clear(); @@ -1376,8 +1423,8 @@ static void renderGLThrownTogether(void *vp);  static void renderGLviaOpenCSG(void *vp)  { -	MainWindow *m = (MainWindow*)vp; -	if (!m->enableOpenCSG) { +	MainWindow *mainwin = (MainWindow *)vp; +	if (!mainwin->enableOpenCSG) {  		renderGLThrownTogether(vp);  		return;  	} @@ -1387,18 +1434,18 @@ static void renderGLviaOpenCSG(void *vp)  		glewInit();  	}  #ifdef ENABLE_MDI -	OpenCSG::setContext(m->screen->opencsg_id); +	OpenCSG::setContext(mainwin->glview->opencsg_id);  #endif -	if (m->root_chain) { -		GLint *shaderinfo = m->screen->shaderinfo; +	if (mainwin->root_chain) { +		GLint *shaderinfo = mainwin->glview->shaderinfo;  		if (!shaderinfo[0])  			shaderinfo = NULL; -		renderCSGChainviaOpenCSG(m->root_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, false); -		if (m->background_chain) { -			renderCSGChainviaOpenCSG(m->background_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, true); +		renderCSGChainviaOpenCSG(mainwin->root_chain, mainwin->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, false); +		if (mainwin->background_chain) { +			renderCSGChainviaOpenCSG(mainwin->background_chain, mainwin->viewActionShowEdges->isChecked() ? shaderinfo : NULL, false, true);  		} -		if (m->highlights_chain) { -			renderCSGChainviaOpenCSG(m->highlights_chain, m->viewActionShowEdges->isChecked() ? shaderinfo : NULL, true, false); +		if (mainwin->highlights_chain) { +			renderCSGChainviaOpenCSG(mainwin->highlights_chain, mainwin->viewActionShowEdges->isChecked() ? shaderinfo : NULL, true, false);  		}  	}  } @@ -1409,11 +1456,11 @@ static void renderGLviaOpenCSG(void *vp)  */  void MainWindow::viewModeOpenCSG()  { -	if (screen->hasOpenCSGSupport()) { +	if (this->glview->hasOpenCSGSupport()) {  		viewModeActionsUncheck();  		viewActionOpenCSG->setChecked(true); -		screen->setRenderFunc(renderGLviaOpenCSG, this); -		screen->updateGL(); +		this->glview->setRenderFunc(renderGLviaOpenCSG, this); +		this->glview->updateGL();  	} else {  		viewModeThrownTogether();  	} @@ -1541,16 +1588,16 @@ void MainWindow::viewModeCGALSurface()  {  	viewModeActionsUncheck();  	viewActionCGALSurfaces->setChecked(true); -	screen->setRenderFunc(renderGLviaCGAL, this); -	screen->updateGL(); +	this->glview->setRenderFunc(renderGLviaCGAL, this); +	this->glview->updateGL();  }  void MainWindow::viewModeCGALGrid()  {  	viewModeActionsUncheck();  	viewActionCGALGrid->setChecked(true); -	screen->setRenderFunc(renderGLviaCGAL, this); -	screen->updateGL(); +	this->glview->setRenderFunc(renderGLviaCGAL, this); +	this->glview->updateGL();  }  #endif /* ENABLE_CGAL */ @@ -1639,25 +1686,25 @@ void MainWindow::viewModeThrownTogether()  {  	viewModeActionsUncheck();  	viewActionThrownTogether->setChecked(true); -	screen->setRenderFunc(renderGLThrownTogether, this); -	screen->updateGL(); +	this->glview->setRenderFunc(renderGLThrownTogether, this); +	this->glview->updateGL();  }  void MainWindow::viewModeShowEdges()  { -	screen->updateGL(); +	this->glview->updateGL();  }  void MainWindow::viewModeShowAxes()  { -	screen->setShowAxes(viewActionShowAxes->isChecked()); -	screen->updateGL(); +	this->glview->setShowAxes(viewActionShowAxes->isChecked()); +	this->glview->updateGL();  }  void MainWindow::viewModeShowCrosshairs()  { -	screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); -	screen->updateGL(); +	this->glview->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); +	this->glview->updateGL();  }  void MainWindow::viewModeAnimate() @@ -1695,82 +1742,82 @@ 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()  {  	viewActionPerspective->setChecked(true);  	viewActionOrthogonal->setChecked(false); -	screen->setOrthoMode(false); -	screen->updateGL(); +	this->glview->setOrthoMode(false); +	this->glview->updateGL();  }  void MainWindow::viewOrthogonal()  {  	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 1a1e30e..49a599a 100644 --- a/src/module.cc +++ b/src/module.cc @@ -78,7 +78,7 @@ QString ModuleInstantiation::dump(QString indent) const  			text += QString(", ");  		if (!argnames[i].isEmpty())  			text += argnames[i] + QString(" = "); -		text += argexpr[i]->dump(); +		text += QString::fromStdString(argexpr[i]->toString());  	}  	if (children.size() == 0) {  		text += QString(");\n"); @@ -166,7 +166,7 @@ QString Module::dump(QString indent, QString name) const  				text += QString(", ");  			text += argnames[i];  			if (argexpr[i]) -				text += QString(" = ") + argexpr[i]->dump(); +				text += QString(" = ") + QString::fromStdString(argexpr[i]->toString());  		}  		text += QString(") {\n");  		tab = "\t"; @@ -186,7 +186,7 @@ QString Module::dump(QString indent, QString name) const  		}  	}  	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()); +		text += QString("%1%2 = %3;\n").arg(indent + tab, assignments_var[i], QString::fromStdString(assignments_expr[i]->toString()));  	}  	for (int i = 0; i < children.size(); i++) {  		text += children[i]->dump(indent + tab); 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/nef2dxf.cc b/src/nef2dxf.cc index 44d7561..4c57e39 100644 --- a/src/nef2dxf.cc +++ b/src/nef2dxf.cc @@ -31,6 +31,7 @@  DxfData::DxfData(const struct CGAL_Nef_polyhedron &N)  { +	assert(N.dim == 2);  	Grid2d<int> grid(GRID_COARSE);  	typedef CGAL_Nef_polyhedron2::Explorer Explorer; diff --git a/src/node.cc b/src/node.cc index 87a7051..884e983 100644 --- a/src/node.cc +++ b/src/node.cc @@ -29,9 +29,13 @@  #include "csgterm.h"  #include "progress.h"  #include "polyset.h" +#include "visitor.h" +#include "nodedumper.h" +  #include <QRegExp> +#include <sstream> -int AbstractNode::idx_counter; +size_t AbstractNode::idx_counter;  AbstractNode::AbstractNode(const ModuleInstantiation *mi)  { @@ -45,129 +49,39 @@ AbstractNode::~AbstractNode()  		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; -} - -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() @@ -182,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; -} - @@ -4,9 +4,7 @@  #include <QCache>  #include <QVector> -#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,79 @@ 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::list<AbstractNode*> getChildren() const {  +		return this->children.toList().toStdList();  +	} +	size_t index() const { return this->idx; } +  	static void resetIndexCounter() { idx_counter = 1; } +	// FIXME: Rewrite to STL container? +	// FIXME: Make protected  	QVector<AbstractNode*> children; -	const class ModuleInstantiation *modinst; +	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 *render_polyset(render_mode_e mode, class PolySetRenderer *renderer) 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 bf24c9f..be5b716 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -31,6 +31,10 @@  #include "value.h"  #include "export.h"  #include "builtin.h" +#include "nodedumper.h" +#include "CGALRenderer.h" +#include "PolySetCGALRenderer.h" +#include "printutils.h"  #ifdef ENABLE_CGAL  #include "cgal.h" @@ -42,6 +46,8 @@  #include <QDir>  #include <QSet>  #include <QSettings> +#include <QTextStream> +  #include <getopt.h>  #ifdef Q_WS_MAC  #include "EventFilter.h" @@ -234,6 +240,15 @@ int main(int argc, char **argv)  		librarydir = libdir.path();  	} +	// Initialize global visitors +	NodeCache nodecache; +	NodeDumper dumper(nodecache); +	Tree tree; +	// FIXME: enforce some maximum cache size (old version had 100K vertices as limit) +	QHash<std::string, CGAL_Nef_polyhedron> cache; +	CGALRenderer cgalrenderer(cache, tree); +	PolySetCGALRenderer psrenderer(cgalrenderer); +  	if (stl_output_file || off_output_file || dxf_output_file)  	{  		if (!filename) @@ -250,9 +265,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); @@ -284,8 +299,8 @@ 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 = cgalrenderer.renderCGALMesh(*tree.root());  		QDir::setCurrent(original_path.absolutePath()); @@ -303,17 +318,43 @@ int main(int argc, char **argv)  			fclose(fp);  		} -		if (stl_output_file) -			export_stl(root_N, stl_output_file, NULL); +		if (stl_output_file) { +			QFile file(stl_output_file); +			if (!file.open(QIODevice::ReadWrite)) { +				PRINTA("Can't open file \"%1\" for export", stl_output_file); +			} +			else { +				QTextStream fstream(&file); +				export_stl(&root_N, fstream, NULL); +				file.close(); +			} +		} -		if (off_output_file) -			export_off(root_N, off_output_file, NULL); +		if (off_output_file) { +			QFile file(stl_output_file); +			if (!file.open(QIODevice::ReadWrite)) { +				PRINTA("Can't open file \"%1\" for export", stl_output_file); +			} +			else { +				QTextStream fstream(&file); +				export_off(&root_N, fstream, NULL); +				file.close(); +			} +		} -		if (dxf_output_file) -			export_dxf(root_N, dxf_output_file, NULL); +		if (dxf_output_file) { +			QFile file(stl_output_file); +			if (!file.open(QIODevice::ReadWrite)) { +				PRINTA("Can't open file \"%1\" for export", stl_output_file); +			} +			else { +				QTextStream fstream(&file); +				export_dxf(&root_N, fstream, NULL); +				file.close(); +			} +		}  		delete root_node; -		delete root_N;  #else  		fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n");  		exit(1); diff --git a/src/parser.y b/src/parser.y index 0cace25..f0a06f5 100644 --- a/src/parser.y +++ b/src/parser.y @@ -331,7 +331,7 @@ expr:  	TOK_STRING {  		$$ = new Expression();  		$$->type = "C"; -		$$->const_value = new Value(QString($1)); +		$$->const_value = new Value(std::string($1));  		free($1);  	} |  	TOK_NUMBER { diff --git a/src/polyset.cc b/src/polyset.cc index d438769..5bfabaa 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -25,7 +25,8 @@  #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> @@ -33,15 +34,6 @@  #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(); -} -  PolySet::PolySet() : grid(GRID_FINE)  {  	is2d = false; @@ -144,7 +136,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) { @@ -154,7 +148,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) { @@ -321,357 +317,3 @@ void PolySet::render_edges(colormode_e colormode, csgmode_e csgmode) const  		}  	}  } - -#ifdef ENABLE_CGAL - -#undef GEN_SURFACE_DEBUG - -class CGAL_Build_PolySet : public CGAL::Modifier_base<CGAL_HDS> -{ -public: -	typedef CGAL_HDS::Vertex::Point Point; - -	const PolySet *ps; -	CGAL_Build_PolySet(const PolySet *ps) : ps(ps) { } - -	void operator()(CGAL_HDS& hds) -	{ -		CGAL_Polybuilder B(hds, true); - -		QList<PolySet::Point> vertices; -		Grid3d<int> vertices_idx(GRID_FINE); - -		for (int i = 0; i < ps->polygons.size(); i++) { -			const PolySet::Polygon *poly = &ps->polygons[i]; -			for (int j = 0; j < poly->size(); j++) { -				const PolySet::Point *p = &poly->at(j); -				if (!vertices_idx.has(p->x, p->y, p->z)) { -					vertices_idx.data(p->x, p->y, p->z) = vertices.size(); -					vertices.append(*p); -				} -			} -		} - -		B.begin_surface(vertices.size(), ps->polygons.size()); -#ifdef GEN_SURFACE_DEBUG -		printf("=== CGAL Surface ===\n"); -#endif - -		for (int i = 0; i < vertices.size(); i++) { -			const PolySet::Point *p = &vertices[i]; -			B.add_vertex(Point(p->x, p->y, p->z)); -#ifdef GEN_SURFACE_DEBUG -			printf("%d: %f %f %f\n", i, p->x, p->y, p->z); -#endif -		} - -		for (int i = 0; i < ps->polygons.size(); i++) { -			const PolySet::Polygon *poly = &ps->polygons[i]; -			QHash<int,int> fc; -			bool facet_is_degenerated = false; -			for (int j = 0; j < poly->size(); j++) { -				const PolySet::Point *p = &poly->at(j); -				int v = vertices_idx.data(p->x, p->y, p->z); -				if (fc[v]++ > 0) -					facet_is_degenerated = true; -			} -			 -			if (!facet_is_degenerated) -				B.begin_facet(); -#ifdef GEN_SURFACE_DEBUG -			printf("F:"); -#endif -			for (int j = 0; j < poly->size(); j++) { -				const PolySet::Point *p = &poly->at(j); -#ifdef GEN_SURFACE_DEBUG -				printf(" %d (%f,%f,%f)", vertices_idx.data(p->x, p->y, p->z), p->x, p->y, p->z); -#endif -				if (!facet_is_degenerated) -					B.add_vertex_to_facet(vertices_idx.data(p->x, p->y, p->z)); -			} -#ifdef GEN_SURFACE_DEBUG -			if (facet_is_degenerated) -				printf(" (degenerated)"); -			printf("\n"); -#endif -			if (!facet_is_degenerated) -				B.end_facet(); -		} - -#ifdef GEN_SURFACE_DEBUG -		printf("====================\n"); -#endif -		B.end_surface(); - -		#undef PointKey -	} -}; - -CGAL_Nef_polyhedron PolySet::render_cgal_nef_polyhedron() const -{ -	if (this->is2d) -	{ -#if 0 -		// This version of the code causes problems in some cases. -		// Example testcase: import_dxf("testdata/polygon8.dxf"); -		// -		typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t; -		typedef point_list_t::iterator point_list_it; -		std::list< point_list_t > pdata_point_lists; -		std::list < std::pair < point_list_it, point_list_it > > pdata; -		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); - -		for (int i = 0; i < this->polygons.size(); i++) { -			pdata_point_lists.push_back(point_list_t()); -			for (int j = 0; j < this->polygons[i].size(); j++) { -				double x = this->polygons[i][j].x; -				double y = this->polygons[i][j].y; -				CGAL_Nef_polyhedron2::Point p; -				if (grid.has(x, y)) { -					p = grid.data(x, y); -				} else { -					p = CGAL_Nef_polyhedron2::Point(x, y); -					grid.data(x, y) = p; -				} -				pdata_point_lists.back().push_back(p); -			} -			pdata.push_back(std::make_pair(pdata_point_lists.back().begin(), -					pdata_point_lists.back().end())); -		} - -		CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS); -		return CGAL_Nef_polyhedron(N); -#endif -#if 0 -		// This version of the code works fine but is pretty slow. -		// -		CGAL_Nef_polyhedron2 N; -		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); - -		for (int i = 0; i < this->polygons.size(); i++) { -			std::list<CGAL_Nef_polyhedron2::Point> plist; -			for (int j = 0; j < this->polygons[i].size(); j++) { -				double x = this->polygons[i][j].x; -				double y = this->polygons[i][j].y; -				CGAL_Nef_polyhedron2::Point p; -				if (grid.has(x, y)) { -					p = grid.data(x, y); -				} else { -					p = CGAL_Nef_polyhedron2::Point(x, y); -					grid.data(x, y) = p; -				} -				plist.push_back(p); -			} -			N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); -		} - -		return CGAL_Nef_polyhedron(N); -#endif -#if 1 -		// This version of the code does essentially the same thing as the 2nd -		// version but merges some triangles before sending them to CGAL. This adds -		// complexity but speeds up things.. -		// -		struct PolyReducer -		{ -			Grid2d<int> grid; -			QHash< QPair<int,int>, QPair<int,int> > egde_to_poly; -			QHash< int, CGAL_Nef_polyhedron2::Point > points; -			QHash< int, QList<int> > polygons; -			int poly_n; - -			void add_edges(int pn) -			{ -				for (int j = 1; j <= this->polygons[pn].size(); j++) { -					int a = this->polygons[pn][j-1]; -					int b = this->polygons[pn][j % this->polygons[pn].size()]; -					if (a > b) { a = a^b; b = a^b; a = a^b; } -					if (this->egde_to_poly[QPair<int,int>(a, b)].first == 0) -						this->egde_to_poly[QPair<int,int>(a, b)].first = pn; -					else if (this->egde_to_poly[QPair<int,int>(a, b)].second == 0) -						this->egde_to_poly[QPair<int,int>(a, b)].second = pn; -					else -						abort(); -				} -			} - -			void del_poly(int pn) -			{ -				for (int j = 1; j <= this->polygons[pn].size(); j++) { -					int a = this->polygons[pn][j-1]; -					int b = this->polygons[pn][j % this->polygons[pn].size()]; -					if (a > b) { a = a^b; b = a^b; a = a^b; } -					if (this->egde_to_poly[QPair<int,int>(a, b)].first == pn) -						this->egde_to_poly[QPair<int,int>(a, b)].first = 0; -					if (this->egde_to_poly[QPair<int,int>(a, b)].second == pn) -						this->egde_to_poly[QPair<int,int>(a, b)].second = 0; -				} -				this->polygons.remove(pn); -			} - -			PolyReducer(const PolySet *ps) : grid(GRID_COARSE), poly_n(1) -			{ -				int point_n = 1; -				for (int i = 0; i < ps->polygons.size(); i++) { -					for (int j = 0; j < ps->polygons[i].size(); j++) { -						double x = ps->polygons[i][j].x; -						double y = ps->polygons[i][j].y; -						if (this->grid.has(x, y)) { -							this->polygons[this->poly_n].append(this->grid.data(x, y)); -						} else { -							this->grid.align(x, y) = point_n; -							this->polygons[this->poly_n].append(point_n); -							this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y); -							point_n++; -						} -					} -					add_edges(this->poly_n); -					this->poly_n++; -				} -			} - -			int merge(int p1, int p1e, int p2, int p2e) -			{ -				for (int i = 1; i < this->polygons[p1].size(); i++) { -					int j = (p1e + i) % this->polygons[p1].size(); -					this->polygons[this->poly_n].append(this->polygons[p1][j]); -				} -				for (int i = 1; i < this->polygons[p2].size(); i++) { -					int j = (p2e + i) % this->polygons[p2].size(); -					this->polygons[this->poly_n].append(this->polygons[p2][j]); -				} -				del_poly(p1); -				del_poly(p2); -				add_edges(this->poly_n); -				return this->poly_n++; -			} - -			void reduce() -			{ -				QList<int> work_queue; -				QHashIterator< int, QList<int> > it(polygons); -				while (it.hasNext()) { -					it.next(); -					work_queue.append(it.key()); -				} -				while (!work_queue.isEmpty()) { -					int poly1_n = work_queue.first(); -					work_queue.removeFirst(); -					if (!this->polygons.contains(poly1_n)) -						continue; -					for (int j = 1; j <= this->polygons[poly1_n].size(); j++) { -						int a = this->polygons[poly1_n][j-1]; -						int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()]; -						if (a > b) { a = a^b; b = a^b; a = a^b; } -						if (this->egde_to_poly[QPair<int,int>(a, b)].first != 0 && -								this->egde_to_poly[QPair<int,int>(a, b)].second != 0) { -							int poly2_n = this->egde_to_poly[QPair<int,int>(a, b)].first + -									this->egde_to_poly[QPair<int,int>(a, b)].second - poly1_n; -							int poly2_edge = -1; -							for (int k = 1; k <= this->polygons[poly2_n].size(); k++) { -								int c = this->polygons[poly2_n][k-1]; -								int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()]; -								if (c > d) { c = c^d; d = c^d; c = c^d; } -								if (a == c && b == d) { -									poly2_edge = k-1; -									continue; -								} -								int poly3_n = this->egde_to_poly[QPair<int,int>(c, d)].first + -										this->egde_to_poly[QPair<int,int>(c, d)].second - poly2_n; -								if (poly3_n < 0) -									continue; -								if (poly3_n == poly1_n) -									goto next_poly1_edge; -							} -							work_queue.append(merge(poly1_n, j-1, poly2_n, poly2_edge)); -							goto next_poly1; -						} -					next_poly1_edge:; -					} -				next_poly1:; -				} -			} - -			CGAL_Nef_polyhedron2 toNef() -			{ -				CGAL_Nef_polyhedron2 N; - -				QHashIterator< int, QList<int> > it(polygons); -				while (it.hasNext()) { -					it.next(); -					std::list<CGAL_Nef_polyhedron2::Point> plist; -					for (int j = 0; j < it.value().size(); j++) { -						int p = it.value()[j]; -						plist.push_back(points[p]); -					} -					N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); -				} - -				return N; -			} -		}; - -		PolyReducer pr(this); -		// printf("Number of polygons before reduction: %d\n", pr.polygons.size()); -		pr.reduce(); -		// printf("Number of polygons after reduction: %d\n", pr.polygons.size()); -		return CGAL_Nef_polyhedron(pr.toNef()); -#endif -#if 0 -		// This is another experimental version. I should run faster than the above, -		// is a lot simpler and has only one known weakness: Degenerate polygons, which -		// get repaired by GLUTess, might trigger a CGAL crash here. The only -		// known case for this is triangle-with-duplicate-vertex.dxf -		// FIXME: If we just did a projection, we need to recreate the border! -		if (this->polygons.size() > 0) assert(this->borders.size() > 0); -		CGAL_Nef_polyhedron2 N; -		Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE); - -		for (int i = 0; i < this->borders.size(); i++) { -			std::list<CGAL_Nef_polyhedron2::Point> plist; -			for (int j = 0; j < this->borders[i].size(); j++) { -				double x = this->borders[i][j].x; -				double y = this->borders[i][j].y; -				CGAL_Nef_polyhedron2::Point p; -				if (grid.has(x, y)) { -					p = grid.data(x, y); -				} else { -					p = CGAL_Nef_polyhedron2::Point(x, y); -					grid.data(x, y) = p; -				} -				plist.push_back(p);		 -			} -			// FIXME: If a border (path) has a duplicate vertex in dxf, -			// the CGAL_Nef_polyhedron2 constructor will crash. -			N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED); -		} - -		return CGAL_Nef_polyhedron(N); - -#endif -	} -	else // not (this->is2d) -	{ -		CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); -		try { -		CGAL_Polyhedron P; -		CGAL_Build_PolySet builder(this); -		P.delegate(builder); -#if 0 -		std::cout << P; -#endif -		CGAL_Nef_polyhedron3 N(P); -		return CGAL_Nef_polyhedron(N); -		} -		catch (CGAL::Assertion_exception e) { -			PRINTF("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object."); -			CGAL::set_error_behaviour(old_behaviour); -			return CGAL_Nef_polyhedron(); -		} -		CGAL::set_error_behaviour(old_behaviour); -	} -	return CGAL_Nef_polyhedron(); -} - -#endif /* ENABLE_CGAL */ - diff --git a/src/polyset.h b/src/polyset.h index 0914e91..0183fd8 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -11,9 +11,6 @@  #ifdef ENABLE_OPENCSG  #  include <opencsg.h>  #endif -#ifdef ENABLE_CGAL -#  include "cgal.h" -#endif  #include <QCache> @@ -65,22 +62,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 ac1f0a3..2c1cf1f 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -32,6 +32,9 @@  #include "builtin.h"  #include "printutils.h"  #include <assert.h> +#include "visitor.h" +#include <sstream> +#include <assert.h>  enum primitive_type_e {  	CUBE, @@ -54,20 +57,52 @@ 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 *render_polyset(render_mode_e mode, class PolySetRenderer *) 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; @@ -75,26 +110,30 @@ AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstanti  	QVector<QString> argnames;  	QVector<Expression*> argexpr; -	if (type == CUBE) { +	switch (this->type) { +	case CUBE:  		argnames = QVector<QString>() << "size" << "center"; -	} -	if (type == SPHERE) { +		break; +	case SPHERE:  		argnames = QVector<QString>() << "r"; -	} -	if (type == CYLINDER) { +		break; +	case CYLINDER:  		argnames = QVector<QString>() << "h" << "r1" << "r2" << "center"; -	} -	if (type == POLYHEDRON) { +		break; +	case POLYHEDRON:  		argnames = QVector<QString>() << "points" << "triangles" << "convexity"; -	} -	if (type == SQUARE) { +		break; +	case SQUARE:  		argnames = QVector<QString>() << "size" << "center"; -	} -	if (type == CIRCLE) { +		break; +	case CIRCLE:  		argnames = QVector<QString>() << "r"; -	} -	if (type == POLYGON) { +		break; +	case POLYGON:  		argnames = QVector<QString>() << "points" << "paths" << "convexity"; +		break; +	default: +		assert(false && "PrimitiveModule::evaluate(): Unknown node type");  	}  	Context c(ctx); @@ -207,25 +246,25 @@ int get_fragments_from_r(double r, double fn, double fs, double fa)  	return (int)ceil(fmax(fmin(360.0 / fa, r*M_PI / fs), 5));  } -PolySet *PrimitiveNode::render_polyset(render_mode_e) const +PolySet *PrimitiveNode::render_polyset(render_mode_e, class PolySetRenderer *) 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 @@ -265,7 +304,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 point2d {  			double x, y; @@ -277,14 +316,14 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const  			double r, z;  		}; -		int rings = get_fragments_from_r(r1, fn, fs, fa); +		int rings = get_fragments_from_r(this->r1, this->fn, this->fs, this->fa);  		ring_s ring[rings];  		for (int i = 0; i < rings; i++) {  			double phi = (M_PI * (i + 0.5)) / rings; -			ring[i].r = r1 * sin(phi); -			ring[i].z = r1 * cos(phi); -			ring[i].fragments = get_fragments_from_r(ring[i].r, fn, fs, fa); +			ring[i].r = this->r1 * sin(phi); +			ring[i].z = this->r1 * cos(phi); +			ring[i].fragments = get_fragments_from_r(ring[i].r, this->fn, this->fs, this->fa);  			ring[i].points = new point2d[ring[i].fragments];  			for (int j = 0; j < ring[i].fragments; j++) {  				phi = (M_PI*2*j) / ring[i].fragments; @@ -307,8 +346,7 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const  					goto sphere_next_r2;  				if (r2i >= r2->fragments)  					goto sphere_next_r1; -				if ((double)r1i / r1->fragments < -						(double)r2i / r2->fragments) +				if ((double)r1i / r1->fragments < (double)r2i / r2->fragments)  				{  sphere_next_r1:  					p->append_poly(); @@ -334,17 +372,18 @@ sphere_next_r2:  			p->insert_vertex(ring[rings-1].points[i].x, ring[rings-1].points[i].y, ring[rings-1].z);  	} -	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;  		}  		struct point2d { @@ -356,16 +395,16 @@ sphere_next_r2:  		for (int i=0; i<fragments; i++) {  			double phi = (M_PI*2*i) / fragments; -			if (r1 > 0) { -				circle1[i].x = r1*cos(phi); -				circle1[i].y = r1*sin(phi); +			if (this->r1 > 0) { +				circle1[i].x = this->r1*cos(phi); +				circle1[i].y = this->r1*sin(phi);  			} else {  				circle1[i].x = 0;  				circle1[i].y = 0;  			} -			if (r2 > 0) { -				circle2[i].x = r2*cos(phi); -				circle2[i].y = r2*sin(phi); +			if (this->r2 > 0) { +				circle2[i].x = this->r2*cos(phi); +				circle2[i].y = this->r2*sin(phi);  			} else {  				circle2[i].x = 0;  				circle2[i].y = 0; @@ -374,13 +413,13 @@ sphere_next_r2:  		for (int i=0; i<fragments; i++) {  			int j = (i+1) % fragments; -			if (r1 > 0) { +			if (this->r1 > 0) {  				p->append_poly();  				p->insert_vertex(circle1[i].x, circle1[i].y, z1);  				p->insert_vertex(circle2[i].x, circle2[i].y, z2);  				p->insert_vertex(circle1[j].x, circle1[j].y, z1);  			} -			if (r2 > 0) { +			if (this->r2 > 0) {  				p->append_poly();  				p->insert_vertex(circle2[i].x, circle2[i].y, z2);  				p->insert_vertex(circle2[j].x, circle2[j].y, z2); @@ -388,48 +427,48 @@ 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);  		}  	} -	if (type == POLYHEDRON) +	if (this->type == POLYHEDRON)  	{ -		p->convexity = convexity; -		for (int i=0; i<triangles.vec.size(); i++) +		p->convexity = this->convexity; +		for (int 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 (int j=0; j<this->triangles.vec[i]->vec.size(); j++) { +				int 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) +	if (this->type == SQUARE)  	{  		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; @@ -440,9 +479,9 @@ 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);  		struct point2d {  			double x, y; @@ -452,8 +491,8 @@ sphere_next_r2:  		for (int i=0; i<fragments; i++) {  			double phi = (M_PI*2*i) / fragments; -			circle[i].x = r1*cos(phi); -			circle[i].y = r1*sin(phi); +			circle[i].x = this->r1*cos(phi); +			circle[i].y = this->r1*sin(phi);  		}  		p->is2d = true; @@ -462,24 +501,24 @@ sphere_next_r2:  			p->append_vertex(circle[i].x, circle[i].y);  	} -	if (type == POLYGON) +	if (this->type == POLYGON)  	{  		DxfData dd; -		for (int i=0; i<points.vec.size(); i++) { +		for (int 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));  		} -		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++) { +			for (int 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); @@ -491,11 +530,11 @@ sphere_next_r2:  		}  		else  		{ -			for (int i=0; i<paths.vec.size(); i++) +			for (int 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; +				for (int 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); @@ -519,26 +558,45 @@ sphere_next_r2:  	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..0f4c67d 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"; diff --git a/src/projection.cc b/src/projection.cc index 7a3f77a..b999d9b 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -23,8 +23,8 @@   *   */ +#include "projectionnode.h"  #include "module.h" -#include "node.h"  #include "context.h"  #include "printutils.h"  #include "builtin.h" @@ -33,6 +33,8 @@  #include "polyset.h"  #include "export.h"  #include "progress.h" +#include "visitor.h" +#include "PolySetRenderer.h"  #ifdef ENABLE_CGAL  #  include <CGAL/assertions_behaviour.h> @@ -43,6 +45,7 @@  #include <sys/stat.h>  #include <unistd.h>  #include <assert.h> +#include <sstream>  #include <QApplication>  #include <QTime> @@ -55,18 +58,6 @@ 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); @@ -94,207 +85,35 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant  	return node;  } -void register_builtin_projection() -{ -	builtin_modules["projection"] = new ProjectionModule(); -} - -#ifdef ENABLE_CGAL - -PolySet *ProjectionNode::render_polyset(render_mode_e) const +PolySet *ProjectionNode::render_polyset(render_mode_e mode, PolySetRenderer *renderer) 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("ERROR: Illegal polygonal object - make sure all polygons are defined with the same winding order. Skipping affected object."); -		CGAL::set_error_behaviour(old_behaviour); +	if (!renderer) { +		PRINTF("WARNING: No suitable PolySetRenderer 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); - -			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; -			} +	print_messages_push(); -			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(); -	} +	PolySet *ps = renderer->renderPolySet(*this, mode); -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..295e48c --- /dev/null +++ b/src/projectionnode.h @@ -0,0 +1,24 @@ +#ifndef PROJECTIONNODE_H_ +#define PROJECTIONNODE_H_ + +#include "node.h" +#include "visitor.h" + +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 *render_polyset(render_mode_e mode, class PolySetRenderer *renderer) 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 b/src/render-opencsg.cc index f6d26ac..fe0fc60 100644 --- a/src/render-opencsg.cc +++ b/src/render-opencsg.cc @@ -1,6 +1,9 @@  #include "render-opencsg.h"  #include "polyset.h"  #include "csgterm.h" +#ifdef ENABLE_OPENCSG +#  include <opencsg.h> +#endif  class OpenCSGPrim : public OpenCSG::Primitive  { diff --git a/src/render.cc b/src/render.cc index d851fc8..8a58b7c 100644 --- a/src/render.cc +++ b/src/render.cc @@ -23,8 +23,8 @@   *   */ +#include "rendernode.h"  #include "module.h" -#include "node.h"  #include "polyset.h"  #include "context.h"  #include "dxfdata.h" @@ -33,13 +33,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>  class RenderModule : public AbstractModule  { @@ -48,18 +46,6 @@ 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); @@ -88,176 +74,11 @@ void register_builtin_render()  	builtin_modules["render"] = new RenderModule();  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron RenderNode::render_cgal_nef_polyhedron() const +std::string RenderNode::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; -	} +	std::stringstream stream; -	print_messages_push(); +	stream << this->name() << "(convexity = " << convexity << ")"; -	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; +	return stream.str();  } - -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 -{ -	return render_csg_term_from_nef(m, highlights, background, "render", this->convexity); -} - -#else - -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; -} - -#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..c5ebdae --- /dev/null +++ b/src/rendernode.h @@ -0,0 +1,20 @@ +#ifndef RENDERNODE_H_ +#define RENDERNODE_H_ + +#include "node.h" +#include "visitor.h" + +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..ae25c0f --- /dev/null +++ b/src/state.h @@ -0,0 +1,36 @@ +#ifndef STATE_H_ +#define STATE_H_ + +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=16;i<20;i++) this->m[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[20]) { memcpy(this->m, m, 20*sizeof(m)); } + +  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; } + +private: +  const AbstractNode * parentnode; +  bool isprefix; +  bool ispostfix; +  unsigned int numchildren; + +	// Transformation matrix incl. color. FIXME: Generalize such state variables? +	double m[20]; +}; + +#endif diff --git a/src/surface.cc b/src/surface.cc index ddcd3a6..c1cbe07 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -31,8 +31,10 @@  #include "dxftess.h"  #include "printutils.h"  #include "openscad.h" // handle_dep() +#include "visitor.h"  #include <QFile> +#include <sstream>  class SurfaceModule : public AbstractModule  { @@ -44,12 +46,17 @@ public:  class SurfaceNode : public AbstractPolyNode  {  public: +	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"; } +  	QString 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 *render_polyset(render_mode_e mode, class PolySetRenderer *) const;  };  AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const @@ -64,7 +71,7 @@ AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiat  	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.get_absolute_path(QString::fromStdString(c.lookup_variable("file").text));  	Value center = c.lookup_variable("center", true);  	if (center.type == Value::BOOL) { @@ -84,14 +91,15 @@ void register_builtin_surface()  	builtin_modules["surface"] = new SurfaceModule();  } -PolySet *SurfaceNode::render_polyset(render_mode_e) const +PolySet *SurfaceNode::render_polyset(render_mode_e, class PolySetRenderer *) const  { +	PolySet *p = new PolySet();  	handle_dep(filename);  	QFile f(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.toAscii().data()); +		return p;  	}  	int lines = 0, columns = 0; @@ -118,7 +126,6 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const  		lines++;  	} -	PolySet *p = new PolySet();  	p->convexity = convexity;  	double ox = center ? -columns/2.0 : 0; @@ -197,14 +204,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 746283e..ccfc1aa 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -23,8 +23,8 @@   *   */ +#include "transformnode.h"  #include "module.h" -#include "node.h"  #include "context.h"  #include "dxfdata.h"  #include "csgterm.h" @@ -32,6 +32,9 @@  #include "dxftess.h"  #include "builtin.h"  #include "printutils.h" +#include "visitor.h" +#include <sstream> +#include <assert.h>  enum transform_type_e {  	SCALE, @@ -50,63 +53,55 @@ public:  	virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;  }; -class TransformNode : public AbstractNode -{ -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; -}; -  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; +		node->matrix[i] = i % 5 == 0 ? 1.0 : 0.0;  	for (int i = 16; i < 20; i++) -		node->m[i] = -1; +		node->matrix[i] = -1;  	QVector<QString> argnames;  	QVector<Expression*> argexpr; -	if (type == SCALE) { +	switch (this->type) { +	case SCALE:  		argnames = QVector<QString>() << "v"; -	} -	if (type == ROTATE) { +		break; +	case ROTATE:  		argnames = QVector<QString>() << "a" << "v"; -	} -	if (type == MIRROR) { +		break; +	case MIRROR:  		argnames = QVector<QString>() << "v"; -	} -	if (type == TRANSLATE) { +		break; +	case TRANSLATE:  		argnames = QVector<QString>() << "v"; -	} -	if (type == MULTMATRIX) { +		break; +	case MULTMATRIX:  		argnames = QVector<QString>() << "m"; -	} -	if (type == COLOR) { +		break; +	case COLOR:  		argnames = QVector<QString>() << "c"; +		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) @@ -138,10 +133,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 @@ -163,21 +158,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; @@ -191,41 +186,41 @@ 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;  				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) +	else if (this->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; +				node->matrix[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0;  		}  	} @@ -238,134 +233,34 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti  	return node;  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const +std::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; +	std::stringstream stream; -	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(); +	if (this->matrix[16] >= 0 || this->matrix[17] >= 0 || this->matrix[18] >= 0 || this->matrix[19] >= 0) { +		stream << "color([" << this->matrix[16] << ", " << this->matrix[17] << ", " << this->matrix[18] << ", " << this->matrix[19] << "])";  	} - -	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()); +	else { +		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 << ", "; +			} +			stream << "]"; +				if (j != 3) stream << ", ";  		} - -		PolySet ps; -		ps.is2d = true; -		dxf_tesselate(&ps, &dd, 0, true, false, 0); - -		N = ps.render_cgal_nef_polyhedron(); -		ps.refcount = 0; +		stream << "])";  	} -	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 +std::string TransformNode::name() const  { -	if (dump_cache.isEmpty()) { -		QString text; -		if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 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() @@ -377,4 +272,3 @@ void register_builtin_transform()  	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..9afa9be --- /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[20]; +}; + +#endif diff --git a/src/traverser.cc b/src/traverser.cc new file mode 100644 index 0000000..27bb116 --- /dev/null +++ b/src/traverser.cc @@ -0,0 +1,40 @@ +#include "traverser.h" +#include "visitor.h" +#include "node.h" +#include "state.h" + +void Traverser::execute()  +{ +	State state(NULL); +	traverse(this->root, 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); +	const std::list<AbstractNode*> &children = node.getChildren(); +	for (std::list<AbstractNode*>::const_iterator iter = children.begin(); +			 iter != children.end(); +			 iter++) { +		 +		traverse(**iter, 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..a96b05b --- /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(); +private: +  // FIXME: reverse parameters +  void traverse(const AbstractNode &node, const class State &state); + +  Visitor &visitor; +  const AbstractNode &root; +  TraversalType traversaltype; +}; + +#endif diff --git a/src/value.cc b/src/value.cc index a237c5a..92aaff1 100644 --- a/src/value.cc +++ b/src/value.cc @@ -25,6 +25,8 @@  #include "value.h"  #include <math.h> +#include <assert.h> +#include <sstream>  Value::Value()  { @@ -33,8 +35,7 @@ Value::Value()  Value::~Value()  { -	for (int i = 0; i < this->vec.size(); i++) -		delete this->vec[i]; +	for (int i = 0; i < this->vec.size(); i++) delete this->vec[i];  	this->vec.clear();  } @@ -52,7 +53,7 @@ Value::Value(double v)  	this->num = v;  } -Value::Value(const QString &t) +Value::Value(const std::string &t)  {  	reset_undef();  	this->type = STRING; @@ -70,8 +71,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 (int 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,7 +111,7 @@ Value Value::operator + (const Value &v) const  		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])); +			r.vec.push_back(new Value(*this->vec[i] + *v.vec[i]));  		return r;  	}  	if (this->type == NUMBER && v.type == NUMBER) { @@ -124,7 +126,7 @@ Value Value::operator - (const Value &v) const  		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])); +			r.vec.push_back(new Value(*this->vec[i] - *v.vec[i]));  		return r;  	}  	if (this->type == NUMBER && v.type == NUMBER) { @@ -139,14 +141,14 @@ Value Value::operator * (const Value &v) const  		Value r;  		r.type = VECTOR;  		for (int i = 0; i < this->vec.size(); i++) -			r.vec.append(new Value(*this->vec[i] * v)); +			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])); +			r.vec.push_back(new Value(*this * *v.vec[i]));  		return r;  	}  	if (this->type == NUMBER && v.type == NUMBER) { @@ -161,14 +163,14 @@ Value Value::operator / (const Value &v) const  		Value r;  		r.type = VECTOR;  		for (int i = 0; i < this->vec.size(); i++) -			r.vec.append(new Value(*this->vec[i] / v)); +			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])); +			r.vec.push_back(new Value(v / *v.vec[i]));  		return r;  	}  	if (this->type == NUMBER && v.type == NUMBER) { @@ -254,7 +256,7 @@ Value Value::inv() const  		Value r;  		r.type = VECTOR;  		for (int i = 0; i < this->vec.size(); i++) -			r.vec.append(new Value(this->vec[i]->inv())); +			r.vec.push_back(new Value(this->vec[i]->inv()));  		return r;  	}  	if (this->type == NUMBER) @@ -306,46 +308,72 @@ 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 (int 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; + +	switch (this->type) { +	case STRING: +		stream << '"' << this->text << '"'; +		break; +	case VECTOR: +		stream << '['; +		for (int 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; +} + +std::ostream &operator<<(std::ostream &stream, const QString &str) +{ +	stream << str.toStdString(); +	return stream; +} + diff --git a/src/value.h b/src/value.h index 3491cbb..9140912 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,18 @@ 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); + +// FIXME: Doesn't belong here.. +#include <QString> +std::ostream &operator<<(std::ostream &stream, const QString &str); +  #endif diff --git a/src/visitor.h b/src/visitor.h new file mode 100644 index 0000000..63f5d08 --- /dev/null +++ b/src/visitor.h @@ -0,0 +1,52 @@ +#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); +	} +	// Add visit() methods for new visitable subtypes of AbstractNode here +}; + +#endif | 
