← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 1908: 1. Finish implementation of sheared periodic space.

 

------------------------------------------------------------
revno: 1908
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Fri 2009-12-18 16:04:57 +0100
message:
  1. Finish implementation of sheared periodic space.
  2. Fix bug which might have caused missed asymetric (Facet - Sphere, for instance) in periodic space, when interaction order was reversed.
  3. Add possiblity to pass a callable to utils.spheres' material parameter, to avoid shared material properties when called indirectly (pac.randomDensePack)
  4. Remove Aabb::{center,halfSize}, since they were used only rarely and were redundant.
modified:
  core/Bound.hpp
  core/Cell.hpp
  core/Interaction.cpp
  core/Scene.cpp
  pkg/common/DataClass/Bound/Aabb.cpp
  pkg/common/DataClass/Bound/Aabb.hpp
  pkg/common/DataClass/Shape/Wall.cpp
  pkg/common/Engine/Dispatcher/BoundDispatcher.cpp
  pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp
  pkg/common/Engine/Functor/Bo1_Box_Aabb.cpp
  pkg/common/Engine/Functor/Bo1_Facet_Aabb.cpp
  pkg/common/Engine/Functor/Bo1_Sphere_Aabb.cpp
  pkg/common/RenderingEngine/Gl1_Aabb.cpp
  pkg/common/RenderingEngine/OpenGLRenderingEngine.cpp
  pkg/common/RenderingEngine/OpenGLRenderingEngine.hpp
  pkg/dem/Engine/GlobalEngine/UniaxialStrainer.cpp
  pkg/dem/meta/Tetra.hpp
  py/utils.py
  py/yadeWrapper/customConverters.cpp
  scripts/test/periodic-shear.py


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

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription.
=== modified file 'core/Bound.hpp'
--- core/Bound.hpp	2009-12-18 09:48:16 +0000
+++ core/Bound.hpp	2009-12-18 15:04:57 +0000
@@ -11,19 +11,17 @@
 #include<yade/lib-serialization/Serializable.hpp>
 #include<yade/lib-multimethods/Indexable.hpp>
 
-/*! \brief Abstract interface for all bounding volumes.
-
-	All the bounding volumes (BoundingSphere, Aabb ...) derive from this class. A bounding volume is used to speed up the
-	collision detection. Instead of computing if 2 complex polyhedrons collide each other, it is much faster to first test
-	if their bounding volumes (for example a Aabb) are in collision.
+/*! Interface for approximate body locations in space
+
+	Note: the min and max members refer to shear coordinates, in periodic
+	and sheared space, not cartesian coordinates in the physical space.
+
 */
 
 class Bound : public Serializable, public Indexable
 {
 	public :
-		Vector3r	 diffuseColor		/// Color of the bounding volume. Used only for drawing purpose
-				,min			/// Minimum of the bounding volume
-				,max;			/// Maximum of the bounding volume
+		Vector3r	 diffuseColor,min,max;
 		Bound(): diffuseColor(Vector3r(1,1,1)), min(Vector3r(0,0,0)), max(Vector3r(0,0,0)) {}
 
 	REGISTER_ATTRIBUTES(Serializable,(diffuseColor));

=== modified file 'core/Cell.hpp'
--- core/Cell.hpp	2009-12-18 09:48:16 +0000
+++ core/Cell.hpp	2009-12-18 15:04:57 +0000
@@ -35,15 +35,18 @@
 
 	// caches
 	Vector3r _shearAngle;
-	Vector3r _shearSine;
+	Vector3r _shearSin;
+	Vector3r _shearCos;
 	Matrix3r _shearTrsf;
 	Matrix3r _unshearTrsf;
+
 	double _glShearMatrix[16];
 	// should be called before every step
 	void updateCache(){
 		for(int i=0; i<3; i++) {
 			_shearAngle[i]=atan(shear[i]);
-			_shearSine[i]=sin(_shearAngle[i]);
+			_shearSin[i]=sin(_shearAngle[i]);
+			_shearCos[i]=cos(_shearAngle[i]);
 		}
 		_shearTrsf=Matrix3r(1,shear[2],shear[1],shear[2],1,shear[0],shear[1],shear[0],1);
 		_unshearTrsf=_shearTrsf.Inverse();
@@ -55,8 +58,8 @@
 	Note: the order of OpenGL transoformations matters; for instance, if you draw sheared wire box of size *size*, centered at *center*, the order is:
 
 		1. translation: glTranslatev(center);
+		3. shearing: glMultMatrixd(scene->cell._glShearMatrix);
 		2. scaling: glScalev(size);
-		3. shearing: glMultMatrixd(scene->cell._glShearMatrix);
 		4. draw: glutWireCube(1);
 	
 	See also http://www.songho.ca/opengl/gl_transform.html#matrix
@@ -86,6 +89,8 @@
 			m[3]=0;        m[7]=0;        m[11]=0;       m[15]=1;
 		}
 	#endif
+	/*! Return point inside periodic cell, even if shear is applied */
+	Vector3r wrapShearedPt(const Vector3r& pt){ return shearPt(wrapPt(unshearPt(pt))); }
 	/*! Apply inverse shear on point; to put it inside (unsheared) periodic cell, apply wrapPt on the returned value. */
 	Vector3r unshearPt(const Vector3r& pt){ return _unshearTrsf*pt; }
 	//! Apply shear on point. 

=== modified file 'core/Interaction.cpp'
--- core/Interaction.cpp	2009-12-04 23:07:34 +0000
+++ core/Interaction.cpp	2009-12-18 15:04:57 +0000
@@ -36,4 +36,5 @@
 		throw std::logic_error("Bodies in interaction cannot be swapped if they have interactionGeometry or interactionPhysics.");
 	}
 	std::swap(id1,id2);
