diff options
33 files changed, 521 insertions, 252 deletions
| diff --git a/doc/OpenSCAD-polygons.graffle b/doc/OpenSCAD-polygons.graffleBinary files differ index 40df7ab..758d575 100644 --- a/doc/OpenSCAD-polygons.graffle +++ b/doc/OpenSCAD-polygons.graffle diff --git a/doc/OpenSCAD-polygons.pdf b/doc/OpenSCAD-polygons.pdfBinary files differ index ac18baf..97dfd2e 100644 --- a/doc/OpenSCAD-polygons.pdf +++ b/doc/OpenSCAD-polygons.pdf diff --git a/doc/TODO.txt b/doc/TODO.txt index 4fac889..8f6d257 100644 --- a/doc/TODO.txt +++ b/doc/TODO.txt @@ -259,7 +259,6 @@ o variants of module transparent() { %child(); }  o define modules  o define functions  o built-in variables and constants (builtin-tests.scad) -o Write a regression test for the hexagonal cylinder orientation issue  o Caching    - Test that caching is actually performed (speedup + same results)    - Test the modifier characters correctly influence the cache (also when diff --git a/openscad.pro b/openscad.pro index 50a419d..0feca74 100644 --- a/openscad.pro +++ b/openscad.pro @@ -146,6 +146,7 @@ HEADERS += src/renderer.h \             src/builtin.h \             src/context.h \             src/csgterm.h \ +           src/csgtermnormalizer.h \             src/dxfdata.h \             src/dxfdim.h \             src/dxftess.h \ @@ -187,22 +188,17 @@ HEADERS += src/renderer.h \             src/system-gl.h \             src/stl-utils.h -SOURCES += src/openscad.cc \ -           src/mainwin.cc \ +SOURCES += src/mathc99.cc \ +	   src/linalg.cc \             src/handle_dep.cc \ -           src/renderer.cc \ -           src/rendersettings.cc \ -           src/ThrownTogetherRenderer.cc \ -           src/glview.cc \ -           src/export.cc \             src/value.cc \             src/expr.cc \             src/func.cc \             src/module.cc \             src/node.cc \ -           src/builtin.cc \             src/context.cc \             src/csgterm.cc \ +           src/csgtermnormalizer.cc \             src/polyset.cc \             src/csgops.cc \             src/transform.cc \ @@ -213,28 +209,38 @@ SOURCES += src/openscad.cc \             src/surface.cc \             src/control.cc \             src/render.cc \ -           src/import.cc \             src/dxfdata.cc \ -           src/dxftess.cc \ -           src/dxftess-glu.cc \ -           src/dxftess-cgal.cc \             src/dxfdim.cc \             src/linearextrude.cc \             src/rotateextrude.cc \ -           src/highlighter.cc \             src/printutils.cc \ +           src/progress.cc \ +           \ +           src/nodedumper.cc \ +           src/traverser.cc \ +           src/PolySetEvaluator.cc \ +           src/PolySetCache.cc \ +           src/Tree.cc \ +           \ +           src/rendersettings.cc \ +           src/highlighter.cc \             src/Preferences.cc \             src/OpenCSGWarningDialog.cc \ -           src/progress.cc \             src/editor.cc \ -           src/traverser.cc \ -           src/nodedumper.cc \ +           src/glview.cc \ +           \ +           src/builtin.cc \ +           src/export.cc \ +           src/import.cc \ +           src/renderer.cc \ +           src/ThrownTogetherRenderer.cc \ +           src/dxftess.cc \ +           src/dxftess-glu.cc \ +           src/dxftess-cgal.cc \             src/CSGTermEvaluator.cc \ -           src/Tree.cc \ -	   src/mathc99.cc \ -	   src/linalg.cc \ -           src/PolySetCache.cc \ -           src/PolySetEvaluator.cc +           \ +           src/openscad.cc \ +           src/mainwin.cc  opencsg {    HEADERS += src/OpenCSGRenderer.h diff --git a/src/CGALCache.cc b/src/CGALCache.cc index 6bdad41..84de722 100644 --- a/src/CGALCache.cc +++ b/src/CGALCache.cc @@ -7,7 +7,9 @@ CGALCache *CGALCache::inst = NULL;  void CGALCache::insert(const std::string &id, const CGAL_Nef_polyhedron &N)  {  	this->cache.insert(id, new CGAL_Nef_polyhedron(N), N.weight()); +#ifdef DEBUG  	PRINTF("CGAL Cache insert: %s (%d verts)", id.substr(0, 40).c_str(), N.weight()); +#endif  }  void CGALCache::print() diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 684ab42..a6b2f06 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -99,7 +99,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyToChildren(const AbstractNode &node, CGA  		const AbstractNode *chnode = item.first;  		const CGAL_Nef_polyhedron &chN = item.second;  		// FIXME: Don't use deep access to modinst members -		if (chnode->modinst->tag_background) continue; +		if (chnode->modinst->isBackground()) continue;      // NB! We insert into the cache here to ensure that all children of      // a node is a valid object. If we inserted as we created them, the  @@ -127,7 +127,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)  		const AbstractNode *chnode = item.first;  		const CGAL_Nef_polyhedron &chN = item.second;  		// FIXME: Don't use deep access to modinst members -		if (chnode->modinst->tag_background) continue; +		if (chnode->modinst->isBackground()) continue;  		if (dim == 0) {  			dim = chN.dim;  		} diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc index fc76d56..65209dd 100644 --- a/src/CSGTermEvaluator.cc +++ b/src/CSGTermEvaluator.cc @@ -56,10 +56,10 @@ void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluato  			}  		}  	} -	if (t1 && node.modinst->tag_highlight) { +	if (t1 && node.modinst->isHighlight()) {  		this->highlights.push_back(t1);  	} -	if (t1 && node.modinst->tag_background) { +	if (t1 && node.modinst->isBackground()) {  		this->background.push_back(t1);  		t1.reset(); // don't propagate background tagged nodes  	} @@ -94,10 +94,10 @@ static shared_ptr<CSGTerm> evaluate_csg_term_from_ps(const State &state,  	std::stringstream stream;  	stream << node.name() << node.index();  	shared_ptr<CSGTerm> t(new CSGTerm(ps, state.matrix(), state.color(), stream.str())); -	if (modinst->tag_highlight) { +	if (modinst->isHighlight()) {  		highlights.push_back(t);  	} -	if (modinst->tag_background) { +	if (modinst->isBackground()) {  		background.push_back(t);  		t.reset();  	} diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index 3285b46..3cd6005 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -26,7 +26,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)  	// Before projecting, union all children  	CGAL_Nef_polyhedron sum;  	BOOST_FOREACH (AbstractNode * v, node.getChildren()) { -		if (v->modinst->tag_background) continue; +		if (v->modinst->isBackground()) continue;  		CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);  		if (N.dim == 3) {  			if (sum.empty()) sum = N.copy(); @@ -259,7 +259,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const LinearExtrudeNode &node)  		// to a single DxfData, then tesselate this into a PolySet  		CGAL_Nef_polyhedron sum;  		BOOST_FOREACH (AbstractNode * v, node.getChildren()) { -			if (v->modinst->tag_background) continue; +			if (v->modinst->isBackground()) continue;  			CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);  			if (N.dim != 2) {  				PRINT("ERROR: linear_extrude() is not defined for 3D child objects!"); @@ -357,7 +357,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const RotateExtrudeNode &node)  		// to a single DxfData, then tesselate this into a PolySet  		CGAL_Nef_polyhedron sum;  		BOOST_FOREACH (AbstractNode * v, node.getChildren()) { -			if (v->modinst->tag_background) continue; +			if (v->modinst->isBackground()) continue;  			CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);  			if (N.dim != 2) {  				PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!"); diff --git a/src/PolySetEvaluator.cc b/src/PolySetEvaluator.cc index b5075ba..fc68fb2 100644 --- a/src/PolySetEvaluator.cc +++ b/src/PolySetEvaluator.cc @@ -7,8 +7,8 @@  /*!  	The task of PolySetEvaluator is to create, keep track of and cache PolySet instances. -	All instances of PolySet which are not strictly temporary should be requested through this -	class. +	All instances of PolySet which are not strictly temporary should be +	requested through this class.  */  /*! diff --git a/src/context.cc b/src/context.cc index 6d0cb3a..f636b05 100644 --- a/src/context.cc +++ b/src/context.cc @@ -45,6 +45,7 @@ Context::Context(const Context *parent, const Module *library)  	ctx_stack.push_back(this);  	if (parent) document_path = parent->document_path;  	if (library) { +		// FIXME: Don't access module members directly  		this->functions_p = &library->functions;  		this->modules_p = &library->modules;  		this->usedlibs_p = &library->usedlibs; @@ -148,24 +149,24 @@ Value Context::evaluate_function(const std::string &name,  AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const  { -	if (this->modules_p && this->modules_p->find(inst.modname) != this->modules_p->end()) { -		AbstractModule *m = this->modules_p->find(inst.modname)->second; -		std::string replacement = Builtins::instance()->isDeprecated(inst.modname); +	if (this->modules_p && this->modules_p->find(inst.name()) != this->modules_p->end()) { +		AbstractModule *m = this->modules_p->find(inst.name())->second; +		std::string replacement = Builtins::instance()->isDeprecated(inst.name());  		if (!replacement.empty()) { -			PRINTF("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.modname.c_str(), replacement.c_str()); +			PRINTF("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.name().c_str(), replacement.c_str());  		}  		return m->evaluate(this, &inst);  	}  	if (this->usedlibs_p) {  		BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) { -			if (m.second->modules.find(inst.modname) != m.second->modules.end()) { +			if (m.second->modules.find(inst.name()) != m.second->modules.end()) {  				Context ctx(this->parent, m.second); -				return m.second->modules[inst.modname]->evaluate(&ctx, &inst); +				return m.second->modules[inst.name()]->evaluate(&ctx, &inst);  			}  		}  	}  	if (this->parent) return this->parent->evaluate_module(inst); -	PRINTF("WARNING: Ignoring unknown module '%s'.", inst.modname.c_str()); +	PRINTF("WARNING: Ignoring unknown module '%s'.", inst.name().c_str());  	return NULL;  } diff --git a/src/csgterm.cc b/src/csgterm.cc index 56fcbb5..b368072 100644 --- a/src/csgterm.cc +++ b/src/csgterm.cc @@ -140,127 +140,6 @@ void CSGTerm::initBoundingBox()  	}  } -shared_ptr<CSGTerm> CSGTerm::normalize(shared_ptr<CSGTerm> term) -{ -	// This function implements the CSG normalization -  // Reference: -	// Goldfeather, J., Molnar, S., Turk, G., and Fuchs, H. Near -	// Realtime CSG Rendering Using Tree Normalization and Geometric -	// Pruning. IEEE Computer Graphics and Applications, 9(3):20-28, -	// 1989. -  // http://www.cc.gatech.edu/~turk/my_papers/pxpl_csg.pdf - -	if (term->type == TYPE_PRIMITIVE) { -		return term; -	} - -	do { -		while (term && normalize_tail(term)) { } -		if (!term || term->type == TYPE_PRIMITIVE) return term; -		term->left = normalize(term->left); -	} while (term->type != TYPE_UNION && -					 (term->right->type != TYPE_PRIMITIVE || term->left->type == TYPE_UNION)); -	term->right = normalize(term->right); - -	// FIXME: Do we need to take into account any transformation of item here? -	if (!term->right) { -		if (term->type == TYPE_UNION || term->type == TYPE_DIFFERENCE) return term->left; -		else return term->right; -	} -	if (!term->left) { -		if (term->type == TYPE_UNION) return term->right; -		else return term->left; -	} - -	return term; -} - -bool CSGTerm::normalize_tail(shared_ptr<CSGTerm> &term) -{ -	if (term->type == TYPE_UNION || term->type == TYPE_PRIMITIVE) return false; - -	// Part A: The 'x . (y . z)' expressions - -	shared_ptr<CSGTerm> x = term->left; -	shared_ptr<CSGTerm> y = term->right->left; -	shared_ptr<CSGTerm> z = term->right->right; - -	shared_ptr<CSGTerm> result = term; - -	// 1.  x - (y + z) -> (x - y) - z -	if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_UNION) { -		term = createCSGTerm(TYPE_DIFFERENCE,  -												 createCSGTerm(TYPE_DIFFERENCE, x, y), -												 z); -		return true; -	} -	// 2.  x * (y + z) -> (x * y) + (x * z) -	else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_UNION) { -		term = createCSGTerm(TYPE_UNION,  -												 createCSGTerm(TYPE_INTERSECTION, x, y),  -												 createCSGTerm(TYPE_INTERSECTION, x, z)); -		return true; -	} -	// 3.  x - (y * z) -> (x - y) + (x - z) -	else if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_INTERSECTION) { -		term = createCSGTerm(TYPE_UNION,  -												 createCSGTerm(TYPE_DIFFERENCE, x, y),  -												 createCSGTerm(TYPE_DIFFERENCE, x, z)); -		return true; -	} -	// 4.  x * (y * z) -> (x * y) * z -	else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_INTERSECTION) { -		term = createCSGTerm(TYPE_INTERSECTION,  -												 createCSGTerm(TYPE_INTERSECTION, x, y), -												 z); -		return true; -	} -	// 5.  x - (y - z) -> (x - y) + (x * z) -	else if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_DIFFERENCE) { -		term = createCSGTerm(TYPE_UNION,  -												 createCSGTerm(TYPE_DIFFERENCE, x, y),  -												 createCSGTerm(TYPE_INTERSECTION, x, z)); -		return true; -	} -	// 6.  x * (y - z) -> (x * y) - z -	else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_DIFFERENCE) { -		term = createCSGTerm(TYPE_DIFFERENCE,  -												 createCSGTerm(TYPE_INTERSECTION, x, y), -												 z); -		return true; -	} - -	// Part B: The '(x . y) . z' expressions - -	x = term->left->left; -	y = term->left->right; -	z = term->right; - -	// 7. (x - y) * z  -> (x * z) - y -	if (term->left->type == TYPE_DIFFERENCE && term->type == TYPE_INTERSECTION) { -		term = createCSGTerm(TYPE_DIFFERENCE,  -												 createCSGTerm(TYPE_INTERSECTION, x, z),  -												 y); -		return true; -	} -	// 8. (x + y) - z  -> (x - z) + (y - z) -	else if (term->left->type == TYPE_UNION && term->type == TYPE_DIFFERENCE) { -		term = createCSGTerm(TYPE_UNION,  -												 createCSGTerm(TYPE_DIFFERENCE, x, z),  -												 createCSGTerm(TYPE_DIFFERENCE, y, z)); -		return true; -	} -	// 9. (x + y) * z  -> (x * z) + (y * z) -	else if (term->left->type == TYPE_UNION && term->type == TYPE_INTERSECTION) { -		term = createCSGTerm(TYPE_UNION,  -												 createCSGTerm(TYPE_INTERSECTION, x, z),  -												 createCSGTerm(TYPE_INTERSECTION, y, z)); -		return true; -	} - -	return false; -} -  std::string CSGTerm::dump()  {  	std::stringstream dump; diff --git a/src/csgterm.h b/src/csgterm.h index 4930349..2e72dbc 100644 --- a/src/csgterm.h +++ b/src/csgterm.h @@ -33,9 +33,6 @@ public:  	const BoundingBox &getBoundingBox() const { return this->bbox; } -	static shared_ptr<CSGTerm> normalize(shared_ptr<CSGTerm> term); -	static bool normalize_tail(shared_ptr<CSGTerm> &term); -  	std::string dump();  private:  	CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right); diff --git a/src/csgtermnormalizer.cc b/src/csgtermnormalizer.cc new file mode 100644 index 0000000..a830422 --- /dev/null +++ b/src/csgtermnormalizer.cc @@ -0,0 +1,150 @@ +#include "csgtermnormalizer.h" +#include "csgterm.h" +#include "printutils.h" + +shared_ptr<CSGTerm> CSGTermNormalizer::normalize(const shared_ptr<CSGTerm> &root) +{ +	shared_ptr<CSGTerm> temp = root; +	while (1) { +		shared_ptr<CSGTerm> n = normalizePass(temp); +		if (temp == n) break; +		temp = n; + +		int num = count(temp); +#ifdef DEBUG +		PRINTF("Normalize count: %d\n", num); +#endif +		if (num > 5000) { +			PRINTF("WARNING: Normalized tree is growing past 5000 elements. Aborting normalization.\n"); +			return root; +		} +	} +	return temp; +} + +shared_ptr<CSGTerm> CSGTermNormalizer::normalizePass(shared_ptr<CSGTerm> term) +{ +	// This function implements the CSG normalization +  // Reference: +	// Goldfeather, J., Molnar, S., Turk, G., and Fuchs, H. Near +	// Realtime CSG Rendering Using Tree Normalization and Geometric +	// Pruning. IEEE Computer Graphics and Applications, 9(3):20-28, +	// 1989. +  // http://www.cc.gatech.edu/~turk/my_papers/pxpl_csg.pdf + +	if (term->type == CSGTerm::TYPE_PRIMITIVE) { +		return term; +	} + +	do { +		while (term && normalize_tail(term)) { } +		if (!term || term->type == CSGTerm::TYPE_PRIMITIVE) return term; +		term->left = normalizePass(term->left); +	} while (term->type != CSGTerm::TYPE_UNION && +					 (term->right->type != CSGTerm::TYPE_PRIMITIVE || term->left->type == CSGTerm::TYPE_UNION)); +	term->right = normalizePass(term->right); + +	// FIXME: Do we need to take into account any transformation of item here? +	if (!term->right) { +		if (term->type == CSGTerm::TYPE_UNION || term->type == CSGTerm::TYPE_DIFFERENCE) return term->left; +		else return term->right; +	} +	if (!term->left) { +		if (term->type == CSGTerm::TYPE_UNION) return term->right; +		else return term->left; +	} + +	return term; +} + +bool CSGTermNormalizer::normalize_tail(shared_ptr<CSGTerm> &term) +{ +	if (term->type == CSGTerm::TYPE_UNION || term->type == CSGTerm::TYPE_PRIMITIVE) return false; + +	// Part A: The 'x . (y . z)' expressions + +	shared_ptr<CSGTerm> x = term->left; +	shared_ptr<CSGTerm> y = term->right->left; +	shared_ptr<CSGTerm> z = term->right->right; + +	shared_ptr<CSGTerm> result = term; + +	// 1.  x - (y + z) -> (x - y) - z +	if (term->type == CSGTerm::TYPE_DIFFERENCE && term->right->type == CSGTerm::TYPE_UNION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, x, y), +												 z); +		return true; +	} +	// 2.  x * (y + z) -> (x * y) + (x * z) +	else if (term->type == CSGTerm::TYPE_INTERSECTION && term->right->type == CSGTerm::TYPE_UNION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, y),  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, z)); +		return true; +	} +	// 3.  x - (y * z) -> (x - y) + (x - z) +	else if (term->type == CSGTerm::TYPE_DIFFERENCE && term->right->type == CSGTerm::TYPE_INTERSECTION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, x, y),  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, x, z)); +		return true; +	} +	// 4.  x * (y * z) -> (x * y) * z +	else if (term->type == CSGTerm::TYPE_INTERSECTION && term->right->type == CSGTerm::TYPE_INTERSECTION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, y), +												 z); +		return true; +	} +	// 5.  x - (y - z) -> (x - y) + (x * z) +	else if (term->type == CSGTerm::TYPE_DIFFERENCE && term->right->type == CSGTerm::TYPE_DIFFERENCE) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, x, y),  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, z)); +		return true; +	} +	// 6.  x * (y - z) -> (x * y) - z +	else if (term->type == CSGTerm::TYPE_INTERSECTION && term->right->type == CSGTerm::TYPE_DIFFERENCE) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, y), +												 z); +		return true; +	} + +	// Part B: The '(x . y) . z' expressions + +	x = term->left->left; +	y = term->left->right; +	z = term->right; + +	// 7. (x - y) * z  -> (x * z) - y +	if (term->left->type == CSGTerm::TYPE_DIFFERENCE && term->type == CSGTerm::TYPE_INTERSECTION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, z),  +												 y); +		return true; +	} +	// 8. (x + y) - z  -> (x - z) + (y - z) +	else if (term->left->type == CSGTerm::TYPE_UNION && term->type == CSGTerm::TYPE_DIFFERENCE) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, x, z),  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, y, z)); +		return true; +	} +	// 9. (x + y) * z  -> (x * z) + (y * z) +	else if (term->left->type == CSGTerm::TYPE_UNION && term->type == CSGTerm::TYPE_INTERSECTION) { +		term = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION,  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, x, z),  +												 CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, y, z)); +		return true; +	} + +	return false; +} + +int CSGTermNormalizer::count(const shared_ptr<CSGTerm> &term) const +{ +	if (!term) return 0; +	return term->type == CSGTerm::TYPE_PRIMITIVE ? 1 : 0 + count(term->left) + count(term->right); +} diff --git a/src/csgtermnormalizer.h b/src/csgtermnormalizer.h new file mode 100644 index 0000000..df37441 --- /dev/null +++ b/src/csgtermnormalizer.h @@ -0,0 +1,22 @@ +#ifndef CSGTERMNORMALIZER_H_ +#define CSGTERMNORMALIZER_H_ + +#include "memory.h" + +class CSGTermNormalizer +{ +public: +	CSGTermNormalizer() : counter(0) {} +	~CSGTermNormalizer() {} + +	shared_ptr<class CSGTerm> normalize(const shared_ptr<CSGTerm> &term); + +private: +	shared_ptr<CSGTerm> normalizePass(shared_ptr<CSGTerm> term) ; +	bool normalize_tail(shared_ptr<CSGTerm> &term); +	int count(const shared_ptr<CSGTerm> &term) const; + +	int counter; +}; + +#endif diff --git a/src/dxfdata.cc b/src/dxfdata.cc index cf9248f..2bc21e2 100644 --- a/src/dxfdata.cc +++ b/src/dxfdata.cc @@ -42,6 +42,20 @@  #include <QDir>  #include "value.h" +/*! \class DxfData + +	The DxfData class fulfils multiple tasks, partially for historical reasons. +	FIXME: It's a bit messy and is a prime target for refactoring. + +	1) Read DXF file from disk +	2) Store contents of DXF files as points, paths and dims +	3) Store 2D polygons, both from the polygon() module and from 2D CSG operations. +	   Used for tesselation into triangles +  4) Store 2D polygons before exporting to DXF +	 + + */ +  struct Line {  	int idx[2]; // indices into DxfData::points  	bool disabled; diff --git a/src/export.cc b/src/export.cc index 99bce98..5ce2d15 100644 --- a/src/export.cc +++ b/src/export.cc @@ -102,6 +102,9 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output, QProgressDial  				double ny = dn[2]*dn[3] - dn[0]*dn[5];  				double nz = dn[0]*dn[4] - dn[1]*dn[3];  				double nlength = sqrt(nx*nx + ny*ny + nz*nz); +				// Avoid generating normals for polygons with zero area +				double eps = 0.000001; +				if (nlength < eps) nlength = 1.0;  				output << "  facet normal "  							 << nx / nlength << " "  							 << ny / nlength << " " diff --git a/src/mainwin.cc b/src/mainwin.cc index 3243847..1b90b60 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -45,6 +45,7 @@  #include "ProgressWidget.h"  #endif  #include "ThrownTogetherRenderer.h" +#include "csgtermnormalizer.h"  #include <QMenu>  #include <QTime> @@ -582,7 +583,7 @@ void MainWindow::load()  AbstractNode *MainWindow::find_root_tag(AbstractNode *n)  {  	BOOST_FOREACH (AbstractNode *v, n->children) { -		if (v->modinst->tag_root) return v; +		if (v->modinst->isRoot()) return v;  		if (AbstractNode *vroot = find_root_tag(v)) return vroot;  	}  	return NULL; @@ -782,15 +783,8 @@ void MainWindow::compileCSG(bool procevents)  		if (procevents)  			QApplication::processEvents(); -		this->root_norm_term = this->root_raw_term; -		 -		// CSG normalization -		while (1) { -			shared_ptr<CSGTerm> n = CSGTerm::normalize(this->root_norm_term); -			if (this->root_norm_term == n) break; -			this->root_norm_term = n; -		} -		 +		CSGTermNormalizer normalizer; +		this->root_norm_term = normalizer.normalize(this->root_raw_term);  		assert(this->root_norm_term);  		root_chain = new CSGChain(); @@ -804,11 +798,7 @@ void MainWindow::compileCSG(bool procevents)  			highlights_chain = new CSGChain();  			for (unsigned int i = 0; i < highlight_terms.size(); i++) { -				while (1) { -					shared_ptr<CSGTerm> n = CSGTerm::normalize(highlight_terms[i]); -					if (highlight_terms[i] == n) break; -					highlight_terms[i] = n; -				} +				highlight_terms[i] = normalizer.normalize(highlight_terms[i]);  				highlights_chain->import(highlight_terms[i]);  			}  		} @@ -821,11 +811,7 @@ void MainWindow::compileCSG(bool procevents)  			background_chain = new CSGChain();  			for (unsigned int i = 0; i < background_terms.size(); i++) { -				while (1) { -					shared_ptr<CSGTerm> n = CSGTerm::normalize(background_terms[i]); -					if (background_terms[i] == n) break; -					background_terms[i] = n; -				} +				background_terms[i] = normalizer.normalize(background_terms[i]);  				background_chain->import(background_terms[i]);  			}  		} @@ -1504,7 +1490,7 @@ void MainWindow::actionFlushCaches()  #endif  	dxf_dim_cache.clear();  	dxf_cross_cache.clear(); -	Module::libs_cache.clear(); +	Module::clear_library_cache();  }  void MainWindow::viewModeActionsUncheck() diff --git a/src/module.cc b/src/module.cc index 269e128..ba0112d 100644 --- a/src/module.cc +++ b/src/module.cc @@ -68,7 +68,6 @@ std::string ModuleInstantiation::dump(const std::string &indent) const  {  	std::stringstream dump;  	dump << indent; -	if (!label.empty()) dump << label <<": ";  	dump << modname + "(";  	for (size_t i=0; i < argnames.size(); i++) {  		if (i > 0) dump << ", "; @@ -96,10 +95,11 @@ AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const  	if (this->ctx) {  		PRINTF("WARNING: Ignoring recursive module instantiation of '%s'.", modname.c_str());  	} else { +		// FIXME: Casting away const..  		ModuleInstantiation *that = (ModuleInstantiation*)this;  		that->argvalues.clear(); -		BOOST_FOREACH (Expression *v, that->argexpr) { -			that->argvalues.push_back(v->evaluate(ctx)); +		BOOST_FOREACH (Expression *expr, that->argexpr) { +			that->argvalues.push_back(expr->evaluate(ctx));  		}  		that->ctx = ctx;  		node = ctx->evaluate_module(*this); @@ -113,9 +113,9 @@ std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *  {  	if (!ctx) ctx = this->ctx;  	std::vector<AbstractNode*> childnodes; -	BOOST_FOREACH (ModuleInstantiation *v, this->children) { -		AbstractNode *n = v->evaluate(ctx); -		if (n != NULL) childnodes.push_back(n); +	BOOST_FOREACH (ModuleInstantiation *modinst, this->children) { +		AbstractNode *node = modinst->evaluate(ctx); +		if (node) childnodes.push_back(node);  	}  	return childnodes;  } @@ -124,9 +124,9 @@ std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const  {  	if (!ctx) ctx = this->ctx;  	std::vector<AbstractNode*> childnodes; -	BOOST_FOREACH (ModuleInstantiation *v, this->else_children) { -		AbstractNode *n = v->evaluate(this->ctx); -		if (n != NULL) childnodes.push_back(n); +	BOOST_FOREACH (ModuleInstantiation *modinst, this->else_children) { +		AbstractNode *node = modinst->evaluate(ctx); +		if (node != NULL) childnodes.push_back(node);  	}  	return childnodes;  } @@ -200,3 +200,8 @@ std::string Module::dump(const std::string &indent, const std::string &name) con  	}  	return dump.str();  } + +void Module::clear_library_cache() +{ +	Module::libs_cache.clear(); +} diff --git a/src/module.h b/src/module.h index c28ab34..557b7c5 100644 --- a/src/module.h +++ b/src/module.h @@ -9,28 +9,38 @@  class ModuleInstantiation  {  public: -	std::string label; -	std::string modname; +	ModuleInstantiation(const std::string &name = "")  +	: ctx(NULL),  +		tag_root(false), tag_highlight(false), tag_background(false), modname(name) { } +	virtual ~ModuleInstantiation(); + +	std::string dump(const std::string &indent) const; +	class AbstractNode *evaluate(const class Context *ctx) const; +	std::vector<AbstractNode*> evaluateChildren(const Context *ctx = NULL) const; + +	const std::string &name() const { return this->modname; } +	bool isBackground() const { return this->tag_background; } +	bool isHighlight() const { return this->tag_highlight; } +	bool isRoot() const { return this->tag_root; } +  	std::vector<std::string> argnames; -	std::vector<class Expression*> argexpr;  	std::vector<Value> argvalues; +	std::vector<class Expression*> argexpr;  	std::vector<ModuleInstantiation*> children; +	const Context *ctx;  	bool tag_root;  	bool tag_highlight;  	bool tag_background; -	const class Context *ctx; - -	ModuleInstantiation() : tag_root(false), tag_highlight(false), tag_background(false), ctx(NULL) { } -	virtual ~ModuleInstantiation(); +protected: +	std::string modname; -	std::string dump(const std::string &indent) const; -	class AbstractNode *evaluate(const Context *ctx) const; -	std::vector<AbstractNode*> evaluateChildren(const Context *ctx = NULL) const; +	friend class Module;  };  class IfElseModuleInstantiation : public ModuleInstantiation {  public: +	IfElseModuleInstantiation() : ModuleInstantiation("if") { }  	virtual ~IfElseModuleInstantiation();  	std::vector<AbstractNode*> evaluateElseChildren(const Context *ctx = NULL) const; @@ -48,18 +58,18 @@ public:  class Module : public AbstractModule  {  public: -	typedef boost::unordered_map<std::string, class Module*> ModuleContainer; -	ModuleContainer usedlibs; +	Module() { } +	virtual ~Module(); +	virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +	virtual std::string dump(const std::string &indent, const std::string &name) const; + +	void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); } -	struct libs_cache_ent { -		Module *mod; -		std::string cache_id, msg; -	}; -	static boost::unordered_map<std::string, libs_cache_ent> libs_cache;  	static Module *compile_library(std::string filename); +	static void clear_library_cache(); -	std::vector<std::string> argnames; -	std::vector<Expression*> argexpr; +	typedef boost::unordered_map<std::string, class Module*> ModuleContainer; +	ModuleContainer usedlibs;  	std::vector<std::string> assignments_var;  	std::vector<Expression*> assignments_expr; @@ -71,11 +81,17 @@ public:  	std::vector<ModuleInstantiation*> children; -	Module() { } -	virtual ~Module(); +	std::vector<std::string> argnames; +	std::vector<Expression*> argexpr; -	virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; -	virtual std::string dump(const std::string &indent, const std::string &name) const; +protected: + +private: +	struct libs_cache_ent { +		Module *mod; +		std::string cache_id, msg; +	}; +	static boost::unordered_map<std::string, libs_cache_ent> libs_cache;  };  #endif diff --git a/src/node.cc b/src/node.cc index eccb9a6..e61174f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -97,7 +97,7 @@ void AbstractNode::progress_report() const  std::ostream &operator<<(std::ostream &stream, const AbstractNode &node)  {  	// FIXME: Don't use deep access to modinst members -	if (node.modinst->tag_background) stream << "%"; +	if (node.modinst->isBackground()) stream << "%";  	stream << node.toString();  	return stream;  } @@ -35,7 +35,9 @@ public:  	    overloaded to provide specialization for e.g. CSG nodes, primitive nodes etc.  	    Used for human-readable output. */  	virtual std::string name() const; -  /*! Should return a PolySet of the given geometry. Returns NULL if smth. goes wrong */ +  /*! Should return a PolySet of the given geometry. Returns NULL if smth. goes wrong. +	 This is only called by PolySetEvaluator, to make sure polysets are inserted into  +	 the cache*/  	virtual class PolySet *evaluate_polyset(class PolySetEvaluator *) const { return NULL; }  	const std::vector<AbstractNode*> &getChildren() const {  diff --git a/src/parser.y b/src/parser.y index d703a81..b0df50d 100644 --- a/src/parser.y +++ b/src/parser.y @@ -146,7 +146,7 @@ statement:  	'{' inner_input '}' |  	module_instantiation {  		if ($1) { -			module->children.push_back($1); +			module->addChild($1);  		} else {  			delete $1;  		} @@ -193,10 +193,8 @@ statement:  children_instantiation:  	module_instantiation {  		$$ = new ModuleInstantiation(); -		if ($1) { +		if ($1) {   			$$->children.push_back($1); -		} else { -			delete $1;  		}  	} |  	'{' module_instantiation_list '}' { @@ -206,7 +204,6 @@ children_instantiation:  if_statement:  	TOK_IF '(' expr ')' children_instantiation {  		$$ = new IfElseModuleInstantiation(); -		$$->modname = "if";  		$$->argnames.push_back("");  		$$->argexpr.push_back($3); @@ -262,8 +259,7 @@ module_instantiation_list:  	module_instantiation_list module_instantiation {  		$$ = $1;  		if ($$) { -			if ($2) -				$$->children.push_back($2); +			if ($2) $$->children.push_back($2);  		} else {  			delete $2;  		} @@ -271,19 +267,12 @@ module_instantiation_list:  single_module_instantiation:  	TOK_ID '(' arguments_call ')' { -		$$ = new ModuleInstantiation(); -		$$->modname = $1; +		$$ = new ModuleInstantiation($1);  		$$->argnames = $3->argnames;  		$$->argexpr = $3->argexpr;  		free($1);  		delete $3;  	} | -	TOK_ID ':' single_module_instantiation { -		$$ = $3; -		if ($$) -			$$->label = $1; -		free($1); -	} |  	'!' single_module_instantiation {  		$$ = $2;  		if ($$) diff --git a/src/polyset.cc b/src/polyset.cc index 481cbec..7e40eac 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -25,12 +25,21 @@   */  #include "polyset.h" -// FIXME: Reenable/rewrite - don't be dependant on GUI -// #include "Preferences.h"  #include "linalg.h"  #include <Eigen/LU>  #include <QColor> +/*! /class PolySet + +	The PolySet class fulfils multiple tasks, partially for historical reasons. +	FIXME: It's a bit messy and is a prime target for refactoring. + +	1) Store 2D and 3D polygon meshes from all origins +	2) Store 2D outlines, used for rendering edges +	3) Rendering of polygons and edges + + */ +  PolySet::PolySet() : grid(GRID_FINE), is2d(false), convexity(1)  {  } diff --git a/testdata/scad/features/cylinder-tests.scad b/testdata/scad/features/cylinder-tests.scad index 54e88cd..71f43a6 100644 --- a/testdata/scad/features/cylinder-tests.scad +++ b/testdata/scad/features/cylinder-tests.scad @@ -12,4 +12,9 @@ translate([22,-11,0]) cylinder(h=5, r=5, r1=0, center=true);  translate([22,0,0]) cylinder(h=5, r=5, r2=0);  translate([22,11,0]) cylinder(h=15, r=5, r2=5); +// This tests for hexagonal cylinder orientation, since people +// tend to "abuse" this for captured nut slots +translate([-10,0,0]) cylinder(h=2, r=3, $fn=6); + +  // FIXME: We could test $fs, $fa, $fn as well diff --git a/testdata/scad/misc/normal-nan.scad b/testdata/scad/misc/normal-nan.scad new file mode 100644 index 0000000..1c3f6ce --- /dev/null +++ b/testdata/scad/misc/normal-nan.scad @@ -0,0 +1,16 @@ +/* +  When exporting this to STL, null polygons appear, causing +  problems normalizing normal vectors (nan output in STL files) +*/ + +$fs=0.2; + +difference() {   +  cube(8); +  +  translate([0,20,4]) rotate([90,0,0]) union() { +    translate([0,-3,14.5]) cube([5.4,6,2.4],center=true); +    translate([0,0,13.3]) rotate([0,0,30]) cylinder(r=3.115,h=2.4,$fn=6); +  } +} + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e33e20b..21cee6b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -281,6 +281,7 @@ set(CORE_SOURCES    ../src/node.cc     ../src/context.cc     ../src/csgterm.cc  +  ../src/csgtermnormalizer.cc     ../src/polyset.cc     ../src/csgops.cc     ../src/transform.cc  @@ -389,6 +390,13 @@ set_target_properties(cgaltest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CX  target_link_libraries(cgaltest tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES})  # +# cgalstlsanitytest +# +add_executable(cgalstlsanitytest cgalstlsanitytest.cc) +set_target_properties(cgalstlsanitytest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") +target_link_libraries(cgalstlsanitytest tests-cgal ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES}) + +#  # cgalpngtest  #  add_executable(cgalpngtest cgalpngtest.cc bboxhelp.cc ../src/CGALRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) @@ -563,6 +571,8 @@ list(APPEND OPENCSGTEST_FILES ${CGALPNGTEST_FILES})  list(APPEND OPENCSGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/bbox-transform-bug.scad)  list(APPEND THROWNTOGETHERTEST_FILES ${OPENCSGTEST_FILES}) +list(APPEND CGALSTLSANITYTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/normal-nan.scad) +  # Disable tests which are known to cause floating point comparison issues  # Once we're capable of comparing these across platforms, we can put these back in  disable_tests(dumptest_transform-tests @@ -626,6 +636,9 @@ add_cmdline_test(csgtermtest txt ${MINIMAL_FILES})  add_cmdline_test(cgalpngtest png ${CGALPNGTEST_FILES})  add_cmdline_test(opencsgtest png ${OPENCSGTEST_FILES})  add_cmdline_test(throwntogethertest png ${THROWNTOGETHERTEST_FILES}) +# FIXME: We don't actually need to compare the output of cgalstlsanitytest +# with anything. It's self-contained and returns != 0 on error +add_cmdline_test(cgalstlsanitytest txt ${CGALSTLSANITYTEST_FILES})  message("Available test configurations: ${TEST_CONFIGS}")  #foreach(CONF ${TEST_CONFIGS}) diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc new file mode 100644 index 0000000..a078dff --- /dev/null +++ b/tests/cgalstlsanitytest.cc @@ -0,0 +1,163 @@ +/* + *  OpenSCAD (www.openscad.org) + *  Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and + *                          Marius Kintel <marius@kintel.net> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  As a special exception, you have permission to link this program + *  with the CGAL library and distribute executables, as long as you + *  follow the requirements of the GNU GPL in regard to all of the + *  software in the executable aside from CGAL. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include "tests-common.h" +#include "openscad.h" +#include "node.h" +#include "module.h" +#include "context.h" +#include "value.h" +#include "export.h" +#include "builtin.h" +#include "Tree.h" +#include "CGAL_Nef_polyhedron.h" +#include "CGALEvaluator.h" +#include "PolySetCGALEvaluator.h" + +#include <QApplication> +#include <QFile> +#include <QDir> +#include <QSet> +#include <QTextStream> +#ifndef _MSC_VER +#include <getopt.h> +#endif +#include <iostream> +#include <assert.h> +#include <sstream> + +std::string commandline_commands; +QString currentdir; +QString examplesdir; +QString librarydir; + +using std::string; + +void cgalTree(Tree &tree) +{ +	assert(tree.root()); + +	CGALEvaluator evaluator(tree); +	Traverser evaluate(evaluator, *tree.root(), Traverser::PRE_AND_POSTFIX); +	evaluate.execute(); +} + +AbstractNode *find_root_tag(AbstractNode *n) +{ +	foreach(AbstractNode *v, n->children) { +		if (v->modinst->tag_root) return v; +		if (AbstractNode *vroot = find_root_tag(v)) return vroot; +	} +	return NULL; +} + +int main(int argc, char **argv) +{ +	int retval = 0; + +	if (argc != 3) { +		fprintf(stderr, "Usage: %s <file.scad> <output.txt>\n", argv[0]); +		exit(1); +	} + +	const char *filename = argv[1]; +	const char *outfilename = argv[2]; + +	Builtins::instance()->initialize(); + +	QApplication app(argc, argv, false); +	QDir original_path = QDir::current(); + +	currentdir = QDir::currentPath(); + +	QDir libdir(QApplication::instance()->applicationDirPath()); +#ifdef Q_WS_MAC +	libdir.cd("../Resources"); // Libraries can be bundled +	if (!libdir.exists("libraries")) libdir.cd("../../.."); +#elif defined(Q_OS_UNIX) +	if (libdir.cd("../share/openscad/libraries")) { +		librarydir = libdir.path(); +	} else +	if (libdir.cd("../../share/openscad/libraries")) { +		librarydir = libdir.path(); +	} else +	if (libdir.cd("../../libraries")) { +		librarydir = libdir.path(); +	} else +#endif +	if (libdir.cd("libraries")) { +		librarydir = libdir.path(); +	} + +	Context root_ctx; +	register_builtin(root_ctx); + +	AbstractModule *root_module; +	ModuleInstantiation root_inst; + +	root_module = parsefile(filename); +	if (!root_module) { +		exit(1); +	} + +	QFileInfo fileInfo(filename); +	QDir::setCurrent(fileInfo.absolutePath()); + +	AbstractNode::resetIndexCounter(); +	AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); +	AbstractNode *root_node; +	// Do we have an explicit root node (! modifier)? +	if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; + +	Tree tree(root_node); + +	CGALEvaluator cgalevaluator(tree); + 	PolySetCGALEvaluator psevaluator(cgalevaluator); + +	CGAL_Nef_polyhedron N = cgalevaluator.evaluateCGALMesh(*root_node); + +	QDir::setCurrent(original_path.absolutePath()); +	if (!N.empty()) { +		std::ofstream outfile; +		outfile.open(outfilename); + +		std::stringstream out; +		export_stl(&N, out, NULL); +		if (out.str().find("nan") != string::npos) { +			outfile << "Error: nan found\n"; +			retval = 2; +		} +		if (out.str().find("inf") != string::npos) { +			outfile << "Error: inf found\n"; +			retval = 2; +		} +		outfile.close(); +	} + +	Builtins::instance(true); + +	return retval; +} diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc index c2be326..e8a6878 100644 --- a/tests/csgtestcore.cc +++ b/tests/csgtestcore.cc @@ -19,6 +19,7 @@  #include "ThrownTogetherRenderer.h"  #include "csgterm.h" +#include "csgtermnormalizer.h"  #include "OffscreenView.h"  #include <QApplication> @@ -315,12 +316,8 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type)  	}  	// CSG normalization -	csgInfo.root_norm_term = root_raw_term; -	while (1) { -		shared_ptr<CSGTerm> n = CSGTerm::normalize(csgInfo.root_norm_term); -		if (csgInfo.root_norm_term == n) break; -		csgInfo.root_norm_term = n; -	} +	CSGTermNormalizer normalizer; +	csgInfo.root_norm_term = normalizer.normalize(root_raw_term);  	assert(csgInfo.root_norm_term); @@ -333,11 +330,7 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type)  		csgInfo.highlights_chain = new CSGChain();  		for (unsigned int i = 0; i < csgInfo.highlight_terms.size(); i++) { -			while (1) { -				shared_ptr<CSGTerm> n = CSGTerm::normalize(csgInfo.highlight_terms[i]); -				if (csgInfo.highlight_terms[i] == n) break; -				csgInfo.highlight_terms[i] = n; -			} +			csgInfo.highlight_terms[i] = normalizer.normalize(csgInfo.highlight_terms[i]);  			csgInfo.highlights_chain->import(csgInfo.highlight_terms[i]);  		}  	} @@ -347,11 +340,7 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type)  		csgInfo.background_chain = new CSGChain();  		for (unsigned int i = 0; i < csgInfo.background_terms.size(); i++) { -			while (1) { -				shared_ptr<CSGTerm> n = CSGTerm::normalize(csgInfo.background_terms[i]); -				if (csgInfo.background_terms[i] == n) break; -				csgInfo.background_terms[i] = n; -			} +			csgInfo.background_terms[i] = normalizer.normalize(csgInfo.background_terms[i]);  			csgInfo.background_chain->import(csgInfo.background_terms[i]);  		}  	} diff --git a/tests/regression/cgalpngtest/cylinder-tests-expected.png b/tests/regression/cgalpngtest/cylinder-tests-expected.pngBinary files differ index 9d96df2..843d70f 100644 --- a/tests/regression/cgalpngtest/cylinder-tests-expected.png +++ b/tests/regression/cgalpngtest/cylinder-tests-expected.png diff --git a/tests/regression/cgalstlsanitytest/normal-nan-expected.txt b/tests/regression/cgalstlsanitytest/normal-nan-expected.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/regression/cgalstlsanitytest/normal-nan-expected.txt diff --git a/tests/regression/dumptest/cylinder-tests-expected.txt b/tests/regression/dumptest/cylinder-tests-expected.txt index 2ac2549..b1e8b6e 100644 --- a/tests/regression/dumptest/cylinder-tests-expected.txt +++ b/tests/regression/dumptest/cylinder-tests-expected.txt @@ -29,4 +29,7 @@  	multmatrix([[1, 0, 0, 22], [0, 1, 0, 11], [0, 0, 1, 0], [0, 0, 0, 1]]) {  		cylinder($fn = 0, $fa = 12, $fs = 2, h = 15, r1 = 5, r2 = 5, center = false);  	} +	multmatrix([[1, 0, 0, -10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		cylinder($fn = 6, $fa = 12, $fs = 2, h = 2, r1 = 3, r2 = 3, center = false); +	} diff --git a/tests/regression/opencsgtest/cylinder-tests-expected.png b/tests/regression/opencsgtest/cylinder-tests-expected.pngBinary files differ index 17c10b8..4c7ab79 100644 --- a/tests/regression/opencsgtest/cylinder-tests-expected.png +++ b/tests/regression/opencsgtest/cylinder-tests-expected.png diff --git a/tests/regression/throwntogethertest/cylinder-tests-expected.png b/tests/regression/throwntogethertest/cylinder-tests-expected.pngBinary files differ index 0a3ed33..4c7ab79 100644 --- a/tests/regression/throwntogethertest/cylinder-tests-expected.png +++ b/tests/regression/throwntogethertest/cylinder-tests-expected.png | 
