← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-pkg/yade/git-trunk] Rev 3957: Polyhedra implementation improvement (Contributed by Jan Elias)

 

------------------------------------------------------------
revno: 3957
committer: Jan Stransky <jan.stransky@xxxxxxxxxxx>
timestamp: Fri 2014-05-16 13:58:59 +0200
message:
  Polyhedra implementation improvement (Contributed by Jan Elias)
modified:
  pkg/dem/Polyhedra.cpp
  pkg/dem/Polyhedra.hpp
  pkg/dem/Polyhedra_Ig2.cpp
  pkg/dem/Polyhedra_splitter.cpp
  pkg/dem/Polyhedra_support.cpp
  py/_polyhedra_utils.cpp
  py/polyhedra_utils.py


--
lp:yade
https://code.launchpad.net/~yade-pkg/yade/git-trunk

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-pkg/yade/git-trunk/+edit-subscription
=== modified file 'pkg/dem/Polyhedra.cpp'
--- pkg/dem/Polyhedra.cpp	2013-10-17 11:59:10 +0000
+++ pkg/dem/Polyhedra.cpp	2014-05-16 11:58:59 +0000
@@ -16,7 +16,7 @@
 YADE_PLUGIN(/* self-contained in hpp: */ (Polyhedra) (PolyhedraGeom) (Bo1_Polyhedra_Aabb) (PolyhedraPhys) (PolyhedraMat) (Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys) (PolyhedraVolumetricLaw)
 	/* some code in cpp (this file): */ 
 	#ifdef YADE_OPENGL
-		(Gl1_Polyhedra)
+		(Gl1_Polyhedra) (Gl1_PolyhedraGeom) (Gl1_PolyhedraPhys)
 	#endif	
 	);
 
@@ -197,9 +197,9 @@
 	Triangulation dt(nuclei.begin(), nuclei.end());
 	Triangulation::Vertex_handle zero_point = dt.insert(CGALpoint(5.,5.,5.));
 	v.clear();
-        std::vector<Triangulation::Cell_handle>  ch_cells;
+        std::vector<typename Triangulation::Cell_handle>  ch_cells;
     	dt.incident_cells(zero_point,std::back_inserter(ch_cells));
-	for(std::vector<Triangulation::Cell_handle>::iterator ci = ch_cells.begin(); ci !=ch_cells.end(); ++ci){
+	for(std::vector<typename Triangulation::Cell_handle>::iterator ci = ch_cells.begin(); ci !=ch_cells.end(); ++ci){
 		v.push_back(FromCGALPoint(dt.dual(*ci))-Vector3r(5.,5.,5.));				
 	}
 
@@ -256,14 +256,15 @@
 
 #ifdef YADE_OPENGL
 	#include<yade/lib/opengl/OpenGLWrapper.hpp>
-	void Gl1_Polyhedra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool,const GLViewInfo&)
+	bool Gl1_Polyhedra::wire;
+	void Gl1_Polyhedra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool wire2,const GLViewInfo&)
 	{
 		glMaterialv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,Vector3r(cm->color[0],cm->color[1],cm->color[2]));
 		glColor3v(cm->color);
 		Polyhedra* t=static_cast<Polyhedra*>(cm.get());
 		vector<int> faceTri = t->GetSurfaceTriangulation();
 
-		if (0) { 
+		if (wire || wire2) { 
 			glDisable(GL_LIGHTING);
 			glBegin(GL_LINES);
 				#define __ONEWIRE(a,b) glVertex3v(t->v[a]);glVertex3v(t->v[b])
@@ -283,6 +284,58 @@
 			glEnd();
 		}
 	}
+
+
+	void Gl1_PolyhedraGeom::go(const shared_ptr<IGeom>& ig, const shared_ptr<Interaction>&, const shared_ptr<Body>&, const shared_ptr<Body>&, bool){ draw(ig);}
+
+	void Gl1_PolyhedraGeom::draw(const shared_ptr<IGeom>& ig){		
+	};
+
+	GLUquadric* Gl1_PolyhedraPhys::gluQuadric=NULL;
+	Real Gl1_PolyhedraPhys::maxFn;
+	Real Gl1_PolyhedraPhys::refRadius;
+	Real Gl1_PolyhedraPhys::maxRadius;
+	int Gl1_PolyhedraPhys::signFilter;
+	int Gl1_PolyhedraPhys::slices;
+	int Gl1_PolyhedraPhys::stacks;
+
+	void Gl1_PolyhedraPhys::go(const shared_ptr<IPhys>& ip, const shared_ptr<Interaction>& i, const shared_ptr<Body>& b1, const shared_ptr<Body>& b2, bool wireFrame){
+		if(!gluQuadric){ gluQuadric=gluNewQuadric(); if(!gluQuadric) throw runtime_error("Gl1_PolyhedraPhys::go unable to allocate new GLUquadric object (out of memory?)."); }
+		PolyhedraPhys* np=static_cast<PolyhedraPhys*>(ip.get());
+		shared_ptr<IGeom> ig(i->geom); if(!ig) return; // changed meanwhile?
+		PolyhedraGeom* geom=YADE_CAST<PolyhedraGeom*>(ig.get());
+		Real fnNorm=np->normalForce.dot(geom->normal);
+		if((signFilter>0 && fnNorm<0) || (signFilter<0 && fnNorm>0)) return;
+		int fnSign=fnNorm>0?1:-1;
+		fnNorm=abs(fnNorm);
+		Real radiusScale=1.;
+		maxFn=max(fnNorm,maxFn);
+		Real realMaxRadius;
+		if(maxRadius<0){
+			refRadius=min(0.03,refRadius);
+			realMaxRadius=refRadius;
+		}
+		else realMaxRadius=maxRadius;
+		Real radius=radiusScale*realMaxRadius*(fnNorm/maxFn); 
+		if (radius<=0.) radius = 1E-8;
+		Vector3r color=Shop::scalarOnColorScale(fnNorm*fnSign,-maxFn,maxFn);
+		
+		Vector3r p1=b1->state->pos, p2=b2->state->pos;
+		Vector3r relPos;
+		relPos=p2-p1;
+		Real dist=relPos.norm();
+				
+		glDisable(GL_CULL_FACE); 
+		glPushMatrix();
+			glTranslatef(p1[0],p1[1],p1[2]);
+			Quaternionr q(Quaternionr().setFromTwoVectors(Vector3r(0,0,1),relPos/dist /* normalized */));
+			// using Transform with OpenGL: http://eigen.tuxfamily.org/dox/TutorialGeometry.html
+			//glMultMatrixd(Eigen::Affine3d(q).data());
+			glMultMatrix(Eigen::Transform<Real,3,Eigen::Affine>(q).data());
+			glColor3v(color);
+			gluCylinder(gluQuadric,radius,radius,dist,slices,stacks);
+		glPopMatrix();
+	}
 #endif
 
 //**********************************************************************************
