diff options
Diffstat (limited to 'src')
71 files changed, 3674 insertions, 2298 deletions
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc new file mode 100644 index 0000000..021537c --- /dev/null +++ b/src/CGALEvaluator.cc @@ -0,0 +1,655 @@ +#include "CGALEvaluator.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 CGALEvaluator::evaluateCGALMesh(const AbstractNode &node) +{ +	if (!isCached(node)) { +		Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX); +		evaluate.execute(); +		assert(isCached(node)); +	} +	return this->cache[this->tree.getString(node)]; +} + +bool CGALEvaluator::isCached(const AbstractNode &node) const +{ +	return this->cache.contains(this->tree.getString(node)); +} + +/*! +	Modifies target by applying op to target and src: +	target = target [op] src + */ +void CGALEvaluator::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, 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; +		case HULL: +			//FIXME: Port convex hull to a binary operator or process it all in the end somehow +			// target.p2 = convexhull2(target.p2, src.p2); +			// target.p2 = convexhull2(polys); +			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; +		case HULL: +			// FIXME: Print warning: hull() not supported in 3D +			break; +		} +	} +} + +/*! +	FIXME: Let caller insert into the cache since caller might modify the result +  (e.g. transform) +*/ +void CGALEvaluator::applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op) +{ +	CGAL_Nef_polyhedron N; +	if (this->visitedchildren[node.index()].size() > 0) { +		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 CGALEvaluator::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 CGALEvaluator::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 CGALEvaluator::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 CGALEvaluator::visit(State &state, const TransformNode &node) +{ +	if (state.isPrefix() && isCached(node)) return PruneTraversal; +	if (state.isPostfix()) { +		if (!isCached(node)) { +			// First union all children +			applyToChildren(node, 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 = evaluateCGALMesh(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: EvaluateNode: Union over children + some magic +// FIXME: CgaladvNode: Iterate over children. Special operation + +// FIXME: Subtypes of AbstractPolyNode: +// ProjectionNode +// DxfLinearExtrudeNode +// DxfRotateExtrudeNode +// (SurfaceNode) +// (PrimitiveNode) +Response CGALEvaluator::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.evaluate_polyset(AbstractPolyNode::RENDER_CGAL, &this->psevaluator); +			if (ps) { +				try { +					CGAL_Nef_polyhedron N = evaluateCGALMesh(*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 CGALEvaluator::addToParent(const State &state, const AbstractNode &node) +{ +	assert(state.isPostfix()); +	this->visitedchildren.erase(node.index()); +	if (state.parent()) { +		this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, this->tree.getString(node))); +	} +} + +#if 0 +/*! +	Static function to evaluate CGAL meshes. +	NB! This is just a support function used for development and debugging +*/ +CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const AbstractPolyNode &node) +{ +	// FIXME: Lookup Nef polyhedron in cache. + +	// 	print_messages_push(); +	 +	PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_CGAL); +	if (ps) { +		try { +			CGAL_Nef_polyhedron N = ps->evaluateCSGMesh(); +			// FIXME: Insert into cache +			// print_messages_pop(); +			node.progress_report(); +			 +			ps->unlink(); +			return N; +		} +		catch (...) { // Don't leak the PolySet on ProgressCancelException +			ps->unlink(); +			throw; +		} +	} +} +#endif + +#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 CGALEvaluator::evaluateCGALMesh(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/CGALEvaluator.h b/src/CGALEvaluator.h new file mode 100644 index 0000000..282d6ab --- /dev/null +++ b/src/CGALEvaluator.h @@ -0,0 +1,56 @@ +#ifndef CGALEVALUATOR_H_ +#define CGALEVALUATOR_H_ + +#include "myqhash.h" +#include "visitor.h" +#include "Tree.h" +#include "cgal.h" +#include "PolySetCGALEvaluator.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 CGALEvaluator : public Visitor +{ +public: +	enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI, HULL}; +	// FIXME: If a cache is not given, we need to fix this ourselves +	CGALEvaluator(QHash<string, CGAL_Nef_polyhedron> &cache, const Tree &tree) : cache(cache), tree(tree), psevaluator(*this) {} +  virtual ~CGALEvaluator() {} + +  virtual Response visit(State &state, const AbstractNode &node); + 	virtual Response visit(State &state, const AbstractIntersectionNode &node); + 	virtual Response visit(State &state, const CsgNode &node); + 	virtual Response visit(State &state, const TransformNode &node); +	virtual Response visit(State &state, const AbstractPolyNode &node); + + 	CGAL_Nef_polyhedron evaluateCGALMesh(const AbstractNode &node); +	CGAL_Nef_polyhedron evaluateCGALMesh(const PolySet &polyset); + +	const Tree &getTree() const { return this->tree; } + +private: +  void addToParent(const State &state, const AbstractNode &node); +  bool isCached(const AbstractNode &node) const; +	void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op); +	void applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op); + +  string currindent; +  typedef list<pair<const AbstractNode *, string> > ChildList; +  map<int, ChildList> visitedchildren; + +	QHash<string, CGAL_Nef_polyhedron> &cache; +	const Tree &tree; +	PolySetCGALEvaluator psevaluator; +}; + +#endif diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc new file mode 100644 index 0000000..b4b4e70 --- /dev/null +++ b/src/CSGTermEvaluator.cc @@ -0,0 +1,351 @@ +#include "CSGTermEvaluator.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 CSGTermEvaluator + +	A visitor responsible for creating a tree of CSGTerm nodes used for rendering +	with OpenCSG. +*/ + +CSGTerm *CSGTermEvaluator::evaluateCSGTerm(const AbstractNode &node, +																				vector<CSGTerm*> *highlights,  +																				vector<CSGTerm*> *background) +{ +	Traverser evaluate(*this, node, Traverser::PRE_AND_POSTFIX); +	evaluate.execute(); +	return this->stored_term[node.index()]; +} + +void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluator::CsgOp op) +{ +	CSGTerm *t1 = NULL; +	for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); +			 iter != this->visitedchildren[node.index()].end(); +			 iter++) { +		const AbstractNode *chnode = *iter; +		CSGTerm *t2 = this->stored_term[chnode->index()]; +		this->stored_term.erase(chnode->index()); +		if (t2 && !t1) { +			t1 = t2; +		} else if (t2 && t1) { +			if (op == 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 CSGTermEvaluator::visit(State &state, const AbstractNode &node) +{ +	if (state.isPostfix()) { +		applyToChildren(node, UNION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const AbstractIntersectionNode &node) +{ +	if (state.isPostfix()) { +		applyToChildren(node, INTERSECTION); +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +static CSGTerm *evaluate_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 CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node) +{ +	if (state.isPostfix()) { +		CSGTerm *t1 = NULL; +		PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_OPENCSG, this->psevaluator); +		if (ps) { +			t1 = evaluate_csg_term_from_ps(state.matrix(), this->highlights, this->background,  +																	 ps, node.modinst, node); +		} +		this->stored_term[node.index()] = t1; +		addToParent(state, node); +	} +	return ContinueTraversal; +} + +Response CSGTermEvaluator::visit(State &state, const CsgNode &node) +{ +	if (state.isPostfix()) { +		CsgOp op; +		switch (node.type) { +		case CSG_TYPE_UNION: +			op = 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 CSGTermEvaluator::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 CSGTermEvaluator::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 CSGTermEvaluator::addToParent(const State &state, const AbstractNode &node) +{ +	assert(state.isPostfix()); +	this->visitedchildren.erase(node.index()); +	if (state.parent()) { +		this->visitedchildren[state.parent()->index()].push_back(&node); +	} +} + + +#if 0 + +// FIXME: #ifdef ENABLE_CGAL +#if 0 +CSGTerm *CgaladvNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +{ +	if (type == MINKOWSKI) +		return evaluate_csg_term_from_nef(m, highlights, background, "minkowski", this->convexity); + +	if (type == GLIDE) +		return evaluate_csg_term_from_nef(m, highlights, background, "glide", this->convexity); + +	if (type == SUBDIV) +		return evaluate_csg_term_from_nef(m, highlights, background, "subdiv", this->convexity); + +	if (type == HULL) +		return evaluate_csg_term_from_nef(m, highlights, background, "hull", this->convexity); + +	return NULL; +} + +#else // ENABLE_CGAL + +CSGTerm *CgaladvNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +{ +	PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!"); +	return NULL; +} + +#endif // ENABLE_CGAL + + + +// FIXME: #ifdef ENABLE_CGAL +#if 0 +CSGTerm *AbstractNode::evaluate_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::evaluate_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->evaluateCSGMesh(); + +		int s = t.elapsed() / 1000; +		PRINTF_NOCACHE("..processing 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::evaluate_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const +{ +	return evaluate_csg_term_from_nef(m, highlights, background, "render", this->convexity); +} + +#else + +CSGTerm *RenderNode::evaluate_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->evaluate_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/CSGTermEvaluator.h b/src/CSGTermEvaluator.h new file mode 100644 index 0000000..d1ea28e --- /dev/null +++ b/src/CSGTermEvaluator.h @@ -0,0 +1,53 @@ +#ifndef CSGTERMEVALUATOR_H_ +#define CSGTERMEVALUATOR_H_ + +#include <string> +#include <map> +#include <list> +#include <vector> +#include "Tree.h" +#include "visitor.h" +#include "node.h" + +using std::string; +using std::map; +using std::list; +using std::vector; + +class CSGTermEvaluator : public Visitor +{ +public: +	CSGTermEvaluator(const Tree &tree, class PolySetEvaluator *psevaluator = NULL) +		: highlights(NULL), background(NULL), tree(tree), psevaluator(psevaluator) { +	} +  virtual ~CSGTermEvaluator() {} + +  virtual Response visit(State &state, const AbstractNode &node); + 	virtual Response visit(State &state, const AbstractIntersectionNode &node); + 	virtual Response visit(State &state, const AbstractPolyNode &node); + 	virtual Response visit(State &state, const CsgNode &node); + 	virtual Response visit(State &state, const TransformNode &node); + 	virtual Response visit(State &state, const RenderNode &node); + +	class CSGTerm *evaluateCSGTerm(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, CSGTermEvaluator::CsgOp op); + +  const AbstractNode *root; +  typedef list<const AbstractNode *> ChildList; +  map<int, ChildList> visitedchildren; + +public: +  map<int, class CSGTerm*> stored_term; // The term evaluated from each node index + +	vector<CSGTerm*> *highlights; +	vector<CSGTerm*> *background; +	const Tree &tree; +	class PolySetEvaluator *psevaluator; +}; + +#endif diff --git a/src/GLView.h b/src/GLView.h index 764b23b..1001754 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -20,6 +20,7 @@ class GLView : public QGLWidget  public:  	GLView(QWidget *parent = NULL); +	GLView(const QGLFormat & format, QWidget *parent = NULL);  	void setRenderer(class Renderer* r);  #ifdef ENABLE_OPENCSG  	bool hasOpenCSGSupport() { return this->opencsg_support; } @@ -52,6 +53,7 @@ public:  #endif  private: +	void init();  	Renderer *renderer;  	bool showfaces; diff --git a/src/MainWindow.h b/src/MainWindow.h index 243a5ad..0745935 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -7,7 +7,9 @@  #include "context.h"  #include "module.h"  #include "polyset.h" +#include "Tree.h"  #include <QPointer> +#include <vector>  class MainWindow : public QMainWindow, public Ui::MainWindow  { @@ -32,6 +34,7 @@ public:  	ModuleInstantiation root_inst;    // Top level instance  	AbstractNode *absolute_root_node; // Result of tree evaluation  	AbstractNode *root_node;          // Root if the root modifier (!) is used +	Tree tree;  	class CSGTerm *root_raw_term;           // Result of CSG term rendering  	CSGTerm *root_norm_term;          // Normalized CSG products @@ -45,9 +48,9 @@ public:  #endif  	class ThrownTogetherRenderer *thrownTogetherRenderer; -	QVector<CSGTerm*> highlight_terms; +	std::vector<CSGTerm*> highlight_terms;  	CSGChain *highlights_chain; -	QVector<CSGTerm*> background_terms; +	std::vector<CSGTerm*> background_terms;  	CSGChain *background_chain;  	QString last_compiled_doc; @@ -77,6 +80,7 @@ private:  	void compileCSG(bool procevents);  	bool maybeSave();  	bool checkModified(); +	QString dumpCSGTree(AbstractNode *root);  	static void consoleOutput(const QString &msg, void *userdata) {  		static_cast<MainWindow*>(userdata)->console->append(msg);  	} @@ -157,4 +161,19 @@ public slots:  	void autoReloadSet(bool);  }; +class GuiLocker +{ +public: +	GuiLocker() { +		gui_locked++; +	} +	~GuiLocker() { +		gui_locked--; +	} +	static bool isLocked() { return gui_locked > 0; } + +private: +	static unsigned int gui_locked; +}; +  #endif diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 6548c8e..1741557 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -38,7 +38,7 @@            <property name="orientation">             <enum>Qt::Vertical</enum>            </property> -          <widget class="GLView" name="screen" native="true"/> +          <widget class="GLView" name="glview" native="true"/>            <widget class="QTextEdit" name="console"/>           </widget>          </item> diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc new file mode 100644 index 0000000..e769730 --- /dev/null +++ b/src/PolySetCGALEvaluator.cc @@ -0,0 +1,426 @@ +#include "PolySetCGALEvaluator.h" +#include "polyset.h" +#include "CGALEvaluator.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 *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalevaluator.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(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->cgalevaluator.evaluateCGALMesh(*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 *PolySetCGALEvaluator::evaluatePolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalevaluator.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	DxfData *dxf; + +	if (node.filename.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->cgalevaluator.evaluateCGALMesh(*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 *PolySetCGALEvaluator::evaluatePolySet(const DxfRotateExtrudeNode &node,  +																						AbstractPolyNode::render_mode_e) +{ +	const string &cacheid = this->cgalevaluator.getTree().getString(node); +	if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link(); + +	DxfData *dxf; + +	if (node.filename.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->cgalevaluator.evaluateCGALMesh(*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; +		points = new double**[fragments]; +		for (int j=0; j < fragments; j++) { +			points[j] = new double*[dxf->paths[i].points.count()]; +			for (int k=0; k < dxf->paths[i].points.count(); k++) +				points[j][k] = new double[3]; +		} + +		for (int j = 0; j < fragments; j++) { +			double a = (j*2*M_PI) / fragments; +			for (int k = 0; k < dxf->paths[i].points.count(); k++) { +				if (dxf->paths[i].points[k]->x == 0) { +					points[j][k][0] = 0; +					points[j][k][1] = 0; +				} else { +					points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); +					points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); +				} +				points[j][k][2] = dxf->paths[i].points[k]->y; +			} +		} + +		for (int j = 0; j < fragments; j++) { +			int j1 = j + 1 < fragments ? j + 1 : 0; +			for (int k = 0; k < dxf->paths[i].points.count(); k++) { +				int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; +				if (points[j][k][0] != points[j1][k][0] || +						points[j][k][1] != points[j1][k][1] || +						points[j][k][2] != points[j1][k][2]) { +					ps->append_poly(); +					ps->append_vertex(points[j ][k ][0], +							points[j ][k ][1], points[j ][k ][2]); +					ps->append_vertex(points[j1][k ][0], +							points[j1][k ][1], points[j1][k ][2]); +					ps->append_vertex(points[j ][k1][0], +							points[j ][k1][1], points[j ][k1][2]); +				} +				if (points[j][k1][0] != points[j1][k1][0] || +						points[j][k1][1] != points[j1][k1][1] || +						points[j][k1][2] != points[j1][k1][2]) { +					ps->append_poly(); +					ps->append_vertex(points[j ][k1][0], +							points[j ][k1][1], points[j ][k1][2]); +					ps->append_vertex(points[j1][k ][0], +							points[j1][k ][1], points[j1][k ][2]); +					ps->append_vertex(points[j1][k1][0], +							points[j1][k1][1], points[j1][k1][2]); +				} +			} +		} + +		for (int j=0; j < fragments; j++) { +			for (int k=0; k < dxf->paths[i].points.count(); k++) +				delete[] points[j][k]; +			delete[] points[j]; +		} +		delete[] points; +	} + +	delete dxf; + +	this->cache.insert(cacheid, new cache_entry(ps->link())); +	return ps; +} diff --git a/src/PolySetCGALEvaluator.h b/src/PolySetCGALEvaluator.h new file mode 100644 index 0000000..5089e0e --- /dev/null +++ b/src/PolySetCGALEvaluator.h @@ -0,0 +1,24 @@ +#ifndef POLYSETCGALEVALUATOR_H_ +#define POLYSETCGALEVALUATOR_H_ + +#include "PolySetEvaluator.h" + +/*! +	This is a PolySet evaluator which uses the CGALEvaluator to support building +	polysets. +*/ +class PolySetCGALEvaluator : public PolySetEvaluator +{ +public: +	PolySetCGALEvaluator(class CGALEvaluator &CGALEvaluator) :  +		PolySetEvaluator(), cgalevaluator(CGALEvaluator) { } +	virtual ~PolySetCGALEvaluator() { } +	virtual PolySet *evaluatePolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e); +	virtual PolySet *evaluatePolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e); +	virtual PolySet *evaluatePolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e); + +private: +	CGALEvaluator &cgalevaluator; +}; + +#endif diff --git a/src/PolySetEvaluator.cc b/src/PolySetEvaluator.cc new file mode 100644 index 0000000..56acb1d --- /dev/null +++ b/src/PolySetEvaluator.cc @@ -0,0 +1,15 @@ +#include "PolySetEvaluator.h" +#include "printutils.h" +#include "polyset.h" + +PolySetEvaluator *PolySetEvaluator::global_evaluator = NULL; + +PolySetEvaluator::cache_entry::cache_entry(PolySet *ps) : +		ps(ps), msg(print_messages_stack.last()) +{ +} + +PolySetEvaluator::cache_entry::~cache_entry() +{ +	ps->unlink(); +} diff --git a/src/PolySetEvaluator.h b/src/PolySetEvaluator.h new file mode 100644 index 0000000..dcc67db --- /dev/null +++ b/src/PolySetEvaluator.h @@ -0,0 +1,39 @@ +#ifndef POLYSETEVALUATOR_H_ +#define POLYSETEVALUATOR_H_ + +#include "myqhash.h" +#include "node.h" +#include <QCache> + +class PolySetEvaluator +{ +public: +	enum EvaluateMode { EVALUATE_CGAL, EVALUATE_OPENCSG }; +	PolySetEvaluator() : cache(100) {} + +	virtual ~PolySetEvaluator() {} + +	virtual PolySet *evaluatePolySet(const class ProjectionNode &, AbstractPolyNode::render_mode_e) = 0; +	virtual PolySet *evaluatePolySet(const class DxfLinearExtrudeNode &, AbstractPolyNode::render_mode_e) = 0; +	virtual PolySet *evaluatePolySet(const class DxfRotateExtrudeNode &, AbstractPolyNode::render_mode_e) = 0; + +	void clearCache() { +		this->cache.clear(); +	} + +protected: + +	struct cache_entry { +		class PolySet *ps; +		QString msg; +		cache_entry(PolySet *ps); +		~cache_entry(); +	}; + +	QCache<std::string, cache_entry> cache; + +private: +	static PolySetEvaluator *global_evaluator; +}; + +#endif diff --git a/src/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 dd797fd..f3f2cbd 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -30,6 +30,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); @@ -55,18 +58,37 @@ 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; +		case HULL: +			return "hull"; +			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 @@ -130,129 +152,27 @@ void register_builtin_cgaladv()  	builtin_modules["hull"] = new CgaladvModule(HULL);  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron CgaladvNode::render_cgal_nef_polyhedron() const +std::string CgaladvNode::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; + +	stream << this->name(); +	switch (type) { +	case MINKOWSKI: +		stream << "(convexity = " << this->convexity << ")"; +		break; +	case GLIDE: +		stream << "(path = " << this->path << ", convexity = " << this->convexity << ")"; +		break; +	case SUBDIV: +		stream << "(level = " << this->level << ", convexity = " << this->convexity << ")"; +		break; +	case HULL: +		stream << "()"; +		break; +	default: +		assert(false);  	} -	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!"); -	} - -	if (type == HULL) -	{ -		std::list<CGAL_Nef_polyhedron2> polys; -		bool all2d = true; -		foreach(AbstractNode * v, children) { -			if (v->modinst->tag_background) -		    continue; -			N = v->render_cgal_nef_polyhedron(); -			if (N.dim == 3) { -		    //polys.push_back(tmp.p3); -				PRINT("WARNING: hull() is not implemented yet for 3D objects!"); -		    all2d=false; -			} -			if (N.dim == 2) { -		    polys.push_back(N.p2); -			} -			v->progress_report(); -		} - -		if (all2d) -			N.p2 = convexhull2(polys); -	} - -	cgal_nef_cache.insert(cache_id, new cgal_nef_cache_entry(N), N.weight()); -	print_messages_pop(); -	progress_report(); - -	return N; +	return stream.str();  } - -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); - -	if (type == HULL) -		return render_csg_term_from_nef(m, highlights, background, "hull", this->convexity); - -	return NULL; -} - -#else // ENABLE_CGAL - -CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const -{ -	PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!"); -	return NULL; -} - -#endif // ENABLE_CGAL - -QString CgaladvNode::dump(QString indent) const -{ -	if (dump_cache.isEmpty()) { -		QString text; -		if (type == MINKOWSKI) -			text.sprintf("minkowski(convexity = %d) {\n", this->convexity); -		if (type == GLIDE) { -			text.sprintf(", convexity = %d) {\n", this->convexity); -			text = QString("glide(path = ") + this->path.dump() + text; -		} -		if (type == SUBDIV) -			text.sprintf("subdiv(level = %d, convexity = %d) {\n", this->level, this->convexity); -		if (type == HULL) -			text.sprintf("hull() {\n"); -		foreach (AbstractNode *v, this->children) -			text += v->dump(indent + QString("\t")); -		text += indent + "}\n"; -		((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; -	} -	return dump_cache; -} - diff --git a/src/cgaladv_minkowski3.cc b/src/cgaladv_minkowski3.cc index f270de2..f12ce21 100644 --- a/src/cgaladv_minkowski3.cc +++ b/src/cgaladv_minkowski3.cc @@ -26,10 +26,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 bfe9eb6..ba2690c 100644 --- a/src/context.cc +++ b/src/context.cc @@ -85,6 +85,8 @@ Value Context::lookup_variable(QString name, bool silent) const  		}  		return Value();  	} +	if (!parent && constants.contains(name)) +		return constants[name];  	if (variables.contains(name))  		return variables[name];  	if (parent) @@ -94,6 +96,14 @@ Value Context::lookup_variable(QString name, bool silent) const  	return Value();  } +void Context::set_constant(QString name, Value value) +{ +    if (constants.contains(name)) +	PRINTA("WARNING: Attempt to modify constant '%1'.",name); +    else +	constants.insert(name,value); +} +  Value Context::evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const  {  	if (functions_p && functions_p->contains(name)) @@ -148,8 +158,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/context.h b/src/context.h index d5be745..cbb1c4f 100644 --- a/src/context.h +++ b/src/context.h @@ -9,6 +9,7 @@ class Context  {  public:  	const Context *parent; +	QHash<QString, Value> constants;  	QHash<QString, Value> variables;  	QHash<QString, Value> config_variables;  	const QHash<QString, class AbstractFunction*> *functions_p; @@ -27,6 +28,8 @@ public:  	void set_variable(QString name, Value value);  	Value lookup_variable(QString name, bool silent = false) const; +	void set_constant(QString name, Value value); +  	QString get_absolute_path(const QString &filename) const;  	Value evaluate_function(QString name, const QVector<QString> &argnames, const QVector<Value> &argvalues) const; diff --git a/src/control.cc b/src/control.cc index ae1d654..ca1a4c6 100644 --- a/src/control.cc +++ b/src/control.cc @@ -124,7 +124,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 55d91d2..53e9ed6 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -24,22 +24,14 @@   *   */ +#include "csgnode.h" +  #include "module.h" -#include "node.h"  #include "csgterm.h"  #include "builtin.h"  #include "printutils.h" -#ifdef ENABLE_CGAL -#  include "cgal.h" -#  include <CGAL/assertions_behaviour.h> -#  include <CGAL/exceptions.h> -#endif - -enum csg_type_e { -	CSG_TYPE_UNION, -	CSG_TYPE_DIFFERENCE, -	CSG_TYPE_INTERSECTION -}; +#include <sstream> +#include <assert.h>  class CsgModule : public AbstractModule  { @@ -49,18 +41,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); @@ -72,105 +52,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 6dd3bd5..930a540 100644 --- a/src/csgterm.cc +++ b/src/csgterm.cc @@ -27,7 +27,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; @@ -200,9 +219,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 4c53d86..f8008f5 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);  	}  	QFileInfo fileInfo(filename); @@ -133,9 +133,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") @@ -176,8 +176,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 9661066..64c4c35 100644 --- a/src/dxflinextrude.cc +++ b/src/dxflinextrude.cc @@ -24,8 +24,9 @@   *   */ +#include "dxflinextrudenode.h" +  #include "module.h" -#include "node.h"  #include "context.h"  #include "printutils.h"  #include "builtin.h" @@ -33,8 +34,12 @@  #include "dxftess.h"  #include "polyset.h"  #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h"  #include "openscad.h" // get_fragments_from_r() +#include <sstream> +  #include <QApplication>  #include <QTime>  #include <QProgressDialog> @@ -48,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); @@ -90,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); @@ -141,203 +126,45 @@ void register_builtin_dxf_linear_extrude()  	builtin_modules["linear_extrude"] = new DxfLinearExtrudeModule();  } -static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2, double h1, double h2) -{ -	for (int j = 1; j < pt->points.count(); j++) -	{ -		int k = j - 1; - -		double jx1 = pt->points[j]->x *  cos(rot1*M_PI/180) + pt->points[j]->y * sin(rot1*M_PI/180); -		double jy1 = pt->points[j]->x * -sin(rot1*M_PI/180) + pt->points[j]->y * cos(rot1*M_PI/180); - -		double jx2 = pt->points[j]->x *  cos(rot2*M_PI/180) + pt->points[j]->y * sin(rot2*M_PI/180); -		double jy2 = pt->points[j]->x * -sin(rot2*M_PI/180) + pt->points[j]->y * cos(rot2*M_PI/180); - -		double kx1 = pt->points[k]->x *  cos(rot1*M_PI/180) + pt->points[k]->y * sin(rot1*M_PI/180); -		double ky1 = pt->points[k]->x * -sin(rot1*M_PI/180) + pt->points[k]->y * cos(rot1*M_PI/180); - -		double kx2 = pt->points[k]->x *  cos(rot2*M_PI/180) + pt->points[k]->y * sin(rot2*M_PI/180); -		double ky2 = pt->points[k]->x * -sin(rot2*M_PI/180) + pt->points[k]->y * cos(rot2*M_PI/180); - -		double dia1_len_sq = (jy1-ky2)*(jy1-ky2) + (jx1-kx2)*(jx1-kx2); -		double dia2_len_sq = (jy2-ky1)*(jy2-ky1) + (jx2-kx1)*(jx2-kx1); - -		if (dia1_len_sq > dia2_len_sq) -		{ -			ps->append_poly(); -			if (pt->is_inner) { -				ps->append_vertex(kx1, ky1, h1); -				ps->append_vertex(jx1, jy1, h1); -				ps->append_vertex(jx2, jy2, h2); -			} else { -				ps->insert_vertex(kx1, ky1, h1); -				ps->insert_vertex(jx1, jy1, h1); -				ps->insert_vertex(jx2, jy2, h2); -			} - -			ps->append_poly(); -			if (pt->is_inner) { -				ps->append_vertex(kx2, ky2, h2); -				ps->append_vertex(kx1, ky1, h1); -				ps->append_vertex(jx2, jy2, h2); -			} else { -				ps->insert_vertex(kx2, ky2, h2); -				ps->insert_vertex(kx1, ky1, h1); -				ps->insert_vertex(jx2, jy2, h2); -			} -		} -		else -		{ -			ps->append_poly(); -			if (pt->is_inner) { -				ps->append_vertex(kx1, ky1, h1); -				ps->append_vertex(jx1, jy1, h1); -				ps->append_vertex(kx2, ky2, h2); -			} else { -				ps->insert_vertex(kx1, ky1, h1); -				ps->insert_vertex(jx1, jy1, h1); -				ps->insert_vertex(kx2, ky2, h2); -			} - -			ps->append_poly(); -			if (pt->is_inner) { -				ps->append_vertex(jx2, jy2, h2); -				ps->append_vertex(kx2, ky2, h2); -				ps->append_vertex(jx1, jy1, h1); -			} else { -				ps->insert_vertex(jx2, jy2, h2); -				ps->insert_vertex(kx2, ky2, h2); -				ps->insert_vertex(jx1, jy1, h1); -			} -		} -	} -} - -PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e) const +PolySet *DxfLinearExtrudeNode::evaluate_polyset(render_mode_e mode,  +																							PolySetEvaluator *evaluator) const  { -	QString key = mk_cache_id(); -	if (PolySet::ps_cache.contains(key)) { -		PRINT(PolySet::ps_cache[key]->msg); -		return PolySet::ps_cache[key]->ps->link(); +	if (!evaluator) { +		PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); +		PolySet *ps = new PolySet(); +		ps->is2d = true; +		return ps;  	}  	print_messages_push(); -	DxfData *dxf; - -	if (filename.isEmpty()) -	{ -#ifdef ENABLE_CGAL - -		// Before extruding, union all (2D) children nodes -		// to a single DxfData, then tesselate this into a PolySet -		CGAL_Nef_polyhedron N; -		N.dim = 2; -		foreach(AbstractNode * v, children) { -			if (v->modinst->tag_background) -				continue; -			N.p2 += v->render_cgal_nef_polyhedron().p2; -		} -		dxf = new DxfData(N); - -#else // ENABLE_CGAL -		PRINT("WARNING: Found linear_extrude() statement without dxf file but compiled without CGAL support!"); -		dxf = new DxfData(); -#endif // ENABLE_CGAL -	} else { -		dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); -	} - -	PolySet *ps = 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 = evaluator->evaluatePolySet(*this, mode); -	PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));  	print_messages_pop(); -	delete dxf;  	return ps;  } -QString DxfLinearExtrudeNode::dump(QString indent) const +std::string DxfLinearExtrudeNode::toString() const  { -	if (dump_cache.isEmpty()) { -		QString text; -        QFileInfo fileInfo(filename); -		text.sprintf("linear_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " -				"height = %g, origin = [ %g %g ], scale = %g, center = %s, convexity = %d", -				filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(),  -				(int)fileInfo.size(), layername.toAscii().data(), height, origin_x,  -				origin_y, scale, center ? "true" : "false", convexity); -		if (has_twist) { -			QString t2; -			t2.sprintf(", twist = %g, slices = %d", twist, slices); -			text += t2; -		} -		QString t3; -		t3.sprintf(", $fn = %g, $fa = %g, $fs = %g) {\n", fn, fa, fs); -		text += t3; -		foreach (AbstractNode *v, children) -			text += v->dump(indent + QString("\t")); -		text += indent + "}\n"; -		((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; +	std::stringstream stream; + +	QString text; +	 +	stream << this->name() << "(" +		"file = \"" << this->filename << "\", " +		"cache = \"" << 	QFileInfo(this->filename) << "\", " +		"layer = \"" << this->layername << "\", " +		"height = " << std::dec << this->height << ", " +		"origin = [ " << this->origin_x << " " << this->origin_y << " ], " +		"scale = " << this->scale << ", " +		"center = " << (this->center?"true":"false") << ", " +		"convexity = " << this->convexity; +	 +	if (this->has_twist) { +		stream << ", twist = " << this->twist << ", slices = " << this->slices;  	} -	return dump_cache; +	stream << ", $fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; +	 +	return stream.str();  } - diff --git a/src/dxflinextrudenode.h b/src/dxflinextrudenode.h new file mode 100644 index 0000000..dba03e8 --- /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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/dxfrotextrude.cc b/src/dxfrotextrude.cc index 1be2265..ec75a52 100644 --- a/src/dxfrotextrude.cc +++ b/src/dxfrotextrude.cc @@ -24,16 +24,20 @@   *   */ +#include "dxfrotextrudenode.h"  #include "module.h" -#include "node.h"  #include "context.h"  #include "printutils.h"  #include "builtin.h"  #include "polyset.h"  #include "dxfdata.h"  #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h"  #include "openscad.h" // get_fragments_from_r() +#include <sstream> +  #include <QTime>  #include <QApplication>  #include <QProgressDialog> @@ -47,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); @@ -83,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; @@ -116,131 +102,37 @@ void register_builtin_dxf_rotate_extrude()  	builtin_modules["rotate_extrude"] = new DxfRotateExtrudeModule();  } -PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e) const +PolySet *DxfRotateExtrudeNode::evaluate_polyset(render_mode_e mode,  +																							PolySetEvaluator *evaluator) const  { -	QString key = mk_cache_id(); -	if (PolySet::ps_cache.contains(key)) { -		PRINT(PolySet::ps_cache[key]->msg); -		return PolySet::ps_cache[key]->ps->link(); +	if (!evaluator) { +		PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); +		PolySet *ps = new PolySet(); +		ps->is2d = true; +		return ps;  	}  	print_messages_push(); -	DxfData *dxf; - -	if (filename.isEmpty()) -	{ -#ifdef ENABLE_CGAL -		CGAL_Nef_polyhedron N; -		N.dim = 2; -		foreach(AbstractNode * v, children) { -			if (v->modinst->tag_background) -				continue; -			N.p2 += v->render_cgal_nef_polyhedron().p2; -		} -		dxf = new DxfData(N); - -#else // ENABLE_CGAL -		PRINT("WARNING: Found rotate_extrude() statement without dxf file but compiled without CGAL support!"); -		dxf = new DxfData(); -#endif // ENABLE_CGAL -	} else { -		dxf = new DxfData(fn, fs, fa, filename, layername, origin_x, origin_y, scale); -	} -	PolySet *ps = new PolySet(); -	ps->convexity = convexity; - -	for (int i = 0; i < dxf->paths.count(); i++) -	{ -		double max_x = 0; -		for (int j = 0; j < dxf->paths[i].points.count(); j++) { -			max_x = fmax(max_x, dxf->paths[i].points[j]->x); -		} - -		int fragments = get_fragments_from_r(max_x, fn, fs, fa); - -        double ***points; -        points = new double**[fragments]; -        for (int j=0; j < fragments; j++) { -            points[j] = new double*[dxf->paths[i].points.count()]; -            for (int k=0; k < dxf->paths[i].points.count(); k++) -                points[j][k] = new double[3]; -        } - -		for (int j = 0; j < fragments; j++) { -			double a = (j*2*M_PI) / fragments; -			for (int k = 0; k < dxf->paths[i].points.count(); k++) { -				if (dxf->paths[i].points[k]->x == 0) { -					points[j][k][0] = 0; -					points[j][k][1] = 0; -				} else { -					points[j][k][0] = dxf->paths[i].points[k]->x * sin(a); -					points[j][k][1] = dxf->paths[i].points[k]->x * cos(a); -				} -				points[j][k][2] = dxf->paths[i].points[k]->y; -			} -		} - -		for (int j = 0; j < fragments; j++) { -			int j1 = j + 1 < fragments ? j + 1 : 0; -			for (int k = 0; k < dxf->paths[i].points.count(); k++) { -				int k1 = k + 1 < dxf->paths[i].points.count() ? k + 1 : 0; -				if (points[j][k][0] != points[j1][k][0] || -						points[j][k][1] != points[j1][k][1] || -						points[j][k][2] != points[j1][k][2]) { -					ps->append_poly(); -					ps->append_vertex(points[j ][k ][0], -							points[j ][k ][1], points[j ][k ][2]); -					ps->append_vertex(points[j1][k ][0], -							points[j1][k ][1], points[j1][k ][2]); -					ps->append_vertex(points[j ][k1][0], -							points[j ][k1][1], points[j ][k1][2]); -				} -				if (points[j][k1][0] != points[j1][k1][0] || -						points[j][k1][1] != points[j1][k1][1] || -						points[j][k1][2] != points[j1][k1][2]) { -					ps->append_poly(); -					ps->append_vertex(points[j ][k1][0], -							points[j ][k1][1], points[j ][k1][2]); -					ps->append_vertex(points[j1][k ][0], -							points[j1][k ][1], points[j1][k ][2]); -					ps->append_vertex(points[j1][k1][0], -							points[j1][k1][1], points[j1][k1][2]); -				} -			} -		} - -        for (int j=0; j < fragments; j++) { -            for (int k=0; k < dxf->paths[i].points.count(); k++) -                delete[] points[j][k]; -            delete[] points[j]; -        } -        delete[] points; -	} - -	PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); +	PolySet *ps = evaluator->evaluatePolySet(*this, mode); +	  	print_messages_pop(); -	delete dxf;  	return ps;  } -QString DxfRotateExtrudeNode::dump(QString indent) const +std::string DxfRotateExtrudeNode::toString() const  { -	if (dump_cache.isEmpty()) { -		QString text; -		QFileInfo fileInfo(filename); -		text.sprintf("rotate_extrude(file = \"%s\", cache = \"%x.%x\", layer = \"%s\", " -				"origin = [ %g %g ], scale = %g, convexity = %d, " -				"$fn = %g, $fa = %g, $fs = %g) {\n", -				filename.toAscii().data(), (int)fileInfo.lastModified().toTime_t(), -				(int)fileInfo.size(),layername.toAscii().data(), origin_x, origin_y,  -				scale, convexity, fn, fa, fs); -		foreach (AbstractNode *v, children) -			text += v->dump(indent + QString("\t")); -		text += indent + "}\n"; -		((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; -	} -	return dump_cache; +	std::stringstream stream; + +	stream << this->name() << "(" +		"file = \"" << this->filename << "\", " +		"cache = \"" << QFileInfo(this->filename) << "\", " +		"layer = \"" << this->layername << "\", " +		"origin = [ " << std::dec << this->origin_x << " " << this->origin_y << " ], " +		"scale = " << this->scale << ", " +		"convexity = " << this->convexity << ", " +		"$fn = " << this->fn << ", $fa = " << this->fa << ", $fs = " << this->fs << ")"; + +	return stream.str();  } - diff --git a/src/dxfrotextrudenode.h b/src/dxfrotextrudenode.h new file mode 100644 index 0000000..38ca087 --- /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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/export.cc b/src/export.cc index 8e0ab16..1f9046e 100644 --- a/src/export.cc +++ b/src/export.cc @@ -30,6 +30,7 @@  #include <QApplication>  #include <QProgressDialog> +#include <QTextStream>  #include <errno.h>  #ifdef ENABLE_CGAL @@ -72,9 +73,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); @@ -86,14 +88,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) { @@ -127,14 +122,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) { @@ -143,12 +140,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.");  } @@ -156,29 +152,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++) @@ -190,37 +177,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 50950d0..72f92a0 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -27,6 +27,8 @@  #include "expression.h"  #include "value.h"  #include "context.h" +#include <assert.h> +#include <sstream>  Expression::Expression()  { @@ -109,7 +111,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") @@ -143,45 +145,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 2608960..fd4bd7e 100644 --- a/src/func.cc +++ b/src/func.cc @@ -29,8 +29,8 @@  #include "context.h"  #include "dxfdim.h"  #include "builtin.h" +#include <sstream>  #include "mathc99.h" -#include <time.h>  AbstractFunction::~AbstractFunction()  { @@ -70,9 +70,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;  } @@ -164,7 +164,7 @@ Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Valu  	for (int i=0; i<args[2].num; i++)  	{	  		Value * r = new Value(frand(args[0].num, args[1].num)); -		v.vec.append(r); +		v.vec.push_back(r);  	}  	return v; @@ -304,15 +304,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 9d82443..896ff0a 100644 --- a/src/glview.cc +++ b/src/glview.cc @@ -50,32 +50,42 @@  GLView::GLView(QWidget *parent) : QGLWidget(parent), renderer(NULL)  { -	viewer_distance = 500; -	object_rot_x = 35; -	object_rot_y = 0; -	object_rot_z = 25; -	object_trans_x = 0; -	object_trans_y = 0; -	object_trans_z = 0; +	init(); +} -	mouse_drag_active = false; +GLView::GLView(const QGLFormat & format, QWidget *parent) : QGLWidget(format, parent) +{ +	init(); +} -	orthomode = false; -	showaxes = false; -	showcrosshairs = false; -	showedges = false; -	showfaces = true; +void GLView::init() +{ +	this->viewer_distance = 500; +	this->object_rot_x = 35; +	this->object_rot_y = 0; +	this->object_rot_z = 25; +	this->object_trans_x = 0; +	this->object_trans_y = 0; +	this->object_trans_z = 0; + +	this->mouse_drag_active = false; + +	this->showedges = false; +	this->showfaces = true; +	this->orthomode = false; +	this->showaxes = false; +	this->showcrosshairs = false;  	for (int i = 0; i < 10; i++) -		shaderinfo[i] = 0; +		this->shaderinfo[i] = 0; -	statusLabel = NULL; +	this->statusLabel = NULL;  	setMouseTracking(true);  #ifdef ENABLE_OPENCSG -	opencsg_support = true; +	this->opencsg_support = true;  	static int sId = 0; -	opencsg_id = sId++; +	this->opencsg_id = sId++;  #endif  } @@ -527,4 +537,3 @@ void GLView::mouseReleaseEvent(QMouseEvent*)  	mouse_drag_active = false;  	releaseMouse();  } - @@ -1,6 +1,7 @@  #ifndef GRID_H_  #define GRID_H_ +#include <QHash>  #include "mathc99.h"  #ifdef WIN32  typedef __int64 int64_t; @@ -8,7 +9,6 @@ typedef __int64 int64_t;  #include <stdint.h>  #endif  #include <stdlib.h> -#include <QHash>  const double GRID_COARSE = 0.001;  const double GRID_FINE   = 0.000001; diff --git a/src/import.cc b/src/import.cc index a924e24..fdac5b9 100644 --- a/src/import.cc +++ b/src/import.cc @@ -24,8 +24,9 @@   *   */ +#include "importnode.h" +  #include "module.h" -#include "node.h"  #include "polyset.h"  #include "context.h"  #include "builtin.h" @@ -37,12 +38,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  { @@ -52,26 +49,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"; @@ -94,8 +77,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) @@ -120,17 +105,17 @@ void register_builtin_import()  	builtin_modules["import_dxf"] = new ImportModule(TYPE_DXF);  } -PolySet *ImportNode::render_polyset(render_mode_e) const +PolySet *ImportNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) 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;  		} @@ -202,14 +187,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); @@ -218,28 +203,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..bdbf193 --- /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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const; +}; + +#endif diff --git a/src/lexer.l b/src/lexer.l index 0da3f5d..bc61c20 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -88,7 +88,7 @@ DIGIT [0-9]  include[ \t\r\n>]*"<"	{ BEGIN(include); }  <include>{ -[^\t\r\n>]+"/"	{ filepath = yytext; } +[^\t\r\n>]*"/"	{ filepath = yytext; }  [^\t\r\n>/]+	{ filename = yytext; }  ">"		{ BEGIN(INITIAL); includefile(); }  } diff --git a/src/mainwin.cc b/src/mainwin.cc index 3453b1c..5bc16cb 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -40,11 +40,14 @@  #include "dxftess.h"  #include "progress.h"  #ifdef ENABLE_OPENCSG +#include "CSGTermEvaluator.h"  #include "opencsgRenderer.h"  #endif  #ifdef USE_PROGRESSWIDGET  #include "ProgressWidget.h"  #endif +#include "CGALEvaluator.h" +#include "PolySetCGALEvaluator.h"  #include "thrownTogetherRenderer.h"  #include <QMenu> @@ -74,19 +77,26 @@  #include "qlanguagefactory.h"  #endif +#include <algorithm> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +using namespace boost::lambda; +  #ifdef ENABLE_CGAL  #include "cgalrenderer.h"  #endif // ENABLE_CGAL +// Global application state +unsigned int GuiLocker::gui_locked = 0; +  #define QUOTE(x__) # x__  #define QUOTED(x__) QUOTE(x__)  static char helptitle[] = -	"OpenSCAD " -	QUOTED(OPENSCAD_VERSION) -  " (www.openscad.org)\n"; +	"OpenSCAD " QUOTED(OPENSCAD_VERSION) " (www.openscad.org)\n" +	"Visitor refactored version\n";  static char copyrighttext[] =  	"Copyright (C) 2009-2011 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n"  	"\n" @@ -137,11 +147,13 @@ MainWindow::MainWindow(const QString &filename)  	root_ctx.set_variable("$fa", Value(12.0));  	root_ctx.set_variable("$t", Value(0.0)); +	root_ctx.set_constant("PI",Value(M_PI)); +  	Value zero3;  	zero3.type = Value::VECTOR; -	zero3.vec.append(new Value(0.0)); -	zero3.vec.append(new Value(0.0)); -	zero3.vec.append(new Value(0.0)); +	zero3.append(new Value(0.0)); +	zero3.append(new Value(0.0)); +	zero3.append(new Value(0.0));  	root_ctx.set_variable("$vpt", zero3);  	root_ctx.set_variable("$vpr", zero3); @@ -180,8 +192,8 @@ MainWindow::MainWindow(const QString &filename)  	editor->setLineWrapping(true); // Not designable  	setFont("", 0); // Init default font -	screen->statusLabel = new QLabel(this); -	statusBar()->addWidget(screen->statusLabel); +	this->glview->statusLabel = new QLabel(this); +	statusBar()->addWidget(this->glview->statusLabel);  	animate_timer = new QTimer(this);  	connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); @@ -276,7 +288,7 @@ MainWindow::MainWindow(const QString &filename)  	this->viewActionOpenCSG->setVisible(false);  #else  	connect(this->viewActionOpenCSG, SIGNAL(triggered()), this, SLOT(viewModeOpenCSG())); -	if (!screen->hasOpenCSGSupport()) { +	if (!this->glview->hasOpenCSGSupport()) {  		this->viewActionOpenCSG->setEnabled(false);  	}  #endif @@ -332,9 +344,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(); @@ -572,7 +584,7 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)  }  /*! -	Parse and evaluate the design -> this->root_node +	Parse and evaluate the design => this->root_node  */  void MainWindow::compile(bool procevents)  { @@ -581,7 +593,7 @@ void MainWindow::compile(bool procevents)  		QApplication::processEvents();    // Invalidate renderers before we kill the CSG tree -	screen->setRenderer(NULL); +	this->glview->setRenderer(NULL);  	if (this->opencsgRenderer) {  		delete this->opencsgRenderer;  		this->opencsgRenderer = NULL; @@ -592,80 +604,83 @@ void MainWindow::compile(bool procevents)  	}  	// Remove previous CSG tree -	if (root_module) { -		delete root_module; -		root_module = NULL; +	if (this->root_module) { +		delete this->root_module; +		this->root_module = NULL;  	} -	if (absolute_root_node) { -		delete absolute_root_node; -		absolute_root_node = NULL; +	if (this->absolute_root_node) { +		delete this->absolute_root_node; +		this->absolute_root_node = NULL;  	} -	if (root_raw_term) { -		root_raw_term->unlink(); -		root_raw_term = NULL; +	if (this->root_raw_term) { +		this->root_raw_term->unlink(); +		this->root_raw_term = NULL;  	} -	if (root_norm_term) { -		root_norm_term->unlink(); -		root_norm_term = NULL; +	if (this->root_norm_term) { +		this->root_norm_term->unlink(); +		this->root_norm_term = NULL;  	} -	if (root_chain) { -		delete root_chain; -		root_chain = NULL; +	if (this->root_chain) { +		delete this->root_chain; +		this->root_chain = NULL;  	} -	foreach(CSGTerm *v, highlight_terms) { -		v->unlink(); -	} -	highlight_terms.clear(); -	if (highlights_chain) { -		delete highlights_chain; -		highlights_chain = NULL; -	} -	foreach(CSGTerm *v, background_terms) { -		v->unlink(); -	} -	background_terms.clear(); -	if (background_chain) { -		delete background_chain; -		background_chain = NULL; -	} -	root_node = NULL; +	std::for_each(this->highlight_terms.begin(), this->highlight_terms.end(),  +								bind(&CSGTerm::unlink, _1)); + +	this->highlight_terms.clear(); +	delete this->highlights_chain; +	this->highlights_chain = NULL; + +	std::for_each(this->background_terms.begin(), this->background_terms.end(),  +								bind(&CSGTerm::unlink, _1)); +	this->background_terms.clear(); +	delete this->background_chain; +	this->background_chain = NULL; + +	this->root_node = NULL; +	this->tree.setRoot(NULL);  	// Initialize special variables -	root_ctx.set_variable("$t", Value(e_tval->text().toDouble())); +	this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));  	Value vpt;  	vpt.type = Value::VECTOR; -	vpt.vec.append(new Value(-screen->object_trans_x)); -	vpt.vec.append(new Value(-screen->object_trans_y)); -	vpt.vec.append(new Value(-screen->object_trans_z)); -	root_ctx.set_variable("$vpt", vpt); +	vpt.append(new Value(-this->glview->object_trans_x)); +	vpt.append(new Value(-this->glview->object_trans_y)); +	vpt.append(new Value(-this->glview->object_trans_z)); +	this->root_ctx.set_variable("$vpt", vpt);  	Value vpr;  	vpr.type = Value::VECTOR; -	vpr.vec.append(new Value(fmodf(360 - screen->object_rot_x + 90, 360))); -	vpr.vec.append(new Value(fmodf(360 - screen->object_rot_y, 360))); -	vpr.vec.append(new Value(fmodf(360 - screen->object_rot_z, 360))); +	vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360))); +	vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360))); +	vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));  	root_ctx.set_variable("$vpr", vpr);  	// Parse -	last_compiled_doc = editor->toPlainText(); -	root_module = parse((last_compiled_doc + "\n" + commandline_commands).toAscii().data(), this->fileName.isEmpty() ? "" : QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false); +	this->last_compiled_doc = editor->toPlainText(); +	this->root_module = parse((this->last_compiled_doc + "\n" +  +														 commandline_commands).toAscii().data(),  +														this->fileName.isEmpty() ?  +														"" :  +														QFileInfo(this->fileName).absolutePath().toLocal8Bit(),  +														false);  	// Error highlighting -	if (highlighter) { -		delete highlighter; -		highlighter = NULL; +	if (this->highlighter) { +		delete this->highlighter; +		this->highlighter = NULL;  	}  	if (parser_error_pos >= 0) { -		highlighter = new Highlighter(editor->document()); +		this->highlighter = new Highlighter(editor->document());  	} -	if (!root_module) { +	if (!this->root_module) {  		if (!animate_panel->isVisible()) {  #ifdef _QCODE_EDIT_  			QDocumentCursor cursor = editor->cursor(); @@ -685,19 +700,23 @@ void MainWindow::compile(bool procevents)  		QApplication::processEvents();  	AbstractNode::resetIndexCounter(); -	root_inst = ModuleInstantiation(); -	absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); +	this->root_inst = ModuleInstantiation(); +	this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst); -	if (!absolute_root_node) +	if (!this->absolute_root_node)  		goto fail;  	// Do we have an explicit root node (! modifier)? -	if (!(this->root_node = find_root_tag(absolute_root_node))) { -		this->root_node = absolute_root_node; +	if (!(this->root_node = find_root_tag(this->absolute_root_node))) { +		this->root_node = this->absolute_root_node;  	} -	root_node->dump(""); +	// FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers +	this->tree.setRoot(this->root_node); +  // Dump the tree (to initialize caches). +  // FIXME: We shouldn't really need to do this explicitly.. +	this->tree.getString(*this->root_node); -	if (1) { + 	if (1) {  		PRINT("Compilation finished.");  		if (procevents)  			QApplication::processEvents(); @@ -707,7 +726,7 @@ fail:  			PRINT("ERROR: Compilation failed! (no top level object found)");  		} else {  			int line = 1; -			QByteArray pb = last_compiled_doc.toAscii(); +			QByteArray pb = this->last_compiled_doc.toAscii();  			char *p = pb.data();  			for (int i = 0; i < parser_error_pos; i++) {  				if (p[i] == '\n') @@ -735,13 +754,6 @@ void MainWindow::compileCSG(bool procevents)  	if (procevents)  		QApplication::processEvents(); -	double m[20]; - -	for (int i = 0; i < 16; i++) -		m[i] = i % 5 == 0 ? 1.0 : 0.0; -	for (int i = 16; i < 20; i++) -		m[i] = -1; -  	// Main CSG evaluation  	QTime t;  	t.start(); @@ -762,7 +774,12 @@ void MainWindow::compileCSG(bool procevents)  	progress_report_prep(root_node, report_func, pd);  	try { -		root_raw_term = root_node->render_csg_term(m, &highlight_terms, &background_terms); +		// FIXME: put cache somewhere else as it's pretty useless now +		QHash<std::string, CGAL_Nef_polyhedron> cache; +		CGALEvaluator cgalevaluator(cache, this->tree); +		PolySetCGALEvaluator psevaluator(cgalevaluator); +		CSGTermEvaluator csgrenderer(this->tree, &psevaluator); +		root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, &highlight_terms, &background_terms);  		if (!root_raw_term) {  			PRINT("ERROR: CSG generation failed! (no top level object found)");  			if (procevents) @@ -801,12 +818,12 @@ void MainWindow::compileCSG(bool procevents)  		if (highlight_terms.size() > 0)  		{ -			PRINTF("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); +			PRINTF("Compiling highlights (%zu CSG Trees)...", highlight_terms.size());  			if (procevents)  				QApplication::processEvents();  			highlights_chain = new CSGChain(); -			for (int i = 0; i < highlight_terms.size(); i++) { +			for (unsigned int i = 0; i < highlight_terms.size(); i++) {  				while (1) {  					CSGTerm *n = highlight_terms[i]->normalize();  					highlight_terms[i]->unlink(); @@ -820,12 +837,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(); @@ -845,7 +862,7 @@ void MainWindow::compileCSG(bool procevents)  			this->opencsgRenderer = new OpenCSGRenderer(this->root_chain,   																									this->highlights_chain,   																									this->background_chain,  -																									this->screen->shaderinfo); +																									this->glview->shaderinfo);  		}  		this->thrownTogetherRenderer = new ThrownTogetherRenderer(this->root_chain,   																															this->highlights_chain,  @@ -1027,7 +1044,7 @@ void MainWindow::pasteViewportTranslation()  	QTextCursor cursor = editor->textCursor();  #endif  	QString txt; -	txt.sprintf("[ %.2f, %.2f, %.2f ]", -screen->object_trans_x, -screen->object_trans_y, -screen->object_trans_z); +	txt.sprintf("[ %.2f, %.2f, %.2f ]", -this->glview->object_trans_x, -this->glview->object_trans_y, -this->glview->object_trans_z);  	cursor.insertText(txt);  } @@ -1040,7 +1057,7 @@ void MainWindow::pasteViewportRotation()  #endif  	QString txt;  	txt.sprintf("[ %.2f, %.2f, %.2f ]", -		fmodf(360 - screen->object_rot_x + 90, 360), fmodf(360 - screen->object_rot_y, 360), fmodf(360 - screen->object_rot_z, 360)); +		fmodf(360 - this->glview->object_rot_x + 90, 360), fmodf(360 - this->glview->object_rot_y, 360), fmodf(360 - this->glview->object_rot_z, 360));  	cursor.insertText(txt);  } @@ -1084,6 +1101,9 @@ bool MainWindow::checkModified()  void MainWindow::actionReloadCompile()  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock; +  	if (!checkModified()) return;  	console->clear(); @@ -1102,13 +1122,16 @@ void MainWindow::actionReloadCompile()  	else  #endif  	{ -		screen->updateGL(); +		this->glview->updateGL();  	}  	clearCurrentOutput();  }  void MainWindow::actionCompile()  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock; +  	setCurrentOutput();  	console->clear(); @@ -1128,7 +1151,7 @@ void MainWindow::actionCompile()  	}  	if (viewActionAnimate->isChecked() && e_dump->isChecked()) { -		QImage img = screen->grabFrameBuffer(); +		QImage img = this->glview->grabFrameBuffer();  		QString filename;  		double s = e_fsteps->text().toDouble();  		double t = e_tval->text().toDouble(); @@ -1143,15 +1166,19 @@ void MainWindow::actionCompile()  void MainWindow::actionRenderCGAL()  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock; +  	setCurrentOutput();  	console->clear();  	compile(true); -	if (!root_module || !root_node) +	if (!this->root_module || !this->root_node) {  		return; +	} -	this->screen->setRenderer(NULL); +	this->glview->setRenderer(NULL);  	delete this->cgalRenderer;  	this->cgalRenderer = NULL;  	if (this->root_N) { @@ -1181,9 +1208,12 @@ void MainWindow::actionRenderCGAL()  	QApplication::processEvents(); -	progress_report_prep(root_node, report_func, pd); +	progress_report_prep(this->root_node, report_func, pd);  	try { -		this->root_N = new CGAL_Nef_polyhedron(root_node->render_cgal_nef_polyhedron()); +		// FIXME: put cache somewhere else as it's pretty useless now +		QHash<std::string, CGAL_Nef_polyhedron> cache; +		CGALEvaluator evaluator(cache, this->tree); +		this->root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->root_node));  	}  	catch (ProgressCancelException e) {  		PRINT("Rendering cancelled."); @@ -1192,8 +1222,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) { @@ -1287,8 +1318,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...");  	} @@ -1317,6 +1348,8 @@ void MainWindow::actionExportSTLorOFF(bool stl_mode)  void MainWindow::actionExportSTLorOFF(bool)  #endif  { +	if (GuiLocker::isLocked()) return; +	GuiLocker lock;  #ifdef ENABLE_CGAL  	setCurrentOutput(); @@ -1338,8 +1371,10 @@ void MainWindow::actionExportSTLorOFF(bool)  		return;  	} +	QString suffix = stl_mode ? ".stl" : ".off";  	QString stl_filename = QFileDialog::getSaveFileName(this, -			stl_mode ? "Export STL File" : "Export OFF File", "", +			stl_mode ? "Export STL File" : "Export OFF File",  +			this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix,  			stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)");  	if (stl_filename.isEmpty()) {  		PRINTF("No filename specified. %s export aborted.", stl_mode ? "STL" : "OFF"); @@ -1355,13 +1390,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(); @@ -1395,16 +1435,26 @@ void MainWindow::actionExportDXF()  		return;  	} -	QString stl_filename = QFileDialog::getSaveFileName(this, -			"Export DXF File", "", "DXF Files (*.dxf)"); -	if (stl_filename.isEmpty()) { +	QString dxf_filename = QFileDialog::getSaveFileName(this, +			"Export DXF File",  +			this->fileName.isEmpty() ? "Untitled.dxf" : QFileInfo(this->fileName).baseName()+".dxf", +			"DXF Files (*.dxf)"); +	if (dxf_filename.isEmpty()) {  		PRINTF("No filename specified. DXF export aborted.");  		clearCurrentOutput();  		return;  	} -	export_dxf(this->root_N, stl_filename, NULL); -	PRINTF("DXF export finished."); +	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 */ @@ -1412,7 +1462,7 @@ void MainWindow::actionExportDXF()  void MainWindow::actionExportImage()  { -	QImage img = screen->grabFrameBuffer(); +	QImage img = this->glview->grabFrameBuffer();  	setCurrentOutput();  	QString img_filename = QFileDialog::getSaveFileName(this, @@ -1430,9 +1480,12 @@ void MainWindow::actionExportImage()  void MainWindow::actionFlushCaches()  { -	PolySet::ps_cache.clear(); +// FIXME: Polycache -> PolySetEvaluator +// FIXME: PolySetEvaluator->clearCache();  #ifdef ENABLE_CGAL -	AbstractNode::cgal_nef_cache.clear(); +// FIXME: Flush caches through whatever channels we have +	// CGALEvaluator::evaluator()->getCache().clear(); +	// this->dumper->clearCache();  #endif  	dxf_dim_cache.clear();  	dxf_cross_cache.clear(); @@ -1456,10 +1509,10 @@ void MainWindow::viewModeActionsUncheck()  */  void MainWindow::viewModeOpenCSG()  { -	if (screen->hasOpenCSGSupport()) { +	if (this->glview->hasOpenCSGSupport()) {  		viewModeActionsUncheck();  		viewActionOpenCSG->setChecked(true); -		screen->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer); +		this->glview->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer);  	} else {  		viewModeThrownTogether();  	} @@ -1473,17 +1526,17 @@ void MainWindow::viewModeCGALSurface()  {  	viewModeActionsUncheck();  	viewActionCGALSurfaces->setChecked(true); -	screen->setShowFaces(true); -	screen->setRenderer(this->cgalRenderer); -	screen->updateGL(); +	this->glview->setShowFaces(true); +	this->glview->setRenderer(this->cgalRenderer); +	this->glview->updateGL();  }  void MainWindow::viewModeCGALGrid()  {  	viewModeActionsUncheck();  	viewActionCGALGrid->setChecked(true); -	screen->setShowFaces(false); -	screen->setRenderer(this->cgalRenderer); +	this->glview->setShowFaces(false); +	this->glview->setRenderer(this->cgalRenderer);  }  #endif /* ENABLE_CGAL */ @@ -1492,31 +1545,31 @@ void MainWindow::viewModeThrownTogether()  {  	viewModeActionsUncheck();  	viewActionThrownTogether->setChecked(true); -	screen->setRenderer(this->thrownTogetherRenderer); +	this->glview->setRenderer(this->thrownTogetherRenderer);  }  void MainWindow::viewModeShowEdges()  {  	QSettings settings;  	settings.setValue("view/showEdges",viewActionShowEdges->isChecked()); -	screen->setShowEdges(viewActionShowEdges->isChecked()); -	screen->updateGL(); +	this->glview->setShowEdges(viewActionShowEdges->isChecked()); +	this->glview->updateGL();  }  void MainWindow::viewModeShowAxes()  {  	QSettings settings;  	settings.setValue("view/showAxes",viewActionShowAxes->isChecked()); -	screen->setShowAxes(viewActionShowAxes->isChecked()); -	screen->updateGL(); +	this->glview->setShowAxes(viewActionShowAxes->isChecked()); +	this->glview->updateGL();  }  void MainWindow::viewModeShowCrosshairs()  {  	QSettings settings;  	settings.setValue("view/showCrosshairs",viewActionShowCrosshairs->isChecked()); -	screen->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); -	screen->updateGL(); +	this->glview->setShowCrosshairs(viewActionShowCrosshairs->isChecked()); +	this->glview->updateGL();  }  void MainWindow::viewModeAnimate() @@ -1554,66 +1607,66 @@ void MainWindow::animateUpdate()  void MainWindow::viewAngleTop()  { -	screen->object_rot_x = 90; -	screen->object_rot_y = 0; -	screen->object_rot_z = 0; -	screen->updateGL(); +	this->glview->object_rot_x = 90; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 0; +	this->glview->updateGL();  }  void MainWindow::viewAngleBottom()  { -	screen->object_rot_x = 270; -	screen->object_rot_y = 0; -	screen->object_rot_z = 0; -	screen->updateGL(); +	this->glview->object_rot_x = 270; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 0; +	this->glview->updateGL();  }  void MainWindow::viewAngleLeft()  { -	screen->object_rot_x = 0; -	screen->object_rot_y = 0; -	screen->object_rot_z = 90; -	screen->updateGL(); +	this->glview->object_rot_x = 0; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 90; +	this->glview->updateGL();  }  void MainWindow::viewAngleRight()  { -	screen->object_rot_x = 0; -	screen->object_rot_y = 0; -	screen->object_rot_z = 270; -	screen->updateGL(); +	this->glview->object_rot_x = 0; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 270; +	this->glview->updateGL();  }  void MainWindow::viewAngleFront()  { -	screen->object_rot_x = 0; -	screen->object_rot_y = 0; -	screen->object_rot_z = 0; -	screen->updateGL(); +	this->glview->object_rot_x = 0; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 0; +	this->glview->updateGL();  }  void MainWindow::viewAngleBack()  { -	screen->object_rot_x = 0; -	screen->object_rot_y = 0; -	screen->object_rot_z = 180; -	screen->updateGL(); +	this->glview->object_rot_x = 0; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 180; +	this->glview->updateGL();  }  void MainWindow::viewAngleDiagonal()  { -	screen->object_rot_x = 35; -	screen->object_rot_y = 0; -	screen->object_rot_z = 25; -	screen->updateGL(); +	this->glview->object_rot_x = 35; +	this->glview->object_rot_y = 0; +	this->glview->object_rot_z = 25; +	this->glview->updateGL();  }  void MainWindow::viewCenter()  { -	screen->object_trans_x = 0; -	screen->object_trans_y = 0; -	screen->object_trans_z = 0; -	screen->updateGL(); +	this->glview->object_trans_x = 0; +	this->glview->object_trans_y = 0; +	this->glview->object_trans_z = 0; +	this->glview->updateGL();  }  void MainWindow::viewPerspective() @@ -1622,8 +1675,8 @@ void MainWindow::viewPerspective()  	settings.setValue("view/orthogonalProjection",false);  	viewActionPerspective->setChecked(true);  	viewActionOrthogonal->setChecked(false); -	screen->setOrthoMode(false); -	screen->updateGL(); +	this->glview->setOrthoMode(false); +	this->glview->updateGL();  }  void MainWindow::viewOrthogonal() @@ -1632,8 +1685,8 @@ void MainWindow::viewOrthogonal()  	settings.setValue("view/orthogonalProjection",true);  	viewActionPerspective->setChecked(false);  	viewActionOrthogonal->setChecked(true); -	screen->setOrthoMode(true); -	screen->updateGL(); +	this->glview->setOrthoMode(true); +	this->glview->updateGL();  }  void MainWindow::hideConsole() diff --git a/src/module.cc b/src/module.cc index 54b151c..efe98e4 100644 --- a/src/module.cc +++ b/src/module.cc @@ -79,7 +79,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"); @@ -167,7 +167,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"; @@ -187,7 +187,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/namedcolors.cpp b/src/namedcolors.cpp new file mode 100644 index 0000000..3150a40 --- /dev/null +++ b/src/namedcolors.cpp @@ -0,0 +1,155 @@ +#define rgb(r,g,b) (0xff000000 | (r << 16) |  (g << 8) | b) + +static const struct RGBData { +    const char *name; +    uint  value; +} named_colors[] = { +    { "aliceblue", rgb(240, 248, 255) }, +    { "antiquewhite", rgb(250, 235, 215) }, +    { "aqua", rgb( 0, 255, 255) }, +    { "aquamarine", rgb(127, 255, 212) }, +    { "azure", rgb(240, 255, 255) }, +    { "beige", rgb(245, 245, 220) }, +    { "bisque", rgb(255, 228, 196) }, +    { "black", rgb( 0, 0, 0) }, +    { "blanchedalmond", rgb(255, 235, 205) }, +    { "blue", rgb( 0, 0, 255) }, +    { "blueviolet", rgb(138, 43, 226) }, +    { "brown", rgb(165, 42, 42) }, +    { "burlywood", rgb(222, 184, 135) }, +    { "cadetblue", rgb( 95, 158, 160) }, +    { "chartreuse", rgb(127, 255, 0) }, +    { "chocolate", rgb(210, 105, 30) }, +    { "coral", rgb(255, 127, 80) }, +    { "cornflowerblue", rgb(100, 149, 237) }, +    { "cornsilk", rgb(255, 248, 220) }, +    { "crimson", rgb(220, 20, 60) }, +    { "cyan", rgb( 0, 255, 255) }, +    { "darkblue", rgb( 0, 0, 139) }, +    { "darkcyan", rgb( 0, 139, 139) }, +    { "darkgoldenrod", rgb(184, 134, 11) }, +    { "darkgray", rgb(169, 169, 169) }, +    { "darkgreen", rgb( 0, 100, 0) }, +    { "darkgrey", rgb(169, 169, 169) }, +    { "darkkhaki", rgb(189, 183, 107) }, +    { "darkmagenta", rgb(139, 0, 139) }, +    { "darkolivegreen", rgb( 85, 107, 47) }, +    { "darkorange", rgb(255, 140, 0) }, +    { "darkorchid", rgb(153, 50, 204) }, +    { "darkred", rgb(139, 0, 0) }, +    { "darksalmon", rgb(233, 150, 122) }, +    { "darkseagreen", rgb(143, 188, 143) }, +    { "darkslateblue", rgb( 72, 61, 139) }, +    { "darkslategray", rgb( 47, 79, 79) }, +    { "darkslategrey", rgb( 47, 79, 79) }, +    { "darkturquoise", rgb( 0, 206, 209) }, +    { "darkviolet", rgb(148, 0, 211) }, +    { "deeppink", rgb(255, 20, 147) }, +    { "deepskyblue", rgb( 0, 191, 255) }, +    { "dimgray", rgb(105, 105, 105) }, +    { "dimgrey", rgb(105, 105, 105) }, +    { "dodgerblue", rgb( 30, 144, 255) }, +    { "firebrick", rgb(178, 34, 34) }, +    { "floralwhite", rgb(255, 250, 240) }, +    { "forestgreen", rgb( 34, 139, 34) }, +    { "fuchsia", rgb(255, 0, 255) }, +    { "gainsboro", rgb(220, 220, 220) }, +    { "ghostwhite", rgb(248, 248, 255) }, +    { "gold", rgb(255, 215, 0) }, +    { "goldenrod", rgb(218, 165, 32) }, +    { "gray", rgb(128, 128, 128) }, +    { "green", rgb( 0, 128, 0) }, +    { "greenyellow", rgb(173, 255, 47) }, +    { "grey", rgb(128, 128, 128) }, +    { "honeydew", rgb(240, 255, 240) }, +    { "hotpink", rgb(255, 105, 180) }, +    { "indianred", rgb(205, 92, 92) }, +    { "indigo", rgb( 75, 0, 130) }, +    { "ivory", rgb(255, 255, 240) }, +    { "khaki", rgb(240, 230, 140) }, +    { "lavender", rgb(230, 230, 250) }, +    { "lavenderblush", rgb(255, 240, 245) }, +    { "lawngreen", rgb(124, 252, 0) }, +    { "lemonchiffon", rgb(255, 250, 205) }, +    { "lightblue", rgb(173, 216, 230) }, +    { "lightcoral", rgb(240, 128, 128) }, +    { "lightcyan", rgb(224, 255, 255) }, +    { "lightgoldenrodyellow", rgb(250, 250, 210) }, +    { "lightgray", rgb(211, 211, 211) }, +    { "lightgreen", rgb(144, 238, 144) }, +    { "lightgrey", rgb(211, 211, 211) }, +    { "lightpink", rgb(255, 182, 193) }, +    { "lightsalmon", rgb(255, 160, 122) }, +    { "lightseagreen", rgb( 32, 178, 170) }, +    { "lightskyblue", rgb(135, 206, 250) }, +    { "lightslategray", rgb(119, 136, 153) }, +    { "lightslategrey", rgb(119, 136, 153) }, +    { "lightsteelblue", rgb(176, 196, 222) }, +    { "lightyellow", rgb(255, 255, 224) }, +    { "lime", rgb( 0, 255, 0) }, +    { "limegreen", rgb( 50, 205, 50) }, +    { "linen", rgb(250, 240, 230) }, +    { "magenta", rgb(255, 0, 255) }, +    { "maroon", rgb(128, 0, 0) }, +    { "mediumaquamarine", rgb(102, 205, 170) }, +    { "mediumblue", rgb( 0, 0, 205) }, +    { "mediumorchid", rgb(186, 85, 211) }, +    { "mediumpurple", rgb(147, 112, 219) }, +    { "mediumseagreen", rgb( 60, 179, 113) }, +    { "mediumslateblue", rgb(123, 104, 238) }, +    { "mediumspringgreen", rgb( 0, 250, 154) }, +    { "mediumturquoise", rgb( 72, 209, 204) }, +    { "mediumvioletred", rgb(199, 21, 133) }, +    { "midnightblue", rgb( 25, 25, 112) }, +    { "mintcream", rgb(245, 255, 250) }, +    { "mistyrose", rgb(255, 228, 225) }, +    { "moccasin", rgb(255, 228, 181) }, +    { "navajowhite", rgb(255, 222, 173) }, +    { "navy", rgb( 0, 0, 128) }, +    { "oldlace", rgb(253, 245, 230) }, +    { "olive", rgb(128, 128, 0) }, +    { "olivedrab", rgb(107, 142, 35) }, +    { "orange", rgb(255, 165, 0) }, +    { "orangered", rgb(255, 69, 0) }, +    { "orchid", rgb(218, 112, 214) }, +    { "palegoldenrod", rgb(238, 232, 170) }, +    { "palegreen", rgb(152, 251, 152) }, +    { "paleturquoise", rgb(175, 238, 238) }, +    { "palevioletred", rgb(219, 112, 147) }, +    { "papayawhip", rgb(255, 239, 213) }, +    { "peachpuff", rgb(255, 218, 185) }, +    { "peru", rgb(205, 133, 63) }, +    { "pink", rgb(255, 192, 203) }, +    { "plum", rgb(221, 160, 221) }, +    { "powderblue", rgb(176, 224, 230) }, +    { "purple", rgb(128, 0, 128) }, +    { "red", rgb(255, 0, 0) }, +    { "rosybrown", rgb(188, 143, 143) }, +    { "royalblue", rgb( 65, 105, 225) }, +    { "saddlebrown", rgb(139, 69, 19) }, +    { "salmon", rgb(250, 128, 114) }, +    { "sandybrown", rgb(244, 164, 96) }, +    { "seagreen", rgb( 46, 139, 87) }, +    { "seashell", rgb(255, 245, 238) }, +    { "sienna", rgb(160, 82, 45) }, +    { "silver", rgb(192, 192, 192) }, +    { "skyblue", rgb(135, 206, 235) }, +    { "slateblue", rgb(106, 90, 205) }, +    { "slategray", rgb(112, 128, 144) }, +    { "slategrey", rgb(112, 128, 144) }, +    { "snow", rgb(255, 250, 250) }, +    { "springgreen", rgb( 0, 255, 127) }, +    { "steelblue", rgb( 70, 130, 180) }, +    { "tan", rgb(210, 180, 140) }, +    { "teal", rgb( 0, 128, 128) }, +    { "thistle", rgb(216, 191, 216) }, +    { "tomato", rgb(255, 99, 71) }, +    { "transparent", 0 }, +    { "turquoise", rgb( 64, 224, 208) }, +    { "violet", rgb(238, 130, 238) }, +    { "wheat", rgb(245, 222, 179) }, +    { "white", rgb(255, 255, 255) }, +    { "whitesmoke", rgb(245, 245, 245) }, +    { "yellow", rgb(255, 255, 0) }, +    { "yellowgreen", rgb(154, 205, 50) } +}; diff --git a/src/nef2dxf.cc b/src/nef2dxf.cc index 320d6b8..b22e605 100644 --- a/src/nef2dxf.cc +++ b/src/nef2dxf.cc @@ -32,6 +32,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 87b3c2b..3bc8d7b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -30,9 +30,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)  { @@ -46,129 +50,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() @@ -183,52 +97,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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const = 0;  }; +std::ostream &operator<<(std::ostream &stream, const AbstractNode &node); +  #endif diff --git a/src/nodecache.h b/src/nodecache.h new file mode 100644 index 0000000..cc3355e --- /dev/null +++ b/src/nodecache.h @@ -0,0 +1,46 @@ +#ifndef NODECACHE_H_ +#define NODECACHE_H_ + +#include <vector> +#include <string> +#include "node.h" + +/*! +	Caches string values per node based on the node.index(). +	The node index guaranteed to be unique per node tree since the index is reset +	every time a new tree is generated. +*/ +class NodeCache +{ +public: +  NodeCache() { } +  virtual ~NodeCache() { } + +	bool contains(const AbstractNode &node) const { + 		return !(*this)[node].empty(); +	} + +  const std::string & operator[](const AbstractNode &node) const { +    if (this->cache.size() > node.index()) return this->cache[node.index()]; +    else return this->nullvalue; +  } + +  void insert(const class AbstractNode &node, const std::string & value) { +    if (this->cache.size() <= node.index()) this->cache.resize(node.index() + 1); +    this->cache[node.index()] = value; +  } + +  void remove(const class AbstractNode &node) { +    if (this->cache.size() > node.index()) this->cache[node.index()] = std::string(); +  } + +	void clear() { +		this->cache.clear(); +	} + +private: +  std::vector<std::string> cache; +	std::string nullvalue; +}; + +#endif diff --git a/src/nodedumper.cc b/src/nodedumper.cc new file mode 100644 index 0000000..d75c703 --- /dev/null +++ b/src/nodedumper.cc @@ -0,0 +1,103 @@ +#include "nodedumper.h" +#include <string> +#include <map> +#include <list> +#include "visitor.h" +#include "state.h" +#include "nodecache.h" + +#include <sstream> +#include <iostream> +#include <assert.h> + +/*! +	\class NodeDumper + +	A visitor responsible for creating a text dump of a node tree.  Also +	contains a cache for fast retrieval of the text representation of +	any node or subtree. +*/ + +bool NodeDumper::isCached(const AbstractNode &node) const +{ +	return !this->cache[node].empty(); +} + +/*! +	Indent or deindent. Must be called before we output any children. +*/ +void NodeDumper::handleIndent(const State &state) +{ +	if (state.isPrefix()) { +		this->currindent += "\t"; +	} +	else if (state.isPostfix()) { +		this->currindent.erase((this->currindent.length() >= 1) ?  +													 this->currindent.length() - 1 : 0); +	} +} + +/*! +	Dumps the block of children contained in this->visitedchildren, +	including braces and indentation. +	All children are assumed to be cached already. + */ +string NodeDumper::dumpChildren(const AbstractNode &node) +{ +	std::stringstream dump; +	if (!this->visitedchildren[node.index()].empty()) { +		dump << " {\n"; +		 +		for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin(); +				 iter != this->visitedchildren[node.index()].end(); +				 iter++) { +			assert(isCached(**iter)); +			dump << this->cache[**iter] << "\n"; +		} +		 +		dump << this->currindent << "}"; +	} +	else { +		dump << ";"; +	} +	return dump.str(); +} + +/*! +	Called for each node in the tree. +	Will abort traversal if we're cached +*/ +Response NodeDumper::visit(State &state, const AbstractNode &node) +{ +	if (isCached(node)) return PruneTraversal; + +	handleIndent(state); +	if (state.isPostfix()) { +		std::stringstream dump; +		dump << this->currindent; +		if (this->idprefix) dump << "n" << node.index() << ":"; +		dump << node; +		dump << dumpChildren(node); +		this->cache.insert(node, dump.str()); +	} + +	handleVisitedChildren(state, node); +	return ContinueTraversal; +} + +/*! +	Adds this given node to its parent's child list. +	Should be called for all nodes, including leaf nodes. +*/ +void NodeDumper::handleVisitedChildren(const State &state, const AbstractNode &node) +{ +	if (state.isPostfix()) { +		this->visitedchildren.erase(node.index()); +		if (!state.parent()) { +			this->root = &node; +		} +		else { +			this->visitedchildren[state.parent()->index()].push_back(&node); +		} +	} +} diff --git a/src/nodedumper.h b/src/nodedumper.h new file mode 100644 index 0000000..efaf4fa --- /dev/null +++ b/src/nodedumper.h @@ -0,0 +1,40 @@ +#ifndef NODEDUMPER_H_ +#define NODEDUMPER_H_ + +#include <string> +#include <map> +#include <list> +#include "visitor.h" +#include "nodecache.h" + +using std::string; +using std::map; +using std::list; + +class NodeDumper : public Visitor +{ +public: +	/*! If idPrefix is true, we will output "n<id>:" in front of each node,  +		  which is useful for debugging. */ +	NodeDumper(NodeCache &cache, bool idPrefix = false) :  +		cache(cache), idprefix(idPrefix), root(NULL) { } +  virtual ~NodeDumper() {} + +  virtual Response visit(State &state, const AbstractNode &node); + +private: +  void handleVisitedChildren(const State &state, const AbstractNode &node); +  bool isCached(const AbstractNode &node) const; +  void handleIndent(const State &state); +	string dumpChildren(const AbstractNode &node); + +  NodeCache &cache; +	bool idprefix; + +  string currindent; +  const AbstractNode *root; +  typedef list<const AbstractNode *> ChildList; +  map<int, ChildList> visitedchildren; +}; + +#endif diff --git a/src/opencsgrenderer.cc b/src/opencsgrenderer.cc index 768176c..2c9c3d0 100644 --- a/src/opencsgrenderer.cc +++ b/src/opencsgrenderer.cc @@ -24,6 +24,7 @@   *   */ +#include <GL/glew.h>  #include "opencsgrenderer.h"  #include "polyset.h"  #include "csgterm.h" @@ -53,11 +54,6 @@ OpenCSGRenderer::OpenCSGRenderer(CSGChain *root_chain, CSGChain *highlights_chai  void OpenCSGRenderer::draw(bool showfaces, bool showedges) const  { -	static int glew_initialized = 0; -	if (!glew_initialized) { -		glew_initialized = 1; -		glewInit(); -	}  	if (this->root_chain) {  		GLint *shaderinfo = this->shaderinfo;  		if (!shaderinfo[0]) shaderinfo = NULL; @@ -76,18 +72,14 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,  {  	std::vector<OpenCSG::Primitive*> primitives;  	int j = 0; -	for (int i = 0;; i++) -	{ +	for (int i = 0;; i++) {  		bool last = i == chain->polysets.size(); - -		if (last || chain->types[i] == CSGTerm::TYPE_UNION) -		{ +		if (last || chain->types[i] == CSGTerm::TYPE_UNION) {  			if (j+1 != i) { -				OpenCSG::render(primitives); +				 OpenCSG::render(primitives);  				glDepthFunc(GL_EQUAL);  			} -			if (shaderinfo) -				glUseProgram(shaderinfo[0]); +			if (shaderinfo) glUseProgram(shaderinfo[0]);  			for (; j < i; j++) {  				double *m = chain->matrices[j];  				glPushMatrix(); @@ -112,8 +104,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,  				}  				glPopMatrix();  			} -			if (shaderinfo) -				glUseProgram(0); +			if (shaderinfo) glUseProgram(0);  			for (unsigned int k = 0; k < primitives.size(); k++) {  				delete primitives[k];  			} @@ -121,18 +112,15 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,  			primitives.clear();  		} -		if (last) -			break; +		if (last) break;  		OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ?  				OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity);  		prim->p = chain->polysets[i];  		prim->m = chain->matrices[i];  		prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; -		if (highlight) -			prim->csgmode += 20; -		else if (background) -			prim->csgmode += 10; +		if (highlight) prim->csgmode += 20; +		else if (background) prim->csgmode += 10;  		primitives.push_back(prim);  	}  } diff --git a/src/openscad.cc b/src/openscad.cc index bf22246..f3aee76 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -32,6 +32,10 @@  #include "value.h"  #include "export.h"  #include "builtin.h" +#include "nodedumper.h" +#include "CGALEvaluator.h" +#include "PolySetCGALEvaluator.h" +#include "printutils.h"  #include <string>  #include <vector> @@ -46,7 +50,9 @@  #include <QDir>  #include <QSet>  #include <QSettings> +#include <QTextStream>  #include <boost/program_options.hpp> +  #ifdef Q_WS_MAC  #include "EventFilter.h"  #include "AppleEvents.h" @@ -249,6 +255,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; +	CGALEvaluator cgalevaluator(cache, tree); +	PolySetCGALEvaluator psevaluator(cgalevaluator); +  	if (stl_output_file || off_output_file || dxf_output_file)  	{  		if (!filename) @@ -265,9 +280,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); @@ -299,8 +314,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 = cgalevaluator.evaluateCGALMesh(*tree.root());  		QDir::setCurrent(original_path.absolutePath()); @@ -318,17 +333,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 aad5ba0..2bd425f 100644 --- a/src/parser.y +++ b/src/parser.y @@ -334,7 +334,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 cccdaad..eda6304 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -26,7 +26,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> @@ -34,15 +35,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; @@ -145,7 +137,9 @@ void PolySet::render_surface(colormode_e colormode, csgmode_e csgmode, double *m  	bool mirrored = m3f.determinant() < 0;  	if (colormode == COLORMODE_MATERIAL) { -		const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); +// FIXME: Reenable/rewrite - don't be dependant on GUI +//		const QColor &col = Preferences::inst()->color(Preferences::OPENCSG_FACE_FRONT_COLOR); +		const QColor &col = QColor(0xf9, 0xd7, 0x2c);  		glColor3f(col.redF(), col.greenF(), col.blueF());  #ifdef ENABLE_OPENCSG  		if (shaderinfo) { @@ -155,7 +149,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) { @@ -322,357 +318,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 8712ff2..aae32f6 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -8,9 +8,6 @@  #ifdef ENABLE_OPENCSG  #  include <opencsg.h>  #endif -#ifdef ENABLE_CGAL -#  include "cgal.h" -#endif  #include <QCache> @@ -62,22 +59,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 5180c16..204bb11 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -33,6 +33,9 @@  #include "builtin.h"  #include "printutils.h"  #include <assert.h> +#include "visitor.h" +#include <sstream> +#include <assert.h>  #define F_MINIMUM 0.01 @@ -57,20 +60,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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;  };  AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const  { -	PrimitiveNode *node = new PrimitiveNode(inst, type); +	PrimitiveNode *node = new PrimitiveNode(inst, this->type);  	node->center = false;  	node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1; @@ -78,26 +113,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); @@ -233,25 +272,25 @@ static void generate_circle(point2d *circle, double r, int fragments)  	}  } -PolySet *PrimitiveNode::render_polyset(render_mode_e) const +PolySet *PrimitiveNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const  {  	PolySet *p = new PolySet(); -	if (type == CUBE && x > 0 && y > 0 && z > 0) +	if (this->type == CUBE && this->x > 0 && this->y > 0 && this->z > 0)  	{  		double x1, x2, y1, y2, z1, z2; -		if (center) { -			x1 = -x/2; -			x2 = +x/2; -			y1 = -y/2; -			y2 = +y/2; -			z1 = -z/2; -			z2 = +z/2; +		if (this->center) { +			x1 = -this->x/2; +			x2 = +this->x/2; +			y1 = -this->y/2; +			y2 = +this->y/2; +			z1 = -this->z/2; +			z2 = +this->z/2;  		} else {  			x1 = y1 = z1 = 0; -			x2 = x; -			y2 = y; -			z2 = z; +			x2 = this->x; +			y2 = this->y; +			z2 = this->z;  		}  		p->append_poly(); // top @@ -291,7 +330,7 @@ PolySet *PrimitiveNode::render_polyset(render_mode_e) const  		p->append_vertex(x1, y2, z2);  	} -	if (type == SPHERE && r1 > 0) +	if (this->type == SPHERE && this->r1 > 0)  	{  		struct ring_s {  			point2d *points; @@ -358,17 +397,18 @@ sphere_next_r2:  		delete[] ring;  	} -	if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0)) +	if (this->type == CYLINDER &&  +			this->h > 0 && this->r1 >=0 && this->r2 >= 0 && (this->r1 > 0 || this->r2 > 0))  	{ -		int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa); +		int fragments = get_fragments_from_r(fmax(this->r1, this->r2), this->fn, this->fs, this->fa);  		double z1, z2; -		if (center) { -			z1 = -h/2; -			z2 = +h/2; +		if (this->center) { +			z1 = -this->h/2; +			z2 = +this->h/2;  		} else {  			z1 = 0; -			z2 = h; +			z2 = this->h;  		}  		point2d *circle1 = new point2d[fragments]; @@ -401,13 +441,13 @@ sphere_next_r2:  			}  		} -		if (r1 > 0) { +		if (this->r1 > 0) {  			p->append_poly();  			for (int i=0; i<fragments; i++)  				p->insert_vertex(circle1[i].x, circle1[i].y, z1);  		} -		if (r2 > 0) { +		if (this->r2 > 0) {  			p->append_poly();  			for (int i=0; i<fragments; i++)  				p->append_vertex(circle2[i].x, circle2[i].y, z2); @@ -417,35 +457,35 @@ sphere_next_r2:  		delete[] circle2;  	} -	if (type == POLYHEDRON) +	if (this->type == POLYHEDRON)  	{ -		p->convexity = convexity; -		for (int i=0; i<triangles.vec.size(); i++) +		p->convexity = this->convexity; +		for (size_t i=0; i<this->triangles.vec.size(); i++)  		{  			p->append_poly(); -			for (int j=0; j<triangles.vec[i]->vec.size(); j++) { -				int pt = triangles.vec[i]->vec[j]->num; -				if (pt < points.vec.size()) { +			for (size_t j=0; j<this->triangles.vec[i]->vec.size(); j++) { +				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; @@ -456,37 +496,37 @@ sphere_next_r2:  		p->append_vertex(x1, y2);  	} -	if (type == CIRCLE) +	if (this->type == CIRCLE)  	{ -		int fragments = get_fragments_from_r(r1, fn, fs, fa); +		int fragments = get_fragments_from_r(this->r1, this->fn, this->fs, this->fa);  		p->is2d = true;  		p->append_poly();  		for (int i=0; i < fragments; i++) {  			double phi = (M_PI*2*i) / fragments; -			p->append_vertex(r1*cos(phi), r1*sin(phi)); +			p->append_vertex(this->r1*cos(phi), this->r1*sin(phi));  		}  	} -	if (type == POLYGON) +	if (this->type == POLYGON)  	{  		DxfData dd; -		for (int i=0; i<points.vec.size(); i++) { +		for (size_t i=0; i<this->points.vec.size(); i++) {  			double x,y; -			if (!points.vec[i]->getv2(x, y)) { +			if (!this->points.vec[i]->getv2(x, y)) {  				PRINTF("ERROR: Unable to convert point at index %d to a vec2 of numbers", i); -				// FIXME: Return NULL and make sure this is checked by all callers? -				return p; +				p->unlink(); +				return NULL;  			}  			dd.points.append(DxfData::Point(x, y));  		} -		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 (size_t i=0; i<this->points.vec.size(); i++) {  				assert(i < dd.points.size()); // FIXME: Not needed, but this used to be an 'if'  				DxfData::Point *p = &dd.points[i];  				dd.paths.last().points.append(p); @@ -498,11 +538,11 @@ sphere_next_r2:  		}  		else  		{ -			for (int i=0; i<paths.vec.size(); i++) +			for (size_t i=0; i<this->paths.vec.size(); i++)  			{  				dd.paths.append(DxfData::Path()); -				for (int j=0; j<paths.vec[i]->vec.size(); j++) { -					int idx = paths.vec[i]->vec[j]->num; +				for (size_t j=0; j<this->paths.vec[i]->vec.size(); j++) { +					int idx = this->paths.vec[i]->vec[j]->num;  					if (idx < dd.points.size()) {  						DxfData::Point *p = &dd.points[idx];  						dd.paths.last().points.append(p); @@ -526,26 +566,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..01fa06b 100644 --- a/src/printutils.cc +++ b/src/printutils.cc @@ -18,8 +18,7 @@ void print_messages_push()  void print_messages_pop()  { -	QString msg = print_messages_stack.last(); -	print_messages_stack.removeLast(); +	QString msg = print_messages_stack.takeLast();  	if (print_messages_stack.size() > 0 && !msg.isNull()) {  		if (!print_messages_stack.last().isEmpty())  			print_messages_stack.last() += "\n"; @@ -49,3 +48,8 @@ void PRINT_NOCACHE(const QString &msg)  		outputhandler(msg, outputhandler_data);  	}  } + +std::ostream &operator<<(std::ostream &os, const QFileInfo &fi) { +	os << std::hex << (fi.exists()?fi.lastModified().toTime_t():0) << "." << fi.size(); +	return os;  +} diff --git a/src/printutils.h b/src/printutils.h index 7f2e828..0432622 100644 --- a/src/printutils.h +++ b/src/printutils.h @@ -3,6 +3,9 @@  #include <QString>  #include <QList> +#include <iostream> +#include <QFileInfo> +#include <QDateTime>  typedef void (OutputHandlerFunc)(const QString &msg, void *userdata);  extern OutputHandlerFunc *outputhandler; @@ -22,4 +25,6 @@ void PRINT_NOCACHE(const QString &msg);  #define PRINTF_NOCACHE(_fmt, ...) do { QString _m; _m.sprintf(_fmt, ##__VA_ARGS__); PRINT_NOCACHE(_m); } while (0)  #define PRINTA_NOCACHE(_fmt, ...) do { QString _m = QString(_fmt).arg(__VA_ARGS__); PRINT_NOCACHE(_m); } while (0) +std::ostream &operator<<(std::ostream &os, const QFileInfo &fi); +  #endif diff --git a/src/projection.cc b/src/projection.cc index f41ba56..8497405 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -24,8 +24,8 @@   *   */ +#include "projectionnode.h"  #include "module.h" -#include "node.h"  #include "context.h"  #include "printutils.h"  #include "builtin.h" @@ -34,6 +34,8 @@  #include "polyset.h"  #include "export.h"  #include "progress.h" +#include "visitor.h" +#include "PolySetEvaluator.h"  #ifdef ENABLE_CGAL  #  include <CGAL/assertions_behaviour.h> @@ -41,6 +43,7 @@  #endif  #include <assert.h> +#include <sstream>  #include <QApplication>  #include <QTime> @@ -53,18 +56,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); @@ -92,207 +83,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::evaluate_polyset(render_mode_e mode, PolySetEvaluator *evaluator) const  { -	QString key = mk_cache_id(); -	if (PolySet::ps_cache.contains(key)) { -		PRINT(PolySet::ps_cache[key]->msg); -		return PolySet::ps_cache[key]->ps->link(); -	} - -	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 (!evaluator) { +		PRINTF("WARNING: No suitable PolySetEvaluator found for %s module!", this->name().c_str()); +		PolySet *ps = new PolySet(); +		ps->is2d = true;  		return ps;  	} -  CGAL::set_error_behaviour(old_behaviour); - -	if (cut_mode) -	{ -		PolySet *cube = new PolySet(); -		double infval = 1e8, eps = 0.1; -		double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps; - -		cube->append_poly(); // top -		cube->append_vertex(x1, y1, z2); -		cube->append_vertex(x2, y1, z2); -		cube->append_vertex(x2, y2, z2); -		cube->append_vertex(x1, y2, z2); - -		cube->append_poly(); // bottom -		cube->append_vertex(x1, y2, z1); -		cube->append_vertex(x2, y2, z1); -		cube->append_vertex(x2, y1, z1); -		cube->append_vertex(x1, y1, z1); - -		cube->append_poly(); // side1 -		cube->append_vertex(x1, y1, z1); -		cube->append_vertex(x2, y1, z1); -		cube->append_vertex(x2, y1, z2); -		cube->append_vertex(x1, y1, z2); - -		cube->append_poly(); // side2 -		cube->append_vertex(x2, y1, z1); -		cube->append_vertex(x2, y2, z1); -		cube->append_vertex(x2, y2, z2); -		cube->append_vertex(x2, y1, z2); - -		cube->append_poly(); // side3 -		cube->append_vertex(x2, y2, z1); -		cube->append_vertex(x1, y2, z1); -		cube->append_vertex(x1, y2, z2); -		cube->append_vertex(x2, y2, z2); - -		cube->append_poly(); // side4 -		cube->append_vertex(x1, y2, z1); -		cube->append_vertex(x1, y1, z1); -		cube->append_vertex(x1, y1, z2); -		cube->append_vertex(x1, y2, z2); -		CGAL_Nef_polyhedron Ncube = cube->render_cgal_nef_polyhedron(); -		cube->unlink(); - -		// N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED); -		N.p3 *= Ncube.p3; -		if (!N.p3.is_simple()) { -			PRINTF("WARNING: Body of projection(cut = true) isn't valid 2-manifold! Modify your design.."); -			goto cant_project_non_simple_polyhedron; -		} - -		PolySet *ps3 = new PolySet(); -		cgal_nef3_to_polyset(ps3, &N); -		Grid2d<int> conversion_grid(GRID_COARSE); -		for (int i = 0; i < ps3->polygons.size(); i++) { -			for (int j = 0; j < ps3->polygons[i].size(); j++) { -				double x = ps3->polygons[i][j].x; -				double y = ps3->polygons[i][j].y; -				double z = ps3->polygons[i][j].z; -				if (z != 0) -					goto next_ps3_polygon_cut_mode; -				if (conversion_grid.align(x, y) == i+1) -					goto next_ps3_polygon_cut_mode; -				conversion_grid.data(x, y) = i+1; -			} -			ps->append_poly(); -			for (int j = 0; j < ps3->polygons[i].size(); j++) { -				double x = ps3->polygons[i][j].x; -				double y = ps3->polygons[i][j].y; -				conversion_grid.align(x, y); -				ps->insert_vertex(x, y); -			} -		next_ps3_polygon_cut_mode:; -		} -		ps3->unlink(); -	} -	else -	{ -		if (!N.p3.is_simple()) { -			PRINTF("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design.."); -			goto cant_project_non_simple_polyhedron; -		} -		PolySet *ps3 = new PolySet(); -		cgal_nef3_to_polyset(ps3, &N); -		CGAL_Nef_polyhedron np; -		np.dim = 2; -		for (int i = 0; i < ps3->polygons.size(); i++) -		{ -			int min_x_p = -1; -			double min_x_val = 0; -			for (int j = 0; j < ps3->polygons[i].size(); j++) { -				double x = ps3->polygons[i][j].x; -				if (min_x_p < 0 || x < min_x_val) { -					min_x_p = j; -					min_x_val = x; -				} -			} -			int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size(); -			int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size(); -			double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x; -			double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y; -			double at = atan2(ay, ax); -			double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x; -			double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y; -			double bt = atan2(by, bx); - -			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 = evaluator->evaluatePolySet(*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..0c32181 --- /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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *evaluator) const; +}; + +#endif diff --git a/src/qhash.cc b/src/qhash.cc new file mode 100644 index 0000000..cec9adf --- /dev/null +++ b/src/qhash.cc @@ -0,0 +1,19 @@ +#include "myqhash.h" + +static uint hash(const uchar *p, int n) +{ +    uint h = 0; +    uint g; + +    while (n--) { +        h = (h << 4) + *p++; +        if ((g = (h & 0xf0000000)) != 0) +            h ^= g >> 23; +        h &= ~g; +    } +    return h; +} + +uint qHash(const std::string &str) { +	return hash(reinterpret_cast<const uchar *>(str.c_str()), str.length()); +} diff --git a/src/render-opencsg.cc.org b/src/render-opencsg.cc.org new file mode 100644 index 0000000..fe0fc60 --- /dev/null +++ b/src/render-opencsg.cc.org @@ -0,0 +1,87 @@ +#include "render-opencsg.h" +#include "polyset.h" +#include "csgterm.h" +#ifdef ENABLE_OPENCSG +#  include <opencsg.h> +#endif + +class OpenCSGPrim : public OpenCSG::Primitive +{ +public: +	OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) : +			OpenCSG::Primitive(operation, convexity) { } +	PolySet *p; +	double *m; +	int csgmode; +	virtual void render() { +		glPushMatrix(); +		glMultMatrixd(m); +		p->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m); +		glPopMatrix(); +	} +}; + +void renderCSGChainviaOpenCSG(CSGChain *chain, GLint *shaderinfo, bool highlight, bool background) +{ +	std::vector<OpenCSG::Primitive*> primitives; +	int j = 0; +	for (int i = 0;; i++) +	{ +		bool last = i == chain->polysets.size(); + +		if (last || chain->types[i] == CSGTerm::TYPE_UNION) +		{ +			if (j+1 != i) { +				OpenCSG::render(primitives); +				glDepthFunc(GL_EQUAL); +			} +			if (shaderinfo) +				glUseProgram(shaderinfo[0]); +			for (; j < i; j++) { +				double *m = chain->matrices[j]; +				glPushMatrix(); +				glMultMatrixd(m); +				int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; +				if (highlight) { +					chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo); +				} else if (background) { +					chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo); +				} else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) { +					// User-defined color from source +					glColor4d(m[16], m[17], m[18], m[19]); +					if (shaderinfo) { +						glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]); +						glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0); +					} +					chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo); +				} else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { +					chain->polysets[j]->render_surface(PolySet::COLORMODE_CUTOUT, PolySet::csgmode_e(csgmode), m, shaderinfo); +				} else { +					chain->polysets[j]->render_surface(PolySet::COLORMODE_MATERIAL, PolySet::csgmode_e(csgmode), m, shaderinfo); +				} +				glPopMatrix(); +			} +			if (shaderinfo) +				glUseProgram(0); +			for (unsigned int k = 0; k < primitives.size(); k++) { +				delete primitives[k]; +			} +			glDepthFunc(GL_LEQUAL); +			primitives.clear(); +		} + +		if (last) +			break; + +		OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? +				OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); +		prim->p = chain->polysets[i]; +		prim->m = chain->matrices[i]; +		prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; +		if (highlight) +			prim->csgmode += 20; +		else if (background) +			prim->csgmode += 10; +		primitives.push_back(prim); +	} +} diff --git a/src/render.cc b/src/render.cc index 9fa7ab6..cac03c3 100644 --- a/src/render.cc +++ b/src/render.cc @@ -24,8 +24,8 @@   *   */ +#include "rendernode.h"  #include "module.h" -#include "node.h"  #include "polyset.h"  #include "context.h"  #include "dxfdata.h" @@ -34,13 +34,11 @@  #include "builtin.h"  #include "printutils.h"  #include "progress.h" -#ifdef ENABLE_CGAL -#  include "cgal.h" -#endif +#include "visitor.h" -#include <QProgressDialog>  #include <QApplication>  #include <QTime> +#include <sstream>  class RenderModule : public AbstractModule  { @@ -49,18 +47,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); @@ -89,176 +75,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 92b661f..94d039e 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -32,8 +32,10 @@  #include "dxftess.h"  #include "printutils.h"  #include "openscad.h" // handle_dep() +#include "visitor.h"  #include <QFile> +#include <sstream>  class SurfaceModule : public AbstractModule  { @@ -45,12 +47,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 *evaluate_polyset(render_mode_e mode, class PolySetEvaluator *) const;  };  AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const @@ -65,7 +72,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) { @@ -85,14 +92,15 @@ void register_builtin_surface()  	builtin_modules["surface"] = new SurfaceModule();  } -PolySet *SurfaceNode::render_polyset(render_mode_e) const +PolySet *SurfaceNode::evaluate_polyset(render_mode_e, class PolySetEvaluator *) const  { +	PolySet *p = new PolySet();  	handle_dep(filename);  	QFile f(filename);  	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; @@ -119,7 +127,6 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const  		lines++;  	} -	PolySet *p = new PolySet();  	p->convexity = convexity;  	double ox = center ? -columns/2.0 : 0; @@ -198,14 +205,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 7b15a7e..b22b766 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -24,8 +24,8 @@   *   */ +#include "transformnode.h"  #include "module.h" -#include "node.h"  #include "context.h"  #include "dxfdata.h"  #include "csgterm.h" @@ -33,6 +33,10 @@  #include "dxftess.h"  #include "builtin.h"  #include "printutils.h" +#include "visitor.h" +#include <sstream> +#include <vector> +#include <assert.h>  enum transform_type_e {  	SCALE, @@ -51,63 +55,80 @@ public:  	virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;  }; -class TransformNode : public AbstractNode +using std::string; +using std::vector; + +static vector<string> split(const string &str, const string &delim)  { -public: -	double m[20]; -	TransformNode(const ModuleInstantiation *mi) : AbstractNode(mi) { } -#ifdef ENABLE_CGAL -	virtual CGAL_Nef_polyhedron render_cgal_nef_polyhedron() const; -#endif -	virtual CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const; -	virtual QString dump(QString indent) const; -}; +  assert(delim.size() > 0); + +	vector<string> strvec; +  size_t start = 0, end = 0; +  while (end != string::npos) { +    end = str.find(delim, start); +		// If at end, use length=maxLength.  Else use length=end-start. +    strvec.push_back(str.substr(start, (end == string::npos) ? string::npos : end - start)); +		// If at end, use start=maxSize.  Else use start=end+delimiter. +    start = ((end > (string::npos - delim.size())) ? string::npos : end + delim.size()); +  } +	return strvec; +} + +template <class T> static bool from_string(T &t, const string &s) +{ +  std::istringstream iss(s); +  return !(iss >> t).fail(); +}  AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const  {  	TransformNode *node = new TransformNode(inst);  	for (int i = 0; i < 16; i++) -		node->m[i] = i % 5 == 0 ? 1.0 : 0.0; +		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" << "alpha"; +		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) @@ -139,10 +160,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 @@ -164,21 +185,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; @@ -192,59 +213,62 @@ 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; +// FIXME: Port to non-Qt +#if 0  		} else if (v.type == Value::STRING) {  			QString colorname = v.text;  			QColor color;  			color.setNamedColor(colorname);  			if (color.isValid()) { -				node->m[16+0] = color.redF(); -				node->m[16+1] = color.greenF(); -				node->m[16+2] = color.blueF(); +				node->matrix[16+0] = color.redF(); +				node->matrix[16+1] = color.greenF(); +				node->matrix[16+2] = color.blueF();  			} else {  				PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data());  				PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");  			} +#endif  		}  		Value alpha = c.lookup_variable("alpha");  		if (alpha.type == Value::NUMBER) { -			node->m[16+3] = alpha.num; +			node->matrix[16+3] = alpha.num;  		} else { -			node->m[16+3] = 1.0; +			node->matrix[16+3] = 1.0;  		}  	} @@ -257,134 +281,34 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti  	return node;  } -#ifdef ENABLE_CGAL - -CGAL_Nef_polyhedron TransformNode::render_cgal_nef_polyhedron() const +string TransformNode::toString() const  { -	QString cache_id = mk_cache_id(); -	if (cgal_nef_cache.contains(cache_id)) { -		progress_report(); -		PRINT(cgal_nef_cache[cache_id]->msg); -		return cgal_nef_cache[cache_id]->N; -	} - -	print_messages_push(); - -	bool first = true; -	CGAL_Nef_polyhedron N; +	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; -	} -	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]; +		stream << "])";  	} -	for (int i = 16; i < 20; i++) -		x[i] = m[i] < 0 ? c[i] : m[i]; - -	CSGTerm *t1 = NULL; -	foreach(AbstractNode *v, children) -	{ -		CSGTerm *t2 = v->render_csg_term(x, highlights, background); -		if (t2 && !t1) { -			t1 = t2; -		} else if (t2 && t1) { -			t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2); -		} -	} -	if (t1 && modinst->tag_highlight && highlights) -		highlights->append(t1->link()); -	if (t1 && modinst->tag_background && background) { -		background->append(t1); -		return NULL; -	} -	return t1; +	return stream.str();  } -QString TransformNode::dump(QString indent) const +string TransformNode::name() const  { -	if (dump_cache.isEmpty()) { -		QString text; -		if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || 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() @@ -396,4 +320,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 b0a79a4..139bd1c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -26,6 +26,8 @@  #include "value.h"  #include "mathc99.h" +#include <assert.h> +#include <sstream>  Value::Value()  { @@ -34,8 +36,7 @@ Value::Value()  Value::~Value()  { -	for (int i = 0; i < this->vec.size(); i++) -		delete this->vec[i]; +	for (int i = 0; i < this->vec.size(); i++) delete this->vec[i];  	this->vec.clear();  } @@ -53,7 +54,7 @@ Value::Value(double v)  	this->num = v;  } -Value::Value(const QString &t) +Value::Value(const std::string &t)  {  	reset_undef();  	this->type = STRING; @@ -71,8 +72,9 @@ Value& Value::operator = (const Value &v)  	this->type = v.type;  	this->b = v.b;  	this->num = v.num; -	for (int i = 0; i < v.vec.size(); i++) -		this->vec.append(new Value(*v.vec[i])); +	for (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; @@ -110,7 +112,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) { @@ -125,7 +127,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) { @@ -140,14 +142,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) { @@ -162,14 +164,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) { @@ -255,7 +257,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) @@ -307,46 +309,77 @@ 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; +	stream.precision(16); + +	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  | 