+	cellDist*=-1;
 }

=== modified file 'core/Scene.cpp'
--- core/Scene.cpp	2009-12-17 07:13:18 +0000
+++ core/Scene.cpp	2009-12-18 15:04:57 +0000
@@ -68,6 +68,8 @@
 		assert(b->material->id < (int)materials.size());
 		b->material=materials[b->material->id];
 	}
+	// update cell cache, so that rendering is correct even before we start
+	cell.updateCache();
 }
 
 

=== modified file 'pkg/common/DataClass/Bound/Aabb.cpp'
--- pkg/common/DataClass/Bound/Aabb.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/DataClass/Bound/Aabb.cpp	2009-12-18 15:04:57 +0000
@@ -8,13 +8,9 @@
 
 #include "Aabb.hpp"
 
-Aabb::Aabb(): Bound(), halfSize(0,0,0), center(0,0,0){
-	createIndex();
-}
+Aabb::Aabb(): Bound(){ createIndex(); }
 
-Aabb::~Aabb ()
-{
-}
+Aabb::~Aabb(){}
 
 YADE_PLUGIN((Aabb));
 

=== modified file 'pkg/common/DataClass/Bound/Aabb.hpp'
--- pkg/common/DataClass/Bound/Aabb.hpp	2009-12-18 09:48:16 +0000
+++ pkg/common/DataClass/Bound/Aabb.hpp	2009-12-18 15:04:57 +0000
@@ -10,19 +10,19 @@
 
 #include<yade/core/Bound.hpp>
 
-class Aabb : public Bound
-{
+/*! Representation of bound by min and max points.
+
+This class is redundant, since it has no data members; don't delete it, though,
+as Bound::{min,max} might move here one day.
+
+*/
+class Aabb : public Bound{
 	public :
-		Vector3r	 halfSize
-				,center;
-
 		Aabb();
 		virtual ~Aabb();
 	
-/// Serialization
 	REGISTER_CLASS_AND_BASE(Aabb,Bound);	
-	REGISTER_ATTRIBUTES(Bound,/* no attributes */);
-/// Indexable
+	REGISTER_ATTRIBUTES(Bound, /* (min)(max) */ );  // not necessary to store min and max, but it is handy for debugging/python inspection 
 	REGISTER_CLASS_INDEX(Aabb,Bound);
 };
 REGISTER_SERIALIZABLE(Aabb);

=== modified file 'pkg/common/DataClass/Shape/Wall.cpp'
--- pkg/common/DataClass/Shape/Wall.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/DataClass/Shape/Wall.cpp	2009-12-18 15:04:57 +0000
@@ -14,9 +14,8 @@
 void Bo1_Wall_Aabb::go(const shared_ptr<Shape>& cm, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
 	Wall* wall=static_cast<Wall*>(cm.get());
 	Aabb* aabb=static_cast<Aabb*>(bv.get());
-	aabb->center=se3.position;
+	if(scene->isPeriodic && scene->cell.shear!=Vector3r::ZERO) throw logic_error(__FILE__ "Walls not (yet?) supported in sheared cell.");
 	const Real& inf=std::numeric_limits<Real>::infinity();
-	aabb->halfSize=Vector3r(inf,inf,inf); aabb->halfSize[wall->axis]=0.;
 	aabb->min=Vector3r(-inf,-inf,-inf); aabb->min[wall->axis]=se3.position[wall->axis];
 	aabb->max=Vector3r( inf, inf, inf); aabb->max[wall->axis]=se3.position[wall->axis];
 }

=== modified file 'pkg/common/Engine/Dispatcher/BoundDispatcher.cpp'
--- pkg/common/Engine/Dispatcher/BoundDispatcher.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/Engine/Dispatcher/BoundDispatcher.cpp	2009-12-18 15:04:57 +0000
@@ -41,14 +41,14 @@
 		#endif
 		if(sweepDist>0){
 			Aabb* aabb=YADE_CAST<Aabb*>(b->bound.get());
-			aabb->halfSize+=Vector3r(sweepDist,sweepDist,sweepDist);
-			aabb->min=aabb->center-aabb->halfSize; aabb->max=aabb->center+aabb->halfSize;
+			aabb->min-=Vector3r(sweepDist,sweepDist,sweepDist);
+			aabb->max+=Vector3r(sweepDist,sweepDist,sweepDist);
 		}
 		if(haveBins){
 			Aabb* aabb=YADE_CAST<Aabb*>(b->bound.get());
 			Real sweep=velocityBins->bins[velocityBins->bodyBins[b->getId()]].maxDist;
-			aabb->halfSize+=Vector3r(sweep,sweep,sweep);
-			aabb->min=aabb->center-aabb->halfSize; aabb->max=aabb->center+aabb->halfSize;
+			aabb->min-=Vector3r(sweep,sweep,sweep);
+			aabb->max+=Vector3r(sweep,sweep,sweep);
 		}
 	}
 	scene->updateBound();