@@ -362,9 +415,9 @@
 // Apply forces on polyhedrons in collision based on geometric configuration
 void PolyhedraVolumetricLaw::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* I){
 
-		if (!I->geom) {I->phys=shared_ptr<IPhys>(); return;} 
+		if (!I->geom) {return;} 
 		const shared_ptr<PolyhedraGeom>& contactGeom(dynamic_pointer_cast<PolyhedraGeom>(I->geom));
-		if(!contactGeom) {I->phys=shared_ptr<IPhys>(); return;} 
+		if(!contactGeom) {return;} 
 		const Body::id_t idA=I->getId1(), idB=I->getId2();
 		const shared_ptr<Body>& A=Body::byId(idA), B=Body::byId(idB);
 
@@ -373,11 +426,12 @@
 		//erase the interaction when aAbB shows separation, otherwise keep it to be able to store previous separating plane for fast detection of separation 
 		if (A->bound->min[0] >= B->bound->max[0] || B->bound->min[0] >= A->bound->max[0] || A->bound->min[1] >= B->bound->max[1] || B->bound->min[1] >= A->bound->max[1] || A->bound->min[2] >= B->bound->max[2] || B->bound->min[2] >= A->bound->max[2])  {
 			scene->interactions->requestErase(I);
+			phys->normalForce = Vector3r(0.,0.,0.); phys->shearForce = Vector3r(0.,0.,0.);
 			return;
 		}
 			
 		//zero penetration depth means no interaction force
-		if(!(contactGeom->penetrationVolume > 0) ) {I->phys=shared_ptr<IPhys>(); return;} 
+		if(!(contactGeom->equivalentPenetrationDepth > 1E-18) || !(contactGeom->penetrationVolume > 0)) {phys->normalForce = Vector3r(0.,0.,0.); phys->shearForce = Vector3r(0.,0.,0.); return;} 
 		Vector3r normalForce=contactGeom->normal*contactGeom->penetrationVolume*phys->kn;
 
 		//shear force: in case the polyhdras are separated and come to contact again, one should not use the previous shear force
@@ -418,7 +472,22 @@
 		scene->forces.addForce (idB, -F);
 		scene->forces.addTorque(idA, -(A->state->pos-contactGeom->contactPoint).cross(F));
 		scene->forces.addTorque(idB, (B->state->pos-contactGeom->contactPoint).cross(F));	
-	
+
+		/*
+		FILE * fin = fopen("Forces.dat","a");
+		fprintf(fin,"************** IDS %d %d **************\n",idA, idB);
+		Vector3r T = (B->state->pos-contactGeom->contactPoint).cross(F);
+		fprintf(fin,"volume\t%e\n",contactGeom->penetrationVolume);	
+		fprintf(fin,"normal_force\t%e\t%e\t%e\n",normalForce[0],normalForce[1],normalForce[2]);	
+		fprintf(fin,"shear_force\t%e\t%e\t%e\n",shearForce[0],shearForce[1],shearForce[2]);	
+		fprintf(fin,"total_force\t%e\t%e\t%e\n",F[0],F[1],F[2]);		
+		fprintf(fin,"torsion\t%e\t%e\t%e\n",T[0],T[1],T[2]);
+		fprintf(fin,"A\t%e\t%e\t%e\n",A->state->pos[0],A->state->pos[1],A->state->pos[2]);
+		fprintf(fin,"B\t%e\t%e\t%e\n",B->state->pos[0],B->state->pos[1],B->state->pos[2]);
+		fprintf(fin,"centroid\t%e\t%e\t%e\n",contactGeom->contactPoint[0],contactGeom->contactPoint[1],contactGeom->contactPoint[2]);
+		fclose(fin);	
+		*/		
+
 		//needed to be able to acces interaction forces in other parts of yade
 		phys->normalForce = normalForce;
 		phys->shearForce = shearForce;

=== modified file 'pkg/dem/Polyhedra.hpp'
--- pkg/dem/Polyhedra.hpp	2014-01-28 08:43:35 +0000
+++ pkg/dem/Polyhedra.hpp	2014-05-16 11:58:59 +0000
@@ -145,19 +145,6 @@
 REGISTER_SERIALIZABLE(Bo1_Polyhedra_Aabb);
 
 //***************************************************************************
-#ifdef YADE_OPENGL
-	#include<yade/pkg/common/GLDrawFunctors.hpp>
-	/*! Draw Polyhedra using OpenGL */
-	class Gl1_Polyhedra: public GlShapeFunctor{	
-		public:
-			virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
-			YADE_CLASS_BASE_DOC(Gl1_Polyhedra,GlShapeFunctor,"Renders :yref:`Polyhedra` object");
-			RENDERS(Polyhedra);
-	};
-	REGISTER_SERIALIZABLE(Gl1_Polyhedra);
-#endif
-
-//***************************************************************************
 /*! Elastic material */
 class PolyhedraMat: public Material{
 	public:
@@ -193,6 +180,53 @@
 REGISTER_SERIALIZABLE(PolyhedraPhys);
 
 //***************************************************************************
+#ifdef YADE_OPENGL
+	#include<yade/pkg/common/GLDrawFunctors.hpp>
+	#include<yade/lib/opengl/OpenGLWrapper.hpp>
+	#include<yade/lib/opengl/GLUtils.hpp>
+	#include<GL/glu.h>
+	#include<yade/pkg/dem/Shop.hpp>
+	
+	/*! Draw Polyhedra using OpenGL */
+	class Gl1_Polyhedra: public GlShapeFunctor{	
+		public:
+			virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
+			YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_Polyhedra,GlShapeFunctor,"Renders :yref:`Polyhedra` object",
+			((bool,wire,false,,"Only show wireframe"))
+			);
+			RENDERS(Polyhedra);
+	};
+	REGISTER_SERIALIZABLE(Gl1_Polyhedra);
+
+	struct Gl1_PolyhedraGeom: public GlIGeomFunctor{
+		RENDERS(PolyhedraGeom);
+		void go(const shared_ptr<IGeom>&, const shared_ptr<Interaction>&, const shared_ptr<Body>&, const shared_ptr<Body>&, bool);
+		void draw(const shared_ptr<IGeom>&);
+		YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_PolyhedraGeom,GlIGeomFunctor,"Render :yref:`PolyhedraGeom` geometry.",
+		);
+	};
+	REGISTER_SERIALIZABLE(Gl1_PolyhedraGeom);
+
+	class Gl1_PolyhedraPhys: public GlIPhysFunctor{	
+		static GLUquadric* gluQuadric; // needed for gluCylinder, initialized by ::go if no initialized yet
+		public:
+			virtual void go(const shared_ptr<IPhys>&,const shared_ptr<Interaction>&,const shared_ptr<Body>&,const shared_ptr<Body>&,bool wireFrame);
+		YADE_CLASS_BASE_DOC_STATICATTRS(Gl1_PolyhedraPhys,GlIPhysFunctor,"Renders :yref:`PolyhedraPhys` objects as cylinders of which diameter and color depends on :yref:`PolyhedraPhys::normForce` magnitude.",
+			((Real,maxFn,0,,"Value of :yref:`NormPhys.normalForce` corresponding to :yref:`maxDiameter<Gl1_NormPhys.maxDiameter>`. This value will be increased (but *not decreased* ) automatically."))
+			((Real,refRadius,std::numeric_limits<Real>::infinity(),,"Reference (minimum) particle radius"))
+			((int,signFilter,0,,"If non-zero, only display contacts with negative (-1) or positive (+1) normal forces; if zero, all contacts will be displayed."))
+			((Real,maxRadius,-1,,"Cylinder radius corresponding to the maximum normal force."))
+			((int,slices,6,,"Number of sphere slices; (see `glutCylinder reference <http://www.opengl.org/sdk/docs/man/xhtml/gluCylinder.xml>`__)"))
+		(	(int,stacks,1,,"Number of sphere stacks; (see `glutCylinder reference <http://www.opengl.org/sdk/docs/man/xhtml/gluCylinder.xml>`__)"))			
+		);
+		RENDERS(PolyhedraPhys);
+	};
+	REGISTER_SERIALIZABLE(Gl1_PolyhedraPhys);
+
+#endif
+
+
+//***************************************************************************
 class Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys: public IPhysFunctor{
 	public:
 		virtual void go(const shared_ptr<Material>& b1,
@@ -247,6 +281,8 @@
 Polyhedron Polyhedron_Polyhedron_intersection(Polyhedron A, Polyhedron B, CGALpoint X, CGALpoint centroidA, CGALpoint centroidB,  std::vector<int> &code);
 //return intersection of plane & polyhedron 
 Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Plane B, CGALpoint centroid, CGALpoint X);
+//Test if point is inside Polyhedron
+bool Is_inside_Polyhedron(Polyhedron P, CGALpoint inside);
 //return approximate intersection of sphere & polyhedron 
 bool Sphere_Polyhedron_intersection(Polyhedron A, double r, CGALpoint C, CGALpoint centroid,  double volume, CGALvector normal, double area);
 //return volume and centroid of polyhedra
@@ -263,12 +299,13 @@
 Polyhedron Simplify(Polyhedron P, double lim);
 //list of facets and edges
 void PrintPolyhedron(Polyhedron P);
+void PrintPolyhedron2File(Polyhedron P,FILE* X);
 //normal by least square fitting of separating segments
 Vector3r FindNormal(Polyhedron Int, Polyhedron PA, Polyhedron PB);
 //calculate area of projection of polyhedron into the plane
 double CalculateProjectionArea(Polyhedron Int, CGALvector CGALnormal);
 //split polyhedron
-void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction);
+shared_ptr<Body> SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction, Vector3r point);
 //new polyhedra
 shared_ptr<Body> NewPolyhedra(vector<Vector3r> v, shared_ptr<Material> mat);
 

