← Back to team overview

yade-dev team mailing list archive

[svn] r1794 - in trunk: gui gui/py scripts/test

 

Author: eudoxos
Date: 2009-06-13 17:09:51 +0200 (Sat, 13 Jun 2009)
New Revision: 1794

Added:
   trunk/gui/py/_packPredicates.cpp
   trunk/gui/py/pack.py
Modified:
   trunk/gui/SConscript
   trunk/gui/py/PythonUI.cpp
   trunk/gui/py/PythonUI_rc.py
   trunk/scripts/test/regular-sphere-pack.py
Log:
1. Move all python modules to PREFIX/lib/yadeSUFFIX/py/yade instead of */gui/yade
2. Add pack module for generating regular packings (thanks to Anton Gladky for his ideas & code)
3. Add _packPredicates module for solid inclusion testing.
4. Update regular-sphere-pack.py to test new packing features.



Modified: trunk/gui/SConscript
===================================================================
--- trunk/gui/SConscript	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/gui/SConscript	2009-06-13 15:09:51 UTC (rev 1794)
@@ -49,7 +49,7 @@
 		env.File('PythonUI_rc.py','py'),
 	])
 	if 'qt3' not in env['exclude']:
-		env.Install('$PREFIX/lib/yade$SUFFIX/gui/yade',[
+		env.Install('$PREFIX/lib/yade$SUFFIX/py/yade',[
 			env.SharedLibrary('_qt',['qt3/QtGUI-python.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['QtGUI'],CPPPATH=env['CPPPATH']+[env['buildDir']+'/gui/qt3']), # CPPPATH is for files generated by moc which are indirectly included
 			env.File('qt.py','qt3'),
 		])
@@ -57,7 +57,7 @@
 	env.InstallAs('$PREFIX/bin/yade$SUFFIX-multi',env.File('yade-multi','py'))
 
 	# python modules are one level deeper so that you can say: from yade.wrapper import *
-	env.Install('$PREFIX/lib/yade$SUFFIX/gui/yade',[
+	env.Install('$PREFIX/lib/yade$SUFFIX/py/yade',[
 		env.SharedLibrary('wrapper',['py/yadeControl.cpp'],SHLIBPREFIX='',
 			LIBS=env['LIBS']+['XMLFormatManager','yade-factory','yade-serialization','Shop',
 				'BoundingVolumeMetaEngine',
@@ -75,6 +75,7 @@
 			),
 		env.SharedLibrary('_utils',['py/_utils.cpp'],SHLIBPREFIX='',
 			LIBS=env['LIBS']+['Shop','ConcretePM']),
+		env.SharedLibrary('_packPredicates',['py/_packPredicates.cpp'],SHLIBPREFIX=''),
 		env.SharedLibrary('_eudoxos',['py/_eudoxos.cpp'],SHLIBPREFIX='',CXXFLAGS=env['CXXFLAGS']+([] if not os.path.exists('../../brefcom-mm.hh') else ['-include','../brefcom-mm.hh']),LIBS=env['LIBS']+['Shop','ConcretePM']),
 		env.SharedLibrary('log',['py/log.cpp'],SHLIBPREFIX=''),
 		env.File('__init__.py','py'),
@@ -86,6 +87,7 @@
 		env.File('linterpolation.py','py'),
 		env.File('timing.py','py'),
 		env.File('PythonTCPServer.py','py'),
+		env.File('pack.py','py'),
 	])
 	if os.path.exists('../../brefcom-mm.hh'): Depends('py/_eudoxos.cpp','../../brefcom-mm.hh')
 

Modified: trunk/gui/py/PythonUI.cpp
===================================================================
--- trunk/gui/py/PythonUI.cpp	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/gui/py/PythonUI.cpp	2009-06-13 15:09:51 UTC (rev 1794)
@@ -79,7 +79,7 @@
 	PyGILState_STATE pyState = PyGILState_Ensure();
 		LOG_DEBUG("Got Global Interpreter Lock, good.");
 		/* import yade (for startUI()) and yade.runtime (initially empty) namespaces */
-		PyRun_SimpleString("import sys; sys.path.insert(0,'" PREFIX "/lib/yade" SUFFIX "/gui')");
+		PyRun_SimpleString("import sys; sys.path.insert(0,'" PREFIX "/lib/yade" SUFFIX "/py')");
 		PyRun_SimpleString("import yade");
 		PyRun_SimpleString("from __future__ import division");
 

Modified: trunk/gui/py/PythonUI_rc.py
===================================================================
--- trunk/gui/py/PythonUI_rc.py	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/gui/py/PythonUI_rc.py	2009-06-13 15:09:51 UTC (rev 1794)
@@ -122,7 +122,7 @@
 	  | | (_| | |_| |  __/ | |__| (_) | | | \__ \ (_) | |  __/
 	  |_|\__,_|____/ \___|  \____\___/|_| |_|___/\___/|_|\___|
 	""",exit_msg='Bye.'
-		,rc_override={'execfile':[runtime.prefix+'/lib/yade'+runtime.suffix+'/gui/yade/ipython.py']})
+		,rc_override={'execfile':[runtime.prefix+'/lib/yade'+runtime.suffix+'/py/yade/ipython.py']})
 
 		ipshell()
 		# save history -- a workaround for atexit handlers not being run (why?)

Added: trunk/gui/py/_packPredicates.cpp
===================================================================
--- trunk/gui/py/_packPredicates.cpp	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/gui/py/_packPredicates.cpp	2009-06-13 15:09:51 UTC (rev 1794)
@@ -0,0 +1,93 @@
+// 2009 © Václav Šmilauer <eudoxos@xxxxxxxx>
+#include<boost/python.hpp>
+#include<yade/extra/boost_python_len.hpp>
+#include<yade/lib-base/Logging.hpp>
+#include<yade/lib-base/yadeWm3.hpp>
+#include<Wm3Vector3.h>
+
+using namespace boost;
+using namespace std;
+#ifdef LOG4CXX
+	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.predicates");
+#endif
+
+/*
+This file contains various predicates that say whether a given point is within the solid,
+or, not closer than "pad" to its boundary, if pad is nonzero
+Besides the (point,pad) operator, each predicate defines aabb() method that returns
+(min,max) tuple defining minimum and maximum point of axis-aligned bounding box 
+for the predicate.
+
+These classes are primarily used for yade.pack.* functions creating packings.
+See scripts/test/regular-sphere-pack.py for an example.
+
+*/
+
+// aux functions
+python::tuple vec2tuple(const Vector3r& v){return boost::python::make_tuple(v[0],v[1],v[2]);}
+Vector3r tuple2vec(const python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
+void ttuple2vvec(const python::tuple& t, Vector3r& v1, Vector3r& v2){ v1=tuple2vec(python::extract<python::tuple>(t[0])()); v2=tuple2vec(python::extract<python::tuple>(t[1])()); }
+python::tuple vvec2ttuple(const Vector3r&v1, const Vector3r&v2){ return python::make_tuple(vec2tuple(v1),vec2tuple(v2)); }
+
+
+/*! Sphere predicate */
+class inSphere {
+	Vector3r center; Real radius;
+public:
+	inSphere(python::tuple _center, Real _radius){center=tuple2vec(_center); radius=_radius;}
+	bool operator()(python::tuple _pt, Real pad=0.){
+		Vector3r pt=tuple2vec(_pt);
+		return ((pt-center).Length()-pad<=radius-pad);
+	}
+	python::tuple aabb(){return vvec2ttuple(Vector3r(center[0]-radius,center[1]-radius,center[2]-radius),Vector3r(center[0]+radius,center[1]+radius,center[2]+radius));}
+};
+
+/* Axis-aligned box predicate */
+class inAlignedBox{
+	Vector3r mn, mx;
+public:
+	inAlignedBox(python::tuple _mn, python::tuple _mx){mn=tuple2vec(_mn); mx=tuple2vec(_mx);}
+	bool operator()(python::tuple _pt, Real pad=0.){
+		Vector3r pt=tuple2vec(_pt);
+		return
+			mn[0]+pad<=pt[0] && mx[0]-pad>=pt[0] &&
+			mn[1]+pad<=pt[1] && mx[1]-pad>=pt[1] &&
+			mn[2]+pad<=pt[2] && mx[2]-pad>=pt[2];
+	}
+	python::tuple aabb(){ return vvec2ttuple(mn,mx); }
+};
+
+/* Arbitrarily oriented cylinder predicate */
+class inCylinder{
+	Vector3r c1,c2,c12; Real radius,ht;
+public:
+	inCylinder(python::tuple _c1, python::tuple _c2, Real _radius){c1=tuple2vec(_c1); c2=tuple2vec(_c2); c12=c2-c1; radius=_radius; ht=c12.Length(); }
+	bool operator()(python::tuple _pt, Real pad=0.){
+		Vector3r pt=tuple2vec(_pt);
+		Real u=(pt.Dot(c12)-c1.Dot(c12))/(ht*ht); // normalized coordinate along the c1--c2 axis
+		if((u*ht<0+pad) || (u*ht>ht-pad)) return false; // out of cylinder along the axis
+		Real axisDist=((pt-c1).Cross(pt-c2)).Length()/ht;
+		if(axisDist>radius-pad) return false;
+		return true;
+	}
+	python::tuple aabb(){
+		// see http://www.gamedev.net/community/forums/topic.asp?topic_id=338522&forum_id=20&gforum_id=0 for the algorithm
+		Vector3r& A(c1); Vector3r& B(c2); 
+		Vector3r k(
+			sqrt((pow(A[1]-B[1],2)+pow(A[2]-B[2],2)))/ht,
+			sqrt((pow(A[0]-B[0],2)+pow(A[2]-B[2],2)))/ht,
+			sqrt((pow(A[0]-B[0],2)+pow(A[1]-B[1],2)))/ht);
+		Vector3r mn(min(A[0],B[0]),min(A[1],B[1]),min(A[2],B[2])), mx(max(A[0],B[0]),max(A[1],B[1]),max(A[2],B[2]));
+		return vvec2ttuple(mn-radius*k,mx+radius*k);
+	}
+};
+
+BOOST_PYTHON_MODULE(_packPredicates){
+	boost::python::class_<inSphere>("inSphere","Sphere predicate.",python::init<python::tuple,Real>("Ctor taking center (as a 3-tuple) and radius"))
+		.def("__call__",&inSphere::operator(),"Tell whether given point lies within this sphere, still having 'pad' space to the solid boundary").def("aabb",&inSphere::aabb,"Return minimum and maximum values for AABB");
+	boost::python::class_<inAlignedBox>("inAlignedBox","Axis-aligned box predicate",python::init<python::tuple,python::tuple>("Ctor taking minumum and maximum points of the box (as 3-tuples)."))
+		.def("__call__",&inAlignedBox::operator(),"Tell whether given point lies within this box, still having 'pad' space to the solid boundary").def("aabb",&inAlignedBox::aabb,"Return minimum and maximum values for AABB");
+	boost::python::class_<inCylinder>("inCylinder","Cylinder predicate",python::init<python::tuple,python::tuple,Real>("Ctor taking centers of the lateral walls (as 3-tuples) and radius."))
+		.def("__call__",&inCylinder::operator(),"Tell whether given point lies within this cylinder, still having 'pad' space to the solid boundary").def("aabb",&inCylinder::aabb,"Return minimum and maximum values for AABB");
+}
+

Added: trunk/gui/py/pack.py
===================================================================
--- trunk/gui/py/pack.py	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/gui/py/pack.py	2009-06-13 15:09:51 UTC (rev 1794)
@@ -0,0 +1,34 @@
+
+from numpy import arange; from math import sqrt
+import itertools
+from yade import utils
+
+# make predicates available from yade.pack
+from _packPredicates import *
+
+def regularOrtho(predicate,radius,gap,**kw):
+	"""Return set of spheres in regular orthogonal grid, clipped inside solid given by predicate.
+	Created spheres will have given radius and will be separated by gap space."""
+	ret=[]
+	mn,mx=predicate.aabb()
+	xx,yy,zz=[arange(mn[i]+radius,mx[i]-radius,2*radius+gap) for i in 0,1,2]
+	for xyz in itertools.product(xx,yy,zz):
+		if predicate(xyz,radius): ret+=[utils.sphere(xyz,radius=radius,**kw)]
+	return ret
+
+def regularHexa(predicate,radius,gap,**kw):
+	"""Return set of spheres in regular hexagonal grid, clipped inside solid given by predicate.
+	Created spheres will have given radius and will be separated by gap space."""
+	ret=[]
+	a=2*radius+gap
+	h=a*sqrt(3)/2.
+	mn,mx=predicate.aabb()
+	dim=[mx[i]-mn[i] for i in 0,1,2]
+	ii,jj,kk=[range(0,int(2*dim[0]/a)+1),range(0,int(dim[1]/h)+1),range(0,int(dim[2]/h)+1)]
+	for i,j,k in itertools.product(ii,jj,kk):
+		x,y,z=mn[0]+radius+i*a,mn[1]+radius+j*h,mn[2]+radius+k*h
+		if j%2==0: x+= a/2. if k%2==0 else -a/2.
+		if k%2!=0: x+=a/2.; y+=h/2.
+		if predicate((x,y,z),radius): ret+=[utils.sphere((x,y,z),radius=radius,**kw)]
+	return ret
+

Modified: trunk/scripts/test/regular-sphere-pack.py
===================================================================
--- trunk/scripts/test/regular-sphere-pack.py	2009-06-09 11:23:06 UTC (rev 1793)
+++ trunk/scripts/test/regular-sphere-pack.py	2009-06-13 15:09:51 UTC (rev 1794)
@@ -1,3 +1,43 @@
-O.bodies.append(utils.regularSphereOrthoPack([0,0,0],extents=[2,2,2],radius=.1,gap=.1,color=(1,0,1)))
-O.bodies.append(utils.regularSphereOrthoPack([0,0,4],extents=2,radius=.1,gap=.1,color=(0,1,0),velocity=[0,10,0]))
+from yade import pack
 
+rad,gap=.15,.02
+rho=1e3
+kw={'density':rho}
+
+O.bodies.append(
+	pack.regularHexa(pack.inSphere((0,0,4),2),radius=rad,gap=gap,color=(0,1,0),density=10*rho) # head
+	+[utils.sphere((.8,1.9,5),radius=.2,color=(.6,.6,.6),density=rho),utils.sphere((-.8,1.9,5),radius=.2,color=(.6,.6,.6),density=rho),utils.sphere((0,2.4,4),radius=.4,color=(1,0,0),density=rho)] # eyes and nose
+	+[utils.facet([(12,0,-6),(0,12,-6,),(-12,-12,-6)],dynamic=False)] # ground
+)
+
+for part in [
+	pack.regularHexa (pack.inAlignedBox((-2,-2,-2),(2,2,2)),radius=1.5*rad,gap=2*gap,color=(1,0,1),**kw), # body,
+	pack.regularOrtho(pack.inCylinder((-1,0,-2),(-1,0,-6),1),radius=rad,gap=gap,color=(0,1,1),**kw), # left leg
+	pack.regularHexa (pack.inCylinder((+1,1,-2.5),(0,3,-5),1),radius=rad,gap=gap,color=(0,1,1),**kw), # right leg
+	pack.regularHexa (pack.inCylinder((+2,0,1),(+6,0,1),1),radius=rad,gap=gap,color=(0,0,1),**kw), # right hand
+	pack.regularOrtho(pack.inCylinder((-2,0,2),(-5,0,4),1),radius=rad,gap=gap,color=(0,0,1),**kw) # left hand
+	]: O.bodies.appendClumped(part)
+
+
+
+try:
+	from yade import qt
+	qt.Controller()
+except ImportError: pass
+
+O.engines=[
+	BexResetter(),
+	BoundingVolumeMetaEngine([InteractingSphere2AABB(),InteractingFacet2AABB(),MetaInteractingGeometry2AABB()]),
+	InsertionSortCollider(),
+	InteractionDispatchers(
+		[ef2_Sphere_Sphere_Dem3DofGeom(),ef2_Facet_Sphere_Dem3DofGeom()],
+		[SimpleElasticRelationships()],
+		[ef2_Dem3Dof_Elastic_ElasticLaw()],
+	),
+	GravityEngine(gravity=(1e-2,1e-2,-1000)),
+	NewtonsDampedLaw(damping=.1)
+]
+# we don't care about physical accuracy here, over-critical step is fine as long as the simulation doesn't explode
+O.dt=2*utils.PWaveTimeStep()
+O.saveTmp('init')
+#for b in O.bodies: