← Back to team overview

yade-dev team mailing list archive

[svn] r1929 - in trunk: extra gui/qt3 pkg/common/Engine/MetaEngine pkg/common/Engine/StandAloneEngine pkg/common/RenderingEngine/OpenGLRenderingEngine scripts/test

 

Author: eudoxos
Date: 2009-08-07 12:27:49 +0200 (Fri, 07 Aug 2009)
New Revision: 1929

Modified:
   trunk/extra/PeriodicInsertionSortCollider.cpp
   trunk/extra/PeriodicInsertionSortCollider.hpp
   trunk/gui/qt3/GLViewer.cpp
   trunk/gui/qt3/GLViewer.hpp
   trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp
   trunk/pkg/common/Engine/MetaEngine/InteractionGeometryMetaEngine.cpp
   trunk/pkg/common/Engine/StandAloneEngine/InsertionSortCollider.cpp
   trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.cpp
   trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.hpp
   trunk/scripts/test/periodic-simple.py
Log:
1. Beta version of periodic boundary conditions (try scripts/test/periodic-simple.py


Modified: trunk/extra/PeriodicInsertionSortCollider.cpp
===================================================================
--- trunk/extra/PeriodicInsertionSortCollider.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/extra/PeriodicInsertionSortCollider.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -18,34 +18,33 @@
 YADE_PLUGIN((PeriodicInsertionSortCollider))
 CREATE_LOGGER(PeriodicInsertionSortCollider);
 
-Real PeriodicInsertionSortCollider::cellWrap(const Real x, const Real x0, const Real x1, long& period){
+Real PeriodicInsertionSortCollider::cellWrap(const Real x, const Real x0, const Real x1, int& period){
 	Real xNorm=(x-x0)/(x1-x0);
-	period=(long)floor(xNorm); // FIXME: some people say this is very slow
+	period=(int)floor(xNorm); // FIXME: some people say this is very slow
 	return x0+(xNorm-period)*(x1-x0);
 }
 
+Real PeriodicInsertionSortCollider::cellWrapRel(const Real x, const Real x0, const Real x1){
+	Real xNorm=(x-x0)/(x1-x0);
+	return (xNorm-floor(xNorm))*(x1-x0);
+}
 
+
 // return true if bodies bb overlap in all 3 dimensions
 bool PeriodicInsertionSortCollider::spatialOverlap(body_id_t id1, body_id_t id2,MetaBody* rb, Vector3<int>& periods) const {
-	assert(id1!=id2) // programming error, or weird bodies (too large?)
+	assert(id1!=id2); // programming error, or weird bodies (too large?)
 	for(int axis=0; axis<3; axis++){
 		Real dim=rb->cellMax[axis]-rb->cellMin[axis];
-		// wrap all 4 numbers to the period starting and the most minimal number
-		#if 0
-			Real mn=min(minima[3*id1+axis],minima[3*id2+axis])-0.001*dim; // avoid rounding issues
-			Real mx=max(maxima[3*id1+axis],maxima[3*id2+axis]);
-			TRVAR2(mn,mx);
-		#endif
 		// too big bodies in interaction
 		assert(maxima[3*id1+axis]-minima[3*id1+axis]<.99*dim); assert(maxima[3*id2+axis]-minima[3*id2+axis]<.99*dim);
 		// different way: find body of which when taken as period start will make the gap smaller
-		long p;
-		Real mn1w=cellWrap(minima[3*id1+axis],0,dim,p), mn2w=cellWrap(minima[3*id2+axis],0,dim,p);
-		Real wMn=(abs(mn2w-mn1w)<dim/2 ? mn1w : mn2w) -/*avoid rounding issues*/1e-4*dim; /* selected wrap base */
+		Real m1=minima[3*id1+axis],m2=minima[3*id2+axis];
+		Real wMn=(cellWrapRel(m1,m2,m2+dim)<cellWrapRel(m2,m1,m1+dim)) ? m2 : m1;
 		//TRVAR3(id1,id2,axis);
 		//TRVAR4(minima[3*id1+axis],maxima[3*id1+axis],minima[3*id2+axis],maxima[3*id2+axis]);
-		//TRVAR3(mn1w,mn2w,wMn);
-		long pmn1,pmx1,pmn2,pmx2;
+		//TRVAR2(cellWrapRel(m1,m2,m2+dim),cellWrapRel(m2,m1,m1+dim));
+		//TRVAR3(m1,m2,wMn);
+		int pmn1,pmx1,pmn2,pmx2;
 		Real mn1=cellWrap(minima[3*id1+axis],wMn,wMn+dim,pmn1), mx1=cellWrap(maxima[3*id1+axis],wMn,wMn+dim,pmx1);
 		Real mn2=cellWrap(minima[3*id2+axis],wMn,wMn+dim,pmn2), mx2=cellWrap(maxima[3*id2+axis],wMn,wMn+dim,pmx2);
 		//TRVAR4(mn1,mx1,mn2,mx2);
@@ -93,16 +92,17 @@
 			long i_1=v.norm(i-1), loIdx_1=v.norm(loIdx-1);
 			// fast test, if the first pair is inverted
 			if(v[i].coord<v[i_1].coord-(i_1==loIdx_1 ? v.cellDim : 0) ){
-				// v.dump(cerr);
+				//v.dump(cerr);
+				if(i==loIdx && v[i].coord<v.cellMin){ v[i].period-=1; v[i].coord+=v.cellDim; loIdx=v.norm(loIdx+1); }
 				hadInversion=true; Bound vi=v[i]; int j; const bool viBB=vi.flags.hasBB;
 				for(j=i_1; vi.coord<v[j].coord-(j==v.norm(loIdx-1) ? v.cellDim : 0); j=v.norm(j-1)) {
 					//{ Bound vj1=v[v.norm(j+1)]; v[v.norm(j+1)]=vi;
 					//v[v.norm(j+1)]=vj1; }
-					long j1=v.norm(j+1); // j2=v.norm(j+2);
-					//LOG_TRACE("Inversion of i="<<i<<"(#"<<vi.id<<" @ "<<vi.coord<<") j="<<j<<"(#"<<v[j].id<<" @ "<<v[j].coord<<"); j1="<<j1<<", j2="<<j2); v.dump(cerr);
+					long j1=v.norm(j+1);
+					//LOG_TRACE("Inversion of i="<<i<<"(#"<<vi.id<<" @ "<<vi.coord<<") j="<<j<<"(#"<<v[j].id<<" @ "<<v[j].coord<<"); j1="<<j1); v.dump(cerr);
 					v[j1]=v[j];
 					//if(v[j1].coord>v.cellMax && j2==loIdx){ v[j1].period+=1; v[j1].coord-=v.cellDim; loIdx=v.norm(loIdx-1); }
-					if(j1==loIdx) { assert(v[j1].coord>v.cellMax); v[j1].period+=1; v[j1].coord-=v.cellDim; loIdx=v.norm(loIdx-1); }
+					if(j1==loIdx) { assert(v[j1].coord>=v.cellMax); v[j1].period+=1; v[j1].coord-=v.cellDim; loIdx=v.norm(loIdx-1); }
 					else if (vi.coord<v.cellMin && j==loIdx){ vi.period-=1; vi.coord+=v.cellDim; loIdx=v.norm(loIdx+1); }
 					if(doCollide && viBB && v[j].flags.hasBB) handleBoundInversion(vi.id,v[j].id,interactions,rb);
 					//v.dump(cerr);
@@ -163,6 +163,13 @@
 			XX[i].coord=((XX[i].flags.hasBB=(bool)bvXX) ? (XX[i].flags.isMin ? bvXX->min[0] : bvXX->max[0]) : (Body::byId(idXX,rb)->physicalParameters->se3.position[0])) - XX.cellDim*XX[i].period;
 			YY[i].coord=((YY[i].flags.hasBB=(bool)bvYY) ? (YY[i].flags.isMin ? bvYY->min[1] : bvYY->max[1]) : (Body::byId(idYY,rb)->physicalParameters->se3.position[1])) - YY.cellDim*YY[i].period;
 			ZZ[i].coord=((ZZ[i].flags.hasBB=(bool)bvZZ) ? (ZZ[i].flags.isMin ? bvZZ->min[2] : bvZZ->max[2]) : (Body::byId(idZZ,rb)->physicalParameters->se3.position[2])) - ZZ.cellDim*ZZ[i].period;
+			// PERI: at the initial step, fix periods of bodies
+			// doInitSort is also called when bodies are just added; changing the period should not have influence here, though.
+			if(doInitSort){
+				if(XX[i].coord<XX.cellMin || XX[i].coord>=XX.cellMax) XX[i].coord=cellWrap(XX[i].coord,XX.cellMin,XX.cellMax,XX[i].period);
+				if(YY[i].coord<XX.cellMin || YY[i].coord>=YY.cellMax) YY[i].coord=cellWrap(YY[i].coord,YY.cellMin,YY.cellMax,YY[i].period);
+				if(ZZ[i].coord<ZZ.cellMin || ZZ[i].coord>=ZZ.cellMax) ZZ[i].coord=cellWrap(ZZ[i].coord,ZZ.cellMin,ZZ.cellMax,ZZ[i].period);
+			}
 			// and for each body, copy its minima and maxima arrays as well
 			if(XX[i].flags.isMin){
 				BOOST_STATIC_ASSERT(sizeof(Vector3r)==3*sizeof(Real));
@@ -171,15 +178,15 @@
 				}  
 				else{ const Vector3r& pos=Body::byId(idXX,rb)->physicalParameters->se3.position; memcpy(&minima[3*idXX],pos,3*sizeof(Real)); memcpy(&maxima[3*idXX],pos,3*sizeof(Real)); }
 				// PERI: add periods, but such that both minimum and maximum is within the cell!
-				Vector3r period(XX[i].period*XX.cellDim,YY[i].period*YY.cellDim,ZZ[i].period*ZZ.cellDim);
-				*(Vector3r*)(&minima[3*idXX])+=period; *(Vector3r*)(&maxima[3*idXX])+=period; //ugh
+				//Vector3r period(XX[i].period*XX.cellDim,YY[i].period*YY.cellDim,ZZ[i].period*ZZ.cellDim);
+				//*(Vector3r*)(&minima[3*idXX])+=period; *(Vector3r*)(&maxima[3*idXX])+=period; //ugh
 			}
 		}
 
 	// process interactions that the constitutive law asked to be erased
 		interactions->erasePending(*this,rb);
-	LOG_DEBUG("Step "<<Omega::instance().getCurrentIteration());
-	ZZ.dump(cerr);
+	//LOG_TRACE("Step "<<Omega::instance().getCurrentIteration());
+	//ZZ.dump(cerr);
 	// XX.dump(cerr); YY.dump(cerr); ZZ.dump(cerr);
 
 	// sort
@@ -188,13 +195,10 @@
 			insertionSort(XX,interactions,rb); insertionSort(YY,interactions,rb); insertionSort(ZZ,interactions,rb);
 		}
 		else {
-			if(doInitSort){
-				// the initial sort is in independent in 3 dimensions, may be run in parallel
-				// it seems that there is no time gain running this in parallel, though
-				 std::sort(XX.vec.begin(),XX.vec.end()); std::sort(YY.vec.begin(),YY.vec.end()); std::sort(ZZ.vec.begin(),ZZ.vec.end());
-			} else { // sortThenCollide
-				insertionSort(XX,interactions,rb,false); insertionSort(YY,interactions,rb,false); insertionSort(ZZ,interactions,rb,false);
-			}
+			// the initial sort is in independent in 3 dimensions, may be run in parallel
+			// it seems that there is no time gain running this in parallel, though
+			 std::sort(XX.vec.begin(),XX.vec.end()); std::sort(YY.vec.begin(),YY.vec.end()); std::sort(ZZ.vec.begin(),ZZ.vec.end());
+
 			// traverse the container along requested axis
 			assert(sortAxis==0 || sortAxis==1 || sortAxis==2);
 			VecBounds& V=(sortAxis==0?XX:(sortAxis==1?YY:ZZ));
@@ -219,7 +223,7 @@
 				*/
 				// TRVAR3(i,iid,V[i].coord);
 				// go up until we meet the upper bound
-				for(size_t j=i+1; V[j].id!=iid && /* handle case 2. of swapped min/max */ j<2*(size_t)nBodies; j++){
+				for(size_t j=i+1; /* handle case 2. of swapped min/max */ j<2*(size_t)nBodies && V[j].id!=iid; j++){
 					const body_id_t& jid=V[j].id;
 					/// Not sure why this doesn't work. If this condition is commented out, we have exact same interactions as from SpatialQuickSort. Otherwise some interactions are missing!
 					// skip bodies with smaller (arbitrary, could be greater as well) id, since they will detect us when their turn comes

Modified: trunk/extra/PeriodicInsertionSortCollider.hpp
===================================================================
--- trunk/extra/PeriodicInsertionSortCollider.hpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/extra/PeriodicInsertionSortCollider.hpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -43,8 +43,10 @@
 	void insertionSort(VecBounds& v,InteractionContainer*,MetaBody*,bool doCollide=true);
 	void handleBoundInversion(body_id_t,body_id_t,InteractionContainer*,MetaBody*);
 	bool spatialOverlap(body_id_t,body_id_t, MetaBody*, Vector3<int>&) const;
-	static Real cellWrap(Real,Real,Real,long&);
+	static Real cellWrap(const Real, const Real, const Real, int&);
+	static Real cellWrapRel(const Real, const Real, const Real);
 
+
 	public:
 	//! axis for the initial sort
 	int sortAxis;

Modified: trunk/gui/qt3/GLViewer.cpp
===================================================================
--- trunk/gui/qt3/GLViewer.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/gui/qt3/GLViewer.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -323,6 +323,18 @@
 	else if(e->key()!=Qt::Key_Escape && e->key()!=Qt::Key_Space) QGLViewer::keyPressEvent(e);
 	updateGL();
 }
+/* Center the scene such that periodic cell is contained in the view */
+void GLViewer::centerPeriodic(){
+	MetaBody* rb=Omega::instance().getRootBody().get();
+	assert(rb->isPeriodic);
+	Vector3r center=.5*(rb->cellMin+rb->cellMax);
+	Vector3r halfSize=.5*(rb->cellMax-rb->cellMin);
+	float radius=std::max(halfSize[0],std::max(halfSize[1],halfSize[2]));
+	LOG_DEBUG("Periodic scene center="<<center<<", halfSize="<<halfSize<<", radius="<<radius);
+	setSceneCenter(qglviewer::Vec(center[0],center[1],center[2]));
+	setSceneRadius(radius*1.5);
+	showEntireScene();
+}
 
 /* Calculate medians for x, y and z coordinates of all bodies;
  *then set scene center to median position and scene radius to 2*inter-quartile distance.
@@ -332,6 +344,7 @@
  * "central" (where most bodies is) part very small or even invisible.
  */
 void GLViewer::centerMedianQuartile(){
+	if(Omega::instance().getRootBody()->isPeriodic){ centerPeriodic(); return; }
 	long nBodies=Omega::instance().getRootBody()->bodies->size();
 	if(nBodies<4) {
 		LOG_INFO("Less than 4 bodies, median makes no sense; calling centerScene() instead.");
@@ -357,6 +370,7 @@
 void GLViewer::centerScene(){
 	MetaBody* rb=Omega::instance().getRootBody().get();
 	if (!rb) return;
+	if(rb->isPeriodic){ centerPeriodic(); return; }
 
 	if(rb->bodies->size()<renderer->selectBodyLimit){LOG_INFO("Less than "+lexical_cast<string>(renderer->selectBodyLimit)+" bodies, moving possible. Select with shift, press 'm' to move.");}
 	else{LOG_INFO("More than "+lexical_cast<string>(renderer->selectBodyLimit)+" (OpenGLRenderingEngine::selectBodyLimit) bodies. Moving not possible.");}

Modified: trunk/gui/qt3/GLViewer.hpp
===================================================================
--- trunk/gui/qt3/GLViewer.hpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/gui/qt3/GLViewer.hpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -66,6 +66,7 @@
 		virtual void draw();
 		virtual void drawWithNames();
 		void centerScene();
+		void centerPeriodic();
 		void mouseMovesCamera();
 		void mouseMovesManipulatedFrame(qglviewer::Constraint* c=NULL);
 		void resetManipulation();

Modified: trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp
===================================================================
--- trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/pkg/common/Engine/MetaEngine/InteractionDispatchers.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -34,6 +34,7 @@
 		LOG_WARN("Interactions pending erase found (erased), no collider being used?");
 		alreadyWarnedNoCollider=true;
 	}
+	Vector3r cellSize; if(rootBody->isPeriodic) cellSize=rootBody->cellMax-rootBody->cellMin;
 	#ifdef YADE_OPENMP
 		const long size=rootBody->interactions->size();
 		#pragma omp parallel for schedule(guided)
@@ -72,7 +73,12 @@
 
 			assert(I->functorCache.geom);
 			bool wasReal=I->isReal();
-			bool geomCreated=I->functorCache.geom->go(b1->interactingGeometry,b2->interactingGeometry,b1->physicalParameters->se3, b2->physicalParameters->se3,I);
+			bool geomCreated;
+			if(!rootBody->isPeriodic) geomCreated=I->functorCache.geom->go(b1->interactingGeometry,b2->interactingGeometry,b1->physicalParameters->se3, b2->physicalParameters->se3,I);
+			else{ // handle periodicity
+				Se3r se32=b2->physicalParameters->se3; se32.position+=Vector3r(I->cellDist[0]*cellSize[0],I->cellDist[1]*cellSize[1],I->cellDist[2]*cellSize[2]);
+				geomCreated=I->functorCache.geom->go(b1->interactingGeometry,b2->interactingGeometry,b1->physicalParameters->se3,se32,I);
+			}
 			if(!geomCreated){
 				if(wasReal) rootBody->interactions->requestErase(I->getId1(),I->getId2()); // fully created interaction without geometry is reset and perhaps erased in the next step
 				continue; // in any case don't care about this one anymore

Modified: trunk/pkg/common/Engine/MetaEngine/InteractionGeometryMetaEngine.cpp
===================================================================
--- trunk/pkg/common/Engine/MetaEngine/InteractionGeometryMetaEngine.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/pkg/common/Engine/MetaEngine/InteractionGeometryMetaEngine.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -51,6 +51,7 @@
 	}
 
 	shared_ptr<BodyContainer>& bodies = ncb->bodies;
+	Vector3r cellSize; if(ncb->isPeriodic) cellSize=ncb->cellMax-ncb->cellMin;
 	#ifdef YADE_OPENMP
 		const long size=ncb->transientInteractions->size();
 		#pragma omp parallel for
@@ -63,10 +64,16 @@
 			const shared_ptr<Body>& b2=(*bodies)[I->getId2()];
 			bool wasReal=I->isReal();
 			if (!b1->interactingGeometry || !b2->interactingGeometry) { assert(!wasReal); continue; } // some bodies do not have interactingGeometry
-			bool geomCreated=operator()(b1->interactingGeometry, b2->interactingGeometry, b1->physicalParameters->se3, b2->physicalParameters->se3, I);
+			bool geomCreated;
+			if(!ncb->isPeriodic){
+				geomCreated=operator()(b1->interactingGeometry, b2->interactingGeometry, b1->physicalParameters->se3, b2->physicalParameters->se3, I);
+			} else{
+				Se3r se32=b2->physicalParameters->se3; se32.position+=Vector3r(I->cellDist[0]*cellSize[0],I->cellDist[1]*cellSize[1],I->cellDist[2]*cellSize[2]); // add periodicity to the position of the 2nd body
+				geomCreated=operator()(b1->interactingGeometry, b2->interactingGeometry, b1->physicalParameters->se3, se32, I);
+			}
 			// reset && erase interaction that existed but now has no geometry anymore
 			if(wasReal && !geomCreated){ ncb->interactions->requestErase(I->getId1(),I->getId2()); }
 	}
 }
 
-YADE_PLUGIN((InteractionGeometryMetaEngine));
\ No newline at end of file
+YADE_PLUGIN((InteractionGeometryMetaEngine));

Modified: trunk/pkg/common/Engine/StandAloneEngine/InsertionSortCollider.cpp
===================================================================
--- trunk/pkg/common/Engine/StandAloneEngine/InsertionSortCollider.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/pkg/common/Engine/StandAloneEngine/InsertionSortCollider.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -230,7 +230,7 @@
 				*/
 				// TRVAR3(i,iid,V[i].coord);
 				// go up until we meet the upper bound
-				for(size_t j=i+1; V[j].id!=iid && /* handle case 2. of swapped min/max */ j<2*nBodies; j++){
+				for(size_t j=i+1; /* handle case 2. of swapped min/max */ j<2*nBodies && V[j].id!=iid; j++){
 					const body_id_t& jid=V[j].id;
 					/// Not sure why this doesn't work. If this condition is commented out, we have exact same interactions as from SpatialQuickSort. Otherwise some interactions are missing!
 					// skip bodies with smaller (arbitrary, could be greater as well) id, since they will detect us when their turn comes

Modified: trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.cpp
===================================================================
--- trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.cpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.cpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -109,16 +109,28 @@
 	numBodiesWhenRefSe3LastSet=rootBody->bodies->size();
 	numIterWhenRefSe3LastSet=Omega::instance().getCurrentIteration();
 }
+/* mostly copied from PeriodicInsertionSortCollider
+ 	FIXME: common implementation somewhere */
 
+Real OpenGLRenderingEngine::wrapCell(const Real x, const Real x0, const Real x1){
+	Real xNorm=(x-x0)/(x1-x0);
+	return x0+(xNorm-floor(xNorm))*(x1-x0);
+}
+Vector3r OpenGLRenderingEngine::wrapCellPt(const Vector3r& pt, MetaBody* rb){
+	if(!rb->isPeriodic) return pt;
+	return Vector3r(wrapCell(pt[0],rb->cellMin[0],rb->cellMax[0]),wrapCell(pt[1],rb->cellMin[1],rb->cellMax[1]),wrapCell(pt[2],rb->cellMin[2],rb->cellMax[2]));
+}
+
 void OpenGLRenderingEngine::setBodiesDispSe3(const shared_ptr<MetaBody>& rootBody){
 	FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
 		if(!b->physicalParameters) continue;
 		const Se3r& se3=b->physicalParameters->se3; const Se3r& refSe3=b->physicalParameters->refSe3; Se3r& dispSe3=b->physicalParameters->dispSe3;
-		b->physicalParameters->isDisplayed=!pointClipped(se3.position);
+		Vector3r posCell=wrapCellPt(se3.position,rootBody.get());
+		b->physicalParameters->isDisplayed=!pointClipped(posCell);
 		// if no scaling, return quickly
-		if(!(scaleDisplacements||scaleRotations)){ b->physicalParameters->dispSe3=b->physicalParameters->se3; continue; }
+		if(!(scaleDisplacements||scaleRotations||rootBody->isPeriodic)){ b->physicalParameters->dispSe3=b->physicalParameters->se3; continue; }
 		// apply scaling
-		dispSe3.position=(scaleDisplacements ? diagMult(displacementScale,se3.position-refSe3.position)+refSe3.position : se3.position );
+		dispSe3.position=(scaleDisplacements ? diagMult(displacementScale,se3.position-refSe3.position)+wrapCellPt(refSe3.position,rootBody.get()) : posCell );
 		if(scaleRotations){
 			Quaternionr relRot=refSe3.orientation.Conjugate()*se3.orientation;
 			Vector3r axis; Real angle; relRot.ToAxisAngle(axis,angle);
@@ -127,7 +139,19 @@
 		} else {dispSe3.orientation=se3.orientation;}
 	}
 }
+// draw periodic cell, if active
+void OpenGLRenderingEngine::drawPeriodicCell(MetaBody* rootBody){
+	if(!rootBody->isPeriodic) return;
+	glPushMatrix();
+		glColor3v(Vector3r(1,1,0));
+		Vector3r cent=.5*(rootBody->cellMin+rootBody->cellMax); Vector3r size=rootBody->cellMax-rootBody->cellMin;
+		glTranslate(cent[0],cent[1],cent[2]); glScale(size[0],size[1],size[2]);
+		glutWireCube(1);
+	glPopMatrix();
+}
 
+
+
 void OpenGLRenderingEngine::render(const shared_ptr<MetaBody>& rootBody, body_id_t selection	/*FIXME: not sure. maybe a list of selections, or maybe bodies themselves should remember if they are selected? */) {
 
 	assert(glutInitDone);
@@ -167,6 +191,8 @@
 	// set displayed Se3 of body (scaling) and isDisplayed (clipping)
 	setBodiesDispSe3(rootBody);
 
+	drawPeriodicCell(rootBody.get());
+
 	if (Show_DOF || Show_ID) renderDOF_ID(rootBody);
 	if (Body_geometrical_model){
 		if (Cast_shadows){	
@@ -401,49 +427,78 @@
 	if(rootBody->geometricalModel) geometricalModelDispatcher(rootBody->geometricalModel,rootBody->physicalParameters,Body_wire);
 }
 
-void OpenGLRenderingEngine::renderGeometricalModel(const shared_ptr<MetaBody>& rootBody){	
+void OpenGLRenderingEngine::renderGeometricalModel(const shared_ptr<MetaBody>& 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};
-	if((rootBody->geometricalModel || Draw_inside) && Draw_inside) {
-		FOREACH(const shared_ptr<Body> b, *rootBody->bodies){
-			if(b->geometricalModel && ((b->getGroupMask() & Draw_mask) || b->getGroupMask()==0)){
-				if(b->physicalParameters && !b->physicalParameters->isDisplayed) continue;
-				const Se3r& se3=b->physicalParameters->dispSe3;
-				glPushMatrix();
-				Real angle; Vector3r axis;	se3.orientation.ToAxisAngle(axis,angle);	
-				glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
-				glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
-				if(current_selection==b->getId() || b->geometricalModel->highlight){
-					glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientColorSelected);
-					glColorMaterial(GL_FRONT_AND_BACK,GL_EMISSION);
-					const Vector3r& h(current_selection==b->getId() ? highlightEmission0 : highlightEmission1);
-					glColor4(h[0],h[1],h[2],.2);
-					glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
+	if(rootBody->geometricalModel) geometricalModelDispatcher(rootBody->geometricalModel,rootBody->physicalParameters,Body_wire);
+	if(!Draw_inside) return;
+	FOREACH(const shared_ptr<Body> b, *rootBody->bodies){
+		if(!b->geometricalModel || (!((b->getGroupMask() & Draw_mask) || b->getGroupMask()==0))) continue;
+		if(b->physicalParameters && !b->physicalParameters->isDisplayed) continue;
+		const Se3r& se3=b->physicalParameters->dispSe3;
+		glPushMatrix();
+		Real angle; Vector3r axis;	se3.orientation.ToAxisAngle(axis,angle);	
+		glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
+		glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
+		if(current_selection==b->getId() || b->geometricalModel->highlight){
+			glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientColorSelected);
+			glColorMaterial(GL_FRONT_AND_BACK,GL_EMISSION);
+			const Vector3r& h(current_selection==b->getId() ? highlightEmission0 : highlightEmission1);
+			glColor4(h[0],h[1],h[2],.2);
+			glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
 
-					geometricalModelDispatcher(b->geometricalModel,b->physicalParameters,Body_wire);
+			geometricalModelDispatcher(b->geometricalModel,b->physicalParameters,Body_wire);
 
-					glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientColorUnselected);
-					glColorMaterial(GL_FRONT_AND_BACK,GL_EMISSION);
-					glColor3v(Vector3r::ZERO);
-					glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
-				} else {
-					geometricalModelDispatcher(b->geometricalModel,b->physicalParameters,Body_wire);
+			glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientColorUnselected);
+			glColorMaterial(GL_FRONT_AND_BACK,GL_EMISSION);
+			glColor3v(Vector3r::ZERO);
+			glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
+		} else {
+			geometricalModelDispatcher(b->geometricalModel,b->physicalParameters,Body_wire);
+		}
+		glPopMatrix();
+		if(current_selection==b->getId() || b->geometricalModel->highlight){
+			if(!b->boundingVolume || Body_wire || b->geometricalModel->wire) GLUtils::GLDrawInt(b->getId(),se3.position);
+			else {
+				// move the label towards the camera by the bounding box so that it is not hidden inside the body
+				const Vector3r& mn=b->boundingVolume->min; const Vector3r& mx=b->boundingVolume->max; const Vector3r& p=se3.position;
+				Vector3r ext(viewDirection[0]>0?p[0]-mn[0]:p[0]-mx[0],viewDirection[1]>0?p[1]-mn[1]:p[1]-mx[1],viewDirection[2]>0?p[2]-mn[2]:p[2]-mx[2]); // signed extents towards the camera
+				Vector3r dr=-1.01*(viewDirection.Dot(ext)*viewDirection);
+				GLUtils::GLDrawInt(b->getId(),se3.position+dr,Vector3r::ONE);
+			}
+		}
+		// if the body goes over the cell margin, draw it in all other positions with wire
+		if(b->boundingVolume && rootBody->isPeriodic){
+			const Vector3r& cellMin(rootBody->cellMin); const Vector3r& cellMax(rootBody->cellMax); Vector3r cellSize=cellMax-cellMin;
+			Vector3<int> bodyPer,minPer,maxPer;
+			for(int i=0; i<3; i++){
+				bodyPer[i]=(int)floor((b->physicalParameters->se3.position[i]-cellMin[i])/cellSize[i]);
+				minPer[i]=(int)floor((b->boundingVolume->min[i]-cellMin[i])/cellSize[i]);
+				maxPer[i]=(int)floor((b->boundingVolume->max[i]-cellMin[i])/cellSize[i]);
+				//assert(bodyPer[i]<=maxPer[i]); assert(bodyPer[i]>=minPer[i]);
+			}
+			/* m is bitmask from 3 couples (0…64=2^6) */
+			for(int m=0; m<64; m++){
+				// any mask containing 00 couple is invalid
+				if((!(m&1) && (!(m&2))) || (!(m&4) && (!(m&8))) || (!(m&16) && (!(m&32)))) continue;
+				Vector3r pt(se3.position);
+				bool isInside=false;
+				for(int j=0; j<3; j++){
+					if(m&(1<<(2*j))) {
+						if(m&(1<<(2*j+1))) { if(bodyPer[j]>=maxPer[j]) {isInside=true; break; } pt[j]-=cellSize[j]; }
+						else { if(bodyPer[j]<=minPer[j]){ isInside=true; break; } pt[j]+=cellSize[j]; }
+					}
 				}
+				if(isInside) continue;
+				if(pt==se3.position) continue; // shouldn't happen, but it happens :-(
+				glPushMatrix();
+					glTranslatev(pt);
+					glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
+					geometricalModelDispatcher(b->geometricalModel,b->physicalParameters,/*Body_wire*/ true);
 				glPopMatrix();
-				if(current_selection==b->getId() || b->geometricalModel->highlight){
-					if(!b->boundingVolume || Body_wire || b->geometricalModel->wire) GLUtils::GLDrawInt(b->getId(),se3.position);
-					else {
-						// move the label towards the camera by the bounding box so that it is not hidden inside the body
-						const Vector3r& mn=b->boundingVolume->min; const Vector3r& mx=b->boundingVolume->max; const Vector3r& p=se3.position;
-						Vector3r ext(viewDirection[0]>0?p[0]-mn[0]:p[0]-mx[0],viewDirection[1]>0?p[1]-mn[1]:p[1]-mx[1],viewDirection[2]>0?p[2]-mn[2]:p[2]-mx[2]); // signed extents towards the camera
-						Vector3r dr=-1.01*(viewDirection.Dot(ext)*viewDirection);
-						GLUtils::GLDrawInt(b->getId(),se3.position+dr,Vector3r::ONE);
-					}
-				}
 			}
 		}
 	}
-	if(rootBody->geometricalModel) geometricalModelDispatcher(rootBody->geometricalModel,rootBody->physicalParameters,Body_wire);
 }
 
 
@@ -509,10 +564,10 @@
 		const Se3r& se3=b->physicalParameters->dispSe3;
 		if(b->interactingGeometry && ((b->getGroupMask()&Draw_mask) || b->getGroupMask()==0)){
 			glPushMatrix();
-			Real angle;	Vector3r axis;	se3.orientation.ToAxisAngle(axis,angle);	
-			glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
-			glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
-			interactingGeometryDispatcher(b->interactingGeometry,b->physicalParameters,Body_wire);
+				Real angle;	Vector3r axis;	se3.orientation.ToAxisAngle(axis,angle);	
+				glTranslatef(se3.position[0],se3.position[1],se3.position[2]);
+				glRotatef(angle*Mathr::RAD_TO_DEG,axis[0],axis[1],axis[2]);
+				interactingGeometryDispatcher(b->interactingGeometry,b->physicalParameters,Body_wire);
 			glPopMatrix();
 		}
 	}

Modified: trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.hpp
===================================================================
--- trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.hpp	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/pkg/common/RenderingEngine/OpenGLRenderingEngine/OpenGLRenderingEngine.hpp	2009-08-07 10:27:49 UTC (rev 1929)
@@ -39,6 +39,13 @@
 		Real normSaw(Real t, Real period){ Real xi=(t-period*((int)(t/period)))/period; /* normalized value, (0-1〉 */ return (xi<.5?2*xi:2-2*xi); }
 		Real normSquare(Real t, Real period){ Real xi=(t-period*((int)(t/period)))/period; /* normalized value, (0-1〉 */ return (xi<.5?0:1); }
 
+		//! wrap number to interval x0…x1
+		Real wrapCell(const Real x, const Real x0, const Real x1);
+		//! wrap point to inside MetaBody's cell (identity if !MetaBody::isPeriodic)
+		Vector3r wrapCellPt(const Vector3r& pt, MetaBody* rb);
+		void drawPeriodicCell(MetaBody*);
+
+
 	private :
 		DynLibDispatcher< InteractionGeometry , GLDrawInteractionGeometryFunctor, void , TYPELIST_5(const shared_ptr<InteractionGeometry>&, const shared_ptr<Interaction>& , const shared_ptr<Body>&, const shared_ptr<Body>&, bool) > interactionGeometryDispatcher;
 		DynLibDispatcher< InteractionPhysics  , GLDrawInteractionPhysicsFunctor,  void , TYPELIST_5(const shared_ptr<InteractionPhysics>& , const shared_ptr<Interaction>&, const shared_ptr<Body>&, const shared_ptr<Body>&, bool) > interactionPhysicsDispatcher;

Modified: trunk/scripts/test/periodic-simple.py
===================================================================
--- trunk/scripts/test/periodic-simple.py	2009-08-06 22:05:28 UTC (rev 1928)
+++ trunk/scripts/test/periodic-simple.py	2009-08-07 10:27:49 UTC (rev 1929)
@@ -10,15 +10,15 @@
 		[Law2_Dem3Dof_Elastic_Elastic()],
 	),
 	GravityEngine(gravity=[0,0,-10]),
-	NewtonsDampedLaw(damping=.1)
+	TranslationEngine(translationAxis=(1,0,0),velocity=10,subscribedBodies=[0]),
+	NewtonsDampedLaw(damping=.4)
 ]
-O.bodies.append(utils.sphere([0,0,2],1,dynamic=False,density=1000))
-O.bodies.append(utils.sphere([0,0,3],1,density=1000))
+O.bodies.append(utils.sphere([-4,0,11],2,dynamic=False,density=1000))
+O.bodies.append(utils.sphere([0,-1,5.5],2,density=1000))
+O.bodies.append(utils.sphere([0,2,5.5],2,density=2000))
 O.periodicCell=((-5,-5,0),(5,5,10))
-O.dt=utils.PWaveTimeStep()
+O.dt=.1*utils.PWaveTimeStep()
 O.saveTmp()
 from yade import qt
 qt.Controller()
 qt.View()
-O.run(17)
-




Follow ups