=== modified file 'pkg/dem/Polyhedra_Ig2.cpp'
--- pkg/dem/Polyhedra_Ig2.cpp	2013-10-16 15:24:49 +0000
+++ pkg/dem/Polyhedra_Ig2.cpp	2014-05-16 11:58:59 +0000
@@ -29,6 +29,7 @@
 	Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
 	Polyhedron PA = A->GetPolyhedron();
 	std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+	std::transform( PA.facets_begin(), PA.facets_end(), PA.planes_begin(),Plane_equation());	
 
 
 	//move and rotate 2nd the CGAL structure Polyhedron
@@ -37,6 +38,7 @@
 	t_rot_trans = Transformation(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
 	Polyhedron PB = B->GetPolyhedron();
 	std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+	std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
 
 	shared_ptr<PolyhedraGeom> bang;
 	if (isNew) {
@@ -62,6 +64,7 @@
 	Vector3r centroid;	
 	P_volume_centroid(Int, &volume, &centroid);
  	if(isnan(volume) || volume<=1E-25 || volume > min(A->GetVolume(),B->GetVolume())) {bang->equivalentPenetrationDepth=0; 	return true;}
+	if ( (!Is_inside_Polyhedron(PA, ToCGALPoint(centroid))) or (!Is_inside_Polyhedron(PB, ToCGALPoint(centroid))))  {bang->equivalentPenetrationDepth=0; return true;}
 
 	//find normal direction
         Vector3r normal = FindNormal(Int, PA, PB);
@@ -80,6 +83,17 @@
 	bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
 	bang->normal=normal;
 	
+	/*
+	FILE * fin = fopen("Interactions.dat","a");
+	fprintf(fin,"************** IDS %d %d **************\n",interaction->id1, interaction->id2);
+	fprintf(fin,"volume\t%e\n",volume);	
+	fprintf(fin,"centroid\t%e\t%e\t%e\n",centroid[0],centroid[1],centroid[2]);
+	PrintPolyhedron2File(PA,fin);
+	PrintPolyhedron2File(PB,fin);
+	PrintPolyhedron2File(Int,fin);
+	fclose(fin);
+	*/
+
 	return true;	
 }
 
@@ -131,6 +145,7 @@
 	Vector3r centroid;	
 	P_volume_centroid(Int, &volume, &centroid);
 	if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume())  {bang->equivalentPenetrationDepth=0; return true;}