=== modified file 'pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp'
--- pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp	2009-12-17 07:13:18 +0000
+++ pkg/common/Engine/Dispatcher/InteractionDispatchers.cpp	2009-12-18 15:04:57 +0000
@@ -90,6 +90,8 @@
 			if(!scene->isPeriodic) geomCreated=I->functorCache.geom->go(b1->shape,b2->shape, *b1->state, *b2->state, Vector3r::ZERO, /*force*/false, I);
 			else{ // handle periodicity
 				Vector3r shift2(I->cellDist[0]*cellSize[0],I->cellDist[1]*cellSize[1],I->cellDist[2]*cellSize[2]);
+				// in sheared cell, apply shear on the mutual position as well
+				shift2=scene->cell.shearPt(shift2);
 				geomCreated=I->functorCache.geom->go(b1->shape,b2->shape,*b1->state,*b2->state,shift2,/*force*/false,I);
 			}
 			if(!geomCreated){

=== modified file 'pkg/common/Engine/Functor/Bo1_Box_Aabb.cpp'
--- pkg/common/Engine/Functor/Bo1_Box_Aabb.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/Engine/Functor/Bo1_Box_Aabb.cpp	2009-12-18 15:04:57 +0000
@@ -18,18 +18,18 @@
 {
 	Box* box = static_cast<Box*>(cm.get());
 	Aabb* aabb = static_cast<Aabb*>(bv.get());
+
+	if(scene->isPeriodic && scene->cell.shear!=Vector3r::ZERO) throw logic_error(__FILE__ "Boxes not (yet?) supported in sheared cell.");
 	
-	aabb->center = se3.position;
-
 	Matrix3r r;
 	se3.orientation.ToRotationMatrix(r);
-	aabb->halfSize = Vector3r(0,0,0);
+	Vector3r halfSize(Vector3r::ZERO);
 	for( int i=0; i<3; ++i )
 		for( int j=0; j<3; ++j )
-			aabb->halfSize[i] += fabs( r[i][j] * box->extents[j] );
+			halfSize[i] += fabs( r[i][j] * box->extents[j] );
 	
-	aabb->min = aabb->center-aabb->halfSize;
-	aabb->max = aabb->center+aabb->halfSize;
+	aabb->min = se3.position-halfSize;
+	aabb->max = se3.position+halfSize;
 }
 	
 YADE_PLUGIN((Bo1_Box_Aabb));

=== modified file 'pkg/common/Engine/Functor/Bo1_Facet_Aabb.cpp'
--- pkg/common/Engine/Functor/Bo1_Facet_Aabb.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/Engine/Functor/Bo1_Facet_Aabb.cpp	2009-12-18 15:04:57 +0000
@@ -20,15 +20,23 @@
 	const Vector3r& O = se3.position;
 	Matrix3r facetAxisT; se3.orientation.ToRotationMatrix(facetAxisT);
 	const vector<Vector3r>& vertices=facet->vertices;
-	aabb->min=aabb->max = O + facetAxisT * vertices[0];
-	for (int i=1;i<3;++i)
-	{
-	    Vector3r v = O + facetAxisT * vertices[i];
-	    aabb->min = componentMinVector( aabb->min, v);
-	    aabb->max = componentMaxVector( aabb->max, v);
+	if(!scene->isPeriodic){
+		aabb->min=aabb->max = O + facetAxisT * vertices[0];
+		for (int i=1;i<3;++i)
+		{
+			Vector3r v = O + facetAxisT * vertices[i];
+			aabb->min = componentMinVector( aabb->min, v);
+			aabb->max = componentMaxVector( aabb->max, v);
+		}
+	} else {
+		Real inf=std::numeric_limits<Real>::infinity();
+		aabb->min=Vector3r(inf,inf,inf); aabb->max=Vector3r(-inf,-inf,-inf);
+		for(int i=0; i<3; i++){
+			Vector3r v=scene->cell.unshearPt(O+facetAxisT*vertices[i]);
+			aabb->min=componentMinVector(aabb->min,v);
+			aabb->max=componentMaxVector(aabb->max,v);
+		}
 	}
-	aabb->halfSize = (aabb->max - aabb->min)/2;
-	aabb->center = aabb->min + aabb->halfSize;
 }
 	
 YADE_PLUGIN((Bo1_Facet_Aabb));

=== modified file 'pkg/common/Engine/Functor/Bo1_Sphere_Aabb.cpp'
--- pkg/common/Engine/Functor/Bo1_Sphere_Aabb.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/Engine/Functor/Bo1_Sphere_Aabb.cpp	2009-12-18 15:04:57 +0000
@@ -10,14 +10,19 @@
 #include<yade/pkg-common/Sphere.hpp>
 #include<yade/pkg-common/Aabb.hpp>
 
-void Bo1_Sphere_Aabb::go(const shared_ptr<Shape>& cm, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
+void Bo1_Sphere_Aabb::go(const shared_ptr<Shape>& cm, shared_ptr<Bound>& bv, const Se3r& se3, const Body* b){
 	Sphere* sphere = static_cast<Sphere*>(cm.get());
 	Aabb* aabb = static_cast<Aabb*>(bv.get());
-	aabb->center = se3.position;
-	aabb->halfSize = (aabbEnlargeFactor>0?aabbEnlargeFactor:1.)*Vector3r(sphere->radius,sphere->radius,sphere->radius);
-	
-	aabb->min = aabb->center-aabb->halfSize;
-	aabb->max = aabb->center+aabb->halfSize;	
+	Vector3r halfSize = (aabbEnlargeFactor>0?aabbEnlargeFactor:1.)*Vector3r(sphere->radius,sphere->radius,sphere->radius);
+	// adjust box size along axes so that sphere doesn't stick out of the box even if sheared (i.e. parallelepiped)
+	if(scene->isPeriodic) {
+		const Vector3r& sin(scene->cell._shearSin); const Vector3r& cos(scene->cell._shearCos); const Vector3r& tan(scene->cell.shear);
+		Vector3r fac; //enlarge factors
+		for(int i=0; i<3; i++) fac[i]=cos[i]*(1+pow(sin[i],2))/(1-pow(tan[i],2));
+		for(int i=0; i<3; i++) halfSize[i]*=fac[(i+1)%3]*fac[(i+2)%3];
+	}
+	aabb->min = scene->cell.unshearPt(se3.position)-halfSize;
+	aabb->max = scene->cell.unshearPt(se3.position)+halfSize;	
 }
 	
 YADE_PLUGIN((Bo1_Sphere_Aabb));

=== modified file 'pkg/common/RenderingEngine/Gl1_Aabb.cpp'
--- pkg/common/RenderingEngine/Gl1_Aabb.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/RenderingEngine/Gl1_Aabb.cpp	2009-12-18 15:04:57 +0000
@@ -14,10 +14,10 @@
 void Gl1_Aabb::go(const shared_ptr<Bound>& bv, Scene* scene){
 	Aabb* aabb = static_cast<Aabb*>(bv.get());
 	glColor3v(bv->diffuseColor);
-	glTranslatev(aabb->center);
-	glScalev(2.*aabb->halfSize);
+	glTranslatev(scene->cell.wrapShearedPt(scene->cell.shearPt(.5*(aabb->min+aabb->max))));
 	// glDisable(GL_LIGHTING); // ??
 	if(scene->isPeriodic) glMultMatrixd(scene->cell._glShearMatrix);
+	glScalev(aabb->max-aabb->min);
 	glutWireCube(1);
 }
 

=== modified file 'pkg/common/RenderingEngine/OpenGLRenderingEngine.cpp'
--- pkg/common/RenderingEngine/OpenGLRenderingEngine.cpp	2009-12-18 09:48:16 +0000
+++ pkg/common/RenderingEngine/OpenGLRenderingEngine.cpp	2009-12-18 15:04:57 +0000
@@ -32,6 +32,7 @@
 	Body_interacting_geom = true;
 	Body_wire = false;
 	Interaction_wire = false;
+	intrAllWire=false;
 	Draw_inside = true;
 	Draw_mask = ~0;
 	Light_position = Vector3r(75.0,130.0,0.0);
@@ -115,19 +116,19 @@
 	return Vector3r(wrapCell(pt[0],rb->cell.size[0]),wrapCell(pt[1],rb->cell.size[1]),wrapCell(pt[2],rb->cell.size[2]));
 }
 
-void OpenGLRenderingEngine::setBodiesDispInfo(const shared_ptr<Scene>& rootBody){
-	if(rootBody->bodies->size()!=bodyDisp.size()) bodyDisp.resize(rootBody->bodies->size());
-	FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
+void OpenGLRenderingEngine::setBodiesDispInfo(const shared_ptr<Scene>& scene){
+	if(scene->bodies->size()!=bodyDisp.size()) bodyDisp.resize(scene->bodies->size());
+	FOREACH(const shared_ptr<Body>& b, *scene->bodies){
 		if(!b || !b->state) continue;
 		size_t id=b->getId();
 		const Vector3r& pos=b->state->pos; const Vector3r& refPos=b->state->refPos;
 		const Quaternionr& ori=b->state->ori; const Quaternionr& refOri=b->state->refOri;
-		Vector3r posCell=wrapCellPt(pos,rootBody.get());
+		Vector3r posCell=(!scene->isPeriodic ? pos : scene->cell.wrapShearedPt(pos));
 		bodyDisp[id].isDisplayed=!pointClipped(posCell);	
 		// if no scaling and no periodic, return quickly
-		if(!(scaleDisplacements||scaleRotations||rootBody->isPeriodic)){ bodyDisp[id].pos=pos; bodyDisp[id].ori=ori; continue; }
+		if(!(scaleDisplacements||scaleRotations||scene->isPeriodic)){ bodyDisp[id].pos=pos; bodyDisp[id].ori=ori; continue; }
 		// apply scaling
-		bodyDisp[id].pos=(scaleDisplacements ? diagMult(displacementScale,pos-refPos)+wrapCellPt(refPos,rootBody.get()) : posCell );
+		bodyDisp[id].pos=(scaleDisplacements ? diagMult(displacementScale,pos-refPos)+wrapCellPt(refPos,scene.get()) : posCell );
 		if(!scaleRotations) bodyDisp[id].ori=ori;
 		else{
 			Quaternionr relRot=refOri.Conjugate()*ori;
@@ -141,13 +142,12 @@
 // draw periodic cell, if active
 void OpenGLRenderingEngine::drawPeriodicCell(Scene* scene){
 	if(!scene->isPeriodic) return;
-	const Vector3r& shear(scene->cell.shear);
 	glColor3v(Vector3r(1,1,0));
 	glPushMatrix();
 		// order matters
 		glTranslatev(scene->cell._shearTrsf*(.5*scene->cell.size)); // shear center (moves when sheared)
+		glMultMatrixd(scene->cell._glShearMatrix);
 		glScalev(scene->cell.size);
-		glMultMatrixd(scene->cell._glShearMatrix);
 		glutWireCube(1);
 	glPopMatrix();
 }
@@ -212,12 +212,26 @@
 	if (Body_interacting_geom){
 		glEnable(GL_LIGHTING);
 		glEnable(GL_CULL_FACE);
-		renderInteractingGeometry(rootBody);
+		renderShape(rootBody);
 	}
+	if (intrAllWire) renderAllInteractionsWire(rootBody);
 	if (Interaction_geometry) renderInteractionGeometry(rootBody);
 	if (Interaction_physics) renderInteractionPhysics(rootBody);
 }
 
+void OpenGLRenderingEngine::renderAllInteractionsWire(const shared_ptr<Scene>& scene){
+	FOREACH(const shared_ptr<Interaction>& i, *scene->interactions){
+		glColor3v(i->isReal()? Vector3r(0,1,0) : Vector3r(.5,0,1));
+		Vector3r p1=Body::byId(i->getId1(),scene)->state->pos;
+		Vector3r shift2(i->cellDist[0]*scene->cell.size[0],i->cellDist[1]*scene->cell.size[1],i->cellDist[2]*scene->cell.size[2]);
+		// in sheared cell, apply shear on the mutual position as well
+		shift2=scene->cell.shearPt(shift2);
+		Vector3r rel=Body::byId(i->getId2(),scene)->state->pos+shift2-p1;
+		if(scene->isPeriodic) p1=scene->cell.wrapShearedPt(p1);
+		glBegin(GL_LINES); glVertex3v(p1);glVertex3v(p1+rel);glEnd();
+	}
+}
+
 void OpenGLRenderingEngine::renderDOF_ID(const shared_ptr<Scene>& rootBody){	
 	const GLfloat ambientColorSelected[4]={10.0,0.0,0.0,1.0};	
 	const GLfloat ambientColorUnselected[4]={0.5,0.5,0.5,1.0};	
@@ -321,7 +335,7 @@
 }
 
 
-void OpenGLRenderingEngine::renderInteractingGeometry(const shared_ptr<Scene>& rootBody)
+void OpenGLRenderingEngine::renderShape(const shared_ptr<Scene>& scene)
 {
 	// Additional clipping planes: http://fly.srk.fer.hr/~unreal/theredbook/chapter03.html
 	#if 0
@@ -333,7 +347,7 @@
 	const GLfloat ambientColorSelected[4]={10.0,0.0,0.0,1.0};	
 	const GLfloat ambientColorUnselected[4]={0.5,0.5,0.5,1.0};
 
-	FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
+	FOREACH(const shared_ptr<Body>& b, *scene->bodies){
 		if(!b || !b->shape) continue;
 		if(!bodyDisp[b->getId()].isDisplayed) continue;
 		Vector3r pos=bodyDisp[b->getId()].pos;
@@ -372,21 +386,22 @@
 		}
 		// if the body goes over the cell margin, draw it in positions where the bbox overlaps with the cell in wire
 		// precondition: pos is inside the cell.
-		if(b->bound && rootBody->isPeriodic){
-			const Vector3r& cellSize(rootBody->cell.size);
+		if(b->bound && scene->isPeriodic){
+			const Vector3r& cellSize(scene->cell.size);
+			pos=scene->cell.unshearPt(pos); // remove the shear component
 			// traverse all periodic cells around the body, to see if any of them touches
 			Vector3r halfSize=b->bound->max-b->bound->min; halfSize*=.5;
 			Vector3r pmin,pmax;
 			Vector3<int> i;
 			for(i[0]=-1; i[0]<=1; i[0]++) for(i[1]=-1;i[1]<=1; i[1]++) for(i[2]=-1; i[2]<=1; i[2]++){
 				if(i[0]==0 && i[1]==0 && i[2]==0) continue; // middle; already rendered above
-				Vector3r pt=pos+Vector3r(cellSize[0]*i[0],cellSize[1]*i[1],cellSize[2]*i[2]);
-				pmin=pt-halfSize; pmax=pt+halfSize;
+				Vector3r pos2=pos+Vector3r(cellSize[0]*i[0],cellSize[1]*i[1],cellSize[2]*i[2]); // shift, but without shear!
+				pmin=pos2-halfSize; pmax=pos2+halfSize;
 				if(pmin[0]<=cellSize[0] && pmax[0]>=0 &&
 					pmin[1]<=cellSize[1] && pmax[1]>=0 &&
 					pmin[2]<=cellSize[2] && pmax[2]>=0) {
 					glPushMatrix();
-						glTranslatev(pt);
+						glTranslatev(scene->cell.shearPt(pos2));
 						glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
 						shapeDispatcher(b->shape,b->state,/*Body_wire*/ true, viewInfo);
 					glPopMatrix();
@@ -394,7 +409,7 @@
 			}
 		}
 	}
-	if(rootBody->shape){ glPushMatrix(); shapeDispatcher(rootBody->shape,shared_ptr<State>(),true,viewInfo); glPopMatrix(); }
+	if(scene->shape){ glPushMatrix(); shapeDispatcher(scene->shape,shared_ptr<State>(),true,viewInfo); glPopMatrix(); }
 }
 
 

=== modified file 'pkg/common/RenderingEngine/OpenGLRenderingEngine.hpp'
--- pkg/common/RenderingEngine/OpenGLRenderingEngine.hpp	2009-12-18 09:48:16 +0000
+++ pkg/common/RenderingEngine/OpenGLRenderingEngine.hpp	2009-12-18 15:04:57 +0000
@@ -15,7 +15,7 @@
 	public :
 		Vector3r Light_position,Background_color;
 		bool Show_DOF,Show_ID,Body_state,Body_bounding_volume,Body_interacting_geom,
-			Body_wire,Interaction_wire,Draw_inside,Interaction_geometry,Interaction_physics;
+			Body_wire,Interaction_wire,Draw_inside,Interaction_geometry,Interaction_physics, intrAllWire;
 		body_id_t current_selection;
 		int Draw_mask;
 
@@ -96,12 +96,14 @@
 			void renderState(const shared_ptr<Scene>& rootBody);
 		#endif
 		void renderBoundingVolume(const shared_ptr<Scene>& rootBody);
-		void renderInteractingGeometry(const shared_ptr<Scene>& rootBody);
+		void renderShape(const shared_ptr<Scene>& rootBody);
+
+		void renderAllInteractionsWire(const shared_ptr<Scene>& rootBody);
 	
 	protected :
 		void postProcessAttributes(bool deserializing);
 	REGISTER_ATTRIBUTES(Serializable,(scaleDisplacements)(displacementScale)(scaleRotations)(rotationScale)(Light_position)(Background_color)(Body_wire)(Show_DOF)(Show_ID)(Body_state)(Body_bounding_volume)(Body_interacting_geom)
-		(Interaction_wire)(Interaction_geometry)(Interaction_physics)(Draw_mask)(Draw_inside)(clipPlaneSe3)(clipPlaneActive)(selectBodyLimit));
+		(Interaction_wire)(Interaction_geometry)(Interaction_physics)(Draw_mask)(Draw_inside)(clipPlaneSe3)(clipPlaneActive)(selectBodyLimit)(intrAllWire));
 	REGISTER_CLASS_AND_BASE(OpenGLRenderingEngine,RenderingEngine);
 };
 REGISTER_SERIALIZABLE(OpenGLRenderingEngine);

=== modified file 'pkg/dem/Engine/GlobalEngine/UniaxialStrainer.cpp'
--- pkg/dem/Engine/GlobalEngine/UniaxialStrainer.cpp	2009-12-18 09:48:16 +0000
+++ pkg/dem/Engine/GlobalEngine/UniaxialStrainer.cpp	2009-12-18 15:04:57 +0000
@@ -93,7 +93,8 @@
 		shared_ptr<Aabb> rbAABB;
 		if (Omega::instance().getScene()->bound && (rbAABB=dynamic_pointer_cast<Aabb>(Omega::instance().getScene()->bound))){
 			int axis2=(axis+1)%3, axis3=(axis+2)%3; // perpendicular axes indices
-			crossSectionArea=4*rbAABB->halfSize[axis2]*rbAABB->halfSize[axis3];
+			Vector3r size=rbAABB->max-rbAABB->min;
+			crossSectionArea=size[axis2]*size[axis3];
 			LOG_INFO("Setting crossSectionArea="<<crossSectionArea<<", using axes #"<<axis2<<" and #"<<axis3<<".");
 		} else {
 			crossSectionArea=1.;

=== modified file 'pkg/dem/meta/Tetra.hpp'
--- pkg/dem/meta/Tetra.hpp	2009-12-18 09:48:16 +0000
+++ pkg/dem/meta/Tetra.hpp	2009-12-18 15:04:57 +0000
@@ -75,8 +75,6 @@
 				aabb->min=se3.position+Vector3r(__VOP(std::min,0),__VOP(std::min,1),__VOP(std::min,2));
 				aabb->max=se3.position+Vector3r(__VOP(std::max,0),__VOP(std::max,1),__VOP(std::max,2));
 			#undef __VOP
-			aabb->center=(aabb->min+aabb->max)*0.5;
-			aabb->halfSize=(aabb->max-aabb->min)*0.5;
 		}
 		FUNCTOR2D(TetraMold,Aabb);
 		REGISTER_CLASS_NAME(TetraAABB);

=== modified file 'py/utils.py'
--- py/utils.py	2009-12-18 09:48:16 +0000
+++ py/utils.py	2009-12-18 15:04:57 +0000
@@ -89,6 +89,7 @@
 	if isinstance(material,int): b.mat=O.materials[material]
 	elif isinstance(material,str): b.mat=O.materials[material]
 	elif isinstance(material,Material): b.mat=material
+	elif callable(material): b.mat=material()
 	else: raise TypeError("The 'material' argument must be None (for defaultMaterial), string (for shared material label), int (for shared material id) or Material instance.");
 	## resets state (!!)
 	if resetState: b.state=b.mat.newAssocState()
@@ -106,8 +107,11 @@
 			radius
 		`color`: Vector3 or None
 			random color will be assigned if None
-		`material`: int or string or Material instance
-			if int, O.materials[material] will be used; as a special case, if material==0 and there is no shared materials defined, utils.defaultMaterial() will be assigned to O.materials[0].
+		`material`: int | string | Material instance | callable returning Material instance
+			* if int, O.materials[material] will be used; as a special case, if material==0 and there is no shared materials defined, utils.defaultMaterial() will be assigned to O.materials[0]
+			* if string, it is label of an existing material that will be used
+			* if Material instance, this instance will be used
+			* if callable, it will be called without arguments; returned Material value will be used (Material factory object, if you like)
 
 	:return:
 		A Body instance with desired characteristics.
@@ -142,6 +146,18 @@
 		>>> s2.mat['poisson']
 		0.11
 
+	Finally, material can be a callable object (taking no arguments), which returns a Material instance.
+	Use this if you don't call this function directly (for instance, through yade.pack.randomDensePack), passing
+	only 1 *material* parameter, but you don't want material to be shared.
+
+	For instance, randomized material properties can be created like this:
+
+		>>> import random
+		>>> def matFactory(): return ElasticMat(young=1e10*random.random(),density=1e3+1e3*random.random())
+		... 
+		>>> s3=sphere([0,2,0],1,material=matFactory)
+		>>> s4=sphere([1,2,0],1,material=matFactory)
+
 	"""
 	b=Body()
 	b.shape=Sphere(radius=radius,diffuseColor=color if color else randomColor(),wire=wire,highlight=highlight)

=== modified file 'py/yadeWrapper/customConverters.cpp'
--- py/yadeWrapper/customConverters.cpp	2009-12-04 23:07:34 +0000
+++ py/yadeWrapper/customConverters.cpp	2009-12-18 15:04:57 +0000
@@ -60,6 +60,13 @@
 	}
 };
 
+struct custom_vector3i_to_seq{
+	static PyObject* convert(const Vector3<int> v3i){
+		python::tuple ret(python::make_tuple(v3i[0],v3i[1],v3i[2]));
+		return incref(ret.ptr());
+	}
+};
+
 /* two-way se3 handling */
 struct custom_se3_to_tuple{
 	static PyObject* convert(const Se3r& se3){
@@ -120,12 +127,16 @@
 
 
 
+
+
 using namespace boost::python;
 
 BOOST_PYTHON_MODULE(_customConverters){
 	// class_<std::vector<int> >("vecInt").def(indexing::container_suite<std::vector<int> >());
 	custom_Vector3r_from_seq(); // Vector3r is wrapped, it is returned as a Vector3 instance; no to-python converter needed
 	custom_Se3r_from_seq(); to_python_converter<Se3r,custom_se3_to_tuple>();
+	// Vector3<int> to python (not implemented the other way around yet)
+	custom_vector3i_to_seq(); to_python_converter<Vector3<int>,custom_vector3i_to_seq>();
 	// register from-python converter and to-python converter
 	custom_vector_from_seq<int>(); to_python_converter<std::vector<int>, custom_vector_to_list<int> >();
 	custom_vector_from_seq<Real>(); to_python_converter<std::vector<Real>, custom_vector_to_list<Real> >();

=== modified file 'scripts/test/periodic-shear.py'
--- scripts/test/periodic-shear.py	2009-12-18 09:48:16 +0000
+++ scripts/test/periodic-shear.py	2009-12-18 15:04:57 +0000
@@ -1,15 +1,35 @@
 O.cellSize=Vector3(.5,.5,.5)
-O.bodies.append(utils.sphere([.25,.25,.25],.075))
-O.engines=[BoundDispatcher([Bo1_Sphere_Aabb()])]
+O.bodies.append(utils.facet([[.4,.0001,.3],[.2,.0001,.3],[.3,.2,.2]]))
+O.bodies.append(utils.sphere([.3,.1,.4],.05,dynamic=True))
+O.bodies.append(utils.sphere([.200001,.2000001,.4],.05,dynamic=False))
+O.bodies.append(utils.sphere([.3,0,0],.1,dynamic=False))
+O.engines=[
+	BexResetter(),
+	BoundDispatcher([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()]),
+	InsertionSortCollider(),
+	InteractionDispatchers(
+		[Ig2_Sphere_Sphere_Dem3DofGeom(),Ig2_Facet_Sphere_Dem3DofGeom()],
+		[SimpleElasticRelationships()],
+		[Law2_Dem3Dof_Elastic_Elastic()]
+	),
+	GravityEngine(gravity=(0,0,-10)),
+	NewtonIntegrator(),
+]
+
+g=0.
+while False:
+	O.cellShear=Vector3(.2*sin(g),.2*cos(pi*g),.2*sin(2*g)+.2*cos(3*g))
+	time.sleep(0.001)
+	g+=1e-3
+O.cellShear=(.1,.05,.2)
+O.dt=2e-2*utils.PWaveTimeStep()
 O.step()
-g=0.
+O.saveTmp()
+rdr=yade.qt.Renderer()
+#rdr['Body_bounding_volume']=True
+rdr['intrAllWire']=True
+from yade import log
 import yade.qt,time
 v=yade.qt.View()
 v.axes=True
 v.grid=(True,True,True)
-while False:
-	O.cellShear=Vector3(.2*sin(g),.2*cos(pi*g),.2*sin(2*g)+.2*cos(3*g))
-	time.sleep(0.001)
-	g+=1e-3
-O.cellShear=(.1,0,0)
-yade.qt.Renderer()['Body_bounding_volume']=True