diff options
22 files changed, 841 insertions, 217 deletions
| diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 4deb3b3..406614f 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -179,6 +179,62 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)  	return N;  } +CGAL_Nef_polyhedron CGALEvaluator::applyResize(const CgaladvNode &node) +{ +	//	Based on resize() in Giles Bathgate's RapCAD (but not exactly) +	CGAL_Nef_polyhedron N; +  N = applyToChildren(node, CGE_UNION); + +	for (int i=0;i<3;i++) { +		if (node.newsize[i]<0) { +			PRINT("WARNING: Cannot resize to sizes less than 0."); +			return N; +		} +	} + +	CGAL_Iso_cuboid_3 bb; + +	if ( N.dim == 2 ) { +		CGAL_Iso_rectangle_2e bbox = bounding_box( *N.p2 ); +		CGAL_Point_2e min2(bbox.min()), max2(bbox.max()); +		CGAL_Point_3 min3(min2.x(),min2.y(),0), max3(max2.x(),max2.y(),0); +		bb = CGAL_Iso_cuboid_3( min3, max3 ); +	} +	else { +		bb = bounding_box( *N.p3 ); +	} + +	Eigen::Matrix<NT,3,1> scale, bbox_size; +	scale << 1,1,1; +	bbox_size << bb.xmax()-bb.xmin(), bb.ymax()-bb.ymin(), bb.zmax()-bb.zmin(); +	for (int i=0;i<3;i++) { +		if (node.newsize[i]) { +			if (bbox_size[i]==NT(0)) { +				PRINT("WARNING: Cannot resize in direction normal to flat object"); +				return N; +			} +			else { +				scale[i] = NT(node.newsize[i]) / bbox_size[i]; +			} +		} +	} +	NT autoscale = scale.maxCoeff(); +	for (int i=0;i<3;i++) { +		if (node.autosize[i]) scale[i] = autoscale; +	} + +	Eigen::Matrix4d t; +	t << CGAL::to_double(scale[0]),           0,        0,        0, +	     0,        CGAL::to_double(scale[1]),           0,        0, +	     0,        0,        CGAL::to_double(scale[2]),           0, +	     0,        0,        0,                                   1; + +	N.transform( Transform3d( t ) ); +	return N; +} + + +  /*  	Typical visitor behavior:  	o In prefix: Check if we're cached -> prune @@ -253,56 +309,8 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node)  				PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object.");  				N.reset();  			} - -			// Then apply transform -			// If there is no geometry under the transform, N will be empty -			// just silently ignore such nodes -			if (!N.isNull()) { -				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! -					 -					Eigen::Matrix2f testmat; -					testmat << node.matrix(0,0), node.matrix(0,1), node.matrix(1,0), node.matrix(1,1); -					if (testmat.determinant() == 0) { -						PRINT("Warning: Scaling a 2D object with 0 - removing object"); -						N.reset(); -					} -					else { -						CGAL_Aff_transformation2 t( -							node.matrix(0,0), node.matrix(0,1), node.matrix(0,3), -							node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3)); -						 -						DxfData *dd = N.convertToDxfData(); -						for (size_t i=0; i < dd->points.size(); i++) { -							CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]); -							p = t.transform(p); -							dd->points[i][0] = to_double(p.x()); -							dd->points[i][1] = to_double(p.y()); -						} -						 -						PolySet ps; -						ps.is2d = true; -						dxf_tesselate(&ps, *dd, 0, true, false, 0); -						 -						N = evaluateCGALMesh(ps); -						delete dd; -					} -				} -				else if (N.dim == 3) { -					if (node.matrix.matrix().determinant() == 0) { -						PRINT("Warning: Scaling a 3D object with 0 - removing object"); -						N.reset(); -					} -					else { -						CGAL_Aff_transformation t( -							node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3), -							node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3), -							node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3)); -						N.p3->transform(t); -					} -				} +			else { +				N.transform( node.matrix );  			}  		}  		else { @@ -358,6 +366,9 @@ Response CGALEvaluator::visit(State &state, const CgaladvNode &node)  			case HULL:  				N = applyHull(node);  				break; +			case RESIZE: +				N = applyResize(node); +				break;  			}  		}  		else { diff --git a/src/CGALEvaluator.h b/src/CGALEvaluator.h index 42af5a1..818f520 100644 --- a/src/CGALEvaluator.h +++ b/src/CGALEvaluator.h @@ -34,6 +34,7 @@ private:  	void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, CGALEvaluator::CsgOp op);  	CGAL_Nef_polyhedron applyToChildren(const AbstractNode &node, CGALEvaluator::CsgOp op);  	CGAL_Nef_polyhedron applyHull(const CgaladvNode &node); +	CGAL_Nef_polyhedron applyResize(const CgaladvNode &node);  	std::string currindent;    typedef std::pair<const AbstractNode *, CGAL_Nef_polyhedron> ChildItem; diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h index d949a2a..cfab993 100644 --- a/src/CGAL_Nef_polyhedron.h +++ b/src/CGAL_Nef_polyhedron.h @@ -4,6 +4,7 @@  #include "cgalfwd.h"  #include "memory.h"  #include <string> +#include "linalg.h"  class CGAL_Nef_polyhedron  { @@ -27,7 +28,7 @@ public:  	int weight() const;  	class PolySet *convertToPolyset();  	class DxfData *convertToDxfData() const; - +	void transform( const Transform3d &matrix );  	int dim;  	shared_ptr<CGAL_Nef_polyhedron2> p2;  	shared_ptr<CGAL_Nef_polyhedron3> p3; diff --git a/src/CGAL_Nef_polyhedron_DxfData.cc b/src/CGAL_Nef_polyhedron_DxfData.cc index 0d0b8f0..f7ff7f3 100644 --- a/src/CGAL_Nef_polyhedron_DxfData.cc +++ b/src/CGAL_Nef_polyhedron_DxfData.cc @@ -29,7 +29,11 @@  #include "CGAL_Nef_polyhedron.h"  #include "cgal.h"  #include "cgalutils.h" -#include "svg.h" +#include <boost/variant.hpp> +#include "polyset.h" +#include "dxftess.h" +#include "CGALEvaluator.h" +#include "Tree.h"  #ifdef ENABLE_CGAL @@ -89,4 +93,59 @@ std::string CGAL_Nef_polyhedron::dump() const  		return std::string("Nef Polyhedron with dimension != 2 or 3");  } + +void CGAL_Nef_polyhedron::transform( const Transform3d &matrix ) +{ +	if (!this->isNull()) { +		if (this->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! +			Eigen::Matrix2f testmat; +			testmat << matrix(0,0), matrix(0,1), matrix(1,0), matrix(1,1); +			if (testmat.determinant() == 0) { +				PRINT("Warning: Scaling a 2D object with 0 - removing object"); +				this->reset(); +				return; +			} +			else { +				CGAL_Aff_transformation2 t( +					matrix(0,0), matrix(0,1), matrix(0,3), +					matrix(1,0), matrix(1,1), matrix(1,3), matrix(3,3)); + +				DxfData *dd = this->convertToDxfData(); +				for (size_t i=0; i < dd->points.size(); i++) { +					CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]); +					p = t.transform(p); +					dd->points[i][0] = to_double(p.x()); +					dd->points[i][1] = to_double(p.y()); +				} + +				PolySet ps; +				ps.is2d = true; +				dxf_tesselate(&ps, *dd, 0, true, false, 0); + +				Tree nulltree; +				CGALEvaluator tmpeval(nulltree); +				CGAL_Nef_polyhedron N = tmpeval.evaluateCGALMesh(ps); +				this->p2.reset( new CGAL_Nef_polyhedron2( *N.p2 ) ); +				delete dd; +			} +		} +		else if (this->dim == 3) { +			if (matrix.matrix().determinant() == 0) { +				PRINT("Warning: Scaling a 3D object with 0 - removing object"); +				this->reset(); +			} +			else { +				CGAL_Aff_transformation t( +					matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3), +					matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3), +					matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3), matrix(3,3)); +				this->p3->transform(t); +			} +		} +	} +} +  #endif // ENABLE_CGAL diff --git a/src/CsgInfo.h b/src/CsgInfo.h index 37fe0d0..fe953b5 100644 --- a/src/CsgInfo.h +++ b/src/CsgInfo.h @@ -57,7 +57,7 @@ public:  		if (this->root_norm_term) {  			this->root_chain = new CSGChain();  			this->root_chain->import(this->root_norm_term); -			fprintf(stderr, "Normalized CSG tree has %d elements", int(this->root_chain->polysets.size())); +			PRINTB("Normalized CSG tree has %d elements", int(this->root_chain->polysets.size()));  		}  		else {  			this->root_chain = NULL; diff --git a/src/OffscreenView.cc b/src/OffscreenView.cc index 430d4ea..2186eb1 100644 --- a/src/OffscreenView.cc +++ b/src/OffscreenView.cc @@ -6,6 +6,7 @@  #include <string.h>  #include <cstdlib>  #include <sstream> +#include "printutils.h"  OffscreenView::OffscreenView(size_t width, size_t height)  { @@ -23,7 +24,7 @@ OffscreenView::~OffscreenView()  #ifdef ENABLE_OPENCSG  void OffscreenView::display_opencsg_warning()  { -  fprintf(stderr, "OpenSCAD recommended OpenGL version is 2.0. \n"); +  PRINT("OpenSCAD recommended OpenGL version is 2.0.");  }  #endif diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index 224e657..5976daf 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -20,104 +20,6 @@  #include <boost/foreach.hpp>  #include <vector> -/* - -ZRemover - -This class converts one or more already 'flat' Nef3 polyhedra into a Nef2 -polyhedron by stripping off the 'z' coordinates from the vertices. The -resulting Nef2 poly is accumulated in the 'output_nefpoly2d' member variable. - -The 'z' coordinates will either be all 0s, for an xy-plane intersected Nef3, -or, they will be a mixture of -eps and +eps, for a thin-box intersected Nef3. - -Notes on CGAL's Nef Polyhedron2: - -1. The 'mark' on a 2d Nef face is important when doing unions/intersections. - If the 'mark' of a face is wrong the resulting nef2 poly will be unexpected. -2. The 'mark' can be dependent on the points fed to the Nef2 constructor. - This is why we iterate through the 3d faces using the halfedge cycle - source()->target() instead of the ordinary source()->source(). The - the latter can generate sequences of points that will fail the - the CGAL::is_simple_2() test, resulting in improperly marked nef2 polys. -3. 3d facets have 'two sides'. we throw out the 'down' side to prevent dups. - -The class uses the 'visitor' pattern from the CGAL manual. See also -http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html -http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron3.html -OGL_helper.h -*/ - -class ZRemover { -public: -	logstream log; -	CGAL_Nef_polyhedron2::Boundary boundary; -	shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d; -	shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d; -	CGAL::Direction_3<CGAL_Kernel3> up; -	ZRemover() -	{ -		output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() ); -		boundary = CGAL_Nef_polyhedron2::INCLUDED; -		up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1); -		log = logstream(5); -	} -	void visit( CGAL_Nef_polyhedron3::Vertex_const_handle ) {} -	void visit( CGAL_Nef_polyhedron3::Halfedge_const_handle ) {} -	void visit( CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {} -	void visit( CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {} -	void visit( CGAL_Nef_polyhedron3::SFace_const_handle ) {} -	void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) { -		log << " <!-- Halffacet visit. Mark: " << hfacet->mark() << " -->\n"; -		if ( hfacet->plane().orthogonal_direction() != this->up ) { -			log << "  <!-- down-facing half-facet. skipping -->\n"; -			log << " <!-- Halffacet visit end-->\n"; -			return; -		} - -		// possible optimization - throw out facets that are 'side facets' between -		// the top & bottom of the big thin box. (i.e. mixture of z=-eps and z=eps) - -		CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci; -		int contour_counter = 0; -		CGAL_forall_facet_cycles_of( fci, hfacet ) { -			if ( fci.is_shalfedge() ) { -				CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1); -				std::vector<CGAL_Nef_polyhedron2::Explorer::Point> contour; -				CGAL_For_all( c1, cend ) { -					CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); -					CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() ); -					contour.push_back( point2d ); -				} - -				if (contour.size()==0) continue; - -				log << " <!-- is_simple_2:" << CGAL::is_simple_2( contour.begin(), contour.end() ) << " --> \n"; - -				tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) ); - -				if ( contour_counter == 0 ) { -					log << " <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ; -					*(output_nefpoly2d) += *(tmpnef2d); -				} else { -					log << " <!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n"; -					*(output_nefpoly2d) *= *(tmpnef2d); -				} - -				log << "\n<!-- ======== output tmp nef: ==== -->\n" -					<< OpenSCAD::dump_svg( *tmpnef2d ) << "\n" -					<< "\n<!-- ======== output accumulator: ==== -->\n" -					<< OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n"; - -				contour_counter++; -			} else { -				log << " <!-- trivial facet cycle skipped -->\n"; -			} -		} // next facet cycle (i.e. next contour) -		log << " <!-- Halffacet visit end -->\n"; -	} // visit() -}; -  PolySetCGALEvaluator::PolySetCGALEvaluator(CGALEvaluator &cgalevaluator)  	: PolySetEvaluator(cgalevaluator.getTree()), cgalevaluator(cgalevaluator)  { @@ -186,24 +88,23 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)  			return NULL;  		} -		// remove z coordinates to make CGAL_Nef_polyhedron2  		log << OpenSCAD::svg_header( 480, 100000 ) << "\n";  		try { -			ZRemover zremover; -			CGAL_Nef_polyhedron3::Volume_const_iterator i; -			CGAL_Nef_polyhedron3::Shell_entry_const_iterator j; -			CGAL_Nef_polyhedron3::SFace_const_handle sface_handle; -			for ( i = sum.p3->volumes_begin(); i != sum.p3->volumes_end(); ++i ) { -				log << "<!-- volume. mark: " << i->mark() << " -->\n"; -				for ( j = i->shells_begin(); j != i->shells_end(); ++j ) { -					log << "<!-- shell. mark: " << i->mark() << " -->\n"; -					sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j ); -					sum.p3->visit_shell_objects( sface_handle , zremover ); -					log << "<!-- shell. end. -->\n"; -				} -				log << "<!-- volume end. -->\n"; -			} -			nef_poly.p2 = zremover.output_nefpoly2d; +      ZRemover zremover; +      CGAL_Nef_polyhedron3::Volume_const_iterator i; +      CGAL_Nef_polyhedron3::Shell_entry_const_iterator j; +      CGAL_Nef_polyhedron3::SFace_const_handle sface_handle; +      for ( i = sum.p3->volumes_begin(); i != sum.p3->volumes_end(); ++i ) { +        log << "<!-- volume. mark: " << i->mark() << " -->\n"; +        for ( j = i->shells_begin(); j != i->shells_end(); ++j ) { +          log << "<!-- shell. mark: " << i->mark() << " -->\n"; +          sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j ); +          sum.p3->visit_shell_objects( sface_handle , zremover ); +          log << "<!-- shell. end. -->\n"; +        } +        log << "<!-- volume end. -->\n"; +      } +      nef_poly.p2 = zremover.output_nefpoly2d;  		}	catch (const CGAL::Failure_exception &e) {  			PRINTB("CGAL error in projection node while flattening: %s", e.what());  		} diff --git a/src/cgaladv.cc b/src/cgaladv.cc index 1773a90..a4cb5ec 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -58,6 +58,9 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat  	if (type == SUBDIV)  		argnames += "type", "level", "convexity"; +	if (type == RESIZE) +		argnames += "newsize", "auto"; +  	Context c(ctx);  	c.args(argnames, argexpr, inst->argnames, inst->argvalues); @@ -78,6 +81,28 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat  		level = c.lookup_variable("level", true);  	} +	if (type == RESIZE) { +		Value ns = c.lookup_variable("newsize"); +		node->newsize << 0,0,0; +		if ( ns.type() == Value::VECTOR ) { +			Value::VectorType vs = ns.toVector(); +			if ( vs.size() >= 1 ) node->newsize[0] = vs[0].toDouble(); +			if ( vs.size() >= 2 ) node->newsize[1] = vs[1].toDouble(); +			if ( vs.size() >= 3 ) node->newsize[2] = vs[2].toDouble(); +		} +		Value autosize = c.lookup_variable("auto"); +		node->autosize << false, false, false; +		if ( autosize.type() == Value::VECTOR ) { +			Value::VectorType va = autosize.toVector(); +			if ( va.size() >= 1 ) node->autosize[0] = va[0].toBool(); +			if ( va.size() >= 2 ) node->autosize[1] = va[1].toBool(); +			if ( va.size() >= 3 ) node->autosize[2] = va[2].toBool(); +		} +		else if ( autosize.type() == Value::BOOL ) { +			node->autosize << true, true, true; +		} +	} +  	node->convexity = (int)convexity.toDouble();  	node->path = path;  	node->subdiv_type = subdiv_type.toString(); @@ -112,6 +137,9 @@ std::string CgaladvNode::name() const  	case HULL:  		return "hull";  		break; +	case RESIZE: +		return "resize"; +		break;  	default:  		assert(false);  	} @@ -135,6 +163,13 @@ std::string CgaladvNode::toString() const  	case HULL:  		stream << "()";  		break; +	case RESIZE: +		stream << "(newsize = [" +		  << this->newsize[0] << "," << this->newsize[1] << "," << this->newsize[2] << "]" +		  << ", auto = [" +		  << this->autosize[0] << "," << this->autosize[1] << "," << this->autosize[2] << "]" +		  << ")"; +		break;  	default:  		assert(false);  	} @@ -148,4 +183,5 @@ void register_builtin_cgaladv()  	Builtins::init("glide", new CgaladvModule(GLIDE));  	Builtins::init("subdiv", new CgaladvModule(SUBDIV));  	Builtins::init("hull", new CgaladvModule(HULL)); +	Builtins::init("resize", new CgaladvModule(RESIZE));  } diff --git a/src/cgaladvnode.h b/src/cgaladvnode.h index 8e769bf..d3aa525 100644 --- a/src/cgaladvnode.h +++ b/src/cgaladvnode.h @@ -4,12 +4,14 @@  #include "node.h"  #include "visitor.h"  #include "value.h" +#include "linalg.h"  enum cgaladv_type_e {  	MINKOWSKI,  	GLIDE,  	SUBDIV, -	HULL +	HULL, +	RESIZE  };  class CgaladvNode : public AbstractNode @@ -29,6 +31,8 @@ public:  	Value path;  	std::string subdiv_type;  	int convexity, level; +	Vector3d newsize; +	Eigen::Matrix<bool,3,1> autosize;  	cgaladv_type_e type;  }; diff --git a/src/cgalutils.cc b/src/cgalutils.cc index 51838df..8b4c476 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -147,7 +147,7 @@ CGAL_Polyhedron *createPolyhedronFromPolySet(const PolySet &ps)  CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N )  { -	CGAL_Iso_cuboid_3 result(-1,-1,-1,1,1,1); +	CGAL_Iso_cuboid_3 result(0,0,0,0,0,0);  	CGAL_Nef_polyhedron3::Vertex_const_iterator vi;  	std::vector<CGAL_Nef_polyhedron3::Point_3> points;  	// can be optimized by rewriting bounding_box to accept vertices @@ -160,7 +160,7 @@ CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N )  CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N )  { -	CGAL_Iso_rectangle_2e result(-1,-1,1,1); +	CGAL_Iso_rectangle_2e result(0,0,0,0);  	CGAL_Nef_polyhedron2::Explorer explorer = N.explorer();  	CGAL_Nef_polyhedron2::Explorer::Vertex_const_iterator vi;  	std::vector<CGAL_Point_2e> points; @@ -173,5 +173,56 @@ CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N )  	return result;  } +void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) +{ +	log << " <!-- ZRemover Halffacet visit. Mark: " << hfacet->mark() << " -->\n"; +	if ( hfacet->plane().orthogonal_direction() != this->up ) { +		log << "  <!-- ZRemover down-facing half-facet. skipping -->\n"; +		log << " <!-- ZRemover Halffacet visit end-->\n"; +		return; +	} + +	// possible optimization - throw out facets that are vertically oriented + +	CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci; +	int contour_counter = 0; +	CGAL_forall_facet_cycles_of( fci, hfacet ) { +		if ( fci.is_shalfedge() ) { +			log << " <!-- ZRemover Halffacet cycle begin -->\n"; +			CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1); +			std::vector<CGAL_Nef_polyhedron2::Explorer::Point> contour; +			CGAL_For_all( c1, cend ) { +				CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); +				CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() ); +				contour.push_back( point2d ); +			} +			if (contour.size()==0) continue; + +			log << " <!-- is_simple_2:" << CGAL::is_simple_2( contour.begin(), contour.end() ) << " --> \n"; + +			tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) ); + +			if ( contour_counter == 0 ) { +				log << " <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ; +				*(output_nefpoly2d) += *(tmpnef2d); +			} else { +				log << " <!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n"; +				*(output_nefpoly2d) *= *(tmpnef2d); +			} + +			/*log << "\n<!-- ======== output tmp nef: ==== -->\n" +				<< OpenSCAD::dump_svg( *tmpnef2d ) << "\n" +				<< "\n<!-- ======== output accumulator: ==== -->\n" +				<< OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n";*/ + +			contour_counter++; +		} else { +			log << " <!-- ZRemover trivial facet cycle skipped -->\n"; +		} +		log << " <!-- ZRemover Halffacet cycle end -->\n"; +	} +	log << " <!-- ZRemover Halffacet visit end -->\n"; +} +  #endif /* ENABLE_CGAL */ diff --git a/src/cgalutils.h b/src/cgalutils.h index 9093c3f..6ea7711 100644 --- a/src/cgalutils.h +++ b/src/cgalutils.h @@ -2,10 +2,63 @@  #define CGALUTILS_H_  #include <cgalfwd.h> -  class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p);  CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps);  CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N );  CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N ); +#include "svg.h" +#include "printutils.h" + +/* + +ZRemover + +This class converts one or more Nef3 polyhedra into a Nef2 polyhedron by +stripping off the 'z' coordinates from the vertices. The resulting Nef2 +poly is accumulated in the 'output_nefpoly2d' member variable. + +The 'z' coordinates will either be all 0s, for an xy-plane intersected Nef3, +or, they will be a mixture of -eps and +eps, for a thin-box intersected Nef3. + +Notes on CGAL's Nef Polyhedron2: + +1. The 'mark' on a 2d Nef face is important when doing unions/intersections. + If the 'mark' of a face is wrong the resulting nef2 poly will be unexpected. +2. The 'mark' can be dependent on the points fed to the Nef2 constructor. + This is why we iterate through the 3d faces using the halfedge cycle + source()->target() instead of the ordinary source()->source(). The + the latter can generate sequences of points that will fail the + the CGAL::is_simple_2() test, resulting in improperly marked nef2 polys. +3. 3d facets have 'two sides'. we throw out the 'down' side to prevent dups. + +The class uses the 'visitor' pattern from the CGAL manual. See also +http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html +http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron3.html +OGL_helper.h +*/ + +class ZRemover { +public: +	logstream log; +	CGAL_Nef_polyhedron2::Boundary boundary; +	boost::shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d; +	boost::shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d; +	CGAL::Direction_3<CGAL_Kernel3> up; +	ZRemover() +	{ +		output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() ); +		boundary = CGAL_Nef_polyhedron2::INCLUDED; +		up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1); +		log = logstream(5); +	} +	void visit( CGAL_Nef_polyhedron3::Vertex_const_handle ) {} +	void visit( CGAL_Nef_polyhedron3::Halfedge_const_handle ) {} +	void visit( CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {} +	void visit( CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {} +	void visit( CGAL_Nef_polyhedron3::SFace_const_handle ) {} +	void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ); +}; + +  #endif @@ -2,13 +2,13 @@  #include "cgalutils.h"  #include "svg.h"  #include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp>  #include <map>  namespace OpenSCAD {  // SVG code  // currently for debugging, not necessarily pretty or useful for users. (yet) -int svg_cursor_py = 0;  int svg_px_width = SVG_PXW;  int svg_px_height = SVG_PXH; @@ -27,6 +27,26 @@ std::string svg_label(std::string s)  	return out.str();  } +std::string svg_styleblock(std::string strokewidth) +{ +	std::stringstream out; +	// halfedge: f1/f0 = face mark, b1/b0 = body or hole, m1/m0 = halfedge mark +	out << "\ + <style type='text/css'>\n\ +  .halfedge_f0_b1_m0 { stroke: gold; stroke-width: __STROKEW__px } \n\ +  .halfedge_f0_b1_m1 { stroke: gold; stroke-width: __STROKEW__px } \n\ +  .halfedge_f0_b0_m0 { stroke: green; stroke-width: __STROKEW__px } \n\ +  .halfedge_f0_b0_m1 { stroke: green; stroke-width: __STROKEW__px } \n\ +  .halfedge_f1_b1_m0 { stroke: gold; stroke-width: __STROKEW__px } \n\ +  .halfedge_f1_b1_m1 { stroke: gold; stroke-width: __STROKEW__px } \n\ +  .halfedge_f1_b0_m0 { stroke: green; stroke-width: __STROKEW__px } \n\ +  .halfedge_f1_b0_m1 { stroke: green; stroke-width: __STROKEW__px } \n\ + </style>"; +	std::string tmp = out.str(); +	boost::replace_all( tmp, "__STROKEW__", strokewidth ); +	return tmp; +} +  std::string svg_border()  {  	std::stringstream out; @@ -93,36 +113,27 @@ std::string dump_cgal_nef_polyhedron2_face_svg(  	CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c1,  	CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c2,  	CGAL_Nef_polyhedron2::Explorer explorer, -	std::string color, -	bool mark, -	CGAL_Iso_rectangle_2e bbox ) +	bool facemark, bool body )  { +	std::stringstream style; +	style << "halfedge_f" << facemark << "_b" << body << "_m"; +	std::string styleclass = style.str(); +    std::stringstream out;  	CGAL_For_all(c1, c2) {  		if ( explorer.is_standard( explorer.target(c1) ) ) {  			CGAL_Point_2e source = explorer.point( explorer.source( c1 ) );  			CGAL_Point_2e target = explorer.point( explorer.target( c1 ) ); -			CGAL_Point_2e tp1 = project_svg_2to2( source, bbox ); -			CGAL_Point_2e tp2 = project_svg_2to2( target, bbox ); -			double mod=0; -			if (color=="green") mod=10; -			out << "      <!-- Halfedge. Mark: " << c1->mark() << " -->\n"; -      out << "       <line" -			  << " x1='" << CGAL::to_double(tp1.x()) + mod << "'" -			  << " y1='" << CGAL::to_double(tp1.y()) - mod << "'" -			  << " x2='" << CGAL::to_double(tp2.x()) + mod << "'" -			  << " y2='" << CGAL::to_double(tp2.y()) - mod << "'" -			  << " stroke='" << color << "'"; -			if (!mark) out << " stroke-dasharray='4 4' />\n"; -			else out << " />\n"; -			// crude "arrowhead" to indicate directionality -			out << "       <circle" -			  << " cx='" << CGAL::to_double(tp1.x()+ (tp2.x()-tp1.x())* 7/8) + mod << "'" -			  << " cy='" << CGAL::to_double(tp1.y()+ (tp2.y()-tp1.y())* 7/8) - mod << "'" -			  << " r='2'" -			  << " fill='" << color << "' stroke='" << color << "' />\n"; +			out << "    <!-- Halfedge. Mark: " << c1->mark() << " -->\n"; +			std::string he_mark = boost::lexical_cast<std::string>(c1->mark()); +      out << "     <line" +			  << " x1='" << CGAL::to_double(source.x()) << "'" +			  << " y1='" << CGAL::to_double(source.y()) << "'" +			  << " x2='" << CGAL::to_double(target.x()) << "'" +			  << " y2='" << CGAL::to_double(target.y()) << "'" +			  << " class='" << styleclass + he_mark << "' />\n";  		} else { -			out << "       <!-- 2d Nef Rays - not implemented -->\n"; +			out << "     <!-- 2d Nef Rays - not implemented -->\n";  		}  	}  	return out.str(); @@ -132,27 +143,27 @@ std::string dump_svg( const CGAL_Nef_polyhedron2 &N )  {    std::stringstream out;    CGAL_Nef_polyhedron2::Explorer explorer = N.explorer(); -  	CGAL_Iso_rectangle_2e bbox = bounding_box( N ); -    CGAL_Nef_polyhedron2::Explorer::Face_const_iterator i; -  out << " <svg y='" << svg_cursor_py << "' width='" << svg_px_width -		<< "' height='" << svg_px_height -		<< "' xmlns='http://www.w3.org/2000/svg' version='1.1'>\n"; -	out << svg_border() << "\n" << svg_axes() << "\n"; -	svg_cursor_py += svg_px_height; + +	std::string linewidth = "0.05"; + +	out << "<!--CGAL_Nef_polyhedron2 dump begin-->\n"; +	out << svg_header() << "\n" << svg_styleblock( linewidth ) << "\n";  	for ( i = explorer.faces_begin(); i!= explorer.faces_end(); ++i ) {  			out << "  <!-- face begin. mark: " << i->mark() << "  -->\n"; +			out << "   <!-- body begin -->\n";  			CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c1  				= explorer.face_cycle( i ), c2 ( c1 ); -			out << dump_cgal_nef_polyhedron2_face_svg( c1, c2, explorer, "red", i->mark(), bbox ); +			out << dump_cgal_nef_polyhedron2_face_svg( c1, c2, explorer, i->mark(), true ); +			out << "   <!-- body end -->\n";  		  CGAL_Nef_polyhedron2::Explorer::Hole_const_iterator j;  			for ( j = explorer.holes_begin( i ); j!= explorer.holes_end( i ); ++j ) {  				out << "   <!-- hole begin. mark: " << j->mark() << " -->\n";  				CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c3( j ), c4 ( c3 ); -				out << dump_cgal_nef_polyhedron2_face_svg( c3, c4, explorer, "green", j->mark(), bbox ); +				out << dump_cgal_nef_polyhedron2_face_svg( c3, c4, explorer, "green", j->mark() );  				out << "   <!-- hole end -->\n";  			}  			out << "  <!-- face end -->\n"; @@ -182,13 +193,13 @@ public:  	void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )  	{  		int contour_count = 0; -		out << "  <!-- Halffacet. Mark: " << (*hfacet).mark() << " -->\n"; +		out << "  <!-- Halffacet visit. Mark: " << (*hfacet).mark() << " -->\n";  		std::string color = "gold";  		if (!(*hfacet).mark()) color = "green";  		CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;  		CGAL_forall_facet_cycles_of( i, hfacet ) {  			CGAL_Nef_polyhedron3::SHalfloop_const_handle shl_handle; -			out << "   <!-- Halffacet cycle: -->\n"; +			out << "   <!-- Halffacet cycle begin: -->\n";  			if ( contour_count == 0 ) {  				out << "    <!-- Body contour:--> \n";  			} else { @@ -196,13 +207,15 @@ public:  			}  			CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);  			CGAL_For_all( c1, c2 ) { -				out << "     <line";  				// don't know why we use source()->source(), except thats what CGAL does internally  				CGAL_Point_3 source = c1->source()->source()->point();  				CGAL_Point_3 target = c1->source()->target()->point();  				CGAL_Point_2e tp1 = project_svg_3to2 ( source, bbox );  				CGAL_Point_2e tp2 = project_svg_3to2 ( target, bbox ); -				out << " " +        out << "     <!-- " << CGAL::to_double(source.x()) << "," +          << CGAL::to_double(source.y()) << "," +          << CGAL::to_double(source.z()) << " -->\n"; +				out << "     <line "  					<< "x1='" << CGAL::to_double(tp1.x()) << "' "  					<< "y1='" << CGAL::to_double(tp1.y()) << "' "  					<< "x2='" << CGAL::to_double(tp2.x()) << "' " @@ -212,31 +225,34 @@ public:  				else out << " />\n";  			}  			contour_count++; -		} // next facet cycle (i.e. next contour) -	} // visit() - +			out << "   <!-- Halffacet cycle end -->\n"; +		} +		out << "  <!-- Halffacet visit end -->\n"; +	}  };  std::string dump_svg( const CGAL_Nef_polyhedron3 &N )  {    std::stringstream out; -	out << svg_header() << "\n" << svg_border() << "\n" << svg_axes() << "\n"; +	std::string linewidth = "0.05";  	out << "<!--CGAL_Nef_polyhedron3 dump begin-->\n"; +	out << svg_header() << "\n" << svg_border() << "\n"; +	out << svg_styleblock( linewidth ) << "\n" << svg_axes() << "\n";    CGAL_Nef_polyhedron3::Volume_const_iterator c;    CGAL_forall_volumes(c,N) { -    out << " <!--Processing volume...-->\n"; +    out << " <!--Volume begin-->\n";      out << "  <!--Mark: " << (*c).mark() << "-->\n";      CGAL_Nef_polyhedron3::Shell_entry_const_iterator it;      CGAL_forall_shells_of(it,c) { -      out << "  <!--Processing shell...-->\n"; +      out << "  <!--Shell begin-->\n";        NefPoly3_dumper_svg dumper_svg(N);        N.visit_shell_objects(CGAL_Nef_polyhedron3::SFace_const_handle(it), dumper_svg );  			out << dumper_svg.out.str(); -      out << "  <!--Processing shell end-->\n"; +      out << "  <!--Shell end-->\n";      } -    out << " <!--Processing volume end-->\n"; +    out << " <!--Volume end-->\n";    }    out << "<!--CGAL_Nef_polyhedron3 dump end-->\n";  	out << "</svg>"; diff --git a/testdata/scad/features/resize-2d-tests.scad b/testdata/scad/features/resize-2d-tests.scad new file mode 100644 index 0000000..76da09e --- /dev/null +++ b/testdata/scad/features/resize-2d-tests.scad @@ -0,0 +1,45 @@ +// red = reference + +$fn=10; + +// two simple holes +module shape(){ +	difference() { +		square([5,5]); +		translate([1,1]) square(); +		translate([3,3]) circle(); +	} +} + +// holes that have problems (duplicate vertex) +module shape2(){ +	difference() { +		square([5,5]); +		translate([1,1]) square(); +		translate([2,2]) square(); +	} +} + +// one square split into two by another +module shape3(){ +	difference() { +		square([5,5]); +		translate([0,2.5]) square([5,1]); +	} +} + +translate([0,0]) resize([15,15]) shape(); +translate([0,16]) resize([15,15,0]) shape2(); +translate([0,32]) resize([15,15]) shape3(); + +color("red") { +translate([-16,0]) scale([3,3]) shape(); +translate([-16,16]) scale([3,3]) shape2(); +translate([-16,32]) scale([3,3]) shape3(); +} + +color("green"){ +translate([16,0]) resize([15,0],auto=true) shape(); +translate([16,16]) resize([0,15],auto=true) shape2(); +translate([16,32]) resize([0,0,15],auto=true) shape3(); +} diff --git a/testdata/scad/features/resize-tests.scad b/testdata/scad/features/resize-tests.scad new file mode 100644 index 0000000..5853980 --- /dev/null +++ b/testdata/scad/features/resize-tests.scad @@ -0,0 +1,74 @@ +// bottom row (red) = reference +// middle row (gold) = should match reference +// top row (blue) = should be inscribed in middle row in 'top' view +// back row (green) = should be all cubes auto-scaled up +// back top (purple) = uses 'auto' feature +// pink = recursive resize + +$fn=8; + +color("red") { +translate([0, 0,-10]) cube();  +translate([0,10,-10]) cube([5,1,1]); +translate([0,20,-10]) cube([1,6,1]); +translate([0,30,-10]) cube([1,1,7]); +translate([0,40,-10]) cube([5,6,1]);  +translate([0,60,-10]) cube([1,6,7]); +translate([0,50,-10]) cube([5,1,7]); +translate([0,70,-10]) cube([8,9,1]); +translate([0,80,-10]) cube([9,1,1]); +translate([0,90,-10]) cube([5,6,1]); +} + +translate([0, 0,0]) cube();  +translate([0,10,0]) resize([5,0,0]) cube();  +translate([0,20,0]) resize([0,6,0]) cube();  +translate([0,30,0]) resize([0,0,7]) cube();  +translate([0,40,0]) resize([5,6,0]) cube();  +translate([0,60,0]) resize([0,6,7]) cube();  +translate([0,50,0]) resize([5,0,7]) cube();  +translate([0,70,0]) resize([8,9]) cube();  +translate([0,80,0]) resize([9]) cube();  +translate([0,90,0]) resize([5,6,7]) cube();  + +color("blue"){ +translate([0, 0,10]) cube();  +translate([2.5,10.5,10]) resize([5,0,0]) sphere(0.5);  +translate([0.5,23,10]) resize([0,6,0]) sphere(0.5);  +translate([0.5,30.5,10]) resize([0,0,7]) sphere(0.5);  +translate([2.5,43,10]) resize([5,6,0]) sphere(0.5);  +translate([2.5,50.5,10]) resize([5,0,7]) sphere(0.5);  +translate([0.5,63,10]) resize([0,6,7]) sphere(0.5);  +translate([4,74.5,10]) resize([8,9]) sphere(0.5);  +translate([4.5,80.5,10]) resize([9]) sphere(0.5);  +translate([2.5,93,10]) resize([5,6,7]) sphere(0.5);  +} + +color("green"){ +translate([10, 0, 0]) cube();  +translate([10,10,0]) resize([5,0,0],auto=true) cube();  +translate([10,20,0]) resize([0,6,0],auto=true) cube();  +translate([10,30,0]) resize([0,0,7],auto=true) cube();  +translate([10,40,0]) resize([5,6,0],true) cube();  +translate([10,50,0]) resize([5,0,7],true) cube();  +translate([10,60,0]) resize([0,6,7],auto=true) cube();  +translate([10,70,0]) resize([8,9],auto=true) cube();  +translate([10,80,0]) resize([9],true) cube();  +translate([10,90,0]) resize([5,6,7],auto=true) cube();  +} + +color("purple"){ +translate([10, 0, 10]) cube();  +translate([10,10,10]) resize([5,0,0],auto=[true,true,false]) cube();  +translate([10,20,10]) resize([6,0,0],auto=[true,true,true]) cube();  +translate([13.5,33.5,10]) resize([7,0,0],auto=[true,false,false]) sphere();  +translate([10,40,10]) resize([6,0,0],auto=[true,false,true]) cube();  +translate([10,50,10]) resize([7,0,7],auto=[false,true,true]) cube();  +translate([13.5,63.5,10]) resize([7,0,0],auto=[false,true,false]) sphere(); translate([10,70,10]) resize([8,0,0],auto=[false,false,false]) cube();  +translate([10,80,10]) resize([9,0,0],auto=[false,false,true]) cube();  +translate([10,90,10]) resize([-5,0,0]) cube();  +} + +color("pink"){ +translate([10,0,-10]) resize([4,4,4]) resize([5000,100,1000]) cube(); +}
\ No newline at end of file diff --git a/tests/regression/cgalpngtest/resize-2d-tests-expected.png b/tests/regression/cgalpngtest/resize-2d-tests-expected.pngBinary files differ new file mode 100644 index 0000000..1b0cc8a --- /dev/null +++ b/tests/regression/cgalpngtest/resize-2d-tests-expected.png diff --git a/tests/regression/cgalpngtest/resize-tests-expected.png b/tests/regression/cgalpngtest/resize-tests-expected.pngBinary files differ new file mode 100644 index 0000000..0c82744 --- /dev/null +++ b/tests/regression/cgalpngtest/resize-tests-expected.png diff --git a/tests/regression/dumptest/resize-2d-tests-expected.txt b/tests/regression/dumptest/resize-2d-tests-expected.txt new file mode 100644 index 0000000..9d5d3ae --- /dev/null +++ b/tests/regression/dumptest/resize-2d-tests-expected.txt @@ -0,0 +1,131 @@ +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [15,15,0], auto = [0,0,0]) { +			group() { +				difference() { +					square(size = [5, 5], center = false); +					multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +						square(size = [1, 1], center = false); +					} +					multmatrix([[1, 0, 0, 3], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { +						circle($fn = 10, $fa = 12, $fs = 2, r = 1); +					} +				} +			} +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 16], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [15,15,0], auto = [0,0,0]) { +			group() { +				difference() { +					square(size = [5, 5], center = false); +					multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +						square(size = [1, 1], center = false); +					} +					multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) { +						square(size = [1, 1], center = false); +					} +				} +			} +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 32], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [15,15,0], auto = [0,0,0]) { +			group() { +				difference() { +					square(size = [5, 5], center = false); +					multmatrix([[1, 0, 0, 0], [0, 1, 0, 2.5], [0, 0, 1, 0], [0, 0, 0, 1]]) { +						square(size = [5, 1], center = false); +					} +				} +			} +		} +	} +	color([1, 0, 0, 1]) { +		multmatrix([[1, 0, 0, -16], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			multmatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +						multmatrix([[1, 0, 0, 3], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							circle($fn = 10, $fa = 12, $fs = 2, r = 1); +						} +					} +				} +			} +		} +		multmatrix([[1, 0, 0, -16], [0, 1, 0, 16], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			multmatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +						multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +					} +				} +			} +		} +		multmatrix([[1, 0, 0, -16], [0, 1, 0, 32], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			multmatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 0], [0, 1, 0, 2.5], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [5, 1], center = false); +						} +					} +				} +			} +		} +	} +	color([0, 0.501961, 0, 1]) { +		multmatrix([[1, 0, 0, 16], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [15,0,0], auto = [1,1,1]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +						multmatrix([[1, 0, 0, 3], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							circle($fn = 10, $fa = 12, $fs = 2, r = 1); +						} +					} +				} +			} +		} +		multmatrix([[1, 0, 0, 16], [0, 1, 0, 16], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [0,15,0], auto = [1,1,1]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +						multmatrix([[1, 0, 0, 2], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [1, 1], center = false); +						} +					} +				} +			} +		} +		multmatrix([[1, 0, 0, 16], [0, 1, 0, 32], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [0,0,15], auto = [1,1,1]) { +				group() { +					difference() { +						square(size = [5, 5], center = false); +						multmatrix([[1, 0, 0, 0], [0, 1, 0, 2.5], [0, 0, 1, 0], [0, 0, 0, 1]]) { +							square(size = [5, 1], center = false); +						} +					} +				} +			} +		} +	} + diff --git a/tests/regression/dumptest/resize-tests-expected.txt b/tests/regression/dumptest/resize-tests-expected.txt new file mode 100644 index 0000000..5fbbe4a --- /dev/null +++ b/tests/regression/dumptest/resize-tests-expected.txt @@ -0,0 +1,240 @@ +	color([1, 0, 0, 1]) { +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [1, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 10], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [5, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 20], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [1, 6, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 30], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [1, 1, 7], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 40], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [5, 6, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 60], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [1, 6, 7], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [5, 1, 7], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 70], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [8, 9, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 80], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [9, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 90], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			cube(size = [5, 6, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		cube(size = [1, 1, 1], center = false); +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [5,0,0], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [0,6,0], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 30], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [0,0,7], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [5,6,0], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [0,6,7], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [5,0,7], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 70], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [8,9,0], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 80], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [9,0,0], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	multmatrix([[1, 0, 0, 0], [0, 1, 0, 90], [0, 0, 1, 0], [0, 0, 0, 1]]) { +		resize(newsize = [5,6,7], auto = [0,0,0]) { +			cube(size = [1, 1, 1], center = false); +		} +	} +	color([0, 0, 1, 1]) { +		multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			cube(size = [1, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 2.5], [0, 1, 0, 10.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [5,0,0], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 0.5], [0, 1, 0, 23], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [0,6,0], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 0.5], [0, 1, 0, 30.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [0,0,7], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 2.5], [0, 1, 0, 43], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [5,6,0], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 2.5], [0, 1, 0, 50.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [5,0,7], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 0.5], [0, 1, 0, 63], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [0,6,7], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 4], [0, 1, 0, 74.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [8,9,0], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 4.5], [0, 1, 0, 80.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [9,0,0], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +		multmatrix([[1, 0, 0, 2.5], [0, 1, 0, 93], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [5,6,7], auto = [0,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 0.5); +			} +		} +	} +	color([0, 0.501961, 0, 1]) { +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			cube(size = [1, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 10], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [5,0,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [0,6,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 30], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [0,0,7], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 40], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [5,6,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 50], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [5,0,7], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [0,6,7], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 70], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [8,9,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 80], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [9,0,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 90], [0, 0, 1, 0], [0, 0, 0, 1]]) { +			resize(newsize = [5,6,7], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +	} +	color([0.501961, 0, 0.501961, 1]) { +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			cube(size = [1, 1, 1], center = false); +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 10], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [5,0,0], auto = [1,1,0]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 20], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [6,0,0], auto = [1,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 13.5], [0, 1, 0, 33.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [7,0,0], auto = [1,0,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 1); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 40], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [6,0,0], auto = [1,0,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 50], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [7,0,7], auto = [0,1,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 13.5], [0, 1, 0, 63.5], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [7,0,0], auto = [0,1,0]) { +				sphere($fn = 8, $fa = 12, $fs = 2, r = 1); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 70], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [8,0,0], auto = [0,0,0]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 80], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [9,0,0], auto = [0,0,1]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 90], [0, 0, 1, 10], [0, 0, 0, 1]]) { +			resize(newsize = [-5,0,0], auto = [0,0,0]) { +				cube(size = [1, 1, 1], center = false); +			} +		} +	} +	color([1, 0.752941, 0.796078, 1]) { +		multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, -10], [0, 0, 0, 1]]) { +			resize(newsize = [4,4,4], auto = [0,0,0]) { +				resize(newsize = [5000,100,1000], auto = [0,0,0]) { +					cube(size = [1, 1, 1], center = false); +				} +			} +		} +	} + diff --git a/tests/regression/opencsgtest/resize-2d-tests-expected.png b/tests/regression/opencsgtest/resize-2d-tests-expected.pngBinary files differ new file mode 100644 index 0000000..937de11 --- /dev/null +++ b/tests/regression/opencsgtest/resize-2d-tests-expected.png diff --git a/tests/regression/opencsgtest/resize-tests-expected.png b/tests/regression/opencsgtest/resize-tests-expected.pngBinary files differ new file mode 100644 index 0000000..fbe3e08 --- /dev/null +++ b/tests/regression/opencsgtest/resize-tests-expected.png diff --git a/tests/regression/throwntogethertest/resize-2d-tests-expected.png b/tests/regression/throwntogethertest/resize-2d-tests-expected.pngBinary files differ new file mode 100644 index 0000000..d896d8e --- /dev/null +++ b/tests/regression/throwntogethertest/resize-2d-tests-expected.png diff --git a/tests/regression/throwntogethertest/resize-tests-expected.png b/tests/regression/throwntogethertest/resize-tests-expected.pngBinary files differ new file mode 100644 index 0000000..7c6804c --- /dev/null +++ b/tests/regression/throwntogethertest/resize-tests-expected.png | 