+	if (!Is_inside_Polyhedron(PB, ToCGALPoint(centroid)))  {bang->equivalentPenetrationDepth=0; return true;}
 
 	//calculate area of projection of Intersection into the normal plane
         double area = volume/1E-8;
@@ -167,6 +182,7 @@
 	Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
 	Polyhedron PB = B->GetPolyhedron();
 	std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+	std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
 
 	//move and rotate facet
 	vector<CGALpoint> v;
@@ -224,6 +240,7 @@
 	Vector3r centroid;	
 	P_volume_centroid(Int, &volume, &centroid);
  	if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+	if (!Is_inside_Polyhedron(PB, ToCGALPoint(centroid)))  {bang->equivalentPenetrationDepth=0; return true;}
 
 	//find normal direction
 	Vector3r normal = FindNormal(Int, PA, PB);

=== modified file 'pkg/dem/Polyhedra_splitter.cpp'
--- pkg/dem/Polyhedra_splitter.cpp	2013-10-16 15:24:49 +0000
+++ pkg/dem/Polyhedra_splitter.cpp	2014-05-16 11:58:59 +0000
@@ -22,11 +22,9 @@
 		PolyhedraPhys* phys=YADE_CAST<PolyhedraPhys*>(I->phys.get());
 		if(!geom || !phys) continue;
 		Vector3r f=phys->normalForce+phys->shearForce;
-		//Sum f_i*l_j for each contact of each particle
+		//Sum f_i*l_j for each contact of each particle	
 		bStresses[I->getId1()] -=f*((geom->contactPoint-Body::byId(I->getId1(),scene)->state->pos).transpose());
 		bStresses[I->getId2()] +=f*((geom->contactPoint-Body::byId(I->getId2(),scene)->state->pos).transpose());
-
-		
 	}
 }
 
@@ -52,6 +50,20 @@
 	bStress(2,1) = bStress(1,2);
 }
 
+//**********************************************************************************
+//split polyhedra
+void SplitPolyhedraDouble(const shared_ptr<Body>& body, Vector3r direction1, Vector3r direction2){
+
+	const Se3r& se3=body->state->se3; 
+	Vector3r point = se3.position;
+
+	shared_ptr<Body> B2 = SplitPolyhedra(body, direction1, point);
+	shared_ptr<Body> B3 = SplitPolyhedra(B2, direction2, point);
+	shared_ptr<Body> B4 = SplitPolyhedra(body, direction2, point);
+
+}
+
+
 //*********************************************************************************
 /* Split if stress exceed strength */
 
@@ -61,7 +73,7 @@
 	shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
 
 	vector<shared_ptr<Body> > bodies;
-	vector<Vector3r > directions;
+	vector<Vector3r > directions1, directions2;
 	vector<double > sigmas;
 
 
@@ -69,16 +81,14 @@
 	vector<Matrix3r> bStresses;
 	getStressForEachBody(bStresses);
 
-	int i = -1;
 	FOREACH(const shared_ptr<Body>& b, *rb->bodies){
-		i++;
 		if(!b || !b->material || !b->shape) continue;
 		shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
 		shared_ptr<PolyhedraMat> m=dynamic_pointer_cast<PolyhedraMat>(b->material);
 	
 		if(p && m->IsSplitable){
 			//not real strees, to get real one, it has to be divided by body volume
-			Matrix3r stress = bStresses[i];
+			Matrix3r stress = bStresses[b->id];
 
 			//get eigenstresses
 			Symmetrize(stress);
@@ -93,16 +103,18 @@
 			
 			//division of stress by volume
 			double comp_stress = I_valu(min_i,min_i)/p->GetVolume();
-			double tens_stress = I_valu(max_i,max_i)/p->GetVolume();
+			double tens_stress = I_valu(max_i,max_i)/p->GetVolume();			
 			Vector3r dirC = I_vect.col(max_i);
 			Vector3r dirT = I_vect.col(min_i);
-			Vector3r dir  = dirC.normalized() + dirT.normalized();;	
-			double sigma_t = -comp_stress/2.+ tens_stress;
-			if (sigma_t > getStrength(p->GetVolume(),m->GetStrength())) {bodies.push_back(b); directions.push_back(dir); sigmas.push_back(sigma_t);};
+			Vector3r dir1  = dirC.normalized() + dirT.normalized();	
+			Vector3r dir2  = dirC.normalized() - dirT.normalized();	
+			//double sigma_t = -comp_stress/2.+ tens_stress;
+			double sigma_t = pow((pow(I_valu(0,0)-I_valu(1,1),2)+pow(I_valu(0,0)-I_valu(2,2),2)+pow(I_valu(1,1)-I_valu(2,2),2))/2.,0.5)/p->GetVolume();
+			if (sigma_t > getStrength(p->GetVolume(),m->GetStrength())) {bodies.push_back(b); directions1.push_back(dir1.normalized()); directions2.push_back(dir2.normalized()); sigmas.push_back(sigma_t);};
 		}		
 	}
 	for(int i=0; i<int(bodies.size()); i++){
-		SplitPolyhedra(bodies[i], directions[i]);
+		SplitPolyhedraDouble(bodies[i], directions1[i], directions2[i]);
 	}
 }
 

=== modified file 'pkg/dem/Polyhedra_support.cpp'
--- pkg/dem/Polyhedra_support.cpp	2013-10-16 15:24:49 +0000
+++ pkg/dem/Polyhedra_support.cpp	2014-05-16 11:58:59 +0000
@@ -10,13 +10,15 @@
 
 //EMPRIRICAL CONSTANTS - ADJUST IF SEGMENTATION FAULT OCCUR, IT IS A PROBLEM OF CGAL. THESE ARE USED TO CHECK CGAL IMPUTS
 //DISTANCE_LIMIT controls numerical issues in calculating intersection. It should be small enough to neglect only extremely small overlaps, but large enough to prevent errors during computation of convex hull
-#define DISTANCE_LIMIT 1E-11	//-11
+#define DISTANCE_LIMIT 2E-11	//-11
 //MERGE_PLANES_LIMIT - if two facets of two intersecting polyhedron differ less, then they are treated ose one only
 #define MERGE_PLANES_LIMIT 1E-18 //18
 //SIMPLIFY_LIMIT - if two facets of one polyhedron differ less, then they are joint into one facet
-#define SIMPLIFY_LIMIT 1E-20 //19
+#define SIMPLIFY_LIMIT 1E-19 //19
 //FIND_NORMAL_LIMIT - to determine which facet of intersection belongs to which polyhedron
 #define FIND_NORMAL_LIMIT 1E-40
+//SPLITTER_GAP - creates gap between splitted polyhedrons
+#define SPLITTER_GAP 1E-8
 
 
 //**********************************************************************************
@@ -288,6 +290,30 @@
 
 //**********************************************************************************
 //list of facets + edges
+void PrintPolyhedron2File(Polyhedron P,FILE* X){
+	Vector3r A,B,C;
+	fprintf(X,"*** faces ***\n");
+ 	for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter!=P.facets_end(); ++fIter){
+		Polyhedron::Halfedge_around_facet_circulator hfc0;
+		hfc0 = fIter->facet_begin();
+		int n = fIter->facet_degree();	
+		A = FromCGALPoint(hfc0->vertex()->point());
+		C = FromCGALPoint(hfc0->next()->vertex()->point());
+		for (int i=2; i<n; i++){
+			++hfc0;
+			B = C;
+			C = FromCGALPoint(hfc0->next()->vertex()->point());
+			fprintf(X,"%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\n",A[0],A[1],A[2],B[0],B[1],B[2],C[0],C[1],C[2]);
+		}
+	}
+	fprintf(X,"*** edges ***\n");
+ 	for (Polyhedron::Edge_iterator hei = P.edges_begin(); hei!=P.edges_end(); ++hei){
+		fprintf(X,"%e\t%e\t%e\t%e\t%e\t%e\n",hei->vertex()->point()[0],hei->vertex()->point()[1],hei->vertex()->point()[2], hei->opposite()->vertex()->point()[0], hei->opposite()->vertex()->point()[1], hei->opposite()->vertex()->point()[2]);
+	}
+}
+
+//**********************************************************************************
+//list of facets + edges
 void PrintPolyhedron(Polyhedron P){
 	Vector3r A,B,C;
 	cout << "*** faces ***" << endl;
@@ -478,6 +504,7 @@
 	// test if do intersect, find some intersecting point
 	bool intersection_found = false;
 	double lim = pow(DISTANCE_LIMIT,2);
+	std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
 	// test centroid of previous intersection
 	if(Is_inside_Polyhedron(A,X,DISTANCE_LIMIT) && Oriented_squared_distance(B,X)<=-lim) {
 		intersection_found = true;
@@ -486,20 +513,24 @@
  	} else {	
 		for(Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end() && !intersection_found ; vIter++){
 			if(Oriented_squared_distance(B,vIter->point())<=-lim) {
-				intersection_found = true;			
-				CGAL::Object result = CGAL::intersection(Line(vIter->point(),centroid), B);			
-				if (const CGALpoint *ipoint = CGAL::object_cast<CGALpoint>(&result)) {
-					inside = vIter->point() + 0.5*CGALvector(vIter->point(),*ipoint);
+				if (Oriented_squared_distance(B,centroid)<lim/10.){
+					inside = vIter->point() + 0.5*CGALvector(vIter->point(),centroid);
 				}else{
-					cerr << "Error in line-plane intersection" << endl; 
+					CGAL::Object result = CGAL::intersection(Line(vIter->point(),centroid), B);			
+					if (const CGALpoint *ipoint = CGAL::object_cast<CGALpoint>(&result)) {
+						inside = vIter->point() + 0.5*CGALvector(vIter->point(),*ipoint);
+					}else{
+						cerr << "Error in line-plane intersection" << endl; 
+					}
 				}
+				if(Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Oriented_squared_distance(B,inside)<=-lim) intersection_found = true;
 			}
 		}
 	}
 	//no intersectiong point => no intersection polyhedron
 	if(!intersection_found) return Intersection;
 
-	
+
 	//set the intersection point to origin	
 	Transformation transl_back(CGAL::TRANSLATION, inside - CGALpoint(0.,0.,0.));
 	Transformation transl(CGAL::TRANSLATION, CGALpoint(0.,0.,0.)-inside);
@@ -520,7 +551,7 @@
 
  	//compute convex hull of it
 	Intersection = ConvexHull(dual_planes);
-	if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;}
+	if (Intersection.empty()) return Intersection;
 
 	//simplify
 	std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
@@ -542,7 +573,7 @@
 }
 
 //**********************************************************************************
-//returnes intersecting polyhedron of polyhedron & plane defined by diriction and point
+//returnes intersecting polyhedron of polyhedron & plane defined by direction and point
 Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Vector3r direction,  Vector3r plane_point){
 	Plane B(ToCGALPoint(plane_point), ToCGALVector(direction)); 
 	CGALpoint X = ToCGALPoint(plane_point) - 1E-8*ToCGALVector(direction);
@@ -633,7 +664,7 @@
 
  	//compute convex hull of it
 	Intersection = ConvexHull(dual_planes);
-	if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;};
+	if (Intersection.empty())  return Intersection;
 
 	//simplify
 	std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
@@ -759,10 +790,11 @@
 	return body;
 }
 
-
 //**********************************************************************************
 //split polyhedra
-void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction){
+shared_ptr<Body> SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction, Vector3r point){
+
+	direction.normalize();
 
 	const Se3r& se3=body->state->se3; 
 	Polyhedra* A = static_cast<Polyhedra*>(body->shape.get());
@@ -779,10 +811,14 @@
 	Polyhedron PA = A->GetPolyhedron();
 	std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
  
-        //calculate splitted polyhedrons
-	Polyhedron S1 = Polyhedron_Plane_intersection(PA, direction,  trans_vec);
-	Polyhedron S2 = Polyhedron_Plane_intersection(PA, (-1)*direction,  trans_vec);
-	
+	//calculate first splitted polyhedrons
+	Plane B(ToCGALPoint(point-direction*SPLITTER_GAP), ToCGALVector(direction)); 
+	Polyhedron S1 = Polyhedron_Plane_intersection(PA, B, ToCGALPoint(se3.position), B.projection(ToCGALPoint(OrigPos)) - 1E-6*ToCGALVector(direction));
+	B = Plane(ToCGALPoint(point+direction*SPLITTER_GAP), ToCGALVector((-1.)*direction)); 
+	Polyhedron S2 = Polyhedron_Plane_intersection(PA, B, ToCGALPoint(se3.position), B.projection(ToCGALPoint(OrigPos)) + 1E-6*ToCGALVector(direction));
+	Scene* scene=Omega::instance().getScene().get();
+	//scene->bodies->erase(body->id);
+
 	
 	//replace original polyhedron
 	A->Clear();		
@@ -793,6 +829,8 @@
 	X->mass=body->material->density*A->GetVolume();
 	X->refPos[0] = X->refPos[0]+1.;
 	X->inertia =A->GetInertia()*body->material->density;
+	X->vel = OrigVel + OrigAngVel.cross(X->pos-OrigPos);
+	X->angVel = OrigAngVel;	
 	
 	//second polyhedron
 	vector<Vector3r> v2;
@@ -800,18 +838,12 @@
 	shared_ptr<Body> BP = NewPolyhedra(v2, body->material);
 	//BP->shape->color = body->shape->color;
 	BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
-
-	//append to the rest
-	Scene* scene=Omega::instance().getScene().get();
-	//scene->bodies->erase(body->id);
 	scene->bodies->insert(BP);
-
 	//set proper state variables
-	X->vel = OrigVel + OrigAngVel.cross(X->pos-OrigPos);
 	BP->state->vel = OrigVel + OrigAngVel.cross(BP->state->pos-OrigPos);
-	X->angVel = OrigAngVel;	
 	BP->state->angVel = OrigAngVel;
 
+	return BP;
 }
 
 #endif // YADE_CGAL

=== modified file 'py/_polyhedra_utils.cpp'
--- py/_polyhedra_utils.cpp	2014-04-02 15:19:41 +0000
+++ py/_polyhedra_utils.cpp	2014-05-16 11:58:59 +0000
@@ -103,8 +103,7 @@
 			Real density=b->state->mass/p->GetVolume();
 			//get equivalent radius and use same equation as for sphere
 			Real equi_radius=pow(p->GetVolume()/((4./3.)*Mathr::PI),1./3.);				
-			//dt=min(dt,equi_radius/sqrt(ebp->Kn*equi_radius/density));
-			dt=min(dt,equi_radius/sqrt(ebp->Kn/equi_radius/density));
+			dt=min(dt,equi_radius/sqrt(ebp->Kn*equi_radius/density));
 		}
 	}
 	if (dt==std::numeric_limits<Real>::infinity()) {
@@ -326,10 +325,182 @@
 	return v;
 } 
 
+//**************************************************************************
+/* Generate truncated icosahedron*/
+vector<Vector3r> TruncIcosaHedPoints(Vector3r radii){
+    vector<Vector3r> v;
+
+    double p = (1.+sqrt(5.))/2.;
+    Vector3r f,c,b;
+    f = radii/sqrt(9.*p + 1.);
+    vector<Vector3r> A,B;
+    A.push_back(Vector3r(0.,1.,3.*p)); A.push_back(Vector3r(2.,1.+2.*p,p)); A.push_back(Vector3r(1.,2.+p,2.*p));
+    for(int i=0;i<(int) A.size() ;i++){
+        B.clear();
+	c = Vector3r(A[i][0]*f[0],A[i][1]*f[1],A[i][2]*f[2]);
+	B.push_back(c); B.push_back(Vector3r(c[1],c[2],c[0])); B.push_back(Vector3r(c[2],c[0],c[1]));
+        for(int j=0;j<(int) B.size() ;j++){
+		b = B[j];
+		v.push_back(b);
+		if (b[0] != 0.){ 
+			v.push_back(Vector3r(-b[0], b[1], b[2]));	
+			if (b[1] != 0.){ 
+				v.push_back(Vector3r(-b[0],-b[1], b[2]));
+				if (b[2] != 0.) v.push_back(Vector3r(-b[0],-b[1],-b[2]));
+			}
+			if (b[2] != 0.) v.push_back(Vector3r(-b[0], b[1],-b[2]));
+		}
+		if (b[1] != 0.){ 
+			v.push_back(Vector3r( b[0],-b[1], b[2]));
+			if (b[2] != 0.)	v.push_back(Vector3r( b[0],-b[1],-b[2]));
+		}
+		if (b[2] != 0.)	v.push_back(Vector3r( b[0], b[1],-b[2]));
+	}
+    }
+    return v;
+}
+
+//**************************************************************************
+/* Generate SnubCube*/
+vector<Vector3r> SnubCubePoints(Vector3r radii){
+    vector<Vector3r> v;
+
+    double c1 = 0.337754;
+    double c2 = 1.14261;
+    double c3 = 0.621226;
+    Vector3r f,b;
+    f = radii/1.3437133737446;
+    vector<Vector3r> A;
+    A.push_back(Vector3r(c2,c1,c3)); A.push_back(Vector3r(c1,c3,c2)); A.push_back(Vector3r(c3,c2,c1)); 
+    A.push_back(Vector3r(-c1,-c2,-c3)); A.push_back(Vector3r(-c2,-c3,-c1)); A.push_back(Vector3r(-c3,-c1,-c2));
+    for(int i=0;i<(int) A.size();i++){
+	b = Vector3r(A[i][0]*f[0],A[i][1]*f[1],A[i][2]*f[2]);
+	v.push_back(b);
+	v.push_back(Vector3r(-b[0],-b[1], b[2]));
+	v.push_back(Vector3r(-b[0], b[1],-b[2]));
+	v.push_back(Vector3r( b[0],-b[1],-b[2]));
+    }
+    return v;
+}
+
+//**************************************************************************
+/* Generate ball*/
+vector<Vector3r> BallPoints(Vector3r radii, int NumFacets,int seed){
+        vector<Vector3r> v;
+	if (NumFacets == 60) v = TruncIcosaHedPoints(radii);
+	if (NumFacets == 24) v = SnubCubePoints(radii);
+	else{
+		double inc = Mathr::PI * (3. - pow(5.,0.5));
+    		double off = 2. / double(NumFacets);
+		double y,r,phi;
+        	for(int k=0; k<NumFacets; k++){
+	            y = double(k) * off - 1. + (off / 2.);
+        	    r = pow(1. - y*y,0.5);
+        	    phi = double(k) * inc;
+        	    v.push_back(Vector3r(cos(phi)*r*radii[0], y*radii[1], sin(phi)*r*radii[2]));
+		}
+	}
+
+	// randomly rotate
+        srand(seed);
+	Quaternionr Rot(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+	Rot.normalize();
+	for(int i=0; i< (int) v.size();i++) {
+		v[i] = Rot*(Vector3r(v[i][0],v[i][1],v[i][2]));
+	}
+        return v;
+}
+
+//**********************************************************************************
+//generate "packing" of non-overlapping balls
+vector<Vector3r> fillBoxByBalls_cpp(Vector3r minCoord, Vector3r maxCoord, Vector3r sizemin, Vector3r sizemax, Vector3r ratio, int seed, shared_ptr<Material> mat, int NumPoints){	
+	vector<Vector3r> v;
+	Polyhedra trialP;
+	Polyhedron trial, trial_moved;
+	srand(seed);
+	int it = 0;
+	vector<Polyhedron> polyhedrons;
+	vector<vector<Vector3r> > vv;
+	Vector3r position;
+	bool intersection;
+	int count = 0;
+	Vector3r radii;
+
+	
+	bool fixed_ratio = 0;
+	if (ratio[0] > 0 && ratio[1] > 0 && ratio[2]>0){
+		fixed_ratio = 1;
+		sizemax[0] = min(min(sizemax[0]/ratio[0],  sizemax[1]/ratio[1]),  sizemax[2]/ratio[2]);
+		sizemin[0] = max(max(sizemin[0]/ratio[0],  sizemin[1]/ratio[1]),  sizemin[2]/ratio[2]);
+	}
+
+	fixed_ratio = 1; //force spherical
+
+	//it - number of trials to make packing possibly more/less dense
+	Vector3r random_size;
+	while (it<1000){
+		it = it+1;
+		if (it == 1){	
+			if (fixed_ratio) {
+				double rrr = (rand()*(sizemax[0]-sizemin[0])/RAND_MAX + sizemin[0])/2.;
+				radii = Vector3r(rrr,rrr,rrr);
+			}else  {
+				radii = Vector3r(rand()*(sizemax[0]-sizemin[0])/2.,rand()*(sizemax[1]-sizemin[1])/2.,rand()*(sizemax[2]-sizemin[2])/2.)/RAND_MAX + sizemin/2.;
+			}				
+			trialP.v = BallPoints(radii,NumPoints,rand());
+			trialP.Initialize();
+			trial = trialP.GetPolyhedron();	
+			Matrix3r rot_mat = (trialP.GetOri()).toRotationMatrix();
+			Transformation t_rot(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2),rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),1.);	
+			std::transform( trial.points_begin(), trial.points_end(), trial.points_begin(), t_rot);			
+		}		
+		position = Vector3r(rand()*(maxCoord[0]-minCoord[0]),rand()*(maxCoord[1]-minCoord[1]),rand()*(maxCoord[2]-minCoord[2]))/RAND_MAX + minCoord;
+
+		//move CGAL structure Polyhedron
+		Transformation transl(CGAL::TRANSLATION, ToCGALVector(position));
+		trial_moved = trial;		
+		std::transform( trial_moved.points_begin(), trial_moved.points_end(), trial_moved.points_begin(), transl);
+		//calculate plane equations
+		std::transform( trial_moved.facets_begin(), trial_moved.facets_end(), trial_moved.planes_begin(),Plane_equation());	
+
+		intersection = false;	
+		//call test with boundary
+		for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); (vi !=  trial_moved.vertices_end()) && (!intersection); vi++){
+			intersection = (vi->point().x()<minCoord[0]) || (vi->point().x()>maxCoord[0]) || (vi->point().y()<minCoord[1]) || (vi->point().y()>maxCoord[1]) || (vi->point().z()<minCoord[2]) || (vi->point().z()>maxCoord[2]);
+		}
+		//call test with other polyhedrons	
+		for(vector<Polyhedron>::iterator a = polyhedrons.begin(); (a != polyhedrons.end()) && (!intersection); a++){	
+			intersection = do_intersect(*a,trial_moved);
+		        if (intersection) break;
+		}
+		if (!intersection){
+			polyhedrons.push_back(trial_moved);
+			v.clear();
+			for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); vi !=  trial_moved.vertices_end(); vi++){
+				v.push_back(FromCGALPoint(vi->point()));
+			}
+			vv.push_back(v);
+			it = 0;
+			count ++;
+
+		}
+	}
+	cout << "generated " << count << " polyhedrons"<< endl;
+
+	//can't be used - no information about material
+	Scene* scene=Omega::instance().getScene().get();
+	for(vector<vector<Vector3r> >::iterator p=vv.begin(); p!=vv.end(); ++p){
+		shared_ptr<Body> BP = NewPolyhedra(*p, mat);
+		BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+		scene->bodies->insert(BP);
+	}
+	return v;
+} 
+
 //**********************************************************************************
 //split polyhedra
-void Split(const shared_ptr<Body> body, Vector3r direction){
-	SplitPolyhedra(body, direction);
+void Split(const shared_ptr<Body> body, Vector3r direction, Vector3r point){
+	SplitPolyhedra(body, direction, point);
 }
 
 //**********************************************************************************
@@ -361,14 +532,15 @@
 	py::def("PWaveTimeStep",PWaveTimeStep,"Get timestep accoring to the velocity of P-Wave propagation; computed from sphere radii, rigidities and masses.");
 	py::def("do_Polyhedras_Intersect",do_Polyhedras_Intersect,"check polyhedras intersection");
 	py::def("fillBox_cpp",fillBox_cpp,"Generate non-overlaping polyhedrons in box");
+	py::def("fillBoxByBalls_cpp",fillBoxByBalls_cpp,"Generate non-overlaping 'spherical' polyhedrons in box");
 	py::def("MinCoord",MinCoord,"returns min coordinates");
 	py::def("MaxCoord",MaxCoord,"returns max coordinates");
 	py::def("SieveSize",SieveSize,"returns approximate sieve size of polyhedron");
 	py::def("SieveCurve",SieveCurve,"save sieve curve coordinates into file");
 	py::def("SizeOfPolyhedra",SizeOfPolyhedra,"returns max, middle an min size in perpendicular directions");
 	py::def("SizeRatio",SizeRatio,"save sizes of polyhedra into file");
-	py::def("convexHull",convexHull,"");
-	py::def("Split",Split,"split polyhedron perpendicularly to given direction direction");
+	py::def("convexHull",convexHull,"....");
+	py::def("Split",Split,"split polyhedron perpendicularly to given direction through given point");
 }
 
 #endif // YADE_CGAL

=== modified file 'py/polyhedra_utils.py'
--- py/polyhedra_utils.py	2013-10-15 16:14:46 +0000
+++ py/polyhedra_utils.py	2014-05-16 11:58:59 +0000
@@ -79,8 +79,54 @@
 	ball = polyhedra(material,v=pts)
 	ball.state.pos = center
 	return ball
+    
+#**********************************************************************************
+def polyhedraTruncIcosaHed(radius, material, centre,mask=1):
+    pts = []
+ 
+    p = (1.+math.sqrt(5.))/2.
+    f = radius/math.sqrt(9.*p + 1.)
+    A = [[0.,1.,3.*p],[2.,1.+2.*p,p],[1.,2.+p,2.*p]]
+    for a in A:
+	a = [a[0]*f,a[1]*f,a[2]*f]
+	B = [a,[a[1],a[2],a[0]],[a[2],a[0],a[1]]]
+        for b in B:
+		pts.append(b)
+		if not b[0] == 0: 
+			pts.append([-b[0], b[1], b[2]])	
+			if not b[1] == 0:
+				pts.append([-b[0],-b[1], b[2]])	
+				if not b[2] == 0: pts.append([-b[0],-b[1],-b[2]])
+			if not b[2] == 0:
+				pts.append([-b[0], b[1],-b[2]])	
+		if not b[1] == 0: 
+			pts.append([ b[0],-b[1], b[2]])	
+			if not b[2] == 0:
+				pts.append([ b[0],-b[1],-b[2]])
+		if not b[2] == 0: pts.append([ b[0], b[1],-b[2]])
+    ball = polyhedra(material,v=pts)
+    ball.state.pos = centre
+    return ball
 
 #**********************************************************************************
+def polyhedraSnubCube(radius, material, centre, mask=1):
+    pts = []
+ 
+    f = radius/1.3437133737446
+    c1 = 0.337754
+    c2 = 1.14261
+    c3 = 0.621226
+    A = [[c2,c1,c3],[c1,c3,c2],[c3,c2,c1],[-c1,-c2,-c3],[-c2,-c3,-c1],[-c3,-c1,-c2]]
+    for a in A:
+	a = [a[0]*f,a[1]*f,a[2]*f]
+	pts.append([-a[0],-a[1], a[2]])	
+	pts.append([ a[0],-a[1],-a[2]])	
+	pts.append([-a[0], a[1],-a[2]])	
+	pts.append([ a[0], a[1], a[2]])	
+    ball = polyhedra(material,v=pts)
+    ball.state.pos = centre
+    return ball    
+#**********************************************************************************
 #fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
 def fillBox(mincoord, maxcoord,material,sizemin=[1,1,1],sizemax=[1,1,1],ratio=[0,0,0],seed=None,mask=1):
 	"""fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
@@ -98,5 +144,16 @@
 	#	if(math.isnan(v[i][0])):
 	#		O.bodies.append(polyhedra(material,seed=random.randint(0,1E6),v=v[lastnan+1:i],mask=1,fixed=False))
 	#		lastnan = i
+
+#**********************************************************************************
+#fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
+def fillBoxByBalls(mincoord, maxcoord,material,sizemin=[1,1,1],sizemax=[1,1,1],ratio=[0,0,0],seed=None,mask=1,numpoints=60):
+	random.seed(seed);
+	v = fillBoxByBalls_cpp(mincoord, maxcoord, sizemin,sizemax,  ratio, random.randint(0,1E6), material,numpoints)
+	#lastnan = -1
+	#for i in range(0,len(v)):
+	#	if(math.isnan(v[i][0])):
+	#		O.bodies.append(polyhedra(material,seed=random.randint(0,1E6),v=v[lastnan+1:i],mask=1,fixed=False))
+	#		lastnan = i