← Back to team overview

yade-dev team mailing list archive

[svn] r1841 - in trunk: . gui gui/py lib lib/py pkg/common pkg/common/Engine/StandAloneEngine pkg/dem pkg/dem/DataClass/InteractionGeometry pkg/dem/Engine/DeusExMachina pkg/dem/Engine/EngineUnit pkg/dem/Engine/StandAloneEngine pkg/dem/PreProcessor py py/miniWm3Wrap py/yadeWrapper scripts/test

 

Author: eudoxos
Date: 2009-07-07 13:54:14 +0200 (Tue, 07 Jul 2009)
New Revision: 1841

Added:
   trunk/gui/py/pyAttrUtils.hpp
   trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.cpp
   trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.hpp
   trunk/py/
   trunk/py/_eudoxos.cpp
   trunk/py/_packPredicates.cpp
   trunk/py/_packSpheres.cpp
   trunk/py/_utils.cpp
   trunk/py/euclid.py
   trunk/py/eudoxos.py
   trunk/py/linterpolation.py
   trunk/py/log.cpp
   trunk/py/miniWm3Wrap/
   trunk/py/miniWm3Wrap/README
   trunk/py/miniWm3Wrap/__call_policies.pypp.hpp
   trunk/py/miniWm3Wrap/__convenience.pypp.hpp
   trunk/py/miniWm3Wrap/miniWm3Wrap-funcs.ipp
   trunk/py/miniWm3Wrap/miniWm3Wrap-generate.py
   trunk/py/miniWm3Wrap/miniWm3Wrap-toExpose.hpp
   trunk/py/miniWm3Wrap/miniWm3Wrap.cpp
   trunk/py/pack.py
   trunk/py/plot.py
   trunk/py/timing.py
   trunk/py/utils.py
   trunk/py/yadeWrapper/
   trunk/py/yadeWrapper/yadeWrapper.cpp
   trunk/scripts/test/wm3-wrap.py
Removed:
   trunk/gui/py/PeriodicPythonRunner.cpp
   trunk/gui/py/PeriodicPythonRunner.hpp
   trunk/gui/py/pyAttrUtils.hpp
   trunk/gui/py/yadeControl.cpp
   trunk/lib/miniWm3Wrap-generate.py
   trunk/lib/miniWm3Wrap-toExpose.hpp
   trunk/lib/py/_eudoxos.cpp
   trunk/lib/py/_packPredicates.cpp
   trunk/lib/py/_packSpheres.cpp
   trunk/lib/py/_utils.cpp
   trunk/lib/py/euclid.py
   trunk/lib/py/eudoxos.py
   trunk/lib/py/linterpolation.py
   trunk/lib/py/log.cpp
   trunk/lib/py/pack.py
   trunk/lib/py/plot.py
   trunk/lib/py/timing.py
   trunk/lib/py/utils.py
Modified:
   trunk/SConstruct
   trunk/gui/SConscript
   trunk/gui/py/PythonUI_rc.py
   trunk/lib/SConscript
   trunk/pkg/common/SConscript
   trunk/pkg/dem/DataClass/InteractionGeometry/SpheresContactGeometry.hpp
   trunk/pkg/dem/Engine/DeusExMachina/TriaxialStressController.cpp
   trunk/pkg/dem/Engine/EngineUnit/InteractingSphere2InteractingSphere4SpheresContactGeometry.cpp
   trunk/pkg/dem/Engine/StandAloneEngine/ElasticContactLaw.cpp
   trunk/pkg/dem/PreProcessor/TriaxialTest.cpp
   trunk/pkg/dem/PreProcessor/TriaxialTest.hpp
   trunk/pkg/dem/SConscript
   trunk/scripts/test/gts-triax-pack.py
   trunk/scripts/test/shear.py
   trunk/scripts/test/triax-identical-results.py
Log:
1. Shuffling stuff around, moving most python things to py/
2. Wrapping Vector3r, Quaternionr and Vector2r in python directly, see scripts/wm3-wrap.py
3. Change the way utils.box, utils.sphere and utils.facet works: arguments to physical parameters are passed as keywords, not as dictionary now (update your old code by passing **dict instead of dict as the last argument)
4. Fix a bug with useShear, now getting almost identical results for triaxial again.
5. Preliminary Triaxial::facetWalls, to be able to used Dem3DofGeom with Triaxial. (not fully functional yet)
6. TriaxialStressController::thickness is -1 by default now (get thickness from box width), as 0 is real value for Facets. Doesn't change beahvior in any way.

Please report any regressions, I tried to make sure there are none.



Modified: trunk/SConstruct
===================================================================
--- trunk/SConstruct	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/SConstruct	2009-07-07 11:54:14 UTC (rev 1841)
@@ -383,7 +383,7 @@
 env['PREFIX']=os.path.abspath(env['PREFIX'])
 
 # paths to in-tree SConscript files
-libDirs=['lib','pkg/common','pkg/dem','pkg/fem','pkg/lattice','pkg/mass-spring','pkg/realtime-rigidbody','pkg/snow','extra','gui']
+libDirs=['lib','pkg/common','pkg/dem','pkg/fem','pkg/lattice','pkg/mass-spring','pkg/realtime-rigidbody','pkg/snow','extra','gui','py']
 #libDirs = libDirs + ['pkg/gram'] 
 # BUT: exclude stuff that should be excluded
 libDirs=[x for x in libDirs if not re.match('^.*/('+'|'.join(env['exclude'])+')$',x)]

Modified: trunk/gui/SConscript
===================================================================
--- trunk/gui/SConscript	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/SConscript	2009-07-07 11:54:14 UTC (rev 1841)
@@ -45,7 +45,6 @@
 if 'EMBED_PYTHON' in env['CPPDEFINES']:
 	env.Install('$PREFIX/lib/yade$SUFFIX/gui',[
 		env.SharedLibrary('PythonUI',['py/PythonUI.cpp']),
-		env.SharedLibrary('PeriodicPythonRunner',['py/PeriodicPythonRunner.cpp']),
 		env.File('PythonUI_rc.py','py'),
 	])
 	if 'qt3' not in env['exclude']:
@@ -53,41 +52,15 @@
 			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'),
 		])
-
 	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 *
+	# these modules will not be imported directly (probably)
+	# the rest was moved to py/ directory
 	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',
-				'GeometricalModelMetaEngine',
-				'InteractingGeometryMetaEngine',
-				'InteractionGeometryMetaEngine',
-				'InteractionPhysicsMetaEngine',
-				'PhysicalParametersMetaEngine',
-				'ConstitutiveLawDispatcher',
-				'InteractionDispatchers',
-				'STLImporter',
-				'ParallelEngine',
-				'Clump'
-			],
-			),
-		#env.SharedLibrary('_utils',['py/_utils.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['Shop','ConcretePM']),
-		#env.SharedLibrary('_packPredicates',['py/_packPredicates.cpp'],SHLIBPREFIX='',
-		#	# if we compile with GTS, link to the python module, as inGtsSurface uses some of its symbols.
-		#	# because the module doesn't have the lib- suffix, we put it directly to SHLINKFLAGS
-		#	# using the -l: syntax (see man ld) and declare the dependency below
-		#	SHLINKFLAGS=env['SHLINKFLAGS']+(['-l:$PREFIX/lib/yade$SUFFIX/py/gts/_gts.so'] if 'GTS' in env['features'] else [])),
-		#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'),
 		env.File('runtime.py','py'),
 		env.File('ipython.py','py'),
 		env.File('PythonTCPServer.py','py'),
 	])
-	#if os.path.exists('../../brefcom-mm.hh'): Depends('py/_eudoxos.cpp','../../brefcom-mm.hh')
-	# see comments for _packPredicates above
-	#if 'GTS' in env['features']: env.Depends('_packPredicates.so','$PREFIX/lib/yade$SUFFIX/py/gts/_gts.so')
 
 

Deleted: trunk/gui/py/PeriodicPythonRunner.cpp
===================================================================
--- trunk/gui/py/PeriodicPythonRunner.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/PeriodicPythonRunner.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,2 +0,0 @@
-#include<yade/gui-py/PeriodicPythonRunner.hpp>
-YADE_PLUGIN();

Deleted: trunk/gui/py/PeriodicPythonRunner.hpp
===================================================================
--- trunk/gui/py/PeriodicPythonRunner.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/PeriodicPythonRunner.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,32 +0,0 @@
-// 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
-#pragma once
-#include<yade/core/StandAloneEngine.hpp>
-#include<yade/core/MetaBody.hpp>
-#include<yade/pkg-common/PeriodicEngines.hpp>
-/* Execute a python command (in addPlotDataCall) periodically, with defined (and adjustable) periodicity.
- *
- * Period constraints are iterInterval and timeInterval. When either of them is exceeded, the addPlotDataCall is run.
- *
- * Thie engine is primarily conceived for collecting data for yade.plot plots during simulations, hence the name.
- */
-class PeriodicPythonRunner: public StretchPeriodicEngine {
-	private:
-		string command;
-	public :
-		PeriodicPythonRunner(): command("pass"){};
-		/* virtual bool isActivated: not overridden, StretchPeriodicEngine handles that */
-		virtual void action(MetaBody* b){
-			PyGILState_STATE gstate;
-				gstate = PyGILState_Ensure();
-				PyRun_SimpleString(command.c_str()); // this is suboptimal, since it has to be parsed at every execution; critical?
-			PyGILState_Release(gstate);
-		}
-		virtual void registerAttributes(){ StretchPeriodicEngine::registerAttributes(); REGISTER_ATTRIBUTE(command); }
-	protected :
-		virtual void postProcessAttributes(bool deserializing){}
-	REGISTER_CLASS_NAME(PeriodicPythonRunner);
-	REGISTER_BASE_CLASS_NAME(StretchPeriodicEngine);
-};
-
-REGISTER_SERIALIZABLE(PeriodicPythonRunner);
-

Modified: trunk/gui/py/PythonUI_rc.py
===================================================================
--- trunk/gui/py/PythonUI_rc.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/PythonUI_rc.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -8,6 +8,7 @@
 # sys.path.insert(0,runtime.prefix+'/lib/yade'+runtime.suffix+'/extra')
 
 from math import *
+from miniWm3Wrap import *
 from yade.wrapper import *
 from yade import runtime
 from yade import utils

Deleted: trunk/gui/py/pyAttrUtils.hpp
===================================================================
--- trunk/gui/py/pyAttrUtils.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/pyAttrUtils.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,223 +0,0 @@
-// 2007,2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
-#include<sstream>
-#include<boost/any.hpp>
-#include<map>
-#include<vector>
-#include<yade/lib-serialization/Archive.hpp>
-#include<yade/lib-serialization-xml/XMLFormatManager.hpp>
-#include<boost/python.hpp>
-#include<yade/extra/boost_python_len.hpp>
-
-#include<boost/foreach.hpp>
-#ifndef FOREACH
-	#define FOREACH BOOST_FOREACH
-#endif
-
-using namespace std;
-using namespace boost;
-
-/*! Define inline proxy functions that access attributes of some object associated with this class.
- * \param accessor is an shared_ptr<AttrAccess> (or AttrAccess*), properly initialized and associated with the object of your choice
- * \param ensureFunc is member function called before every attribute access. It typically would check whether acessor is not NULL, otherwise instantiate it.
- */
-#define ATTR_ACCESS_CXX(accessor,ensureFunc) \
-	boost::python::object wrappedPyGet(std::string key){ensureFunc();return accessor->pyGet(key);} \
-	void wrappedPySet(std::string key,python::object val){ensureFunc(); accessor->pySet(key,val);} \
-	string wrappedGetAttrStr(std::string key){ensureFunc();vector<string> a=accessor->getAttrStr(key); string ret("["); FOREACH(string s, a) ret+=s+" "; return ret+"]";} \
-	void wrappedSetAttrStr(std::string key, std::string val){ensureFunc();return accessor->setAttrStr(key,val);} \
-	boost::python::list wrappedPyKeys(){ensureFunc(); return accessor->pyKeys();} \
-	boost::python::dict wrappedPyDict(){ensureFunc(); return accessor->pyDict();} \
-	bool wrappedPyHasKey(std::string key){ensureFunc(); return accessor->descriptors.find(key)!=accessor->descriptors.end();} \
-	
-	
-	//boost::python::object wrappedPyGet_throw(std::string key){ensureFunc(); if(wrappedPyHasKey(key)) return accessor->pyGet(key); PyErr_SetString(PyExc_AttributeError, "No such attribute."); boost::python::throw_error_already_set(); /* make compiler happy*/ return boost::python::object(); }
-
-
-/*! Python special functions complementing proxies defined by ATTR_ACCESS_CXX, to be used with boost::python::class_<>.
- *
- * They define python special functions that support dictionary operations on this object and calls proxies for them. */
-#define ATTR_ACCESS_PY(cxxClass) \
-	def("__getitem__",&cxxClass::wrappedPyGet).def("__setitem__",&cxxClass::wrappedPySet).def("keys",&cxxClass::wrappedPyKeys).def("has_key",&cxxClass::wrappedPyHasKey).def("dict",&cxxClass::wrappedPyDict) \
-	.def("getRaw",&cxxClass::wrappedGetAttrStr).def("setRaw",&cxxClass::wrappedSetAttrStr)
-	//def("__getattr__",&cxxClass::wrappedPyGet).def("__setattr__",&cxxClass::wrappedPySet).def("attrs",&cxxClass::wrappedPyKeys)
-
-	//.def("__getattribute__",&cxxClass::wrappedPyGet_throw)
-
-#define BASIC_PY_PROXY_HEAD(pyClass,yadeClass) \
-class pyClass{shared_ptr<AttrAccess> accessor; \
-	private: void init(string clss){ proxee=dynamic_pointer_cast<yadeClass>(ClassFactory::instance().createShared(clss.empty()? #yadeClass : clss)); if(!proxee) throw runtime_error("Invalid class `"+clss+"': either nonexistent, or unable to cast to `"+#yadeClass+"'"); } \
-	public: shared_ptr<yadeClass> proxee; \
-		void ensureAcc(void){ if(!proxee) throw runtime_error(string("No proxied `")+#yadeClass+"'."); if(!accessor) accessor=shared_ptr<AttrAccess>(new AttrAccess(proxee));} \
-		pyClass(string clss="", python::dict attrs=python::dict()){ init(clss); python::list l=attrs.items(); int len=PySequence_Size(l.ptr()); for(int i=0; i<len; i++){ python::extract<python::tuple> t(l[i]); python::extract<string> keyEx(t()[0]); if(!keyEx.check()) throw invalid_argument("Attribute keys must be strings."); wrappedPySet(keyEx(),t()[1]); } } \
-		pyClass(const shared_ptr<yadeClass>& _proxee): proxee(_proxee) {} \
-		std::string pyStr(void){ ensureAcc(); return string(proxee->getClassName()==#yadeClass ? "<"+proxee->getClassName()+">" : "<"+proxee->getClassName()+" "+ #yadeClass +">"); } \
-		string className(void){ ensureAcc(); return proxee->getClassName(); } \
-		void postProcessAttributes(void){ensureAcc(); static_pointer_cast<Serializable>(proxee)->/*__HACK__D@_N@T_ABUSE_*/postProcessAttributes(/*deserializing*/ true); } \
-		ATTR_ACCESS_CXX(accessor,ensureAcc);
-
-#define BASIC_PY_PROXY_TAIL };
-/*! Basic implementation of python proxy class that provides:
- * 1. constructor with (optional) class name and (optional) dictionary of attributes
- * 2. copy constructor from another proxy class
- * 3. className() returning proxy class name as string 
- * 4. ensureAcc() that makes sure we have proxy _and_ attribute access things ready
- */
-#define BASIC_PY_PROXY(pyClass,yadeClass) BASIC_PY_PROXY_HEAD(pyClass,yadeClass) BASIC_PY_PROXY_TAIL
-
-/* Read-write access to some attribute that is not basic-serializable, i.e. must be exported as instance.attribute (not instance['attribute']. That attribute is wrapped in given python class before it is returned. */
-#define NONPOD_ATTRIBUTE_ACCESS(pyName,pyClass,yadeName) \
-	python::object pyName##_get(void){ensureAcc(); return proxee->yadeName ? python::object(pyClass(proxee->yadeName)) : python::object(); } \
-	void pyName##_set(pyClass proxy){ensureAcc(); proxee->yadeName=proxy.proxee; }
-/*! Boost.python's definition of python object corresponding to BASIC_PY_PROXY */
-#define BASIC_PY_PROXY_WRAPPER(pyClass,pyName)  \
-	boost::python::class_<pyClass>(pyName,python::init<python::optional<string,python::dict> >()) \
-	.ATTR_ACCESS_PY(pyClass) \
-	.def("__str__",&pyClass::pyStr).def("__repr__",&pyClass::pyStr) \
-	.add_property("name",&pyClass::className) \
-	.def("postProcessAttributes",&pyClass::postProcessAttributes)
-
-
-
-/*! Helper class for accessing registered attributes through the serialization interface.
- *
- * 4 possible types of attributes are supported: bool, string, number and arrays (homogeneous) of any of them.
- * This class exposes pySet, pyGet and pyKeys methods to python so that associated object supports python syntax for dictionary member access.
- */
-class AttrAccess{
-	struct AttrDesc{int type; shared_ptr<Archive> archive;};
-	private:
-		const shared_ptr<Serializable> ser;
-		Serializable::Archives archives;
-	public:
-		DECLARE_LOGGER;
-		typedef map<string,AttrDesc> DescriptorMap;
-		//! maps attribute name to its archive and vector of its types (given as ints, from the following enum)
-		DescriptorMap descriptors;
-		//! allowed types
-		enum {BOOL,STRING,FLOAT,INTEGER,SEQ_FLOAT, SEQ_STRING, VEC_VEC }; // allowed types
-		
-		AttrAccess(Serializable* _ser): ser(shared_ptr<Serializable>(_ser)){init();}
-		AttrAccess(shared_ptr<Serializable> _ser):ser(_ser){init();}
-
-		//! create archives and descriptors, always called from the constructor
-		void init(){
-			//cerr<<typeid(std::vector<Wm3::Vector3<double> >*).name()<<endl;
-			if(ser->getArchives().empty()) ser->registerSerializableAttributes(false);
-			archives=ser->getArchives();
-			for(Serializable::Archives::iterator ai=archives.begin();ai!=archives.end();++ai){
-				if((*ai)->isFundamental() && (*ai)->getName()!="serializationDynlib"){
-					AttrDesc desc; 
-					desc.archive=*ai;
-					any instance=(*ai)->getAddress(); // gets pointer to the stored value
-					//cerr<<"["<<(*ai)->getName()<<","<<instance.type().name()<<"]";
-					// 3 possibilities: one BOOL, one or more STRINGS, one or more FLOATs (fallback if none matches)
-					if      (any_cast<string*>(&instance)) { desc.type=AttrAccess::STRING; goto found; }
-					else if (any_cast<bool*>(&instance)) { desc.type=AttrAccess::BOOL; goto found; }
-					else if (any_cast<Real*>(&instance) || any_cast<long double*>(&instance) || any_cast<double*>(&instance) || any_cast<float*>(&instance)) { desc.type=AttrAccess::FLOAT; goto found;}
-					else if (any_cast<int*>(&instance) || any_cast<unsigned int*>(&instance) || any_cast<long*>(&instance) || any_cast<unsigned long*>(&instance) || any_cast<long long*>(&instance) || any_cast<unsigned long long*>(&instance)) {desc.type=AttrAccess::INTEGER; goto found; }
-					else if (any_cast<vector<string>*>(&instance)) { desc.type=AttrAccess::SEQ_STRING; goto found; }
-				#if 0
-					else if (any_cast<vector<Vector3r>*>(&instance)) { cerr<<"WWWWWWWWWWWWW"<<endl;}
-					//else if (any_cast<vector<Vector3r>*>(&&instance)) { cerr<"QQQQQQQQQQQQQQ"<<endl;}
-					#define GET_TYPE_DIRECT(_type,ATTR_ACCESS_TYPE) try{ cerr<<"Try "<<instance.type().name()<<" to "<<typeid(_type).name()<<endl; any_cast<_type >(instance); desc.type=ATTR_ACCESS_TYPE; cerr<<"OK!!!"<<endl; goto found; } catch(boost::bad_any_cast& e){}
-					GET_TYPE_DIRECT(std::vector<Wm3::Vector3r>*,AttrAccess::VEC_VEC);
-					else if (any_cast<vector<Wm3::Vector3<double> >*>(&instance)) {
-						desc.type=AttrAccess::VEC_VEC;
-						cerr<<"Attribute "<<(*ai)->getName()<<" is a vector<Vector3r>";
-					//else if (any_cast<vector<Real>*>(&instance)) desc.type=AttrAccess::SEQ_FLOAT;
-					}
-				#endif
-					desc.type=AttrAccess::SEQ_FLOAT;
-					found:
-						descriptors[(*ai)->getName()]=desc;
-				}
-			}
-			//cerr<<dumpAttrs();
-		}
-		
-		//! Return serialized attribute by its name, as vector of strings
-		vector<string> getAttrStr(string name){
-			vector<string> ret;
-			shared_ptr<Archive> arch=descriptors[name].archive;
-			stringstream stream;
-			arch->serialize(stream,*arch,0);
-			IOFormatManager::parseFundamental(stream.str(),ret);
-			return ret;
-		}
-		//! name, values and types of given attribute, on one line as string
-		string dumpAttr(string name){
-			string vals,types; AttrDesc desc=descriptors[name]; vector<string> values=getAttrStr(name);
-			for(size_t i=0; i<values.size(); i++) vals+=(i>0?" ":"")+values[i];
-			string typeDesc(desc.type==BOOL?"BOOL":(desc.type==STRING?"STRING":(desc.type==FLOAT?"FLOAT":(desc.type==INTEGER?"INTEGER":(desc.type==SEQ_FLOAT?"SEQ_FLOAT":(desc.type==SEQ_STRING?"SEQ_STRING":(desc.type==VEC_VEC?"VEC_VEC":"<unknown>")))))));
-			return name+" =\t"+vals+"\t ("+typeDesc+")";
-		}
-		//! call dumpAttr for all attributes (used for debugging)
-		string dumpAttrs(){ string ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++) ret+=dumpAttr(I->first)+"\n"; return ret;}
-		//! set attribute by name from its serialized value
-		void setAttrStr(string name, string value){
-			LOG_DEBUG("Will set `"<<name<<"' to `"<<value<<"'.");
-			stringstream voidStream;
-			descriptors[name].archive->deserialize(voidStream,*(descriptors[name].archive),value);
-		}
-
-		//! return dictionary of attributes and their python values (debugging mosly)
-		boost::python::dict pyDict(){boost::python::dict ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++)ret[I->first]=pyGet(I->first); return ret; }
-		//! return python list of keys (attribute names)
-		boost::python::list pyKeys(){boost::python::list ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++)ret.append(I->first); return ret;}
-
-
-		//! return attribute value as python object
-		boost::python::object pyGet(std::string key){
-			DescriptorMap::iterator I=descriptors.find(key);
-			if(I==descriptors.end()) throw std::invalid_argument(string("Invalid key: `")+key+"'.");
-			vector<string> raw=getAttrStr(key);
-			LOG_DEBUG("Got raw attribute `"<<key<<"'");
-			switch(descriptors[key].type){
-				case BOOL: return python::object(lexical_cast<bool>(raw[0]));
-				case FLOAT: return python::object(lexical_cast_maybeNanInf<double>(raw[0]));
-				case INTEGER: return python::object(lexical_cast_maybeNanInf<long>(raw[0]));
-				case STRING: return python::object(raw[0]);
-				case SEQ_STRING: {python::list ret; for(size_t i=0; i<raw.size(); i++) ret.append(python::object(raw[i])); return ret;}
-				case SEQ_FLOAT: {python::list ret; for(size_t i=0; i<raw.size(); i++){ ret.append(python::object(lexical_cast_maybeNanInf<double>(raw[i]))); LOG_TRACE("Appended "<<raw[i]);} return ret; }
-				case VEC_VEC: {
-					python::list ret; for(size_t i=0; i<raw.size(); i++){
-						/* raw[i] has the form "{number number number}" */
-						vector<string> s; boost::algorithm::split(s,raw[i],algorithm::is_any_of("{} "),algorithm::token_compress_on);
-						python::list subList; FOREACH(string ss, s) subList.append(python::object(lexical_cast_maybeNanInf<double>(ss)));
-						ret.append(subList);
-					}
-				}
-				default: throw runtime_error("Unhandled attribute type!");
-			}
-		}
-		//! set attribute value from python object
-		void pySet(std::string key, python::object val){
-			DescriptorMap::iterator I=descriptors.find(key);
-			if(I==descriptors.end()) throw std::invalid_argument(string("Invalid key: `")+key+"'.");
-			#define SAFE_EXTRACT(from,to,type) python::extract<type> to(from); if(!to.check()) throw invalid_argument(string("Could not extract type ")+#type);
-			switch(descriptors[key].type){
-				case BOOL: {SAFE_EXTRACT(val.ptr(),extr,bool); setAttrStr(key,extr()?"1":"0"); break;}
-				case FLOAT: {SAFE_EXTRACT(val.ptr(),extr,double); setAttrStr(key,lexical_cast<string>(extr())); break; }
-				case INTEGER: {SAFE_EXTRACT(val.ptr(),extr,long); setAttrStr(key,lexical_cast<string>(extr())); break; }
-				case STRING: {SAFE_EXTRACT(val.ptr(),extr,string); setAttrStr(key,extr()); break;}
-				case SEQ_STRING:{
-					if(!PySequence_Check(val.ptr())) throw invalid_argument("String sequence argument required.");
-					string strVal("[");
-					for(int i=0; i<PySequence_Size(val.ptr()); i++){SAFE_EXTRACT(PySequence_GetItem(val.ptr(),i),extr,string); strVal+=extr()+" ";}
-					setAttrStr(key,strVal+"]");
-				} 
-				break;
-				case SEQ_FLOAT:{
-					if(!PySequence_Check(val.ptr())) throw invalid_argument("Number sequence argument required.");
-					string strVal("{");
-					for(int i=0; i<PySequence_Size(val.ptr()); i++){SAFE_EXTRACT(PySequence_GetItem(val.ptr(),i),extr,double); strVal+=lexical_cast<string>(extr())+" ";}
-					setAttrStr(key,strVal+"}");
-				}
-				break;
-				default: throw runtime_error("Invalid argument types!!");
-			}
-		}
-};
-
-CREATE_LOGGER(AttrAccess);
-

Added: trunk/gui/py/pyAttrUtils.hpp
===================================================================
--- trunk/gui/py/pyAttrUtils.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/pyAttrUtils.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,223 @@
+// 2007,2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
+#include<sstream>
+#include<boost/any.hpp>
+#include<map>
+#include<vector>
+#include<yade/lib-serialization/Archive.hpp>
+#include<yade/lib-serialization-xml/XMLFormatManager.hpp>
+#include<boost/python.hpp>
+#include<yade/extra/boost_python_len.hpp>
+
+#include<boost/foreach.hpp>
+#ifndef FOREACH
+	#define FOREACH BOOST_FOREACH
+#endif
+
+using namespace std;
+using namespace boost;
+
+/*! Define inline proxy functions that access attributes of some object associated with this class.
+ * \param accessor is an shared_ptr<AttrAccess> (or AttrAccess*), properly initialized and associated with the object of your choice
+ * \param ensureFunc is member function called before every attribute access. It typically would check whether acessor is not NULL, otherwise instantiate it.
+ */
+#define ATTR_ACCESS_CXX(accessor,ensureFunc) \
+	boost::python::object wrappedPyGet(std::string key){ensureFunc();return accessor->pyGet(key);} \
+	void wrappedPySet(std::string key,python::object val){ensureFunc(); accessor->pySet(key,val);} \
+	string wrappedGetAttrStr(std::string key){ensureFunc();vector<string> a=accessor->getAttrStr(key); string ret("["); FOREACH(string s, a) ret+=s+" "; return ret+"]";} \
+	void wrappedSetAttrStr(std::string key, std::string val){ensureFunc();return accessor->setAttrStr(key,val);} \
+	boost::python::list wrappedPyKeys(){ensureFunc(); return accessor->pyKeys();} \
+	boost::python::dict wrappedPyDict(){ensureFunc(); return accessor->pyDict();} \
+	bool wrappedPyHasKey(std::string key){ensureFunc(); return accessor->descriptors.find(key)!=accessor->descriptors.end();} \
+	
+	
+	//boost::python::object wrappedPyGet_throw(std::string key){ensureFunc(); if(wrappedPyHasKey(key)) return accessor->pyGet(key); PyErr_SetString(PyExc_AttributeError, "No such attribute."); boost::python::throw_error_already_set(); /* make compiler happy*/ return boost::python::object(); }
+
+
+/*! Python special functions complementing proxies defined by ATTR_ACCESS_CXX, to be used with boost::python::class_<>.
+ *
+ * They define python special functions that support dictionary operations on this object and calls proxies for them. */
+#define ATTR_ACCESS_PY(cxxClass) \
+	def("__getitem__",&cxxClass::wrappedPyGet).def("__setitem__",&cxxClass::wrappedPySet).def("keys",&cxxClass::wrappedPyKeys).def("has_key",&cxxClass::wrappedPyHasKey).def("dict",&cxxClass::wrappedPyDict) \
+	.def("getRaw",&cxxClass::wrappedGetAttrStr).def("setRaw",&cxxClass::wrappedSetAttrStr)
+	//def("__getattr__",&cxxClass::wrappedPyGet).def("__setattr__",&cxxClass::wrappedPySet).def("attrs",&cxxClass::wrappedPyKeys)
+
+	//.def("__getattribute__",&cxxClass::wrappedPyGet_throw)
+
+#define BASIC_PY_PROXY_HEAD(pyClass,yadeClass) \
+class pyClass{shared_ptr<AttrAccess> accessor; \
+	private: void init(string clss){ proxee=dynamic_pointer_cast<yadeClass>(ClassFactory::instance().createShared(clss.empty()? #yadeClass : clss)); if(!proxee) throw runtime_error("Invalid class `"+clss+"': either nonexistent, or unable to cast to `"+#yadeClass+"'"); } \
+	public: shared_ptr<yadeClass> proxee; \
+		void ensureAcc(void){ if(!proxee) throw runtime_error(string("No proxied `")+#yadeClass+"'."); if(!accessor) accessor=shared_ptr<AttrAccess>(new AttrAccess(proxee));} \
+		pyClass(string clss="", python::dict attrs=python::dict()){ init(clss); python::list l=attrs.items(); int len=PySequence_Size(l.ptr()); for(int i=0; i<len; i++){ python::extract<python::tuple> t(l[i]); python::extract<string> keyEx(t()[0]); if(!keyEx.check()) throw invalid_argument("Attribute keys must be strings."); wrappedPySet(keyEx(),t()[1]); } } \
+		pyClass(const shared_ptr<yadeClass>& _proxee): proxee(_proxee) {} \
+		std::string pyStr(void){ ensureAcc(); return string(proxee->getClassName()==#yadeClass ? "<"+proxee->getClassName()+">" : "<"+proxee->getClassName()+" "+ #yadeClass +">"); } \
+		string className(void){ ensureAcc(); return proxee->getClassName(); } \
+		void postProcessAttributes(void){ensureAcc(); static_pointer_cast<Serializable>(proxee)->/*__HACK__D@_N@T_ABUSE_*/postProcessAttributes(/*deserializing*/ true); } \
+		ATTR_ACCESS_CXX(accessor,ensureAcc);
+
+#define BASIC_PY_PROXY_TAIL };
+/*! Basic implementation of python proxy class that provides:
+ * 1. constructor with (optional) class name and (optional) dictionary of attributes
+ * 2. copy constructor from another proxy class
+ * 3. className() returning proxy class name as string 
+ * 4. ensureAcc() that makes sure we have proxy _and_ attribute access things ready
+ */
+#define BASIC_PY_PROXY(pyClass,yadeClass) BASIC_PY_PROXY_HEAD(pyClass,yadeClass) BASIC_PY_PROXY_TAIL
+
+/* Read-write access to some attribute that is not basic-serializable, i.e. must be exported as instance.attribute (not instance['attribute']. That attribute is wrapped in given python class before it is returned. */
+#define NONPOD_ATTRIBUTE_ACCESS(pyName,pyClass,yadeName) \
+	python::object pyName##_get(void){ensureAcc(); return proxee->yadeName ? python::object(pyClass(proxee->yadeName)) : python::object(); } \
+	void pyName##_set(pyClass proxy){ensureAcc(); proxee->yadeName=proxy.proxee; }
+/*! Boost.python's definition of python object corresponding to BASIC_PY_PROXY */
+#define BASIC_PY_PROXY_WRAPPER(pyClass,pyName)  \
+	boost::python::class_<pyClass>(pyName,python::init<python::optional<string,python::dict> >()) \
+	.ATTR_ACCESS_PY(pyClass) \
+	.def("__str__",&pyClass::pyStr).def("__repr__",&pyClass::pyStr) \
+	.add_property("name",&pyClass::className) \
+	.def("postProcessAttributes",&pyClass::postProcessAttributes)
+
+
+
+/*! Helper class for accessing registered attributes through the serialization interface.
+ *
+ * 4 possible types of attributes are supported: bool, string, number and arrays (homogeneous) of any of them.
+ * This class exposes pySet, pyGet and pyKeys methods to python so that associated object supports python syntax for dictionary member access.
+ */
+class AttrAccess{
+	struct AttrDesc{int type; shared_ptr<Archive> archive;};
+	private:
+		const shared_ptr<Serializable> ser;
+		Serializable::Archives archives;
+	public:
+		DECLARE_LOGGER;
+		typedef map<string,AttrDesc> DescriptorMap;
+		//! maps attribute name to its archive and vector of its types (given as ints, from the following enum)
+		DescriptorMap descriptors;
+		//! allowed types
+		enum {BOOL,STRING,FLOAT,INTEGER,SEQ_FLOAT, SEQ_STRING, VEC_VEC }; // allowed types
+		
+		AttrAccess(Serializable* _ser): ser(shared_ptr<Serializable>(_ser)){init();}
+		AttrAccess(shared_ptr<Serializable> _ser):ser(_ser){init();}
+
+		//! create archives and descriptors, always called from the constructor
+		void init(){
+			//cerr<<typeid(std::vector<Wm3::Vector3<double> >*).name()<<endl;
+			if(ser->getArchives().empty()) ser->registerSerializableAttributes(false);
+			archives=ser->getArchives();
+			for(Serializable::Archives::iterator ai=archives.begin();ai!=archives.end();++ai){
+				if((*ai)->isFundamental() && (*ai)->getName()!="serializationDynlib"){
+					AttrDesc desc; 
+					desc.archive=*ai;
+					any instance=(*ai)->getAddress(); // gets pointer to the stored value
+					//cerr<<"["<<(*ai)->getName()<<","<<instance.type().name()<<"]";
+					// 3 possibilities: one BOOL, one or more STRINGS, one or more FLOATs (fallback if none matches)
+					if      (any_cast<string*>(&instance)) { desc.type=AttrAccess::STRING; goto found; }
+					else if (any_cast<bool*>(&instance)) { desc.type=AttrAccess::BOOL; goto found; }
+					else if (any_cast<Real*>(&instance) || any_cast<long double*>(&instance) || any_cast<double*>(&instance) || any_cast<float*>(&instance)) { desc.type=AttrAccess::FLOAT; goto found;}
+					else if (any_cast<int*>(&instance) || any_cast<unsigned int*>(&instance) || any_cast<long*>(&instance) || any_cast<unsigned long*>(&instance) || any_cast<long long*>(&instance) || any_cast<unsigned long long*>(&instance)) {desc.type=AttrAccess::INTEGER; goto found; }
+					else if (any_cast<vector<string>*>(&instance)) { desc.type=AttrAccess::SEQ_STRING; goto found; }
+				#if 0
+					else if (any_cast<vector<Vector3r>*>(&instance)) { cerr<<"WWWWWWWWWWWWW"<<endl;}
+					//else if (any_cast<vector<Vector3r>*>(&&instance)) { cerr<"QQQQQQQQQQQQQQ"<<endl;}
+					#define GET_TYPE_DIRECT(_type,ATTR_ACCESS_TYPE) try{ cerr<<"Try "<<instance.type().name()<<" to "<<typeid(_type).name()<<endl; any_cast<_type >(instance); desc.type=ATTR_ACCESS_TYPE; cerr<<"OK!!!"<<endl; goto found; } catch(boost::bad_any_cast& e){}
+					GET_TYPE_DIRECT(std::vector<Wm3::Vector3r>*,AttrAccess::VEC_VEC);
+					else if (any_cast<vector<Wm3::Vector3<double> >*>(&instance)) {
+						desc.type=AttrAccess::VEC_VEC;
+						cerr<<"Attribute "<<(*ai)->getName()<<" is a vector<Vector3r>";
+					//else if (any_cast<vector<Real>*>(&instance)) desc.type=AttrAccess::SEQ_FLOAT;
+					}
+				#endif
+					desc.type=AttrAccess::SEQ_FLOAT;
+					found:
+						descriptors[(*ai)->getName()]=desc;
+				}
+			}
+			//cerr<<dumpAttrs();
+		}
+		
+		//! Return serialized attribute by its name, as vector of strings
+		vector<string> getAttrStr(string name){
+			vector<string> ret;
+			shared_ptr<Archive> arch=descriptors[name].archive;
+			stringstream stream;
+			arch->serialize(stream,*arch,0);
+			IOFormatManager::parseFundamental(stream.str(),ret);
+			return ret;
+		}
+		//! name, values and types of given attribute, on one line as string
+		string dumpAttr(string name){
+			string vals,types; AttrDesc desc=descriptors[name]; vector<string> values=getAttrStr(name);
+			for(size_t i=0; i<values.size(); i++) vals+=(i>0?" ":"")+values[i];
+			string typeDesc(desc.type==BOOL?"BOOL":(desc.type==STRING?"STRING":(desc.type==FLOAT?"FLOAT":(desc.type==INTEGER?"INTEGER":(desc.type==SEQ_FLOAT?"SEQ_FLOAT":(desc.type==SEQ_STRING?"SEQ_STRING":(desc.type==VEC_VEC?"VEC_VEC":"<unknown>")))))));
+			return name+" =\t"+vals+"\t ("+typeDesc+")";
+		}
+		//! call dumpAttr for all attributes (used for debugging)
+		string dumpAttrs(){ string ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++) ret+=dumpAttr(I->first)+"\n"; return ret;}
+		//! set attribute by name from its serialized value
+		void setAttrStr(string name, string value){
+			LOG_DEBUG("Will set `"<<name<<"' to `"<<value<<"'.");
+			stringstream voidStream;
+			descriptors[name].archive->deserialize(voidStream,*(descriptors[name].archive),value);
+		}
+
+		//! return dictionary of attributes and their python values (debugging mosly)
+		boost::python::dict pyDict(){boost::python::dict ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++)ret[I->first]=pyGet(I->first); return ret; }
+		//! return python list of keys (attribute names)
+		boost::python::list pyKeys(){boost::python::list ret; for(DescriptorMap::iterator I=descriptors.begin();I!=descriptors.end();I++)ret.append(I->first); return ret;}
+
+
+		//! return attribute value as python object
+		boost::python::object pyGet(std::string key){
+			DescriptorMap::iterator I=descriptors.find(key);
+			if(I==descriptors.end()) throw std::invalid_argument(string("Invalid key: `")+key+"'.");
+			vector<string> raw=getAttrStr(key);
+			LOG_DEBUG("Got raw attribute `"<<key<<"'");
+			switch(descriptors[key].type){
+				case BOOL: return python::object(lexical_cast<bool>(raw[0]));
+				case FLOAT: return python::object(lexical_cast_maybeNanInf<double>(raw[0]));
+				case INTEGER: return python::object(lexical_cast_maybeNanInf<long>(raw[0]));
+				case STRING: return python::object(raw[0]);
+				case SEQ_STRING: {python::list ret; for(size_t i=0; i<raw.size(); i++) ret.append(python::object(raw[i])); return ret;}
+				case SEQ_FLOAT: {python::list ret; for(size_t i=0; i<raw.size(); i++){ ret.append(python::object(lexical_cast_maybeNanInf<double>(raw[i]))); LOG_TRACE("Appended "<<raw[i]);} return ret; }
+				case VEC_VEC: {
+					python::list ret; for(size_t i=0; i<raw.size(); i++){
+						/* raw[i] has the form "{number number number}" */
+						vector<string> s; boost::algorithm::split(s,raw[i],algorithm::is_any_of("{} "),algorithm::token_compress_on);
+						python::list subList; FOREACH(string ss, s) subList.append(python::object(lexical_cast_maybeNanInf<double>(ss)));
+						ret.append(subList);
+					}
+				}
+				default: throw runtime_error("Unhandled attribute type!");
+			}
+		}
+		//! set attribute value from python object
+		void pySet(std::string key, python::object val){
+			DescriptorMap::iterator I=descriptors.find(key);
+			if(I==descriptors.end()) throw std::invalid_argument(string("Invalid key: `")+key+"'.");
+			#define SAFE_EXTRACT(from,to,type) python::extract<type> to(from); if(!to.check()) throw invalid_argument(string("Could not extract type ")+#type);
+			switch(descriptors[key].type){
+				case BOOL: {SAFE_EXTRACT(val.ptr(),extr,bool); setAttrStr(key,extr()?"1":"0"); break;}
+				case FLOAT: {SAFE_EXTRACT(val.ptr(),extr,double); setAttrStr(key,lexical_cast<string>(extr())); break; }
+				case INTEGER: {SAFE_EXTRACT(val.ptr(),extr,long); setAttrStr(key,lexical_cast<string>(extr())); break; }
+				case STRING: {SAFE_EXTRACT(val.ptr(),extr,string); setAttrStr(key,extr()); break;}
+				case SEQ_STRING:{
+					if(!PySequence_Check(val.ptr())) throw invalid_argument("String sequence argument required.");
+					string strVal("[");
+					for(int i=0; i<PySequence_Size(val.ptr()); i++){SAFE_EXTRACT(PySequence_GetItem(val.ptr(),i),extr,string); strVal+=extr()+" ";}
+					setAttrStr(key,strVal+"]");
+				} 
+				break;
+				case SEQ_FLOAT:{
+					if(!PySequence_Check(val.ptr())) throw invalid_argument("Number sequence argument required.");
+					string strVal("{");
+					for(int i=0; i<PySequence_Size(val.ptr()); i++){SAFE_EXTRACT(PySequence_GetItem(val.ptr(),i),extr,double); strVal+=lexical_cast<string>(extr())+" ";}
+					setAttrStr(key,strVal+"}");
+				}
+				break;
+				default: throw runtime_error("Invalid argument types!!");
+			}
+		}
+};
+
+CREATE_LOGGER(AttrAccess);
+


Property changes on: trunk/gui/py/pyAttrUtils.hpp
___________________________________________________________________
Name: svn:mergeinfo
   + 

Deleted: trunk/gui/py/yadeControl.cpp
===================================================================
--- trunk/gui/py/yadeControl.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/gui/py/yadeControl.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,849 +0,0 @@
-// 2007,2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
-
-#include<sstream>
-#include<map>
-#include<vector>
-#include<unistd.h>
-#include<list>
-
-
-#include<boost/python.hpp>
-#include<boost/python/suite/indexing/vector_indexing_suite.hpp>
-#include<boost/bind.hpp>
-#include<boost/thread/thread.hpp>
-#include<boost/filesystem/operations.hpp>
-#include<boost/date_time/posix_time/posix_time.hpp>
-#include<boost/any.hpp>
-#include<boost/python.hpp>
-#include<boost/foreach.hpp>
-#include<boost/algorithm/string.hpp>
-
-// [boost1.34] #include<boost/python/stl_iterator.hpp>
-
-#include<yade/lib-base/Logging.hpp>
-#include<yade/lib-serialization-xml/XMLFormatManager.hpp>
-#include<yade/core/Omega.hpp>
-#include<yade/core/FileGenerator.hpp>
-
-#include<yade/lib-import/STLImporter.hpp>
-
-#include<yade/core/MetaEngine.hpp>
-#include<yade/core/MetaEngine1D.hpp>
-#include<yade/core/MetaEngine2D.hpp>
-#include<yade/core/StandAloneEngine.hpp>
-#include<yade/core/DeusExMachina.hpp>
-#include<yade/core/EngineUnit.hpp>
-#include<yade/pkg-common/ParallelEngine.hpp>
-#include<yade/core/EngineUnit1D.hpp>
-#include<yade/core/EngineUnit2D.hpp>
-
-#include<yade/pkg-common/BoundingVolumeMetaEngine.hpp>
-#include<yade/pkg-common/GeometricalModelMetaEngine.hpp>
-#include<yade/pkg-common/InteractingGeometryMetaEngine.hpp>
-#include<yade/pkg-common/InteractionGeometryMetaEngine.hpp>
-#include<yade/pkg-common/InteractionPhysicsMetaEngine.hpp>
-#include<yade/pkg-common/PhysicalParametersMetaEngine.hpp>
-#include<yade/pkg-common/ConstitutiveLawDispatcher.hpp>
-#include<yade/pkg-common/InteractionDispatchers.hpp>
-#include<yade/pkg-common/PhysicalActionDamper.hpp>
-#include<yade/pkg-common/PhysicalActionApplier.hpp>
-#include<yade/pkg-common/MetaInteractingGeometry.hpp>
-#include<yade/pkg-common/AABB.hpp>
-#include<yade/pkg-common/ParticleParameters.hpp>
-
-#include<yade/pkg-common/BoundingVolumeEngineUnit.hpp>
-#include<yade/pkg-common/GeometricalModelEngineUnit.hpp>
-#include<yade/pkg-common/InteractingGeometryEngineUnit.hpp>
-#include<yade/pkg-common/InteractionGeometryEngineUnit.hpp>
-#include<yade/pkg-common/InteractionPhysicsEngineUnit.hpp>
-#include<yade/pkg-common/PhysicalParametersEngineUnit.hpp>
-#include<yade/pkg-common/PhysicalActionDamperUnit.hpp>
-#include<yade/pkg-common/PhysicalActionApplierUnit.hpp>
-#include<yade/pkg-common/ConstitutiveLaw.hpp>
-
-#include<yade/extra/Shop.hpp>
-#include<yade/pkg-dem/Clump.hpp>
-
-using namespace boost;
-using namespace std;
-
-#include"pyAttrUtils.hpp"
-#include<yade/extra/boost_python_len.hpp>
-
-class RenderingEngine;
-
-/*!
-	
-	A regular class (not Omega) is instantiated like this:
-
-		RootClass('optional class name as quoted string',{optional dictionary of attributes})
-		
-	if class name is not given, the RootClass itself is instantiated
-
-		p=PhysicalParameters() # p is now instance of PhysicalParameters
-		p=PhysicalParameters('RigidBodyParameters') # p is now instance of RigidBodyParameters, which has PhysicalParameters as the "root" class
-		p=PhysicalParameters('RigidBodyParameters',{'mass':100,'se3':[1,1,2,1,0,0,0]}) # convenience constructor
-
-	The last statement is equivalent to:
-
-		p=PhysicalParameters('RigidBodyParameters')
-		p['mass']=100; 
-		p['se3']=[1,1,2,1,0,0,0]
-
-	Class attributes are those that are registered as serializable, are accessed using the [] operator and are always read-write (be careful)
-
-		p['se3'] # this will show you the se3 attribute inside p
-		p['se3']=[1,2,3,1,0,0,0] # this sets se3 of p
-
-	Those attributes that are not fundamental types (strings, numbers, booleans, se3, vectors, quaternions, arrays of numbers, arrays of strings) can be accessed only through explicit python data members, for example:
-		
-		b=Body()
-		b.mold=InteractingGeometry("InteractingSphere",{'radius':1})
-		b.shape=GeometricalModel("Sphere",{'radius':1})
-		b.mold # will give you the interactingGeometry of body
-	
-	Instances can be queried about attributes and data members they have:
-
-		b.keys() # serializable attributes, accessible via b['attribute']
-		dir(b) # python data members, accessible via b.attribute; the __something__ attributes are python internal attributes/metods -- methods are just callable members
-
-	MetaEngine class has special constructor (for convenience):
-
-		m=MetaEngine('class name as string',[list of engine units])
-
-	and it is equivalent to
-
-		m=MetaEntine('class name as string')
-		m.functors=[list of engine units]
-
-	It is your responsibility to pass the right engineUnits, otherwise crash will results. There is currently no way I know of to prevent that. 
-
-*/
-
-/*
-TODO:
-	1. PhysicalActionContainer (constructor with actionName) with iteration
-	2. from yadeControl import Omega as _Omega, inherit from that and add other convenience functions
-*/
-
-#ifdef LOG4CXX
-	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.python");
-#endif
-
-BASIC_PY_PROXY(pyGeneric,Serializable);
-
-BASIC_PY_PROXY(pyInteractionGeometry,InteractionGeometry);
-BASIC_PY_PROXY(pyInteractionPhysics,InteractionPhysics);
-
-BASIC_PY_PROXY(pyGeometricalModel,GeometricalModel);
-BASIC_PY_PROXY_HEAD(pyPhysicalParameters,PhysicalParameters)
-	python::list blockedDOFs_get(){
-		python::list ret;
-		#define _SET_DOF(DOF_ANY,str) if((proxee->blockedDOFs & PhysicalParameters::DOF_ANY)!=0) ret.append(str);
-		_SET_DOF(DOF_X,"x"); _SET_DOF(DOF_Y,"y"); _SET_DOF(DOF_Z,"z"); _SET_DOF(DOF_RX,"rx"); _SET_DOF(DOF_RY,"ry"); _SET_DOF(DOF_RZ,"rz");
-		#undef _SET_DOF
-		return ret;
-	}
-	void blockedDOFs_set(python::list l){
-		proxee->blockedDOFs=PhysicalParameters::DOF_NONE;
-		int len=python::len(l);
-		for(int i=0; i<len; i++){
-			string s=python::extract<string>(l[i])();
-			#define _GET_DOF(DOF_ANY,str) if(s==str) { proxee->blockedDOFs|=PhysicalParameters::DOF_ANY; continue; }
-			_GET_DOF(DOF_X,"x"); _GET_DOF(DOF_Y,"y"); _GET_DOF(DOF_Z,"z"); _GET_DOF(DOF_RX,"rx"); _GET_DOF(DOF_RY,"ry"); _GET_DOF(DOF_RZ,"rz");
-			#undef _GET_DOF
-			throw std::invalid_argument("Invalid  DOF specification `"+s+"', must be ∈{x,y,z,rx,ry,rz}.");
-		}
-	}
-	Vector3r displ_get(){return proxee->se3.position-proxee->refSe3.position;}
-	python::tuple rot_get(){Quaternionr relRot=proxee->refSe3.orientation.Conjugate()*proxee->se3.orientation; Vector3r axis; Real angle; relRot.ToAxisAngle(axis,angle); axis*=angle; return python::make_tuple(axis[0],axis[1],axis[2]); }
-	Vector3r pos_get(){return proxee->se3.position;}
-	Vector3r refPos_get(){return proxee->refSe3.position;}
-	python::tuple ori_get(){Vector3r axis; Real angle; proxee->se3.orientation.ToAxisAngle(axis,angle); return python::make_tuple(axis[0],axis[1],axis[2],angle);}
-	void pos_set(const Vector3r& p){ proxee->se3.position=p; }
-	void refPos_set(const Vector3r& p){ proxee->refSe3.position=p;}
-	void ori_set(python::list l){if(python::len(l)!=4) throw invalid_argument("Wrong number of quaternion elements "+lexical_cast<string>(python::len(l))+", should be 4"); proxee->se3.orientation=Quaternionr(Vector3r(python::extract<double>(l[0])(),python::extract<double>(l[1])(),python::extract<double>(l[2])()),python::extract<double>(l[3])());}
-BASIC_PY_PROXY_TAIL;
-
-BASIC_PY_PROXY(pyBoundingVolume,BoundingVolume);
-BASIC_PY_PROXY(pyInteractingGeometry,InteractingGeometry);
-
-struct pyTimingDeltas{
-	shared_ptr<TimingDeltas> proxee;
-	pyTimingDeltas(shared_ptr<TimingDeltas> td){proxee=td;}
-	python::list data_get(){
-		python::list ret;
-		for(size_t i=0; i<proxee->data.size(); i++){
-			ret.append(python::make_tuple(proxee->labels[i],proxee->data[i].nsec,proxee->data[i].nExec));
-		}
-		return ret;
-	}
-	void reset(){proxee->data.clear(); proxee->labels.clear();}
-};
-
-#define PY_PROXY_TIMING \
-	TimingInfo::delta execTime_get(void){return proxee->timingInfo.nsec;} void execTime_set(TimingInfo::delta t){proxee->timingInfo.nsec=t;} \
-	long execCount_get(void){return proxee->timingInfo.nExec;} void execCount_set(long n){proxee->timingInfo.nExec=n;} \
-	python::object timingDeltas_get(void){return proxee->timingDeltas?python::object(pyTimingDeltas(proxee->timingDeltas)):python::object();}
-
-
-BASIC_PY_PROXY_HEAD(pyDeusExMachina,DeusExMachina)
-	PY_PROXY_TIMING
-BASIC_PY_PROXY_TAIL;
-
-BASIC_PY_PROXY_HEAD(pyStandAloneEngine,StandAloneEngine)
-	PY_PROXY_TIMING
-BASIC_PY_PROXY_TAIL;
-	
-
-python::list anyEngines_get(const vector<shared_ptr<Engine> >&);
-void anyEngines_set(vector<shared_ptr<Engine> >&, python::object);
-
-BASIC_PY_PROXY_HEAD(pyParallelEngine,ParallelEngine)
-	pyParallelEngine(python::list slaves){init("ParallelEngine"); slaves_set(slaves);}
-	void slaves_set(python::list slaves){
-		ensureAcc(); shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)");
-		int len=python::len(slaves);
-		me->slaves=ParallelEngine::slaveContainer(); // empty the container
-		for(int i=0; i<len; i++){
-			python::extract<python::list> grpMaybe(slaves[i]);
-			python::list grpList;
-			if(grpMaybe.check()){ grpList=grpMaybe(); }
-			else{ /* we got a standalone thing; let's wrap it in list */ grpList.append(slaves[i]); }
-			vector<shared_ptr<Engine> > grpVec;
-			anyEngines_set(grpVec,grpList);
-			me->slaves.push_back(grpVec);
-		}
-	}
-	python::list slaves_get(void){	
-		ensureAcc(); shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)");
-		python::list ret;
-		FOREACH(vector<shared_ptr<Engine > >& grp, me->slaves){
-			python::list rret=anyEngines_get(grp);
-			if(python::len(rret)==1){ ret.append(rret[0]); } else ret.append(rret);
-		}
-		return ret;
-	}
-BASIC_PY_PROXY_TAIL;
-
-
-BASIC_PY_PROXY_HEAD(pyEngineUnit,EngineUnit)
-	python::list bases_get(void){ python::list ret; vector<string> t=proxee->getFunctorTypes(); for(size_t i=0; i<t.size(); i++) ret.append(t[i]); return ret; }
-	python::object timingDeltas_get(){return proxee->timingDeltas?python::object(pyTimingDeltas(proxee->timingDeltas)):python::object();}
-BASIC_PY_PROXY_TAIL;
-
-BASIC_PY_PROXY_HEAD(pyMetaEngine,MetaEngine)
-		// additional constructor
-		pyMetaEngine(string clss, python::list functors){init(clss); functors_set(functors);}
-		python::list functors_get(void){
-			ensureAcc(); shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(proxee); if(!me) throw runtime_error("Proxied class not a MetaEngine (?!)"); python::list ret;
-			/* garbage design: functorArguments are instances of EngineUnits, but they may not be present; therefore, only use them if they exist; our pyMetaEngine, however, will always have both names and EnguneUnit objects in the same count */
-			for(size_t i=0; i<me->functorNames.size(); i++){
-				shared_ptr<EngineUnit> eu;
-				string functorName(*(me->functorNames[i].rbegin()));
-				if(i<=me->functorArguments.size()){ /* count i-th list member */ size_t j=0;
-					for(list<shared_ptr<EngineUnit> >::iterator I=me->functorArguments.begin(); I!=me->functorArguments.end(); I++, j++) { if(j==i) { eu=(*I); break;}}
-				}
-				if(!eu) /* either list was shorter or empty pointer in the functorArguments list */ { eu=dynamic_pointer_cast<EngineUnit>(ClassFactory::instance().createShared(functorName)); if(!eu) throw runtime_error("Unable to construct `"+string(*(me->functorNames[i].rbegin()))+"' EngineUnit"); }
-				assert(eu);
-				ret.append(pyEngineUnit(eu));
-			}
-			return ret;
-		}
-		void functors_set(python::list ftrs){
-			ensureAcc(); shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(proxee); if(!me) throw runtime_error("Proxied class not a MetaEngine. (?!)");
-			me->clear(); int len=python::len(ftrs);
-			for(int i=0; i<len; i++){
-				python::extract<pyEngineUnit> euEx(ftrs[i]); if(!euEx.check()) throw invalid_argument("Unable to extract type EngineUnit from sequence.");
-				bool ok=false;
-				/* FIXME: casting engine unit to the right type via dynamic_cast doesn't work (always unusuccessful),
-				 * do static_cast and if the EngineUnit is of wrong type, it will crash badly immediately. */
-				#define TRY_ADD_FUNCTOR(P,Q) {shared_ptr<P> p(dynamic_pointer_cast<P>(me)); shared_ptr<EngineUnit> eu(euEx().proxee); if(p&&eu){p->add(static_pointer_cast<Q>(eu)); ok=true; }}
-				// shared_ptr<Q> q(dynamic_pointer_cast<Q>(eu)); cerr<<#P<<" "<<#Q<<":"<<(bool)p<<" "<<(bool)q<<endl;
-				TRY_ADD_FUNCTOR(BoundingVolumeMetaEngine,BoundingVolumeEngineUnit);
-				TRY_ADD_FUNCTOR(GeometricalModelMetaEngine,GeometricalModelEngineUnit);
-				TRY_ADD_FUNCTOR(InteractingGeometryMetaEngine,InteractingGeometryEngineUnit);
-				TRY_ADD_FUNCTOR(InteractionGeometryMetaEngine,InteractionGeometryEngineUnit);
-				TRY_ADD_FUNCTOR(InteractionPhysicsMetaEngine,InteractionPhysicsEngineUnit);
-				TRY_ADD_FUNCTOR(PhysicalParametersMetaEngine,PhysicalParametersEngineUnit);
-				TRY_ADD_FUNCTOR(PhysicalActionDamper,PhysicalActionDamperUnit);
-				TRY_ADD_FUNCTOR(PhysicalActionApplier,PhysicalActionApplierUnit);
-				TRY_ADD_FUNCTOR(ConstitutiveLawDispatcher,ConstitutiveLaw);
-				if(!ok) throw runtime_error(string("Unable to cast to suitable MetaEngine type when adding functor (MetaEngine: ")+me->getClassName()+", functor: "+euEx().proxee->getClassName()+")");
-				#undef TRY_ADD_FUNCTOR
-			}
-		}
-	PY_PROXY_TIMING
-BASIC_PY_PROXY_TAIL;
-
-BASIC_PY_PROXY_HEAD(pyInteractionDispatchers,InteractionDispatchers)
-	pyInteractionDispatchers(python::list geomFunctors, python::list physFunctors, python::list constLawFunctors){
-		init("InteractionDispatchers");
-		pyMetaEngine(proxee->geomDispatcher).functors_set(geomFunctors);
-		pyMetaEngine(proxee->physDispatcher).functors_set(physFunctors);
-		pyMetaEngine(proxee->constLawDispatcher).functors_set(constLawFunctors);
-	}
-	pyMetaEngine geomDispatcher_get(void){ return pyMetaEngine(proxee->geomDispatcher);}
-	pyMetaEngine physDispatcher_get(void){ return pyMetaEngine(proxee->physDispatcher);}
-	pyMetaEngine constLawDispatcher_get(void){ return pyMetaEngine(proxee->constLawDispatcher);}
-	PY_PROXY_TIMING
-BASIC_PY_PROXY_TAIL;
-
-python::list anyEngines_get(const vector<shared_ptr<Engine> >& engContainer){
-	python::list ret; 
-	FOREACH(const shared_ptr<Engine>& eng, engContainer){
-		#define APPEND_ENGINE_IF_POSSIBLE(engineType,pyEngineType) { shared_ptr<engineType> e=dynamic_pointer_cast<engineType>(eng); if(e) { ret.append(pyEngineType(e)); continue; } }
-		APPEND_ENGINE_IF_POSSIBLE(InteractionDispatchers,pyInteractionDispatchers); APPEND_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine); APPEND_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine); APPEND_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina); APPEND_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine); 
-		throw std::runtime_error("Unknown engine type: `"+eng->getClassName()+"' (only MetaEngine, StandAloneEngine, DeusExMachina and ParallelEngine are supported)");
-	}
-	return ret;
-}
-
-void anyEngines_set(vector<shared_ptr<Engine> >& engContainer, python::object egs){
-	int len=python::len(egs);
-	//const shared_ptr<MetaBody>& rootBody=OMEGA.getRootBody(); rootBody->engines.clear();
-	engContainer.clear();
-	for(int i=0; i<len; i++){
-		#define PUSH_BACK_ENGINE_IF_POSSIBLE(pyEngineType) if(python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)).check()){ pyEngineType e=python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)); engContainer.push_back(e.proxee); /* cerr<<"added "<<e.pyStr()<<", a "<<#pyEngineType<<endl; */ continue; }
-		PUSH_BACK_ENGINE_IF_POSSIBLE(pyStandAloneEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyMetaEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyDeusExMachina); PUSH_BACK_ENGINE_IF_POSSIBLE(pyParallelEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyInteractionDispatchers);
-		throw std::runtime_error("Encountered unknown engine type (unable to extract from python object)");
-	}
-}
-
-
-
-BASIC_PY_PROXY_HEAD(pyInteraction,Interaction)
-	NONPOD_ATTRIBUTE_ACCESS(geom,pyInteractionGeometry,interactionGeometry);
-	NONPOD_ATTRIBUTE_ACCESS(phys,pyInteractionPhysics,interactionPhysics);
-	/* shorthands */ unsigned id1_get(void){ensureAcc(); return proxee->getId1();} unsigned id2_get(void){ensureAcc(); return proxee->getId2();}
-	bool isReal_get(void){ensureAcc(); return proxee->isReal(); }
-BASIC_PY_PROXY_TAIL;
-
-BASIC_PY_PROXY_HEAD(pyBody,Body)
-	NONPOD_ATTRIBUTE_ACCESS(shape,pyGeometricalModel,geometricalModel);
-	NONPOD_ATTRIBUTE_ACCESS(mold,pyInteractingGeometry,interactingGeometry);
-	NONPOD_ATTRIBUTE_ACCESS(bound,pyBoundingVolume,boundingVolume);
-	NONPOD_ATTRIBUTE_ACCESS(phys,pyPhysicalParameters,physicalParameters);
-	unsigned id_get(){ensureAcc(); return proxee->getId();}
-	int mask_get(){ensureAcc(); return proxee->groupMask;}
-	void mask_set(int m){ensureAcc(); proxee->groupMask=m;}
-	bool dynamic_get(){ensureAcc(); return proxee->isDynamic;} void dynamic_set(bool dyn){ensureAcc(); proxee->isDynamic=dyn;}
-	bool isStandalone(){ensureAcc(); return proxee->isStandalone();} bool isClumpMember(){ensureAcc(); return proxee->isClumpMember();} bool isClump(){ensureAcc(); return proxee->isClump();}
-BASIC_PY_PROXY_TAIL;
-
-class pyBodyContainer{
-	public:
-	const shared_ptr<BodyContainer> proxee;
-	pyBodyContainer(const shared_ptr<BodyContainer>& _proxee): proxee(_proxee){}
-	pyBody pyGetitem(unsigned id){
-		if(id>=proxee->size()){ PyErr_SetString(PyExc_IndexError, "Body id out of range."); python::throw_error_already_set(); /* make compiler happy; never reached */ return pyBody(); }
-		else return pyBody(proxee->operator[](id));
-	}
-	body_id_t insert(pyBody b){return proxee->insert(b.proxee);}
-	python::list insertList(python::list bb){python::list ret; for(int i=0; i<len(bb); i++){ret.append(insert(python::extract<pyBody>(bb[i])()));} return ret;}
-		python::tuple insertClump(python::list bb){/*clump: first add constitutents, then add clump, then add constitutents to the clump, then update clump props*/
-		python::list ids=insertList(bb);
-		shared_ptr<Clump> clump=shared_ptr<Clump>(new Clump());
-		shared_ptr<Body> clumpAsBody=static_pointer_cast<Body>(clump);
-		clump->isDynamic=true;
-		proxee->insert(clumpAsBody);
-		for(int i=0; i<len(ids); i++){clump->add(python::extract<body_id_t>(ids[i])());}
-		clump->updateProperties(false);
-		return python::make_tuple(clump->getId(),ids);
-	}
-	python::list replace(python::list bb){proxee->clear(); return insertList(bb);}
-	long length(){return proxee->size();}
-	void clear(){proxee->clear();}
-};
-
-
-class pyTags{
-	public:
-		pyTags(const shared_ptr<MetaBody> _mb): mb(_mb){}
-		const shared_ptr<MetaBody> mb;
-		bool hasKey(string key){ FOREACH(string val, mb->tags){ if(algorithm::starts_with(val,key+"=")){ return true;} } return false; }
-		string getItem(string key){
-			FOREACH(string& val, mb->tags){
-				if(algorithm::starts_with(val,key+"=")){ string val1(val); algorithm::erase_head(val1,key.size()+1); algorithm::replace_all(val1,"~"," "); return val1;}
-			}
-			PyErr_SetString(PyExc_KeyError, "Invalid key.");
-			python::throw_error_already_set(); /* make compiler happy; never reached */ return string();
-		}
-		void setItem(string key,string newVal){
-			string item=algorithm::replace_all_copy(key+"="+newVal," ","~");
-			FOREACH(string& val, mb->tags){if(algorithm::starts_with(val,key+"=")){ val=item; return; } }
-			mb->tags.push_back(item);
-			}
-		python::list keys(){
-			python::list ret;
-			FOREACH(string val, mb->tags){
-				size_t i=val.find("=");
-				if(i==string::npos) throw runtime_error("Tags must be in the key=value format");
-				algorithm::erase_tail(val,val.size()-i); ret.append(val);
-			}
-			return ret;
-		}
-};
-
-class pyInteractionIterator{
-	InteractionContainer::iterator I, Iend;
-	public:
-	pyInteractionIterator(const shared_ptr<InteractionContainer>& ic){ I=ic->begin(); Iend=ic->end(); }
-	pyInteractionIterator pyIter(){return *this;}
-	pyInteraction pyNext(){ if(!(I!=Iend)){ PyErr_SetNone(PyExc_StopIteration); python::throw_error_already_set(); }
-		InteractionContainer::iterator ret=I; ++I; return pyInteraction(*ret); }
-};
-
-class pyInteractionContainer{
-	public:
-		const shared_ptr<InteractionContainer> proxee;
-		pyInteractionContainer(const shared_ptr<InteractionContainer>& _proxee): proxee(_proxee){}
-		pyInteractionIterator pyIter(){return pyInteractionIterator(proxee);}
-		pyInteraction pyGetitem(python::object id12){
-			if(!PySequence_Check(id12.ptr())) throw invalid_argument("Key must be a tuple");
-			if(python::len(id12)!=2) throw invalid_argument("Key must be a 2-tuple: id1,id2.");
-			python::extract<body_id_t> id1_(PySequence_GetItem(id12.ptr(),0)), id2_(PySequence_GetItem(id12.ptr(),1));
-			if(!id1_.check()) throw invalid_argument("Could not extract id1");
-			if(!id2_.check()) throw invalid_argument("Could not extract id2");
-			shared_ptr<Interaction> i=proxee->find(id1_(),id2_());
-			if(i) return pyInteraction(i); else throw invalid_argument("No such interaction.");
-		}
-		/* return nth _real_ iteration from the container (0-based index); this is to facilitate picking random interaction */
-		pyInteraction pyNth(long n){
-			long i=0; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(!I->isReal()) continue; if(i++==n) return pyInteraction(I); }
-			throw invalid_argument(string("Interaction number out of range (")+lexical_cast<string>(n)+">="+lexical_cast<string>(i)+").");
-		}
-		long len(){return proxee->size();}
-		void clear(){proxee->clear();}
-		python::list withBody(long id){ python::list ret; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(I->isReal() && (I->getId1()==id || I->getId2()==id)) ret.append(pyInteraction(I));} return ret;}
-		python::list withBodyAll(long id){ python::list ret; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(I->getId1()==id || I->getId2()==id) ret.append(pyInteraction(I));} return ret; }
-};
-
-Vector3r tuple2vec(const python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
-
-class pyBexContainer{
-	public:
-		pyBexContainer(){}
-		python::tuple force_get(long id){  MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r f=rb->bex.getForce(id); return python::make_tuple(f[0],f[1],f[2]); }
-		python::tuple torque_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getTorque(id); return python::make_tuple(m[0],m[1],m[2]);}
-		python::tuple move_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getMove(id); return python::make_tuple(m[0],m[1],m[2]);}
-		python::tuple rot_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getRot(id); return python::make_tuple(m[0],m[1],m[2]);}
-		void force_add(long id, const Vector3r& f){  MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addForce (id,f); }
-		void torque_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addTorque(id,t);}
-		void move_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addMove(id,t);}
-		void rot_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addRot(id,t);}
-};
-
-class pyOmega{
-	private:
-		// can be safely removed now, since pyOmega makes an empty rootBody in the constructor, if there is none
-		void assertRootBody(){if(!OMEGA.getRootBody()) throw std::runtime_error("No root body."); }
-		Omega& OMEGA;
-	public:
-	pyOmega(): OMEGA(Omega::instance()){
-		shared_ptr<MetaBody> rb=OMEGA.getRootBody();
-		assert(rb);
-		if(!rb->physicalParameters){rb->physicalParameters=shared_ptr<PhysicalParameters>(new ParticleParameters);} /* PhysicalParameters crashes PhysicalParametersMetaEngine... why? */
-		if(!rb->boundingVolume){rb->boundingVolume=shared_ptr<AABB>(new AABB);}
-		// initialized in constructor now: rb->boundingVolume->diffuseColor=Vector3r(1,1,1); 
-		if(!rb->interactingGeometry){rb->interactingGeometry=shared_ptr<MetaInteractingGeometry>(new MetaInteractingGeometry);}
-		//if(!OMEGA.getRootBody()){shared_ptr<MetaBody> mb=Shop::rootBody(); OMEGA.setRootBody(mb);}
-		/* this is not true if another instance of Omega is created; flag should be stored inside the Omega singleton for clean solution. */
-		if(!OMEGA.hasSimulationLoop()){
-			OMEGA.createSimulationLoop();
-		}
-	};
-	/* Create variables in python's __builtin__ namespace that correspond to labeled objects. At this moment, only engines can be labeled. */
-	void mapLabeledEntitiesToVariables(){
-		FOREACH(const shared_ptr<Engine>& e, OMEGA.getRootBody()->engines){
-			if(!e->label.empty()){
-				PyGILState_STATE gstate; gstate = PyGILState_Ensure();
-				PyRun_SimpleString(("__builtins__."+e->label+"=Omega().labeledEngine('"+e->label+"')").c_str());
-				PyGILState_Release(gstate);
-			}
-			if(isChildClassOf(e->getClassName(),"MetaEngine")){
-				shared_ptr<MetaEngine> ee=dynamic_pointer_cast<MetaEngine>(e);
-				FOREACH(const shared_ptr<EngineUnit>& f, ee->functorArguments){
-					if(!f->label.empty()){
-						PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyRun_SimpleString(("__builtins__."+f->label+"=Omega().labeledEngine('"+f->label+"')").c_str()); PyGILState_Release(gstate);
-					}
-				}
-			}
-			if(e->getClassName()=="InteractionDispatchers"){
-				shared_ptr<InteractionDispatchers> ee=dynamic_pointer_cast<InteractionDispatchers>(e);
-				list<shared_ptr<EngineUnit> > eus;
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->geomDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->physDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->constLawDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,eus){
-					if(!eu->label.empty()){
-						PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyRun_SimpleString(("__builtins__."+eu->label+"=Omega().labeledEngine('"+eu->label+"')").c_str()); PyGILState_Release(gstate);
-					}
-				}
-			}
-		}
-	}
-
-	long iter(){ return OMEGA.getCurrentIteration();}
-	double simulationTime(){return OMEGA.getSimulationTime();}
-	double realTime(){ return OMEGA.getComputationTime(); }
-	// long realTime(){return OMEGA(get...);}
-	double dt_get(){return OMEGA.getTimeStep();}
-	void dt_set(double dt){OMEGA.skipTimeStepper(true); OMEGA.setTimeStep(dt);}
-
-	long stopAtIter_get(){return OMEGA.getRootBody()->stopAtIteration; }
-	void stopAtIter_set(long s){OMEGA.getRootBody()->stopAtIteration=s; }
-
-	bool usesTimeStepper_get(){return OMEGA.timeStepperActive();}
-	void usesTimeStepper_set(bool use){OMEGA.skipTimeStepper(!use);}
-
-	bool timingEnabled_get(){return TimingInfo::enabled;}
-	void timingEnabled_set(bool enabled){TimingInfo::enabled=enabled;}
-	unsigned long bexSyncCount_get(){ return OMEGA.getRootBody()->bex.syncCount;}
-	void bexSyncCount_set(unsigned long count){ OMEGA.getRootBody()->bex.syncCount=count;}
-
-	void run(long int numIter=-1,bool doWait=false){
-		if(numIter>0) OMEGA.getRootBody()->stopAtIteration=OMEGA.getCurrentIteration()+numIter;
-		OMEGA.startSimulationLoop();
-		LOG_DEBUG("RUN"<<((OMEGA.getRootBody()->stopAtIteration-OMEGA.getCurrentIteration())>0?string(" ("+lexical_cast<string>(OMEGA.getRootBody()->stopAtIteration-OMEGA.getCurrentIteration())+" to go)"):string(""))<<"!");
-		if(doWait) wait();
-	}
-	void pause(){Py_BEGIN_ALLOW_THREADS; OMEGA.stopSimulationLoop(); Py_END_ALLOW_THREADS; LOG_DEBUG("PAUSE!");}
-	void step() { LOG_DEBUG("STEP!"); run(1); wait();  }
-	void wait(){ if(OMEGA.isRunning()){LOG_DEBUG("WAIT!");} else return; timespec t1,t2; t1.tv_sec=0; t1.tv_nsec=40000000; /* 40 ms */ Py_BEGIN_ALLOW_THREADS; while(OMEGA.isRunning()) nanosleep(&t1,&t2); Py_END_ALLOW_THREADS; }
-
-	void load(std::string fileName) {
-		Py_BEGIN_ALLOW_THREADS; OMEGA.joinSimulationLoop(); Py_END_ALLOW_THREADS; 
-		OMEGA.setSimulationFileName(fileName);
-		OMEGA.loadSimulation();
-		OMEGA.createSimulationLoop();
-		mapLabeledEntitiesToVariables();
-		LOG_DEBUG("LOAD!");
-	}
-	void reload(){	load(OMEGA.getSimulationFileName());}
-	void saveTmp(string mark=""){ save(":memory:"+mark);}
-	void loadTmp(string mark=""){ load(":memory:"+mark);}
-	void tmpToFile(string mark, string filename){
-		if(OMEGA.memSavedSimulations.count(":memory:"+mark)==0) throw runtime_error("No memory-saved simulation named "+mark);
-		iostreams::filtering_ostream out;
-		if(boost::algorithm::ends_with(filename,".bz2")) out.push(iostreams::bzip2_compressor());
-		out.push(iostreams::file_sink(filename));
-		if(!out.good()) throw runtime_error("Error while opening file `"+filename+"' for writing.");
-		LOG_INFO("Saving :memory:"<<mark<<" to "<<filename);
-		out<<OMEGA.memSavedSimulations[":memory:"+mark];
-	}
-
-
-
-	void reset(){Py_BEGIN_ALLOW_THREADS; OMEGA.reset(); Py_END_ALLOW_THREADS; }
-	void resetTime(){ OMEGA.getRootBody()->currentIteration=0; OMEGA.getRootBody()->simulationTime=0; OMEGA.timeInit(); }
-	void switchWorld(){ std::swap(OMEGA.rootBody,OMEGA.rootBodyAnother); }
-
-	void save(std::string fileName){
-		assertRootBody();
-		OMEGA.saveSimulation(fileName);
-		OMEGA.setSimulationFileName(fileName);
-		LOG_DEBUG("SAVE!");
-	}
-
-	void saveSpheres(std::string fileName){ Shop::saveSpheresToFile(fileName); }
-
-	python::list miscParams_get(){
-		python::list ret;
-		FOREACH(shared_ptr<Serializable>& s, OMEGA.getRootBody()->miscParams){
-			ret.append(pyGeneric(s));
-		}
-		return ret;
-	}
-
-	void miscParams_set(python::list l){
-		int len=python::len(l);
-		vector<shared_ptr<Serializable> >& miscParams=OMEGA.getRootBody()->miscParams;
-		miscParams.clear();
-		for(int i=0; i<len; i++){
-			if(python::extract<pyGeneric>(PySequence_GetItem(l.ptr(),i)).check()){ pyGeneric g=python::extract<pyGeneric>(PySequence_GetItem(l.ptr(),i)); miscParams.push_back(g.proxee); }
-			else throw std::invalid_argument("Unable to extract `Generic' from item #"+lexical_cast<string>(i)+".");
-		}
-	}
-
-	python::list engines_get(void){assertRootBody(); return anyEngines_get(OMEGA.getRootBody()->engines);}
-	void engines_set(python::object egs){assertRootBody(); anyEngines_set(OMEGA.getRootBody()->engines,egs); mapLabeledEntitiesToVariables(); }
-	python::list initializers_get(void){assertRootBody(); return anyEngines_get(OMEGA.getRootBody()->initializers);}
-	void initializers_set(python::object egs){assertRootBody(); anyEngines_set(OMEGA.getRootBody()->initializers,egs); OMEGA.getRootBody()->needsInitializers=true; }
-
-	python::object labeled_engine_get(string label){
-		FOREACH(const shared_ptr<Engine>& eng, OMEGA.getRootBody()->engines){
-			if(eng->label==label){
-				#define RETURN_ENGINE_IF_POSSIBLE(engineType,pyEngineType) { shared_ptr<engineType> e=dynamic_pointer_cast<engineType>(eng); if(e) return python::object(pyEngineType(e)); }
-				RETURN_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine);
-				RETURN_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine);
-				RETURN_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina);
-				RETURN_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine);
-				throw std::runtime_error("Unable to cast engine to MetaEngine, StandAloneEngine, DeusExMachina or ParallelEngine? ??");
-			}
-			shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(eng);
-			if(me){
-				FOREACH(const shared_ptr<EngineUnit>& eu, me->functorArguments){
-					if(eu->label==label) return python::object(pyEngineUnit(eu));
-				}
-			}
-			shared_ptr<InteractionDispatchers> ee=dynamic_pointer_cast<InteractionDispatchers>(eng);
-			if(ee){
-				list<shared_ptr<EngineUnit> > eus;
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->geomDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->physDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,ee->constLawDispatcher->functorArguments) eus.push_back(eu);
-				FOREACH(const shared_ptr<EngineUnit>& eu,eus){
-					if(eu->label==label) return python::object(pyEngineUnit(eu));
-				}
-			}
-		}
-		throw std::invalid_argument(string("No engine labeled `")+label+"'");
-	}
-	
-	pyBodyContainer bodies_get(void){assertRootBody(); return pyBodyContainer(OMEGA.getRootBody()->bodies); }
-	pyInteractionContainer interactions_get(void){assertRootBody(); return pyInteractionContainer(OMEGA.getRootBody()->interactions); }
-	
-	pyBexContainer bex_get(void){return pyBexContainer();}
-	
-
-	boost::python::list listChildClasses(const string& base){
-		boost::python::list ret;
-		for(map<string,DynlibDescriptor>::const_iterator di=Omega::instance().getDynlibsDescriptor().begin();di!=Omega::instance().getDynlibsDescriptor().end();++di) if (Omega::instance().isInheritingFrom((*di).first,base)) ret.append(di->first);
-		return ret;
-	}
-
-	bool isChildClassOf(const string& child, const string& base){
-		return (Omega::instance().isInheritingFrom(child,base));
-	}
-
-	boost::python::list plugins_get(){
-		const map<string,DynlibDescriptor>& plugins=Omega::instance().getDynlibsDescriptor();
-		std::pair<string,DynlibDescriptor> p; boost::python::list ret;
-		FOREACH(p, plugins) ret.append(p.first);
-		return ret;
-	}
-
-	pyTags tags_get(void){assertRootBody(); return pyTags(OMEGA.getRootBody());}
-
-	void interactionContainer_set(string clss){
-		MetaBody* rb=OMEGA.getRootBody().get();
-		if(rb->interactions->size()>0) throw std::runtime_error("Interaction container not empty, will not change its class.");
-		shared_ptr<InteractionContainer> ic=dynamic_pointer_cast<InteractionContainer>(ClassFactory::instance().createShared(clss));
-		rb->interactions=ic;
-	}
-	string interactionContainer_get(string clss){ return OMEGA.getRootBody()->interactions->getClassName(); }
-
-	void bodyContainer_set(string clss){
-		MetaBody* rb=OMEGA.getRootBody().get();
-		if(rb->bodies->size()>0) throw std::runtime_error("Body container not empty, will not change its class.");
-		shared_ptr<BodyContainer> bc=dynamic_pointer_cast<BodyContainer>(ClassFactory::instance().createShared(clss));
-		rb->bodies=bc;
-	}
-	string bodyContainer_get(string clss){ return OMEGA.getRootBody()->bodies->getClassName(); }
-	#ifdef YADE_OPENMP
-		int numThreads_get(){ return omp_get_max_threads();}
-		void numThreads_set(int n){ int bcn=OMEGA.getRootBody()->bex.getNumAllocatedThreads(); if(bcn<n) LOG_WARN("BexContainer has only "<<bcn<<" threads allocated. Changing thread number to on "<<bcn<<" instead of "<<n<<" requested."); omp_set_num_threads(min(n,bcn)); LOG_WARN("BUG: Omega().numThreads=n doesn't work as expected (number of threads is not changed globally). Set env var OMP_NUM_THREADS instead."); }
-	#else
-		int numThreads_get(){return 1;}
-		void numThreads_set(int n){ LOG_WARN("Yade was compiled without openMP support, changing number of threads will have no effect."); }
-	#endif
-
-};
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_run_overloads,run,0,2);
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_saveTmp_overloads,saveTmp,0,1);
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_loadTmp_overloads,loadTmp,0,1);
-
-BASIC_PY_PROXY_HEAD(pyFileGenerator,FileGenerator)
-	void generate(string outFile){ensureAcc(); proxee->setFileName(outFile); proxee->setSerializationLibrary("XMLFormatManager"); bool ret=proxee->generateAndSave(); LOG_INFO((ret?"SUCCESS:\n":"FAILURE:\n")<<proxee->message); if(ret==false) throw runtime_error("Generator reported error: "+proxee->message); };
-	void load(){ ensureAcc(); char tmpnam_str [L_tmpnam]; char* result=tmpnam(tmpnam_str); if(result!=tmpnam_str) throw runtime_error(__FILE__ ": tmpnam(char*) failed!");  string xml(tmpnam_str+string(".xml.bz2")); LOG_DEBUG("Using temp file "<<xml); this->generate(xml); pyOmega().load(xml); }
-BASIC_PY_PROXY_TAIL;
-
-class pySTLImporter : public STLImporter {
-    public:
-	void py_import(pyBodyContainer bc, unsigned int begin=0, bool noInteractingGeometry=false) { import(bc.proxee,begin,noInteractingGeometry); }
-};
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(STLImporter_import_overloads,py_import,1,3);
-
-
-// automatic conversion of Vector3r and 3-tuple
-// this doesn't create any new class in python
-struct custom_Vector3r_to_tuple{
-	static PyObject* convert(Vector3r const& v){
-		return python::incref(python::make_tuple(v[0],v[1],v[2]).ptr());
-	}
-};
-struct custom_Vector3r_from_tuple{
-	custom_Vector3r_from_tuple(){
-		python::converter::registry::push_back(&convertible,&construct,python::type_id<Vector3r>());
-	}
-	static void* convertible(PyObject* obj_ptr){
-		if(!PySequence_Check(obj_ptr) || PySequence_Size(obj_ptr)!=3) return 0;
-		return obj_ptr;
-	}
-	static void construct(PyObject* obj_ptr, python::converter::rvalue_from_python_stage1_data* data){
-		void* storage=((python::converter::rvalue_from_python_storage<Vector3r>*)(data))->storage.bytes;
-		new (storage) Vector3r(python::extract<double>(PySequence_GetItem(obj_ptr,0)),python::extract<double>(PySequence_GetItem(obj_ptr,1)),python::extract<double>(PySequence_GetItem(obj_ptr,2)));
-		data->convertible=storage;
-	}
-};
-
-
-BOOST_PYTHON_MODULE(wrapper)
-{
-
-	python::to_python_converter<Vector3r,custom_Vector3r_to_tuple>();
-	custom_Vector3r_from_tuple();
-
-	boost::python::class_<pyOmega>("Omega")
-		.add_property("iter",&pyOmega::iter)
-		.add_property("stopAtIter",&pyOmega::stopAtIter_get,&pyOmega::stopAtIter_set)
-		.add_property("time",&pyOmega::simulationTime)
-		.add_property("realtime",&pyOmega::realTime)
-		.add_property("dt",&pyOmega::dt_get,&pyOmega::dt_set)
-		.add_property("usesTimeStepper",&pyOmega::usesTimeStepper_get,&pyOmega::usesTimeStepper_set)
-		.def("load",&pyOmega::load)
-		.def("reload",&pyOmega::reload)
-		.def("save",&pyOmega::save)
-		.def("loadTmp",&pyOmega::loadTmp,omega_loadTmp_overloads(python::args("mark")))
-		.def("saveTmp",&pyOmega::saveTmp,omega_saveTmp_overloads(python::args("mark")))
-		.def("tmpToFile",&pyOmega::tmpToFile)
-		.def("saveSpheres",&pyOmega::saveSpheres)
-		.def("run",&pyOmega::run,omega_run_overloads())
-		.def("pause",&pyOmega::pause)
-		.def("step",&pyOmega::step)
-		.def("wait",&pyOmega::wait)
-		.def("reset",&pyOmega::reset)
-		.def("switchWorld",&pyOmega::switchWorld)
-		.def("labeledEngine",&pyOmega::labeled_engine_get)
-		.def("resetTime",&pyOmega::resetTime)
-		.def("plugins",&pyOmega::plugins_get)
-		.add_property("engines",&pyOmega::engines_get,&pyOmega::engines_set)
-		.add_property("miscParams",&pyOmega::miscParams_get,&pyOmega::miscParams_set)
-		.add_property("initializers",&pyOmega::initializers_get,&pyOmega::initializers_set)
-		.add_property("bodies",&pyOmega::bodies_get)
-		.add_property("interactions",&pyOmega::interactions_get)
-		.add_property("actions",&pyOmega::bex_get)
-		.add_property("bex",&pyOmega::bex_get)
-		.add_property("tags",&pyOmega::tags_get)
-		.def("childClasses",&pyOmega::listChildClasses)
-		.def("isChildClassOf",&pyOmega::isChildClassOf)
-		.add_property("bodyContainer",&pyOmega::bodyContainer_get,&pyOmega::bodyContainer_set)
-		.add_property("interactionContainer",&pyOmega::interactionContainer_get,&pyOmega::interactionContainer_set)
-		.add_property("timingEnabled",&pyOmega::timingEnabled_get,&pyOmega::timingEnabled_set)
-		.add_property("bexSyncCount",&pyOmega::bexSyncCount_get,&pyOmega::bexSyncCount_set)
-		.add_property("numThreads",&pyOmega::numThreads_get,&pyOmega::numThreads_set)
-		;
-	boost::python::class_<pyTags>("TagsWrapper",python::init<pyTags&>())
-		.def("__getitem__",&pyTags::getItem)
-		.def("__setitem__",&pyTags::setItem)
-		.def("keys",&pyTags::keys);
-	
-	boost::python::class_<pyBodyContainer>("BodyContainer",python::init<pyBodyContainer&>())
-		.def("__getitem__",&pyBodyContainer::pyGetitem)
-		.def("__len__",&pyBodyContainer::length)
-		.def("append",&pyBodyContainer::insert)
-		.def("append",&pyBodyContainer::insertList)
-		.def("appendClumped",&pyBodyContainer::insertClump)
-		.def("clear", &pyBodyContainer::clear)
-		.def("replace",&pyBodyContainer::replace);
-	boost::python::class_<pyInteractionContainer>("InteractionContainer",python::init<pyInteractionContainer&>())
-		.def("__iter__",&pyInteractionContainer::pyIter)
-		.def("__getitem__",&pyInteractionContainer::pyGetitem)
-		.def("__len__",&pyInteractionContainer::len)
-		.def("nth",&pyInteractionContainer::pyNth)
-		.def("withBody",&pyInteractionContainer::withBody)
-		.def("withBodyAll",&pyInteractionContainer::withBodyAll)
-		.def("nth",&pyInteractionContainer::pyNth)
-		.def("clear",&pyInteractionContainer::clear);
-	boost::python::class_<pyInteractionIterator>("InteractionIterator",python::init<pyInteractionIterator&>())
-		.def("__iter__",&pyInteractionIterator::pyIter)
-		.def("next",&pyInteractionIterator::pyNext);
-
-	boost::python::class_<pyBexContainer>("BexContainer",python::init<pyBexContainer&>())
-		.def("f",&pyBexContainer::force_get)
-		.def("t",&pyBexContainer::torque_get)
-		.def("m",&pyBexContainer::torque_get) // for compatibility with ActionContainer
-		.def("move",&pyBexContainer::move_get)
-		.def("rot",&pyBexContainer::rot_get)
-		.def("addF",&pyBexContainer::force_add)
-		.def("addT",&pyBexContainer::torque_add)
-		.def("addMove",&pyBexContainer::move_add)
-		.def("addRot",&pyBexContainer::rot_add);
-
-	boost::python::class_<pyTimingDeltas>("TimingDeltas",python::init<pyTimingDeltas&>())
-		.def("reset",&pyTimingDeltas::reset)
-		.add_property("data",&pyTimingDeltas::data_get);
-
-	#define TIMING_PROPS(class) .add_property("execTime",&class::execTime_get,&class::execTime_set).add_property("execCount",&class::execCount_get,&class::execCount_set).add_property("timingDeltas",&class::timingDeltas_get)
-
-	BASIC_PY_PROXY_WRAPPER(pyStandAloneEngine,"StandAloneEngine")
-		TIMING_PROPS(pyStandAloneEngine);
-	BASIC_PY_PROXY_WRAPPER(pyMetaEngine,"MetaEngine")
-		.add_property("functors",&pyMetaEngine::functors_get,&pyMetaEngine::functors_set)
-		TIMING_PROPS(pyMetaEngine)
-		.def(python::init<string,python::list>());
-	BASIC_PY_PROXY_WRAPPER(pyParallelEngine,"ParallelEngine")
-		.add_property("slaves",&pyParallelEngine::slaves_get,&pyParallelEngine::slaves_set)
-		.def(python::init<python::list>());
-	BASIC_PY_PROXY_WRAPPER(pyDeusExMachina,"DeusExMachina")
-		TIMING_PROPS(pyDeusExMachina);
-	BASIC_PY_PROXY_WRAPPER(pyInteractionDispatchers,"InteractionDispatchers")
-		.def(python::init<python::list,python::list,python::list>())
-		.add_property("geomDispatcher",&pyInteractionDispatchers::geomDispatcher_get)
-		.add_property("physDispatcher",&pyInteractionDispatchers::physDispatcher_get)
-		.add_property("constLawDispatcher",&pyInteractionDispatchers::constLawDispatcher_get)
-		TIMING_PROPS(pyInteractionDispatchers);
-	BASIC_PY_PROXY_WRAPPER(pyEngineUnit,"EngineUnit")
-		.add_property("timingDeltas",&pyEngineUnit::timingDeltas_get)
-		.add_property("bases",&pyEngineUnit::bases_get);
-
-	#undef TIMING_PROPS
-
-	BASIC_PY_PROXY_WRAPPER(pyGeometricalModel,"GeometricalModel");
-	BASIC_PY_PROXY_WRAPPER(pyInteractingGeometry,"InteractingGeometry");
-	BASIC_PY_PROXY_WRAPPER(pyPhysicalParameters,"PhysicalParameters")	
-		.add_property("blockedDOFs",&pyPhysicalParameters::blockedDOFs_get,&pyPhysicalParameters::blockedDOFs_set)
-		.add_property("pos",&pyPhysicalParameters::pos_get,&pyPhysicalParameters::pos_set)
-		.add_property("ori",&pyPhysicalParameters::ori_get,&pyPhysicalParameters::ori_set)
-		.add_property("refPos",&pyPhysicalParameters::refPos_get,&pyPhysicalParameters::refPos_set)
-		.add_property("displ",&pyPhysicalParameters::displ_get)
-		.add_property("rot",&pyPhysicalParameters::rot_get)
-		;
-	BASIC_PY_PROXY_WRAPPER(pyBoundingVolume,"BoundingVolume");
-	BASIC_PY_PROXY_WRAPPER(pyInteractionGeometry,"InteractionGeometry");
-	BASIC_PY_PROXY_WRAPPER(pyInteractionPhysics,"InteractionPhysics");
-
-	BASIC_PY_PROXY_WRAPPER(pyGeneric,"Generic");
-
-	BASIC_PY_PROXY_WRAPPER(pyBody,"Body")
-		.add_property("shape",&pyBody::shape_get,&pyBody::shape_set)
-		.add_property("mold",&pyBody::mold_get,&pyBody::mold_set)
-		.add_property("bound",&pyBody::bound_get,&pyBody::bound_set)
-		.add_property("phys",&pyBody::phys_get,&pyBody::phys_set)
-		.add_property("dynamic",&pyBody::dynamic_get,&pyBody::dynamic_set)
-		.add_property("id",&pyBody::id_get)
-		.add_property("mask",&pyBody::mask_get,&pyBody::mask_set)
-		.add_property("isStandalone",&pyBody::isStandalone)
-		.add_property("isClumpMember",&pyBody::isClumpMember)
-		.add_property("isClump",&pyBody::isClump);
-
-	BASIC_PY_PROXY_WRAPPER(pyInteraction,"Interaction")
-		.add_property("phys",&pyInteraction::phys_get,&pyInteraction::phys_set)
-		.add_property("geom",&pyInteraction::geom_get,&pyInteraction::geom_set)
-		.add_property("id1",&pyInteraction::id1_get)
-		.add_property("id2",&pyInteraction::id2_get)
-		.add_property("isReal",&pyInteraction::isReal_get);
-
-	BASIC_PY_PROXY_WRAPPER(pyFileGenerator,"Preprocessor")
-		.def("generate",&pyFileGenerator::generate)
-		.def("load",&pyFileGenerator::load);
-
-	boost::python::class_<pySTLImporter>("STLImporter")
-	    .def("open",&pySTLImporter::open)
-	    .add_property("number_of_facets",&pySTLImporter::number_of_facets)
-	    .def_readwrite("wire",&pySTLImporter::wire)
-	    .def("import_geometry",&pySTLImporter::py_import,STLImporter_import_overloads());
-	
-}
-

Modified: trunk/lib/SConscript
===================================================================
--- trunk/lib/SConscript	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/SConscript	2009-07-07 11:54:14 UTC (rev 1841)
@@ -6,36 +6,9 @@
 	env.Install('$PREFIX/lib/yade$SUFFIX/py/yade',[
 		env.SharedLibrary('WeightedAverage2d',['smoothing/WeightedAverage2d.cpp'],SHLIBPREFIX='',
 			LIBS=env['LIBS']+['yade-base']),
-		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.SharedLibrary('_utils',['py/_utils.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['Shop','ConcretePM']),
-		env.SharedLibrary('_packPredicates',['py/_packPredicates.cpp'],SHLIBPREFIX='',
-			# if we compile with GTS, link to the python module, as inGtsSurface uses some of its symbols.
-			# because the module doesn't have the lib- suffix, we put it directly to SHLINKFLAGS
-			# using the -l: syntax (see man ld) and declare the dependency below
-			SHLINKFLAGS=env['SHLINKFLAGS']+(['-l:$PREFIX/lib/yade$SUFFIX/py/gts/_gts.so'] if 'GTS' in env['features'] else [])),
-		env.SharedLibrary('_packSpheres',['py/_packSpheres.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['Shop']),
-		env.File('utils.py','py'),
-		env.File('eudoxos.py','py'),
-		env.File('plot.py','py'),
-		env.File('linterpolation.py','py'),
-		env.File('timing.py','py'),
-		env.File('pack.py','py'),
-
 	])
-	# 3rd party modules:
-	# ==================
-	# do not install them under yade but to the top-level module path
-	# say e.g. 'import euclid' (not 'import  yade.euclid')
-	env.Install('$PREFIX/lib/yade$SUFFIX/py',[
-		env.File('euclid.py','py'),
-		# skip this for now
-		#env.SharedLibrary('miniWm3Wrap',['miniWm3Wrap.cpp'],SHLIBPREFIX='',CPPPATH=env['CPPPATH']+['.'])
-	])
 
 
-
-
 if 'qt3' not in env['exclude']:
 	env.Install('$PREFIX/lib/yade$SUFFIX/lib',[
 		env.SharedLibrary('yade-serialization-qt',

Deleted: trunk/lib/miniWm3Wrap-generate.py
===================================================================
--- trunk/lib/miniWm3Wrap-generate.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/miniWm3Wrap-generate.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,26 +0,0 @@
-import os, os.path
-from pyplusplus import module_builder
-from pyplusplus.decl_wrappers import *
-from pyplusplus.module_builder import call_policies
-
-
-#Creating an instance of class that will help you to expose your declarations
-mb = module_builder.module_builder_t( [os.path.abspath("miniWm3Wrap-toExpose.hpp")]
-                                      , working_directory=r"."
-                                      , include_paths=['miniWm3','.']
-                                      , define_symbols=['USE_MINIWM3'] )
-# we don't need system things from wm3
-mb.decls(lambda decl: 'System' in decl.name).exclude() 
-#mb.decls(lambda decl: 'noexpose' in decl.name).exclude() 
-# exclude casting operators
-mb.decls(lambda decl: 'operator double' in decl.name).exclude()
-mb.member_functions(return_type='double &').exclude()
-# we would have to do some magic here
-mb.member_functions(lambda decl: decl.name in ['ToAxisAngle','ToRotationMatrix']).exclude()
-mb.member_functions(lambda decl: decl.name in ['FromRotationMatrix','FromAxisAngle','Slerp','SlerpExtraSpins','Intermediate','Squad','Align']).exclude()
-#mb.member_function(meth).call_policies=call_policies.return_value_policy(call_policies.return_internal_reference)
-
-#mb.print_declarations()
-mb.build_code_creator(module_name='miniWm3Wrap')
-mb.write_module('miniWm3Wrap.cpp')
-

Deleted: trunk/lib/miniWm3Wrap-toExpose.hpp
===================================================================
--- trunk/lib/miniWm3Wrap-toExpose.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/miniWm3Wrap-toExpose.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,16 +0,0 @@
-//#include<Wm3Vector3.h>
-#include<base/yadeWm3.hpp>
-#include<base/yadeWm3Extra.hpp>
-// instantiate those types so that they are exposed
-// but name them noexpose_*, as we ask for exclusion of such things in the wrapper script
-
-//int i=sizeof(Vector3r);
-//int j=sizeof(Vector2r);
-//int k=sizeof(Quaternionr);
-
-Vector3r noexpose_v3r;
-Vector2r noexpose_v2r;
-Quaternionr noexpose_q;
-//Matrix3r m3r;
-
-

Deleted: trunk/lib/py/_eudoxos.cpp
===================================================================
--- trunk/lib/py/_eudoxos.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/_eudoxos.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,91 +0,0 @@
-#include<yade/pkg-dem/ConcretePM.hpp>
-#include<boost/python.hpp>
-#include<yade/extra/boost_python_len.hpp>
-using namespace boost::python;
-using namespace std;
-#ifdef LOG4CXX
-	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.eudoxos");
-#endif
-
-# if 0
-Real elasticEnergyDensityInAABB(python::tuple AABB){
-	Vector3r bbMin=tuple2vec(python::extract<python::tuple>(AABB[0])()), bbMax=tuple2vec(python::extract<python::tuple>(AABB[1])()); Vector3r box=bbMax-bbMin;
-	shared_ptr<MetaBody> rb=Omega::instance().getRootBody();
-	Real E=0;
-	FOREACH(const shared_ptr<Interaction>&i, *rb->transientInteractions){
-		if(!i->interactionPhysics) continue;
-		shared_ptr<CpmPhys> bc=dynamic_pointer_cast<CpmPhys>(i->interactionPhysics); if(!bc) continue;
-		const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
-		bool isIn1=isInBB(b1->physicalParameters->se3.position,bbMin,bbMax), isIn2=isInBB(b2->physicalParameters->se3.position,bbMin,bbMax);
-		if(!isIn1 && !isIn2) continue;
-		Real weight=1.;
-		if((!isIn1 && isIn2) || (isIn1 && !isIn2)){
-			//shared_ptr<Body> bIn=isIn1?b1:b2, bOut=isIn2?b2:b1;
-			Vector3r vIn=(isIn1?b1:b2)->physicalParameters->se3.position, vOut=(isIn2?b1:b2)->physicalParameters->se3.position;
-			#define _WEIGHT_COMPONENT(axis) if(vOut[axis]<bbMin[axis]) weight=min(weight,abs((vOut[axis]-bbMin[axis])/(vOut[axis]-vIn[axis]))); else if(vOut[axis]>bbMax[axis]) weight=min(weight,abs((vOut[axis]-bbMax[axis])/(vOut[axis]-vIn[axis])));
-			_WEIGHT_COMPONENT(0); _WEIGHT_COMPONENT(1); _WEIGHT_COMPONENT(2);
-			assert(weight>=0 && weight<=1);
-		}
-		E+=.5*bc->E*bc->crossSection*pow(bc->epsN,2)+.5*bc->G*bc->crossSection*pow(bc->epsT.Length(),2);
-	}
-	return E/(box[0]*box[1]*box[2]);
-}
-#endif
-
-/* yield surface for the CPM model; this is used only to make yield surface plot from python, for debugging */
-Real yieldSigmaTMagnitude(Real sigmaN, int yieldSurfType=0){
-	#ifdef CPM_YIELD_SIGMA_T_MAGNITUDE
-		/* find first suitable interaction */
-		MetaBody* rootBody=Omega::instance().getRootBody().get();
-		shared_ptr<Interaction> I;
-		FOREACH(I, *rootBody->transientInteractions){
-			if(I->isReal()) break;
-		}
-		Real nan=std::numeric_limits<Real>::quiet_NaN();
-		if(!I->isReal()) {LOG_ERROR("No real interaction found, returning NaN!"); return nan; }
-		CpmPhys* BC=dynamic_cast<CpmPhys*>(I->interactionPhysics.get());
-		if(!BC) {LOG_ERROR("Interaction physics is not CpmPhys instance, returning NaN!"); return nan;}
-		const Real &omega(BC->omega); const Real& undamagedCohesion(BC->undamagedCohesion); const Real& tanFrictionAngle(BC->tanFrictionAngle);
-		const Real& yieldLogSpeed(Law2_Dem3DofGeom_CpmPhys_Cpm::yieldLogSpeed); // const int& yieldSurfType(Law2_Dem3DofGeom_CpmPhys_Cpm::yieldSurfType);
-		const Real& yieldEllipseShift(Law2_Dem3DofGeom_CpmPhys_Cpm::yieldEllipseShift);
-		return CPM_YIELD_SIGMA_T_MAGNITUDE(sigmaN);
-	#else
-		LOG_FATAL("CPM model not available in this build.");
-		throw;
-	#endif
-}
-
-
-// copied from _utils.cpp
-Vector3r tuple2vec(const python::tuple& t){return Vector3r(extract<double>(t[0])(),extract<double>(t[1])(),extract<double>(t[2])());}
-
-/*! Set velocity of all dynamic particles so that if their motion were unconstrained,
- * axis given by axisPoint and axisDirection would be reached in timeToAxis
- * (or, point at distance subtractDist from axis would be reached).
- *
- * The code is analogous to AxialGravityEngine and is intended to give initial motion
- * to particles subject to axial compaction to speed up the process. */
-void velocityTowardsAxis(const Vector3r& axisPoint, const Vector3r& axisDirection, Real timeToAxis, Real subtractDist=0., Real perturbation=0.1){
-	FOREACH(const shared_ptr<Body>&b, *(Omega::instance().getRootBody()->bodies)){
-		if(!b->isDynamic) continue;
-		ParticleParameters* pp=YADE_CAST<ParticleParameters*>(b->physicalParameters.get());
-		const Vector3r& x0=pp->se3.position;
-		const Vector3r& x1=axisPoint;
-		const Vector3r x2=axisPoint+axisDirection;
-		Vector3r closestAxisPoint=(x2-x1) * /* t */ (-(x1-x0).Dot(x2-x1))/((x2-x1).SquaredLength());
-		Vector3r toAxis=closestAxisPoint-x0;
-		if(subtractDist>0) toAxis*=(toAxis.Length()-subtractDist)/toAxis.Length();
-		pp->velocity=toAxis/timeToAxis;
-		Vector3r ppDiff=perturbation*(1./sqrt(3.))*Vector3r(Mathr::UnitRandom(),Mathr::UnitRandom(),Mathr::UnitRandom())*pp->velocity.Length();
-		pp->velocity+=ppDiff;
-	}
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(velocityTowardsAxis_overloads,velocityTowardsAxis,3,5);
-
-
-
-BOOST_PYTHON_MODULE(_eudoxos){
-	def("velocityTowardsAxis",velocityTowardsAxis,velocityTowardsAxis_overloads(args("axisPoint","axisDirection","timeToAxis","subtractDist","perturbation")));
-	def("yieldSigmaTMagnitude",yieldSigmaTMagnitude);
-}
-

Deleted: trunk/lib/py/_packPredicates.cpp
===================================================================
--- trunk/lib/py/_packPredicates.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/_packPredicates.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,348 +0,0 @@
-// 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<yade/lib-base/yadeWm3Extra.hpp>
-// #include<yade/gui-py/_utils.hpp> // will be: yade/lib-py/_utils.hpp> at some point
-#include<Wm3Vector3.h>
-
-using namespace boost;
-using namespace std;
-#ifdef LOG4CXX
-	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.pack.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]);}
-python::tuple vec2tuple(const Vector2r& v){return boost::python::make_tuple(v[0],v[1]);}
-Vector3r tuple2vec(const python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
-Vector2r tuple2vec2d(const python::tuple& t){return Vector2r(python::extract<double>(t[0])(),python::extract<double>(t[1])());}
-//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])()); }
-void ttuple2vvec(const python::tuple& t, Vector3r& v1, Vector3r& v2){ v1=python::extract<Vector3r>(t[0])(); v2=python::extract<Vector3r>(t[1])(); }
-python::tuple vvec2ttuple(const Vector3r&v1, const Vector3r&v2){ return python::make_tuple(v1,v2); }
-
-struct Predicate{
-	public:
-		virtual bool operator() (const Vector3r& pt,Real pad=0.) const = 0;
-		virtual python::tuple aabb() const = 0;
-		Vector3r dim() const { Vector3r mn,mx; ttuple2vvec(aabb(),mn,mx); return mx-mn; }
-		Vector3r center() const { Vector3r mn,mx; ttuple2vvec(aabb(),mn,mx); return .5*(mn+mx); }
-};
-// make the pad parameter optional
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(PredicateCall_overloads,operator(),1,2);
-
-/* Since we want to make Predicate::operator() and Predicate::aabb() callable from c++ on python::object
-with the right virtual method resolution, we have to wrap the class in the following way. See 
-http://www.boost.org/doc/libs/1_38_0/libs/python/doc/tutorial/doc/html/python/exposing.html for documentation
-on exposing virtual methods.
-
-This makes it possible to derive a python class from Predicate, override its aabb() method, for instance,
-and use it in PredicateUnion, which will call the python implementation of aabb() as it should. This
-approach is used in the inGtsSurface class defined in pack.py.
-
-See scripts/test/gts-operators.py for an example.
-
-NOTE: you still have to call base class ctor in your class' ctor derived in python, e.g.
-super(inGtsSurface,self).__init__() so that virtual methods work as expected.
-*/
-struct PredicateWrap: Predicate, python::wrapper<Predicate>{
-	bool operator()(const Vector3r& pt, Real pad=0.) const { return this->get_override("__call__")(pt,pad);}
-	python::tuple aabb() const { return this->get_override("aabb")(); }
-};
-// make the pad parameter optional
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(PredicateWrapCall_overloads,operator(),1,2);
-
-/*********************************************************************************
-****************** Boolean operations on predicates ******************************
-*********************************************************************************/
-
-const Predicate& obj2pred(python::object obj){ return python::extract<const Predicate&>(obj)();}
-
-class PredicateBoolean: public Predicate{
-	protected:
-		const python::object A,B;
-	public:
-		PredicateBoolean(const python::object _A, const python::object _B): A(_A), B(_B){}
-		const python::object getA(){ return A;}
-		const python::object getB(){ return B;}
-};
-
-// http://www.linuxtopia.org/online_books/programming_books/python_programming/python_ch16s03.html
-class PredicateUnion: public PredicateBoolean{
-	public:
-		PredicateUnion(const python::object _A, const python::object _B): PredicateBoolean(_A,_B){}
-		virtual bool operator()(const Vector3r& pt,Real pad) const {return obj2pred(A)(pt,pad)||obj2pred(B)(pt,pad);}
-		virtual python::tuple aabb() const { Vector3r minA,maxA,minB,maxB; ttuple2vvec(obj2pred(A).aabb(),minA,maxA); ttuple2vvec(obj2pred(B).aabb(),minB,maxB); return vvec2ttuple(componentMinVector(minA,minB),componentMaxVector(maxA,maxB));}
-};
-PredicateUnion makeUnion(const python::object& A, const python::object& B){ return PredicateUnion(A,B);}
-
-class PredicateIntersection: public PredicateBoolean{
-	public:
-		PredicateIntersection(const python::object _A, const python::object _B): PredicateBoolean(_A,_B){}
-		virtual bool operator()(const Vector3r& pt,Real pad) const {return obj2pred(A)(pt,pad) && obj2pred(B)(pt,pad);}
-		virtual python::tuple aabb() const { Vector3r minA,maxA,minB,maxB; ttuple2vvec(obj2pred(A).aabb(),minA,maxA); ttuple2vvec(obj2pred(B).aabb(),minB,maxB); return vvec2ttuple(componentMaxVector(minA,minB),componentMinVector(maxA,maxB));}
-};
-PredicateIntersection makeIntersection(const python::object& A, const python::object& B){ return PredicateIntersection(A,B);}
-
-class PredicateDifference: public PredicateBoolean{
-	public:
-		PredicateDifference(const python::object _A, const python::object _B): PredicateBoolean(_A,_B){}
-		virtual bool operator()(const Vector3r& pt,Real pad) const {return obj2pred(A)(pt,pad) && !obj2pred(B)(pt,-pad);}
-		virtual python::tuple aabb() const { return obj2pred(A).aabb(); }
-};
-PredicateDifference makeDifference(const python::object& A, const python::object& B){ return PredicateDifference(A,B);}
-
-class PredicateSymmetricDifference: public PredicateBoolean{
-	public:
-		PredicateSymmetricDifference(const python::object _A, const python::object _B): PredicateBoolean(_A,_B){}
-		virtual bool operator()(const Vector3r& pt,Real pad) const {bool inA=obj2pred(A)(pt,pad), inB=obj2pred(B)(pt,pad); return (inA && !inB) || (!inA && inB);}
-		virtual python::tuple aabb() const { Vector3r minA,maxA,minB,maxB; ttuple2vvec(obj2pred(A).aabb(),minA,maxA); ttuple2vvec(obj2pred(B).aabb(),minB,maxB); return vvec2ttuple(componentMinVector(minA,minB),componentMaxVector(maxA,maxB));}
-};
-PredicateSymmetricDifference makeSymmetricDifference(const python::object& A, const python::object& B){ return PredicateSymmetricDifference(A,B);}
-
-/*********************************************************************************
-****************************** Primitive predicates ******************************
-*********************************************************************************/
-
-
-/*! Sphere predicate */
-class inSphere: public Predicate {
-	Vector3r center; Real radius;
-public:
-	inSphere(const Vector3r& _center, Real _radius){center=_center; radius=_radius;}
-	virtual bool operator()(const Vector3r& pt, Real pad=0.) const { return ((pt-center).Length()-pad<=radius-pad); }
-	virtual python::tuple aabb() const {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: public Predicate{
-	Vector3r mn, mx;
-public:
-	inAlignedBox(const Vector3r& _mn, const Vector3r& _mx): mn(_mn), mx(_mx) {}
-	virtual bool operator()(const Vector3r& pt, Real pad=0.) const {
-		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];
-	}
-	virtual python::tuple aabb() const { return vvec2ttuple(mn,mx); }
-};
-
-/*! Arbitrarily oriented cylinder predicate */
-class inCylinder: public Predicate{
-	Vector3r c1,c2,c12; Real radius,ht;
-public:
-	inCylinder(const Vector3r& _c1, const Vector3r& _c2, Real _radius){c1=_c1; c2=_c2; c12=c2-c1; radius=_radius; ht=c12.Length(); }
-	bool operator()(const Vector3r& pt, Real pad=0.) const {
-		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() const {
-		// see http://www.gamedev.net/community/forums/topic.asp?topic_id=338522&forum_id=20&gforum_id=0 for the algorithm
-		const Vector3r& A(c1); const 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=componentMinVector(A,B), mx=componentMaxVector(A,B);
-		return vvec2ttuple(mn-radius*k,mx+radius*k);
-	}
-};
-
-/*! Oriented hyperboloid predicate (cylinder as special case).
-
-See http://mathworld.wolfram.com/Hyperboloid.html for the parametrization and meaning of symbols
-*/
-class inHyperboloid: public Predicate{
-	Vector3r c1,c2,c12; Real R,a,ht,c;
-public:
-	inHyperboloid(const Vector3r& _c1, const Vector3r& _c2, Real _R, Real _r){
-		c1=_c1; c2=_c2; R=_R; a=_r;
-		c12=c2-c1; ht=c12.Length();
-		Real uMax=sqrt(pow(R/a,2)-1); c=ht/(2*uMax);
-	}
-	// WARN: this is not accurate, since padding is taken as perpendicular to the axis, not the the surface
-	bool operator()(const Vector3r& pt, Real pad=0.) const {
-		Real v=(pt.Dot(c12)-c1.Dot(c12))/(ht*ht); // normalized coordinate along the c1--c2 axis
-		if((v*ht<0+pad) || (v*ht>ht-pad)) return false; // out of cylinder along the axis
-		Real u=(v-.5)*ht/c; // u from the wolfram parametrization; u is 0 in the center
-		Real rHere=a*sqrt(1+u*u); // pad is taken perpendicular to the axis, not to the surface (inaccurate)
-		Real axisDist=((pt-c1).Cross(pt-c2)).Length()/ht;
-		if(axisDist>rHere-pad) return false;
-		return true;
-	}
-	python::tuple aabb() const {
-		// the lazy way
-		return inCylinder(c1,c2,R).aabb();
-	}
-};
-
-/*! Axis-aligned ellipsoid predicate */
-class inEllipsoid: public Predicate{
-	Vector3r c, abc;
-public:
-	inEllipsoid(const Vector3r& _c, const Vector3r& _abc) {c=_c; abc=_abc;}
-	bool operator()(const Vector3r& pt, Real pad=0.) const {
-		//Define the ellipsoid X-coordinate of given Y and Z
-		Real x = sqrt((1-pow((pt[1]-c[1]),2)/((abc[1]-pad)*(abc[1]-pad))-pow((pt[2]-c[2]),2)/((abc[2]-pad)*(abc[2]-pad)))*((abc[0]-pad)*(abc[0]-pad)))+c[0]; 
-		Vector3r edgeEllipsoid(x,pt[1],pt[2]); // create a vector of these 3 coordinates
-		//check whether given coordinates lie inside ellipsoid or not
-		if ((pt-c).Length()<=(edgeEllipsoid-c).Length()) return true;
-		else return false;
-	}
-	python::tuple aabb() const {
-		const Vector3r& center(c); const Vector3r& ABC(abc);
-		return vvec2ttuple(Vector3r(center[0]-ABC[0],center[1]-ABC[1],center[2]-ABC[2]),Vector3r(center[0]+ABC[0],center[1]+ABC[1],center[2]+ABC[2]));
-	}
-};
-
-/*! Negative notch predicate.
-
-Use intersection (& operator) of another predicate with notInNotch to create notched solid.
-
-
-		
-		geometry explanation:
-		
-			c: the center
-			normalHalfHt (in constructor): A-C
-			inside: perpendicular to notch edge, points inside the notch (unit vector)
-			normal: perpendicular to inside, perpendicular to both notch planes
-			edge: unit vector in the direction of the edge
-
-		          ↑ distUp        A
-		-------------------------
-		                        | C
-		         inside(unit) ← * → distInPlane
-		                        |
-		-------------------------
-		          ↓ distDown      B
-
-*/
-class notInNotch: public Predicate{
-	Vector3r c, edge, normal, inside; Real aperture;
-public:
-	notInNotch(const Vector3r& _c, const Vector3r& _edge, const Vector3r& _normal, Real _aperture){
-		c=_c;
-		edge=_edge; edge.Normalize();
-		normal=_normal; normal-=edge*edge.Dot(normal); normal.Normalize();
-		inside=edge.Cross(normal);
-		aperture=_aperture;
-		// LOG_DEBUG("edge="<<edge<<", normal="<<normal<<", inside="<<inside<<", aperture="<<aperture);
-	}
-	bool operator()(const Vector3r& pt, Real pad=0.) const {
-		Real distUp=normal.Dot(pt-c)-aperture/2, distDown=-normal.Dot(pt-c)-aperture/2, distInPlane=-inside.Dot(pt-c);
-		// LOG_DEBUG("pt="<<pt<<", distUp="<<distUp<<", distDown="<<distDown<<", distInPlane="<<distInPlane);
-		if(distInPlane>=pad) return true;
-		if(distUp     >=pad) return true;
-		if(distDown   >=pad) return true;
-		if(distInPlane<0) return false;
-		if(distUp  >0) return sqrt(pow(distInPlane,2)+pow(distUp,2))>=pad;
-		if(distDown>0) return sqrt(pow(distInPlane,2)+pow(distUp,2))>=pad;
-		// between both notch planes, closer to the edge than pad (distInPlane<pad)
-		return false;
-	}
-	// This predicate is not bounded, return infinities
-	python::tuple aabb() const {
-		Real inf=std::numeric_limits<Real>::infinity();
-		return vvec2ttuple(Vector3r(-inf,-inf,-inf),Vector3r(inf,inf,inf));
-	}
-};
-
-#ifdef YADE_GTS
-extern "C" {
-#include<yade/lib-py/pygts.h>
-}
-/* Helper function for inGtsSurface::aabb() */
-static void vertex_aabb(GtsVertex *vertex, pair<Vector3r,Vector3r> *bb)
-{
-	GtsPoint *_p=GTS_POINT(vertex);
-	Vector3r p(_p->x,_p->y,_p->z);
-	bb->first=componentMinVector(bb->first,p);
-	bb->second=componentMaxVector(bb->second,p);
-}
-
-/*
-This class plays tricks getting aroung pyGTS to get GTS objects and cache bb tree to speed
-up point inclusion tests. For this reason, we have to link with _gts.so (see corresponding
-SConscript file), which is at the same time the python module.
-*/
-class inGtsSurface: public Predicate{
-	python::object pySurf; // to hold the reference so that surf is valid
-	GtsSurface *surf;
-	bool is_open, noPad, noPadWarned;
-	GNode* tree;
-public:
-	inGtsSurface(python::object _surf, bool _noPad=false): pySurf(_surf), noPad(_noPad), noPadWarned(false) {
-		if(!pygts_surface_check(_surf.ptr())) throw invalid_argument("Ctor must receive a gts.Surface() instance."); 
-		surf=PYGTS_SURFACE_AS_GTS_SURFACE(PYGTS_SURFACE(_surf.ptr()));
-	 	if(!gts_surface_is_closed(surf)) throw invalid_argument("Surface is not closed.");
-		is_open=gts_surface_volume(surf)<0.;
-		if((tree=gts_bb_tree_surface(surf))==NULL) throw runtime_error("Could not create GTree.");
-	}
-	~inGtsSurface(){g_node_destroy(tree);}
-	python::tuple aabb() const {
-		Real inf=std::numeric_limits<Real>::infinity();
-		pair<Vector3r,Vector3r> bb; bb.first=Vector3r(inf,inf,inf); bb.second=Vector3r(-inf,-inf,-inf);
-		gts_surface_foreach_vertex(surf,(GtsFunc)vertex_aabb,&bb);
-		return vvec2ttuple(bb.first,bb.second);
-	}
-	bool ptCheck(const Vector3r& pt) const{
-		GtsPoint gp; gp.x=pt[0]; gp.y=pt[1]; gp.z=pt[2];
-		return (bool)gts_point_is_inside_surface(&gp,tree,is_open);
-	}
-	bool operator()(const Vector3r& pt, Real pad=0.) const {
-		if(noPad){
-			if(pad!=0. && noPadWarned) LOG_WARN("inGtsSurface constructed with noPad; requested non-zero pad set to zero.");
-			return ptCheck(pt);
-		}
-		return ptCheck(pt) && ptCheck(pt-Vector3r(pad,0,0)) && ptCheck(pt+Vector3r(pad,0,0)) && ptCheck(pt-Vector3r(0,pad,0))&& ptCheck(pt+Vector3r(0,pad,0)) && ptCheck(pt-Vector3r(0,0,pad))&& ptCheck(pt+Vector3r(0,0,pad));
-	}
-};
-
-#endif
-
-BOOST_PYTHON_MODULE(_packPredicates){
-
-	// base predicate class
-	python::class_<PredicateWrap,/* necessary, as methods are pure virtual*/ boost::noncopyable>("Predicate")
-		.def("__call__",python::pure_virtual(&Predicate::operator()))
-		.def("aabb",python::pure_virtual(&Predicate::aabb))
-		.def("dim",&Predicate::dim)
-		.def("center",&Predicate::center)
-		.def("__or__",makeUnion).def("__and__",makeIntersection).def("__sub__",makeDifference).def("__xor__",makeSymmetricDifference);
-	// boolean operations
-	python::class_<PredicateBoolean,python::bases<Predicate>,boost::noncopyable>("PredicateBoolean","Boolean operation on 2 predicates (abstract class)",python::no_init)
-		.add_property("A",&PredicateBoolean::getA).add_property("B",&PredicateBoolean::getB);
-	python::class_<PredicateUnion,python::bases<PredicateBoolean> >("PredicateUnion","Union of 2 predicates",python::init<python::object,python::object>());
-	python::class_<PredicateIntersection,python::bases<PredicateBoolean> >("PredicateIntersection","Intersection of 2 predicates",python::init<python::object,python::object >());
-	python::class_<PredicateDifference,python::bases<PredicateBoolean> >("PredicateDifference","Difference of 2 predicates",python::init<python::object,python::object >());
-	python::class_<PredicateSymmetricDifference,python::bases<PredicateBoolean> >("PredicateSymmetricDifference","SymmetricDifference of 2 predicates",python::init<python::object,python::object >());
-	// primitive predicates
-	python::class_<inSphere,python::bases<Predicate> >("inSphere","Sphere predicate.",python::init<const Vector3r&,Real>(python::args("center","radius"),"Ctor taking center (as a 3-tuple) and radius"));
-	python::class_<inAlignedBox,python::bases<Predicate> >("inAlignedBox","Axis-aligned box predicate",python::init<const Vector3r&,const Vector3r&>(python::args("minAABB","maxAABB"),"Ctor taking minumum and maximum points of the box (as 3-tuples)."));
-	python::class_<inCylinder,python::bases<Predicate> >("inCylinder","Cylinder predicate",python::init<const Vector3r&,const Vector3r&,Real>(python::args("centerBottom","centerTop","radius"),"Ctor taking centers of the lateral walls (as 3-tuples) and radius."));
-	python::class_<inHyperboloid,python::bases<Predicate> >("inHyperboloid","Hyperboloid predicate",python::init<const Vector3r&,const Vector3r&,Real,Real>(python::args("centerBottom","centerTop","radius","skirt"),"Ctor taking centers of the lateral walls (as 3-tuples), radius at bases and skirt (middle radius)."));
-	python::class_<inEllipsoid,python::bases<Predicate> >("inEllipsoid","Ellipsoid predicate",python::init<const Vector3r&,const Vector3r&>(python::args("centerPoint","abc"),"Ctor taking center of the ellipsoid (3-tuple) and its 3 radii (3-tuple)."));
-	python::class_<notInNotch,python::bases<Predicate> >("notInNotch","Outside of infinite, rectangle-shaped notch predicate",python::init<const Vector3r&,const Vector3r&,const Vector3r&,Real>(python::args("centerPoint","edge","normal","aperture"),"Ctor taking point in the symmetry plane, vector pointing along the edge, plane normal and aperture size.\nThe side inside the notch is edge×normal.\nNormal is made perpendicular to the edge.\nAll vectors are normalized at construction time.")); 
-	#ifdef YADE_GTS
-		python::class_<inGtsSurface,python::bases<Predicate> >("inGtsSurface","GTS surface predicate",python::init<python::object,python::optional<bool> >(python::args("surface","noPad"),"Ctor taking a gts.Surface() instance, which must not be modified during instance lifetime.\nThe optional noPad can disable padding (if set to True), which speeds up calls several times.\nNote: padding checks inclusion of 6 points along +- cardinal directions in the pad distance from given point, which is not exact."));
-	#endif
-}
-

Deleted: trunk/lib/py/_packSpheres.cpp
===================================================================
--- trunk/lib/py/_packSpheres.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/_packSpheres.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,143 +0,0 @@
-// 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<yade/lib-base/yadeWm3Extra.hpp>
-// #include<yade/gui-py/_utils.hpp> // will be: yade/lib-py/_utils.hpp> at some point
-#include<Wm3Vector3.h>
-
-#include<yade/core/Omega.hpp>
-#include<yade/core/MetaBody.hpp>
-#include<yade/pkg-common/InteractingSphere.hpp>
-
-#include<yade/extra/Shop.hpp>
-
-using namespace boost;
-using namespace std;
-#ifdef LOG4CXX
-	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.pack.spheres");
-#endif
-
-// copied from _packPredicates.cpp :-(
-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])());}
-Vector3r tuple2vec(python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
-
-struct SpherePack{
-	struct Sph{ Vector3r c; Real r; Sph(const Vector3r& _c, Real _r): c(_c), r(_r){};
-		python::tuple asTuple() const { return python::make_tuple(c,r); }
-	};
-	vector<Sph> pack;
-	SpherePack(){};
-	SpherePack(const python::list& l){ fromList(l); }
-	// add single sphere
-	void add(const Vector3r& c, Real r){ pack.push_back(Sph(c,r)); }
-	// I/O
-	void fromList(const python::list& l);
-	python::list toList() const;
-	void fromFile(const string file);
-	void toFile(const string file) const;
-	void fromSimulation();
-	// spatial characteristics
-	Vector3r dim() const {Vector3r mn,mx; aabb(mn,mx); return mx-mn;}
-	python::tuple aabb_py() const { Vector3r mn,mx; aabb(mn,mx); return python::make_tuple(mn,mx); }
-	void aabb(Vector3r& mn, Vector3r& mx) const {
-		Real inf=std::numeric_limits<Real>::infinity(); mn=Vector3r(inf,inf,inf); mx=Vector3r(-inf,-inf,-inf);
-		FOREACH(const Sph& s, pack){ Vector3r r(s.r,s.r,s.r); mn=componentMinVector(mn,s.c-r); mx=componentMaxVector(mx,s.c+r);}
-	}
-	Vector3r midPt() const {Vector3r mn,mx; aabb(mn,mx); return .5*(mn+mx);}
-	Real relDensity() const {
-		Real sphVol=0; Vector3r dd=dim();
-		FOREACH(const Sph& s, pack) sphVol+=pow(s.r,3);
-		sphVol*=(4/3.)*Mathr::PI;
-		return sphVol/(dd[0]*dd[1]*dd[2]);
-	}
-	// transformations
-	void translate(const Vector3r& shift){ FOREACH(Sph& s, pack) s.c+=shift; }
-	void rotate(const Vector3r& axis, Real angle){ Vector3r mid=midPt(); Quaternionr q(axis,angle); FOREACH(Sph& s, pack) s.c=q*(s.c-mid)+mid; }
-	void scale(Real scale){ Vector3r mid=midPt(); FOREACH(Sph& s, pack) {s.c=scale*(s.c-mid)+mid; s.r*=abs(scale); } }
-	// iteration 
-	size_t len() const{ return pack.size(); }
-	python::tuple getitem(size_t idx){ if(idx<0 || idx>=pack.size()) throw runtime_error("Index "+lexical_cast<string>(idx)+" out of range 0.."+lexical_cast<string>(pack.size()-1)); return pack[idx].asTuple(); }
-	struct iterator{
-		const SpherePack& sPack; size_t pos;
-		iterator(const SpherePack& _sPack): sPack(_sPack), pos(0){}
-		iterator iter(){ return *this;}
-		python::tuple next(){
-			if(pos==sPack.pack.size()-1){ PyErr_SetNone(PyExc_StopIteration); python::throw_error_already_set(); }
-			return sPack.pack[pos++].asTuple();
-		}
-	};
-	SpherePack::iterator getIterator() const{ return SpherePack::iterator(*this);};
-};
-
-void SpherePack::fromList(const python::list& l){
-	pack.clear();
-	size_t len=python::len(l);
-	for(size_t i=0; i<len; i++){
-		const python::tuple& t=python::extract<python::tuple>(l[i]);
-		const Vector3r t0=python::extract<Vector3r>(t[0]);
-		pack.push_back(Sph(t0,python::extract<double>(t[1])));
-	}
-};
-
-python::list SpherePack::toList() const {
-	python::list ret;
-	FOREACH(const Sph& s, pack) ret.append(python::make_tuple(s.c,s.r));
-	return ret;
-};
-
-void SpherePack::fromFile(string file) {
-	typedef pair<Vector3r,Real> pairVector3rReal;
-	vector<pairVector3rReal> ss;
-	Vector3r mn,mx;
-	ss=Shop::loadSpheresFromFile(file,mn,mx);
-	pack.clear();
-	FOREACH(const pairVector3rReal& s, ss) pack.push_back(Sph(s.first,s.second));
-}
-
-void SpherePack::toFile(const string fname) const {
-	ofstream f(fname.c_str());
-	if(!f.good()) throw runtime_error("Unable to open file `"+fname+"'");
-	FOREACH(const Sph& s, pack) f<<s.c[0]<<" "<<s.c[1]<<" "<<s.c[2]<<" "<<s.r<<endl;
-	f.close();
-};
-
-void SpherePack::fromSimulation() {
-	pack.clear();
-	MetaBody* rootBody=Omega::instance().getRootBody().get();
-	FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
-		shared_ptr<InteractingSphere>	intSph=dynamic_pointer_cast<InteractingSphere>(b->interactingGeometry);
-		if(!intSph) continue;
-		pack.push_back(Sph(b->physicalParameters->se3.position,intSph->radius));
-	}
-}
-
-
-
-BOOST_PYTHON_MODULE(_packSpheres){
-	python::class_<SpherePack>("SpherePack","Set of spheres as centers and radii",python::init<python::optional<python::list> >(python::args("list"),"Empty constructor, optionally taking list [ ((cx,cy,cz),r), … ] for initial data." ))
-		.def("add",&SpherePack::add,"Add single sphere to packing, given center as 3-tuple and radius")
-		.def("toList",&SpherePack::toList,"Return packing data as python list.")
-		.def("fromList",&SpherePack::fromList,"Make packing from given list, same format as for constructor. Discards current data.")
-		.def("load",&SpherePack::fromFile,"Load packing from external text file (current data will be discarded).")
-		.def("save",&SpherePack::toFile,"Save packing to external text file (will be overwritten).")
-		.def("fromSimulation",&SpherePack::fromSimulation,"Make packing corresponding to the current simulation. Discards current data.")
-		.def("aabb",&SpherePack::aabb_py,"Get axis-aligned bounding box coordinates, as 2 3-tuples.")
-		.def("dim",&SpherePack::dim,"Return dimensions of the packing in terms of aabb(), as a 3-tuple.")
-		.def("center",&SpherePack::midPt,"Return coordinates of the bounding box center.")
-		.def("relDensity",&SpherePack::relDensity,"Relative packing density, measured as sum of spheres' volumes / aabb volume.\n(Sphere overlaps are ignored.)")
-		.def("translate",&SpherePack::translate,"Translate all spheres by given vector.")
-		.def("rotate",&SpherePack::rotate,"Rotate all spheres around packing center (in terms of aabb()), given axis and angle of the rotation.")
-		.def("scale",&SpherePack::scale,"Scale the packing around its center (in terms of aabb()) by given factor (may be negative).")
-		.def("__len__",&SpherePack::len,"Get number of spheres in the packing")
-		.def("__getitem__",&SpherePack::getitem,"Get entry at given index, as tuple of center and radius.")
-		.def("__iter__",&SpherePack::getIterator,"Return iterator over spheres.")
-		;
-	python::class_<SpherePack::iterator>("SpherePackIterator",python::init<SpherePack::iterator&>())
-		.def("__iter__",&SpherePack::iterator::iter)
-		.def("next",&SpherePack::iterator::next)
-	;
-}
-

Deleted: trunk/lib/py/_utils.cpp
===================================================================
--- trunk/lib/py/_utils.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/_utils.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,432 +0,0 @@
-#include<yade/extra/Shop.hpp>
-#include<boost/python.hpp>
-#include<yade/extra/boost_python_len.hpp>
-#include<yade/core/MetaBody.hpp>
-#include<yade/core/Omega.hpp>
-#include<yade/pkg-common/Sphere.hpp>
-#include<yade/pkg-dem/SpheresContactGeometry.hpp>
-#include<yade/pkg-dem/DemXDofGeom.hpp>
-#include<yade/pkg-dem/SimpleViscoelasticBodyParameters.hpp>
-#include<yade/pkg-common/NormalShearInteractions.hpp>
-#include<yade/lib-computational-geometry/Hull2d.hpp>
-#include<cmath>
-
-#include<numpy/ndarrayobject.h>
-
-// #include"_utils.hpp"
-
-
-
-using namespace boost::python;
-
-#ifdef LOG4CXX
-	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.utils");
-#endif
-
-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(extract<double>(t[0])(),extract<double>(t[1])(),extract<double>(t[2])());}
-bool isInBB(Vector3r p, Vector3r bbMin, Vector3r bbMax){return p[0]>bbMin[0] && p[0]<bbMax[0] && p[1]>bbMin[1] && p[1]<bbMax[1] && p[2]>bbMin[2] && p[2]<bbMax[2];}
-
-bool ptInAABB(python::tuple p, python::tuple bbMin, python::tuple bbMax){return isInBB(tuple2vec(p),tuple2vec(bbMin),tuple2vec(bbMax));}
-
-/* \todo implement groupMask */
-python::tuple aabbExtrema(Real cutoff=0.0, bool centers=false){
-	if(cutoff<0. || cutoff>1.) throw invalid_argument("Cutoff must be >=0 and <=1.");
-	Real inf=std::numeric_limits<Real>::infinity();
-	Vector3r minimum(inf,inf,inf),maximum(-inf,-inf,-inf);
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->geometricalModel); if(!s) continue;
-		Vector3r rrr(s->radius,s->radius,s->radius);
-		minimum=componentMinVector(minimum,b->physicalParameters->se3.position-(centers?Vector3r::ZERO:rrr));
-		maximum=componentMaxVector(maximum,b->physicalParameters->se3.position+(centers?Vector3r::ZERO:rrr));
-	}
-	Vector3r dim=maximum-minimum;
-	return python::make_tuple(minimum+.5*cutoff*dim,maximum-.5*cutoff*dim);
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(aabbExtrema_overloads,aabbExtrema,0,2);
-
-python::tuple negPosExtremeIds(int axis, Real distFactor=1.1){
-	python::tuple extrema=aabbExtrema();
-	Real minCoord=extract<double>(extrema[0][axis])(),maxCoord=extract<double>(extrema[1][axis])();
-	python::list minIds,maxIds;
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->geometricalModel); if(!s) continue;
-		if(b->physicalParameters->se3.position[axis]-s->radius*distFactor<=minCoord) minIds.append(b->getId());
-		if(b->physicalParameters->se3.position[axis]+s->radius*distFactor>=maxCoord) maxIds.append(b->getId());
-	}
-	return python::make_tuple(minIds,maxIds);
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(negPosExtremeIds_overloads,negPosExtremeIds,1,2);
-
-python::tuple coordsAndDisplacements(int axis,python::tuple AABB=python::tuple()){
-	Vector3r bbMin(Vector3r::ZERO), bbMax(Vector3r::ZERO); bool useBB=python::len(AABB)>0;
-	if(useBB){bbMin=extract<Vector3r>(AABB[0])();bbMax=extract<Vector3r>(AABB[1])();}
-	python::list retCoord,retDispl;
-	FOREACH(const shared_ptr<Body>&b, *Omega::instance().getRootBody()->bodies){
-		if(useBB && !isInBB(b->physicalParameters->se3.position,bbMin,bbMax)) continue;
-		retCoord.append(b->physicalParameters->se3.position[axis]);
-		retDispl.append(b->physicalParameters->se3.position[axis]-b->physicalParameters->refSe3.position[axis]);
-	}
-	return python::make_tuple(retCoord,retDispl);
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(coordsAndDisplacements_overloads,coordsAndDisplacements,1,2);
-
-void setRefSe3(){
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		b->physicalParameters->refSe3.position=b->physicalParameters->se3.position;
-		b->physicalParameters->refSe3.orientation=b->physicalParameters->se3.orientation;
-	}
-}
-
-Real PWaveTimeStep(){return Shop::PWaveTimeStep();};
-
-Real elasticEnergyInAABB(python::tuple AABB){
-	Vector3r bbMin=extract<Vector3r>(AABB[0])(), bbMax=extract<Vector3r>(AABB[1])();
-	shared_ptr<MetaBody> rb=Omega::instance().getRootBody();
-	Real E=0;
-	FOREACH(const shared_ptr<Interaction>&i, *rb->transientInteractions){
-		if(!i->interactionPhysics) continue;
-		shared_ptr<NormalShearInteraction> bc=dynamic_pointer_cast<NormalShearInteraction>(i->interactionPhysics); if(!bc) continue;
-		shared_ptr<Dem3DofGeom> geom=dynamic_pointer_cast<Dem3DofGeom>(i->interactionGeometry); if(!bc){LOG_ERROR("NormalShearInteraction contact doesn't have SpheresContactGeomety associated?!"); continue;}
-		const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
-		bool isIn1=isInBB(b1->physicalParameters->se3.position,bbMin,bbMax), isIn2=isInBB(b2->physicalParameters->se3.position,bbMin,bbMax);
-		if(!isIn1 && !isIn2) continue;
-		LOG_DEBUG("Interaction #"<<i->getId1()<<"--#"<<i->getId2());
-		Real weight=1.;
-		if((!isIn1 && isIn2) || (isIn1 && !isIn2)){
-			//shared_ptr<Body> bIn=isIn1?b1:b2, bOut=isIn2?b2:b1;
-			Vector3r vIn=(isIn1?b1:b2)->physicalParameters->se3.position, vOut=(isIn2?b1:b2)->physicalParameters->se3.position;
-			#define _WEIGHT_COMPONENT(axis) if(vOut[axis]<bbMin[axis]) weight=min(weight,abs((vOut[axis]-bbMin[axis])/(vOut[axis]-vIn[axis]))); else if(vOut[axis]>bbMax[axis]) weight=min(weight,abs((vOut[axis]-bbMax[axis])/(vOut[axis]-vIn[axis])));
-			_WEIGHT_COMPONENT(0); _WEIGHT_COMPONENT(1); _WEIGHT_COMPONENT(2);
-			assert(weight>=0 && weight<=1);
-			//cerr<<"Interaction crosses AABB boundary, weight is "<<weight<<endl;
-			//LOG_DEBUG("Interaction crosses AABB boundary, weight is "<<weight);
-		} else {assert(isIn1 && isIn2); /* cerr<<"Interaction inside, weight is "<<weight<<endl;*/ /*LOG_DEBUG("Interaction inside, weight is "<<weight);*/}
-		E+=geom->refLength*weight*(.5*bc->kn*pow(geom->strainN(),2)+.5*bc->ks*pow(geom->strainT().Length(),2));
-	}
-	return E;
-}
-
-/* return histogram ([bin1Min,bin2Min,…],[value1,value2,…]) from projections of interactions
- * to the plane perpendicular to axis∈[0,1,2]; the number of bins can be specified and they cover
- * the range (0,π), since interactions are bidirecional, hence periodically the same on (π,2π).
- *
- * Only contacts using SpheresContactGeometry are considered.
- * Both bodies must be in the mask (except if mask is 0, when all bodies are considered)
- * If the projection is shorter than minProjLen, it is skipped.
- *
- * If both bodies are _outside_ the aabb (if specified), the interaction is skipped.
- *
- */
-python::tuple interactionAnglesHistogram(int axis, int mask=0, size_t bins=20, python::tuple aabb=python::tuple(), Real minProjLen=1e-6){
-	if(axis<0||axis>2) throw invalid_argument("Axis must be from {0,1,2}=x,y,z.");
-	Vector3r bbMin(Vector3r::ZERO), bbMax(Vector3r::ZERO); bool useBB=python::len(aabb)>0; if(useBB){bbMin=extract<Vector3r>(aabb[0])();bbMax=extract<Vector3r>(aabb[1])();}
-	Real binStep=Mathr::PI/bins; int axis2=(axis+1)%3, axis3=(axis+2)%3;
-	vector<Real> cummProj(bins,0.);
-	shared_ptr<MetaBody> rb=Omega::instance().getRootBody();
-	FOREACH(const shared_ptr<Interaction>& i, *rb->transientInteractions){
-		if(!i->isReal()) continue;
-		const shared_ptr<Body>& b1=Body::byId(i->getId1(),rb), b2=Body::byId(i->getId2(),rb);
-		if(!b1->maskOk(mask) || !b2->maskOk(mask)) continue;
-		if(useBB && !isInBB(b1->physicalParameters->se3.position,bbMin,bbMax) && !isInBB(b2->physicalParameters->se3.position,bbMin,bbMax)) continue;
-		shared_ptr<SpheresContactGeometry> scg=dynamic_pointer_cast<SpheresContactGeometry>(i->interactionGeometry); if(!scg) continue;
-		Vector3r n(scg->normal); n[axis]=0.; Real nLen=n.Length();
-		if(nLen<minProjLen) continue; // this interaction is (almost) exactly parallel to our axis; skip that one
-		Real theta=acos(n[axis2]/nLen)*(n[axis3]>0?1:-1); if(theta<0) theta+=Mathr::PI;
-		int binNo=theta/binStep;
-		cummProj[binNo]+=nLen;
-	}
-	python::list val,binMid;
-	for(size_t i=0; i<(size_t)bins; i++){ val.append(cummProj[i]); binMid.append(i*binStep);}
-	return python::make_tuple(binMid,val);
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(interactionAnglesHistogram_overloads,interactionAnglesHistogram,1,4);
-
-python::tuple bodyNumInteractionsHistogram(python::tuple aabb=python::tuple()){
-	Vector3r bbMin(Vector3r::ZERO), bbMax(Vector3r::ZERO); bool useBB=python::len(aabb)>0; if(useBB){bbMin=extract<Vector3r>(aabb[0])();bbMax=extract<Vector3r>(aabb[1])();}
-	const shared_ptr<MetaBody>& rb=Omega::instance().getRootBody();
-	vector<int> bodyNumInta; bodyNumInta.resize(rb->bodies->size(),-1 /* uninitialized */);
-	int maxInta=0;
-	FOREACH(const shared_ptr<Interaction>& i, *rb->transientInteractions){
-		if(!i->isReal()) continue;
-		const body_id_t id1=i->getId1(), id2=i->getId2(); const shared_ptr<Body>& b1=Body::byId(id1,rb), b2=Body::byId(id2,rb);
-		if(useBB && isInBB(b1->physicalParameters->se3.position,bbMin,bbMax)) bodyNumInta[id1]=bodyNumInta[id1]>0?bodyNumInta[id1]+1:1;
-		if(useBB && isInBB(b2->physicalParameters->se3.position,bbMin,bbMax)) bodyNumInta[id2]=bodyNumInta[id2]>0?bodyNumInta[id2]+1:1;
-		maxInta=max(max(maxInta,bodyNumInta[b1->getId()]),bodyNumInta[b2->getId()]);
-	}
-	vector<int> bins; bins.resize(maxInta+1);
-	for(size_t id=0; id<bodyNumInta.size(); id++){ if(bodyNumInta[id]>=0) bins[bodyNumInta[id]]+=1; }
-	python::list count,num;
-	for(size_t n=1; n<bins.size(); n++){
-		if(bins[n]==0) continue;
-		num.append(n); count.append(bins[n]);
-	}
-	return python::make_tuple(num,count);
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(bodyNumInteractionsHistogram_overloads,bodyNumInteractionsHistogram,0,1);
-
-Vector3r inscribedCircleCenter(const Vector3r& v0, const Vector3r& v1, const Vector3r& v2)
-{
-	return Shop::inscribedCircleCenter(v0,v1,v2);
-}
-
-python::dict getViscoelasticFromSpheresInteraction(Real m, Real tc, Real en, Real es)
-{
-    shared_ptr<SimpleViscoelasticBodyParameters> b = shared_ptr<SimpleViscoelasticBodyParameters>(new SimpleViscoelasticBodyParameters());
-    Shop::getViscoelasticFromSpheresInteraction(m,tc,en,es,b);
-	python::dict d;
-	d["kn"]=b->kn;
-	d["cn"]=b->cn;
-	d["ks"]=b->ks;
-	d["cs"]=b->cs;
-    return d;
-}
-/* reset highlight of all bodies */
-void highlightNone(){
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		if(!b->geometricalModel) continue;
-		b->geometricalModel->highlight=false;
-	}
-}
-
-/*!Sum moments acting on given bodies
- *
- * @param mask is Body::groupMask that must match for a body to be taken in account.
- * @param axis is the direction of axis with respect to which the moment is calculated.
- * @param axisPt is a point on the axis.
- *
- * The computation is trivial: moment from force is is by definition r×F, where r
- * is position relative to axisPt; moment from moment is m; such moment per body is
- * projected onto axis.
- */
-Real sumBexTorques(python::tuple ids, const Vector3r& axis, const Vector3r& axisPt){
-	shared_ptr<MetaBody> rb=Omega::instance().getRootBody();
-	rb->bex.sync();
-	Real ret=0;
-	size_t len=python::len(ids);
-	for(size_t i=0; i<len; i++){
-		const Body* b=(*rb->bodies)[python::extract<int>(ids[i])].get();
-		const Vector3r& m=rb->bex.getTorque(b->getId());
-		const Vector3r& f=rb->bex.getForce(b->getId());
-		Vector3r r=b->physicalParameters->se3.position-axisPt;
-		ret+=axis.Dot(m+r.Cross(f));
-	}
-	return ret;
-}
-/* Sum forces actiong on bodies within mask.
- *
- * @param mask groupMask of matching bodies
- * @param direction direction in which forces are summed
- *
- */
-Real sumBexForces(python::tuple ids, const Vector3r& direction){
-	shared_ptr<MetaBody> rb=Omega::instance().getRootBody();
-	rb->bex.sync();
-	Real ret=0;
-	size_t len=python::len(ids);
-	for(size_t i=0; i<len; i++){
-		body_id_t id=python::extract<int>(ids[i]);
-		const Vector3r& f=rb->bex.getForce(id);
-		ret+=direction.Dot(f);
-	}
-	return ret;
-}
-
-/* Set wire display of all/some/none bodies depending on the filter. */
-void wireSome(string filter){
-	enum{none,all,noSpheres,unknown};
-	int mode=(filter=="none"?none:(filter=="all"?all:(filter=="noSpheres"?noSpheres:unknown)));
-	if(mode==unknown) { LOG_WARN("Unknown wire filter `"<<filter<<"', using noSpheres instead."); mode=noSpheres; }
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		if(!b->geometricalModel) return;
-		bool wire;
-		switch(mode){
-			case none: wire=false; break;
-			case all: wire=true; break;
-			case noSpheres: wire=!(bool)(dynamic_pointer_cast<Sphere>(b->geometricalModel)); break;
-			default: throw logic_error("No such case possible");
-		}
-		b->geometricalModel->wire=wire;
-	}
-}
-void wireAll(){wireSome("all");}
-void wireNone(){wireSome("none");}
-void wireNoSpheres(){wireSome("noSpheres");}
-
-
-/* Tell us whether a point lies in polygon given by array of points.
- *  @param xy is the point that is being tested
- *  @param vertices is Numeric.array (or list or tuple) of vertices of the polygon.
- *         Every row of the array is x and y coordinate, numer of rows is >= 3 (triangle).
- *
- * Copying the algorithm from http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
- * is gratefully acknowledged:
- *
- * License to Use:
- * Copyright (c) 1970-2003, Wm. Randolph Franklin
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
- *   2. Redistributions in binary form must reproduce the above copyright notice in the documentation and/or other materials provided with the distribution.
- *   3. The name of W. Randolph Franklin may not be used to endorse or promote products derived from this Software without specific prior written permission. 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * http://numpy.scipy.org/numpydoc/numpy-13.html told me how to use Numeric.array from c
- */
-bool pointInsidePolygon(python::tuple xy, python::object vertices){
-	Real testx=python::extract<double>(xy[0])(),testy=python::extract<double>(xy[1])();
-	char** vertData; int rows, cols; PyArrayObject* vert=(PyArrayObject*)vertices.ptr();
-	int result=PyArray_As2D((PyObject**)&vert /* is replaced */ ,&vertData,&rows,&cols,PyArray_DOUBLE);
-	if(result!=0) throw invalid_argument("Unable to cast vertices to 2d array");
-	if(cols!=2 || rows<3) throw invalid_argument("Vertices must have 2 columns (x and y) and at least 3 rows.");
-	int i /*current node*/, j/*previous node*/; bool inside=false;
-	for(i=0,j=rows-1; i<rows; j=i++){
-		double vx_i=*(double*)(vert->data+i*vert->strides[0]), vy_i=*(double*)(vert->data+i*vert->strides[0]+vert->strides[1]), vx_j=*(double*)(vert->data+j*vert->strides[0]), vy_j=*(double*)(vert->data+j*vert->strides[0]+vert->strides[1]);
-		if (((vy_i>testy)!=(vy_j>testy)) && (testx < (vx_j-vx_i) * (testy-vy_i) / (vy_j-vy_i) + vx_i) ) inside=!inside;
-	}
-	Py_DECREF(vert);
-	return inside;
-}
-
-/* Compute area of convex hull when when taking (swept) spheres crossing the plane at coord, perpendicular to axis.
-
-	All spheres that touch the plane are projected as hexagons on their circumference to the plane.
-	Convex hull from this cloud is computed.
-	The area of the hull is returned.
-
-*/
-Real approxSectionArea(Real coord, int axis){
-	std::list<Vector2r> cloud;
-	if(axis<0 || axis>2) throw invalid_argument("Axis must be ∈ {0,1,2}");
-	const int ax1=(axis+1)%3, ax2=(axis+2)%3;
-	const Real sqrt3=sqrt(3);
-	Vector2r mm(-10.,0),mx(0,0);
-	FOREACH(const shared_ptr<Body>& b, *Omega::instance().getRootBody()->bodies){
-		Sphere* s=dynamic_cast<Sphere*>(b->geometricalModel.get());
-		if(!s) continue;
-		const Vector3r& pos(b->physicalParameters->se3.position); const Real r(s->radius);
-		if((pos[axis]>coord && (pos[axis]-r)>coord) || (pos[axis]<coord && (pos[axis]+r)<coord)) continue;
-		Vector2r c(pos[ax1],pos[ax2]);
-		cloud.push_back(c+Vector2r(r,0)); cloud.push_back(c+Vector2r(-r,0));
-		cloud.push_back(c+Vector2r( r/2., sqrt3*r)); cloud.push_back(c+Vector2r( r/2.,-sqrt3*r));
-		cloud.push_back(c+Vector2r(-r/2., sqrt3*r)); cloud.push_back(c+Vector2r(-r/2.,-sqrt3*r));
-		if(mm[0]==-10.){ mm=c, mx=c;}
-		mm=Vector2r(min(c[0]-r,mm[0]),min(c[1]-r,mm[1]));
-		mx=Vector2r(max(c[0]+r,mx[0]),max(c[1]+r,mx[1]));
-	}
-	if(cloud.size()==0) return 0;
-	ConvexHull2d ch2d(cloud);
-	vector<Vector2r> hull=ch2d();
-	return simplePolygonArea2d(hull);
-}
-/* Find all interactions deriving from NormalShearInteraction that cross plane given by a point and normal
-	(the normal may not be normalized in this case, though) and sum forces (both normal and shear) on them.
-	
-	Return a 3-tuple with the components along global x,y,z axes.
-
-	(This could be easily extended to return sum of only normal forces or only of shear forces.)
-*/
-Vector3r forcesOnPlane(const Vector3r& planePt, const Vector3r&  normal){
-	Vector3r ret(Vector3r::ZERO);
-	MetaBody* rootBody=Omega::instance().getRootBody().get();
-	FOREACH(const shared_ptr<Interaction>&I, *rootBody->interactions){
-		if(!I->isReal()) continue;
-		NormalShearInteraction* nsi=dynamic_cast<NormalShearInteraction*>(I->interactionPhysics.get());
-		if(!nsi) continue;
-		Vector3r pos1,pos2;
-		Dem3DofGeom* d3dg=dynamic_cast<Dem3DofGeom*>(I->interactionGeometry.get()); // Dem3DofGeom has copy of se3 in itself, otherwise we have to look up the bodies
-		if(d3dg){ pos1=d3dg->se31.position; pos2=d3dg->se32.position; }
-		else{ pos1=Body::byId(I->getId1(),rootBody)->physicalParameters->se3.position; pos2=Body::byId(I->getId2(),rootBody)->physicalParameters->se3.position; }
-		Real dot1=(pos1-planePt).Dot(normal), dot2=(pos2-planePt).Dot(normal);
-		if(dot1*dot2>0) continue; // both interaction points on the same side of the plane
-		// if pt1 is on the negative plane side, d3dg->normal.Dot(normal)>0, the force is well oriented;
-		// otherwise, reverse its contribution
-		ret+=(dot1<0.?1.:-1.)*(nsi->normalForce+nsi->shearForce);
-	}
-	return ret;
-}
-
-/* Less general than forcesOnPlane, computes force on plane perpendicular to axis, passing through coordinate coord. */
-Vector3r forcesOnCoordPlane(Real coord, int axis){
-	Vector3r planePt(Vector3r::ZERO); planePt[axis]=coord;
-	Vector3r normal(Vector3r::ZERO); normal[axis]=1;
-	return forcesOnPlane(planePt,normal);
-}
-
-
-/* Project 3d point into 2d using spiral projection along given axis;
- * the returned tuple is
- * 	
- *  ( (height relative to the spiral, distance from axis), theta )
- *
- * dH_dTheta is the inclination of the spiral (height increase per radian),
- * theta0 is the angle for zero height (by given axis).
- */
-python::tuple spiralProject(const Vector3r& pt, Real dH_dTheta, int axis=2, Real periodStart=std::numeric_limits<Real>::quiet_NaN(), Real theta0=0){
-	int ax1=(axis+1)%3,ax2=(axis+2)%3;
-	Real r=sqrt(pow(pt[ax1],2)+pow(pt[ax2],2));
-	Real theta;
-	if(r>Mathr::ZERO_TOLERANCE){
-		theta=acos(pt[ax1]/r);
-		if(pt[ax2]<0) theta=Mathr::TWO_PI-theta;
-	}
-	else theta=0;
-	Real hRef=dH_dTheta*(theta-theta0);
-	long period;
-	if(isnan(periodStart)){
-		Real h=Shop::periodicWrap(pt[axis]-hRef,hRef-Mathr::PI*dH_dTheta,hRef+Mathr::PI*dH_dTheta,&period);
-		return python::make_tuple(python::make_tuple(r,h),theta);
-	}
-	else{
-		// Real hPeriodStart=(periodStart-theta0)*dH_dTheta;
-		//TRVAR4(hPeriodStart,periodStart,theta0,theta);
-		//Real h=Shop::periodicWrap(pt[axis]-hRef,hPeriodStart,hPeriodStart+2*Mathr::PI*dH_dTheta,&period);
-		theta=Shop::periodicWrap(theta,periodStart,periodStart+2*Mathr::PI,&period);
-		Real h=pt[axis]-hRef+period*2*Mathr::PI*dH_dTheta;
-		//TRVAR3(pt[axis],pt[axis]-hRef,period);
-		//TRVAR2(h,theta);
-		return python::make_tuple(python::make_tuple(r,h),theta);
-	}
-}
-BOOST_PYTHON_FUNCTION_OVERLOADS(spiralProject_overloads,spiralProject,2,5);
-
-// for now, don't return anything, since we would have to include the whole yadeControl.cpp because of pyInteraction
-void Shop__createExplicitInteraction(body_id_t id1, body_id_t id2){ (void) Shop::createExplicitInteraction(id1,id2);}
-
-//Vector3r Shop__scalarOnColorScale(Real scalar){ return Shop::scalarOnColorScale(scalar);}
-
-BOOST_PYTHON_FUNCTION_OVERLOADS(unbalancedForce_overloads,Shop::unbalancedForce,0,1);
-Real Shop__kineticEnergy(){return Shop::kineticEnergy();}
-
-BOOST_PYTHON_MODULE(_utils){
-	// http://numpy.scipy.org/numpydoc/numpy-13.html mentions this must be done in module init, otherwise we will crash
-	import_array();
-
-	def("PWaveTimeStep",PWaveTimeStep,"Get timestep accoring to the velocity of P-Wave propagation; computed from sphere radii, rigidities and masses.");
-	def("aabbExtrema",aabbExtrema,aabbExtrema_overloads(args("cutoff","centers"),"Return coordinates of box enclosing all bodies\n centers: do not take sphere radii in account, only their centroids (default=False)\n cutoff: 0-1 number by which the box will be scaled around its center (default=0)"));
-	def("ptInAABB",ptInAABB,"Return True/False whether the point (3-tuple) p is within box given by its min (3-tuple) and max (3-tuple) corners");
-	def("negPosExtremeIds",negPosExtremeIds,negPosExtremeIds_overloads(args("axis","distFactor"),"Return list of ids for spheres (only) that are on extremal ends of the specimen along given axis; distFactor multiplies their radius so that sphere that do not touch the boundary coordinate can also be returned."));
-	def("approxSectionArea",approxSectionArea,"Compute area of convex hull when when taking (swept) spheres crossing the plane at coord, perpendicular to axis.");
-	def("coordsAndDisplacements",coordsAndDisplacements,coordsAndDisplacements_overloads(args("AABB"),"Return tuple of 2 same-length lists for coordinates and displacements (coordinate minus reference coordinate) along given axis (1st arg); if the AABB=((x_min,y_min,z_min),(x_max,y_max,z_max)) box is given, only bodies within this box will be considered."));
-	def("setRefSe3",setRefSe3,"Set reference positions and orientation of all bodies equal to their current ones.");
-	def("interactionAnglesHistogram",interactionAnglesHistogram,interactionAnglesHistogram_overloads(args("axis","mask","bins","aabb")));
-	def("bodyNumInteractionsHistogram",bodyNumInteractionsHistogram,bodyNumInteractionsHistogram_overloads(args("aabb")));
-	def("elasticEnergy",elasticEnergyInAABB);
-	def("inscribedCircleCenter",inscribedCircleCenter);
-	def("getViscoelasticFromSpheresInteraction",getViscoelasticFromSpheresInteraction);
-	def("unbalancedForce",&Shop::unbalancedForce,unbalancedForce_overloads(args("useMaxForce")));
-	def("kineticEnergy",Shop__kineticEnergy);
-	def("sumBexForces",sumBexForces);
-	def("sumBexTorques",sumBexTorques);
-	def("forcesOnPlane",forcesOnPlane);
-	def("forcesOnCoordPlane",forcesOnCoordPlane);
-	def("createInteraction",Shop__createExplicitInteraction);
-	def("spiralProject",spiralProject,spiralProject_overloads(args("axis","periodStart","theta0")));
-	def("pointInsidePolygon",pointInsidePolygon);
-	def("scalarOnColorScale",Shop::scalarOnColorScale);
-	def("highlightNone",highlightNone);
-	def("wireAll",wireAll);
-	def("wireNone",wireNone);
-	def("wireNoSpheres",wireNoSpheres);
-}
-
-

Deleted: trunk/lib/py/euclid.py
===================================================================
--- trunk/lib/py/euclid.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/euclid.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,2228 +0,0 @@
-#!/usr/bin/env python
-#
-# euclid graphics maths module
-#
-# Copyright (c) 2006 Alex Holkner
-# Alex.Holkner@xxxxxxxxxxxxxxx
-#
-# This library is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation; either version 2.1 of the License, or (at your
-# option) any later version.
-# 
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-# 
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-'''euclid graphics maths module
-
-Documentation and tests are included in the file "euclid.txt", or online
-at http://code.google.com/p/pyeuclid
-'''
-
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-__revision__ = '$Revision$'
-
-import math
-import operator
-import types
-
-# Some magic here.  If _use_slots is True, the classes will derive from
-# object and will define a __slots__ class variable.  If _use_slots is
-# False, classes will be old-style and will not define __slots__.
-#
-# _use_slots = True:   Memory efficient, probably faster in future versions
-#                      of Python, "better".
-# _use_slots = False:  Ordinary classes, much faster than slots in current
-#                      versions of Python (2.4 and 2.5).
-_use_slots = True
-
-# If True, allows components of Vector2 and Vector3 to be set via swizzling;
-# e.g.  v.xyz = (1, 2, 3).  This is much, much slower than the more verbose
-# v.x = 1; v.y = 2; v.z = 3,  and slows down ordinary element setting as
-# well.  Recommended setting is False.
-_enable_swizzle_set = False
-
-# Requires class to derive from object.
-if _enable_swizzle_set:
-    _use_slots = True
-
-# Implement _use_slots magic.
-class _EuclidMetaclass(type):
-    def __new__(cls, name, bases, dct):
-        if '__slots__' in dct:
-            dct['__getstate__'] = cls._create_getstate(dct['__slots__'])
-            dct['__setstate__'] = cls._create_setstate(dct['__slots__'])
-        if _use_slots:
-            return type.__new__(cls, name, bases + (object,), dct)
-        else:
-            if '__slots__' in dct:
-                del dct['__slots__']
-            return types.ClassType.__new__(types.ClassType, name, bases, dct)
-
-    @classmethod
-    def _create_getstate(cls, slots):
-        def __getstate__(self):
-            d = {}
-            for slot in slots:
-                d[slot] = getattr(self, slot)
-            return d
-        return __getstate__
-
-    @classmethod
-    def _create_setstate(cls, slots):
-        def __setstate__(self, state):
-            for name, value in state.items():
-                setattr(self, name, value)
-        return __setstate__
-
-__metaclass__ = _EuclidMetaclass
-
-class Vector2:
-    __slots__ = ['x', 'y']
-
-    def __init__(self, x=0, y=0):
-        self.x = x
-        self.y = y
-
-    def __copy__(self):
-        return self.__class__(self.x, self.y)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Vector2(%.2f, %.2f)' % (self.x, self.y)
-
-    def __eq__(self, other):
-        if isinstance(other, Vector2):
-            return self.x == other.x and \
-                   self.y == other.y
-        else:
-            assert hasattr(other, '__len__') and len(other) == 2
-            return self.x == other[0] and \
-                   self.y == other[1]
-
-    def __neq__(self, other):
-        return not self.__eq__(other)
-
-    def __nonzero__(self):
-        return self.x != 0 or self.y != 0
-
-    def __len__(self):
-        return 2
-
-    def __getitem__(self, key):
-        return (self.x, self.y)[key]
-
-    def __setitem__(self, key, value):
-        l = [self.x, self.y]
-        l[key] = value
-        self.x, self.y = l
-
-    def __iter__(self):
-        return iter((self.x, self.y))
-
-    def __getattr__(self, name):
-        try:
-            return tuple([(self.x, self.y)['xy'.index(c)] \
-                          for c in name])
-        except ValueError:
-            raise AttributeError, name
-
-    if _enable_swizzle_set:
-        # This has detrimental performance on ordinary setattr as well
-        # if enabled
-        def __setattr__(self, name, value):
-            if len(name) == 1:
-                object.__setattr__(self, name, value)
-            else:
-                try:
-                    l = [self.x, self.y]
-                    for c, v in map(None, name, value):
-                        l['xy'.index(c)] = v
-                    self.x, self.y = l
-                except ValueError:
-                    raise AttributeError, name
-
-    def __add__(self, other):
-        if isinstance(other, Vector2):
-            # Vector + Vector -> Vector
-            # Vector + Point -> Point
-            # Point + Point -> Vector
-            if self.__class__ is other.__class__:
-                _class = Vector2
-            else:
-                _class = Point2
-            return _class(self.x + other.x,
-                          self.y + other.y)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 2
-            return Vector2(self.x + other[0],
-                           self.y + other[1])
-    __radd__ = __add__
-
-    def __iadd__(self, other):
-        if isinstance(other, Vector2):
-            self.x += other.x
-            self.y += other.y
-        else:
-            self.x += other[0]
-            self.y += other[1]
-        return self
-
-    def __sub__(self, other):
-        if isinstance(other, Vector2):
-            # Vector - Vector -> Vector
-            # Vector - Point -> Point
-            # Point - Point -> Vector
-            if self.__class__ is other.__class__:
-                _class = Vector2
-            else:
-                _class = Point2
-            return _class(self.x - other.x,
-                          self.y - other.y)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 2
-            return Vector2(self.x - other[0],
-                           self.y - other[1])
-
-   
-    def __rsub__(self, other):
-        if isinstance(other, Vector2):
-            return Vector2(other.x - self.x,
-                           other.y - self.y)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 2
-            return Vector2(other.x - self[0],
-                           other.y - self[1])
-
-    def __mul__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(self.x * other,
-                       self.y * other)
-
-    __rmul__ = __mul__
-
-    def __imul__(self, other):
-        assert type(other) in (int, long, float)
-        self.x *= other
-        self.y *= other
-        return self
-
-    def __div__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.div(self.x, other),
-                       operator.div(self.y, other))
-
-
-    def __rdiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.div(other, self.x),
-                       operator.div(other, self.y))
-
-    def __floordiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.floordiv(self.x, other),
-                       operator.floordiv(self.y, other))
-
-
-    def __rfloordiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.floordiv(other, self.x),
-                       operator.floordiv(other, self.y))
-
-    def __truediv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.truediv(self.x, other),
-                       operator.truediv(self.y, other))
-
-
-    def __rtruediv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector2(operator.truediv(other, self.x),
-                       operator.truediv(other, self.y))
-    
-    def __neg__(self):
-        return Vector2(-self.x,
-                        -self.y)
-
-    __pos__ = __copy__
-    
-    def __abs__(self):
-        return math.sqrt(self.x ** 2 + \
-                         self.y ** 2)
-
-    magnitude = __abs__
-
-    def magnitude_squared(self):
-        return self.x ** 2 + \
-               self.y ** 2
-
-    def normalize(self):
-        d = self.magnitude()
-        if d:
-            self.x /= d
-            self.y /= d
-        return self
-
-    def normalized(self):
-        d = self.magnitude()
-        if d:
-            return Vector2(self.x / d, 
-                           self.y / d)
-        return self.copy()
-
-    def dot(self, other):
-        assert isinstance(other, Vector2)
-        return self.x * other.x + \
-               self.y * other.y
-
-    def cross(self):
-        return Vector2(self.y, -self.x)
-
-    def reflect(self, normal):
-        # assume normal is normalized
-        assert isinstance(normal, Vector2)
-        d = 2 * (self.x * normal.x + self.y * normal.y)
-        return Vector2(self.x - d * normal.x,
-                       self.y - d * normal.y)
-
-class Vector3:
-    __slots__ = ['x', 'y', 'z']
-
-    def __init__(self, x=0, y=0, z=0):
-        self.x = x
-        self.y = y
-        self.z = z
-
-    def __copy__(self):
-        return self.__class__(self.x, self.y, self.z)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Vector3(%.2f, %.2f, %.2f)' % (self.x,
-                                              self.y,
-                                              self.z)
-
-    def __eq__(self, other):
-        if isinstance(other, Vector3):
-            return self.x == other.x and \
-                   self.y == other.y and \
-                   self.z == other.z
-        else:
-            assert hasattr(other, '__len__') and len(other) == 3
-            return self.x == other[0] and \
-                   self.y == other[1] and \
-                   self.z == other[2]
-
-    def __neq__(self, other):
-        return not self.__eq__(other)
-
-    def __nonzero__(self):
-        return self.x != 0 or self.y != 0 or self.z != 0
-
-    def __len__(self):
-        return 3
-
-    def __getitem__(self, key):
-        return (self.x, self.y, self.z)[key]
-
-    def __setitem__(self, key, value):
-        l = [self.x, self.y, self.z]
-        l[key] = value
-        self.x, self.y, self.z = l
-
-    def __iter__(self):
-        return iter((self.x, self.y, self.z))
-
-    def __getattr__(self, name):
-        try:
-            return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \
-                          for c in name])
-        except ValueError:
-            raise AttributeError, name
-
-    if _enable_swizzle_set:
-        # This has detrimental performance on ordinary setattr as well
-        # if enabled
-        def __setattr__(self, name, value):
-            if len(name) == 1:
-                object.__setattr__(self, name, value)
-            else:
-                try:
-                    l = [self.x, self.y, self.z]
-                    for c, v in map(None, name, value):
-                        l['xyz'.index(c)] = v
-                    self.x, self.y, self.z = l
-                except ValueError:
-                    raise AttributeError, name
-
-
-    def __add__(self, other):
-        if isinstance(other, Vector3):
-            # Vector + Vector -> Vector
-            # Vector + Point -> Point
-            # Point + Point -> Vector
-            if self.__class__ is other.__class__:
-                _class = Vector3
-            else:
-                _class = Point3
-            return _class(self.x + other.x,
-                          self.y + other.y,
-                          self.z + other.z)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 3
-            return Vector3(self.x + other[0],
-                           self.y + other[1],
-                           self.z + other[2])
-    __radd__ = __add__
-
-    def __iadd__(self, other):
-        if isinstance(other, Vector3):
-            self.x += other.x
-            self.y += other.y
-            self.z += other.z
-        else:
-            self.x += other[0]
-            self.y += other[1]
-            self.z += other[2]
-        return self
-
-    def __sub__(self, other):
-        if isinstance(other, Vector3):
-            # Vector - Vector -> Vector
-            # Vector - Point -> Point
-            # Point - Point -> Vector
-            if self.__class__ is other.__class__:
-                _class = Vector3
-            else:
-                _class = Point3
-            return Vector3(self.x - other.x,
-                           self.y - other.y,
-                           self.z - other.z)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 3
-            return Vector3(self.x - other[0],
-                           self.y - other[1],
-                           self.z - other[2])
-
-   
-    def __rsub__(self, other):
-        if isinstance(other, Vector3):
-            return Vector3(other.x - self.x,
-                           other.y - self.y,
-                           other.z - self.z)
-        else:
-            assert hasattr(other, '__len__') and len(other) == 3
-            return Vector3(other.x - self[0],
-                           other.y - self[1],
-                           other.z - self[2])
-
-    def __mul__(self, other):
-        if isinstance(other, Vector3):
-            # TODO component-wise mul/div in-place and on Vector2; docs.
-            if self.__class__ is Point3 or other.__class__ is Point3:
-                _class = Point3
-            else:
-                _class = Vector3
-            return _class(self.x * other.x,
-                          self.y * other.y,
-                          self.z * other.z)
-        else: 
-            assert type(other) in (int, long, float)
-            return Vector3(self.x * other,
-                           self.y * other,
-                           self.z * other)
-
-    __rmul__ = __mul__
-
-    def __imul__(self, other):
-        assert type(other) in (int, long, float)
-        self.x *= other
-        self.y *= other
-        self.z *= other
-        return self
-
-    def __div__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.div(self.x, other),
-                       operator.div(self.y, other),
-                       operator.div(self.z, other))
-
-
-    def __rdiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.div(other, self.x),
-                       operator.div(other, self.y),
-                       operator.div(other, self.z))
-
-    def __floordiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.floordiv(self.x, other),
-                       operator.floordiv(self.y, other),
-                       operator.floordiv(self.z, other))
-
-
-    def __rfloordiv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.floordiv(other, self.x),
-                       operator.floordiv(other, self.y),
-                       operator.floordiv(other, self.z))
-
-    def __truediv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.truediv(self.x, other),
-                       operator.truediv(self.y, other),
-                       operator.truediv(self.z, other))
-
-
-    def __rtruediv__(self, other):
-        assert type(other) in (int, long, float)
-        return Vector3(operator.truediv(other, self.x),
-                       operator.truediv(other, self.y),
-                       operator.truediv(other, self.z))
-    
-    def __neg__(self):
-        return Vector3(-self.x,
-                        -self.y,
-                        -self.z)
-
-    __pos__ = __copy__
-    
-    def __abs__(self):
-        return math.sqrt(self.x ** 2 + \
-                         self.y ** 2 + \
-                         self.z ** 2)
-
-    magnitude = __abs__
-
-    def magnitude_squared(self):
-        return self.x ** 2 + \
-               self.y ** 2 + \
-               self.z ** 2
-
-    def normalize(self):
-        d = self.magnitude()
-        if d:
-            self.x /= d
-            self.y /= d
-            self.z /= d
-        return self
-
-    def normalized(self):
-        d = self.magnitude()
-        if d:
-            return Vector3(self.x / d, 
-                           self.y / d, 
-                           self.z / d)
-        return self.copy()
-
-    def dot(self, other):
-        assert isinstance(other, Vector3)
-        return self.x * other.x + \
-               self.y * other.y + \
-               self.z * other.z
-
-    def cross(self, other):
-        assert isinstance(other, Vector3)
-        return Vector3(self.y * other.z - self.z * other.y,
-                       -self.x * other.z + self.z * other.x,
-                       self.x * other.y - self.y * other.x)
-
-    def reflect(self, normal):
-        # assume normal is normalized
-        assert isinstance(normal, Vector3)
-        d = 2 * (self.x * normal.x + self.y * normal.y + self.z * normal.z)
-        return Vector3(self.x - d * normal.x,
-                       self.y - d * normal.y,
-                       self.z - d * normal.z)
-
-# a b c 
-# e f g 
-# i j k 
-
-class Matrix3:
-    __slots__ = list('abcefgijk')
-
-    def __init__(self):
-        self.identity()
-
-    def __copy__(self):
-        M = Matrix3()
-        M.a = self.a
-        M.b = self.b
-        M.c = self.c
-        M.e = self.e 
-        M.f = self.f
-        M.g = self.g
-        M.i = self.i
-        M.j = self.j
-        M.k = self.k
-        return M
-
-    copy = __copy__
-    def __repr__(self):
-        return ('Matrix3([% 8.2f % 8.2f % 8.2f\n'  \
-                '         % 8.2f % 8.2f % 8.2f\n'  \
-                '         % 8.2f % 8.2f % 8.2f])') \
-                % (self.a, self.b, self.c,
-                   self.e, self.f, self.g,
-                   self.i, self.j, self.k)
-
-    def __getitem__(self, key):
-        return [self.a, self.e, self.i,
-                self.b, self.f, self.j,
-                self.c, self.g, self.k][key]
-
-    def __setitem__(self, key, value):
-        L = self[:]
-        L[key] = value
-        (self.a, self.e, self.i,
-         self.b, self.f, self.j,
-         self.c, self.g, self.k) = L
-
-    def __mul__(self, other):
-        if isinstance(other, Matrix3):
-            # Caching repeatedly accessed attributes in local variables
-            # apparently increases performance by 20%.  Attrib: Will McGugan.
-            Aa = self.a
-            Ab = self.b
-            Ac = self.c
-            Ae = self.e
-            Af = self.f
-            Ag = self.g
-            Ai = self.i
-            Aj = self.j
-            Ak = self.k
-            Ba = other.a
-            Bb = other.b
-            Bc = other.c
-            Be = other.e
-            Bf = other.f
-            Bg = other.g
-            Bi = other.i
-            Bj = other.j
-            Bk = other.k
-            C = Matrix3()
-            C.a = Aa * Ba + Ab * Be + Ac * Bi
-            C.b = Aa * Bb + Ab * Bf + Ac * Bj
-            C.c = Aa * Bc + Ab * Bg + Ac * Bk
-            C.e = Ae * Ba + Af * Be + Ag * Bi
-            C.f = Ae * Bb + Af * Bf + Ag * Bj
-            C.g = Ae * Bc + Af * Bg + Ag * Bk
-            C.i = Ai * Ba + Aj * Be + Ak * Bi
-            C.j = Ai * Bb + Aj * Bf + Ak * Bj
-            C.k = Ai * Bc + Aj * Bg + Ak * Bk
-            return C
-        elif isinstance(other, Point2):
-            A = self
-            B = other
-            P = Point2(0, 0)
-            P.x = A.a * B.x + A.b * B.y + A.c
-            P.y = A.e * B.x + A.f * B.y + A.g
-            return P
-        elif isinstance(other, Vector2):
-            A = self
-            B = other
-            V = Vector2(0, 0)
-            V.x = A.a * B.x + A.b * B.y 
-            V.y = A.e * B.x + A.f * B.y 
-            return V
-        else:
-            other = other.copy()
-            other._apply_transform(self)
-            return other
-
-    def __imul__(self, other):
-        assert isinstance(other, Matrix3)
-        # Cache attributes in local vars (see Matrix3.__mul__).
-        Aa = self.a
-        Ab = self.b
-        Ac = self.c
-        Ae = self.e
-        Af = self.f
-        Ag = self.g
-        Ai = self.i
-        Aj = self.j
-        Ak = self.k
-        Ba = other.a
-        Bb = other.b
-        Bc = other.c
-        Be = other.e
-        Bf = other.f
-        Bg = other.g
-        Bi = other.i
-        Bj = other.j
-        Bk = other.k
-        self.a = Aa * Ba + Ab * Be + Ac * Bi
-        self.b = Aa * Bb + Ab * Bf + Ac * Bj
-        self.c = Aa * Bc + Ab * Bg + Ac * Bk
-        self.e = Ae * Ba + Af * Be + Ag * Bi
-        self.f = Ae * Bb + Af * Bf + Ag * Bj
-        self.g = Ae * Bc + Af * Bg + Ag * Bk
-        self.i = Ai * Ba + Aj * Be + Ak * Bi
-        self.j = Ai * Bb + Aj * Bf + Ak * Bj
-        self.k = Ai * Bc + Aj * Bg + Ak * Bk
-        return self
-
-    def identity(self):
-        self.a = self.f = self.k = 1.
-        self.b = self.c = self.e = self.g = self.i = self.j = 0
-        return self
-
-    def scale(self, x, y):
-        self *= Matrix3.new_scale(x, y)
-        return self
-
-    def translate(self, x, y):
-        self *= Matrix3.new_translate(x, y)
-        return self 
-
-    def rotate(self, angle):
-        self *= Matrix3.new_rotate(angle)
-        return self
-
-    # Static constructors
-    def new_identity(cls):
-        self = cls()
-        return self
-    new_identity = classmethod(new_identity)
-
-    def new_scale(cls, x, y):
-        self = cls()
-        self.a = x
-        self.f = y
-        return self
-    new_scale = classmethod(new_scale)
-
-    def new_translate(cls, x, y):
-        self = cls()
-        self.c = x
-        self.g = y
-        return self
-    new_translate = classmethod(new_translate)
-
-    def new_rotate(cls, angle):
-        self = cls()
-        s = math.sin(angle)
-        c = math.cos(angle)
-        self.a = self.f = c
-        self.b = -s
-        self.e = s
-        return self
-    new_rotate = classmethod(new_rotate)
-
-# a b c d
-# e f g h
-# i j k l
-# m n o p
-
-class Matrix4:
-    __slots__ = list('abcdefghijklmnop')
-
-    def __init__(self):
-        self.identity()
-
-    def __copy__(self):
-        M = Matrix4()
-        M.a = self.a
-        M.b = self.b
-        M.c = self.c
-        M.d = self.d
-        M.e = self.e 
-        M.f = self.f
-        M.g = self.g
-        M.h = self.h
-        M.i = self.i
-        M.j = self.j
-        M.k = self.k
-        M.l = self.l
-        M.m = self.m
-        M.n = self.n
-        M.o = self.o
-        M.p = self.p
-        return M
-
-    copy = __copy__
-
-
-    def __repr__(self):
-        return ('Matrix4([% 8.2f % 8.2f % 8.2f % 8.2f\n'  \
-                '         % 8.2f % 8.2f % 8.2f % 8.2f\n'  \
-                '         % 8.2f % 8.2f % 8.2f % 8.2f\n'  \
-                '         % 8.2f % 8.2f % 8.2f % 8.2f])') \
-                % (self.a, self.b, self.c, self.d,
-                   self.e, self.f, self.g, self.h,
-                   self.i, self.j, self.k, self.l,
-                   self.m, self.n, self.o, self.p)
-
-    def __getitem__(self, key):
-        return [self.a, self.e, self.i, self.m,
-                self.b, self.f, self.j, self.n,
-                self.c, self.g, self.k, self.o,
-                self.d, self.h, self.l, self.p][key]
-
-    def __setitem__(self, key, value):
-        L = self[:]
-        L[key] = value
-        (self.a, self.e, self.i, self.m,
-         self.b, self.f, self.j, self.n,
-         self.c, self.g, self.k, self.o,
-         self.d, self.h, self.l, self.p) = L
-
-    def __mul__(self, other):
-        if isinstance(other, Matrix4):
-            # Cache attributes in local vars (see Matrix3.__mul__).
-            Aa = self.a
-            Ab = self.b
-            Ac = self.c
-            Ad = self.d
-            Ae = self.e
-            Af = self.f
-            Ag = self.g
-            Ah = self.h
-            Ai = self.i
-            Aj = self.j
-            Ak = self.k
-            Al = self.l
-            Am = self.m
-            An = self.n
-            Ao = self.o
-            Ap = self.p
-            Ba = other.a
-            Bb = other.b
-            Bc = other.c
-            Bd = other.d
-            Be = other.e
-            Bf = other.f
-            Bg = other.g
-            Bh = other.h
-            Bi = other.i
-            Bj = other.j
-            Bk = other.k
-            Bl = other.l
-            Bm = other.m
-            Bn = other.n
-            Bo = other.o
-            Bp = other.p
-            C = Matrix4()
-            C.a = Aa * Ba + Ab * Be + Ac * Bi + Ad * Bm
-            C.b = Aa * Bb + Ab * Bf + Ac * Bj + Ad * Bn
-            C.c = Aa * Bc + Ab * Bg + Ac * Bk + Ad * Bo
-            C.d = Aa * Bd + Ab * Bh + Ac * Bl + Ad * Bp
-            C.e = Ae * Ba + Af * Be + Ag * Bi + Ah * Bm
-            C.f = Ae * Bb + Af * Bf + Ag * Bj + Ah * Bn
-            C.g = Ae * Bc + Af * Bg + Ag * Bk + Ah * Bo
-            C.h = Ae * Bd + Af * Bh + Ag * Bl + Ah * Bp
-            C.i = Ai * Ba + Aj * Be + Ak * Bi + Al * Bm
-            C.j = Ai * Bb + Aj * Bf + Ak * Bj + Al * Bn
-            C.k = Ai * Bc + Aj * Bg + Ak * Bk + Al * Bo
-            C.l = Ai * Bd + Aj * Bh + Ak * Bl + Al * Bp
-            C.m = Am * Ba + An * Be + Ao * Bi + Ap * Bm
-            C.n = Am * Bb + An * Bf + Ao * Bj + Ap * Bn
-            C.o = Am * Bc + An * Bg + Ao * Bk + Ap * Bo
-            C.p = Am * Bd + An * Bh + Ao * Bl + Ap * Bp
-            return C
-        elif isinstance(other, Point3):
-            A = self
-            B = other
-            P = Point3(0, 0, 0)
-            P.x = A.a * B.x + A.b * B.y + A.c * B.z + A.d
-            P.y = A.e * B.x + A.f * B.y + A.g * B.z + A.h
-            P.z = A.i * B.x + A.j * B.y + A.k * B.z + A.l
-            return P
-        elif isinstance(other, Vector3):
-            A = self
-            B = other
-            V = Vector3(0, 0, 0)
-            V.x = A.a * B.x + A.b * B.y + A.c * B.z
-            V.y = A.e * B.x + A.f * B.y + A.g * B.z
-            V.z = A.i * B.x + A.j * B.y + A.k * B.z
-            return V
-        else:
-            other = other.copy()
-            other._apply_transform(self)
-            return other
-
-    def __imul__(self, other):
-        assert isinstance(other, Matrix4)
-        # Cache attributes in local vars (see Matrix3.__mul__).
-        Aa = self.a
-        Ab = self.b
-        Ac = self.c
-        Ad = self.d
-        Ae = self.e
-        Af = self.f
-        Ag = self.g
-        Ah = self.h
-        Ai = self.i
-        Aj = self.j
-        Ak = self.k
-        Al = self.l
-        Am = self.m
-        An = self.n
-        Ao = self.o
-        Ap = self.p
-        Ba = other.a
-        Bb = other.b
-        Bc = other.c
-        Bd = other.d
-        Be = other.e
-        Bf = other.f
-        Bg = other.g
-        Bh = other.h
-        Bi = other.i
-        Bj = other.j
-        Bk = other.k
-        Bl = other.l
-        Bm = other.m
-        Bn = other.n
-        Bo = other.o
-        Bp = other.p
-        self.a = Aa * Ba + Ab * Be + Ac * Bi + Ad * Bm
-        self.b = Aa * Bb + Ab * Bf + Ac * Bj + Ad * Bn
-        self.c = Aa * Bc + Ab * Bg + Ac * Bk + Ad * Bo
-        self.d = Aa * Bd + Ab * Bh + Ac * Bl + Ad * Bp
-        self.e = Ae * Ba + Af * Be + Ag * Bi + Ah * Bm
-        self.f = Ae * Bb + Af * Bf + Ag * Bj + Ah * Bn
-        self.g = Ae * Bc + Af * Bg + Ag * Bk + Ah * Bo
-        self.h = Ae * Bd + Af * Bh + Ag * Bl + Ah * Bp
-        self.i = Ai * Ba + Aj * Be + Ak * Bi + Al * Bm
-        self.j = Ai * Bb + Aj * Bf + Ak * Bj + Al * Bn
-        self.k = Ai * Bc + Aj * Bg + Ak * Bk + Al * Bo
-        self.l = Ai * Bd + Aj * Bh + Ak * Bl + Al * Bp
-        self.m = Am * Ba + An * Be + Ao * Bi + Ap * Bm
-        self.n = Am * Bb + An * Bf + Ao * Bj + Ap * Bn
-        self.o = Am * Bc + An * Bg + Ao * Bk + Ap * Bo
-        self.p = Am * Bd + An * Bh + Ao * Bl + Ap * Bp
-        return self
-
-    def transform(self, other):
-        A = self
-        B = other
-        P = Point3(0, 0, 0)
-        P.x = A.a * B.x + A.b * B.y + A.c * B.z + A.d
-        P.y = A.e * B.x + A.f * B.y + A.g * B.z + A.h
-        P.z = A.i * B.x + A.j * B.y + A.k * B.z + A.l
-        w =   A.m * B.x + A.n * B.y + A.o * B.z + A.p
-        if w != 0:
-            P.x /= w
-            P.y /= w
-            P.z /= w
-        return P
-
-    def identity(self):
-        self.a = self.f = self.k = self.p = 1.
-        self.b = self.c = self.d = self.e = self.g = self.h = \
-        self.i = self.j = self.l = self.m = self.n = self.o = 0
-        return self
-
-    def scale(self, x, y, z):
-        self *= Matrix4.new_scale(x, y, z)
-        return self
-
-    def translate(self, x, y, z):
-        self *= Matrix4.new_translate(x, y, z)
-        return self 
-
-    def rotatex(self, angle):
-        self *= Matrix4.new_rotatex(angle)
-        return self
-
-    def rotatey(self, angle):
-        self *= Matrix4.new_rotatey(angle)
-        return self
-
-    def rotatez(self, angle):
-        self *= Matrix4.new_rotatez(angle)
-        return self
-
-    def rotate_axis(self, angle, axis):
-        self *= Matrix4.new_rotate_axis(angle, axis)
-        return self
-
-    def rotate_euler(self, heading, attitude, bank):
-        self *= Matrix4.new_rotate_euler(heading, attitude, bank)
-        return self
-
-    def rotate_triple_axis(self, x, y, z):
-        self *= Matrix4.new_rotate_triple_axis(x, y, z)
-        return self
-
-    def transpose(self):
-        (self.a, self.e, self.i, self.m,
-         self.b, self.f, self.j, self.n,
-         self.c, self.g, self.k, self.o,
-         self.d, self.h, self.l, self.p) = \
-        (self.a, self.b, self.c, self.d,
-         self.e, self.f, self.g, self.h,
-         self.i, self.j, self.k, self.l,
-         self.m, self.n, self.o, self.p)
-
-    def transposed(self):
-        M = self.copy()
-        M.transpose()
-        return M
-
-    # Static constructors
-    def new(cls, *values):
-        M = cls()
-        M[:] = values
-        return M
-    new = classmethod(new)
-
-    def new_identity(cls):
-        self = cls()
-        return self
-    new_identity = classmethod(new_identity)
-
-    def new_scale(cls, x, y, z):
-        self = cls()
-        self.a = x
-        self.f = y
-        self.k = z
-        return self
-    new_scale = classmethod(new_scale)
-
-    def new_translate(cls, x, y, z):
-        self = cls()
-        self.d = x
-        self.h = y
-        self.l = z
-        return self
-    new_translate = classmethod(new_translate)
-
-    def new_rotatex(cls, angle):
-        self = cls()
-        s = math.sin(angle)
-        c = math.cos(angle)
-        self.f = self.k = c
-        self.g = -s
-        self.j = s
-        return self
-    new_rotatex = classmethod(new_rotatex)
-
-    def new_rotatey(cls, angle):
-        self = cls()
-        s = math.sin(angle)
-        c = math.cos(angle)
-        self.a = self.k = c
-        self.c = s
-        self.i = -s
-        return self    
-    new_rotatey = classmethod(new_rotatey)
-    
-    def new_rotatez(cls, angle):
-        self = cls()
-        s = math.sin(angle)
-        c = math.cos(angle)
-        self.a = self.f = c
-        self.b = -s
-        self.e = s
-        return self
-    new_rotatez = classmethod(new_rotatez)
-
-    def new_rotate_axis(cls, angle, axis):
-        assert(isinstance(axis, Vector3))
-        vector = axis.normalized()
-        x = vector.x
-        y = vector.y
-        z = vector.z
-
-        self = cls()
-        s = math.sin(angle)
-        c = math.cos(angle)
-        c1 = 1. - c
-        
-        # from the glRotate man page
-        self.a = x * x * c1 + c
-        self.b = x * y * c1 - z * s
-        self.c = x * z * c1 + y * s
-        self.e = y * x * c1 + z * s
-        self.f = y * y * c1 + c
-        self.g = y * z * c1 - x * s
-        self.i = x * z * c1 - y * s
-        self.j = y * z * c1 + x * s
-        self.k = z * z * c1 + c
-        return self
-    new_rotate_axis = classmethod(new_rotate_axis)
-
-    def new_rotate_euler(cls, heading, attitude, bank):
-        # from http://www.euclideanspace.com/
-        ch = math.cos(heading)
-        sh = math.sin(heading)
-        ca = math.cos(attitude)
-        sa = math.sin(attitude)
-        cb = math.cos(bank)
-        sb = math.sin(bank)
-
-        self = cls()
-        self.a = ch * ca
-        self.b = sh * sb - ch * sa * cb
-        self.c = ch * sa * sb + sh * cb
-        self.e = sa
-        self.f = ca * cb
-        self.g = -ca * sb
-        self.i = -sh * ca
-        self.j = sh * sa * cb + ch * sb
-        self.k = -sh * sa * sb + ch * cb
-        return self
-    new_rotate_euler = classmethod(new_rotate_euler)
-
-    def new_rotate_triple_axis(cls, x, y, z):
-      m = cls()
-      
-      m.a, m.b, m.c = x.x, y.x, z.x
-      m.e, m.f, m.g = x.y, y.y, z.y
-      m.i, m.j, m.k = x.z, y.z, z.z
-      
-      return m
-    new_rotate_triple_axis = classmethod(new_rotate_triple_axis)
-
-    def new_look_at(cls, eye, at, up):
-      z = (eye - at).normalized()
-      x = up.cross(z).normalized()
-      y = z.cross(x)
-      
-      m = cls.new_rotate_triple_axis(x, y, z)
-      m.d, m.h, m.l = eye.x, eye.y, eye.z
-      return m
-    new_look_at = classmethod(new_look_at)
-    
-    def new_perspective(cls, fov_y, aspect, near, far):
-        # from the gluPerspective man page
-        f = 1 / math.tan(fov_y / 2)
-        self = cls()
-        assert near != 0.0 and near != far
-        self.a = f / aspect
-        self.f = f
-        self.k = (far + near) / (near - far)
-        self.l = 2 * far * near / (near - far)
-        self.o = -1
-        self.p = 0
-        return self
-    new_perspective = classmethod(new_perspective)
-
-    def determinant(self):
-        return ((self.a * self.f - self.e * self.b)
-              * (self.k * self.p - self.o * self.l)
-              - (self.a * self.j - self.i * self.b)
-              * (self.g * self.p - self.o * self.h)
-              + (self.a * self.n - self.m * self.b)
-              * (self.g * self.l - self.k * self.h)
-              + (self.e * self.j - self.i * self.f)
-              * (self.c * self.p - self.o * self.d)
-              - (self.e * self.n - self.m * self.f)
-              * (self.c * self.l - self.k * self.d)
-              + (self.i * self.n - self.m * self.j)
-              * (self.c * self.h - self.g * self.d))
-
-    def inverse(self):
-        tmp = Matrix4()
-        d = self.determinant();
-
-        if abs(d) < 0.001:
-            # No inverse, return identity
-            return tmp
-        else:
-            d = 1.0 / d;
-
-            tmp.a = d * (self.f * (self.k * self.p - self.o * self.l) + self.j * (self.o * self.h - self.g * self.p) + self.n * (self.g * self.l - self.k * self.h));
-            tmp.e = d * (self.g * (self.i * self.p - self.m * self.l) + self.k * (self.m * self.h - self.e * self.p) + self.o * (self.e * self.l - self.i * self.h));
-            tmp.i = d * (self.h * (self.i * self.n - self.m * self.j) + self.l * (self.m * self.f - self.e * self.n) + self.p * (self.e * self.j - self.i * self.f));
-            tmp.m = d * (self.e * (self.n * self.k - self.j * self.o) + self.i * (self.f * self.o - self.n * self.g) + self.m * (self.j * self.g - self.f * self.k));
-            
-            tmp.b = d * (self.j * (self.c * self.p - self.o * self.d) + self.n * (self.k * self.d - self.c * self.l) + self.b * (self.o * self.l - self.k * self.p));
-            tmp.f = d * (self.k * (self.a * self.p - self.m * self.d) + self.o * (self.i * self.d - self.a * self.l) + self.c * (self.m * self.l - self.i * self.p));
-            tmp.j = d * (self.l * (self.a * self.n - self.m * self.b) + self.p * (self.i * self.b - self.a * self.j) + self.d * (self.m * self.j - self.i * self.n));
-            tmp.n = d * (self.i * (self.n * self.c - self.b * self.o) + self.m * (self.b * self.k - self.j * self.c) + self.a * (self.j * self.o - self.n * self.k));
-            
-            tmp.c = d * (self.n * (self.c * self.h - self.g * self.d) + self.b * (self.g * self.p - self.o * self.h) + self.f * (self.o * self.d - self.c * self.p));
-            tmp.g = d * (self.o * (self.a * self.h - self.e * self.d) + self.c * (self.e * self.p - self.m * self.h) + self.g * (self.m * self.d - self.a * self.p));
-            tmp.k = d * (self.p * (self.a * self.f - self.e * self.b) + self.d * (self.e * self.n - self.m * self.f) + self.h * (self.m * self.b - self.a * self.n));
-            tmp.o = d * (self.m * (self.f * self.c - self.b * self.g) + self.a * (self.n * self.g - self.f * self.o) + self.e * (self.b * self.o - self.n * self.c));
-            
-            tmp.d = d * (self.b * (self.k * self.h - self.g * self.l) + self.f * (self.c * self.l - self.k * self.d) + self.j * (self.g * self.d - self.c * self.h));
-            tmp.h = d * (self.c * (self.i * self.h - self.e * self.l) + self.g * (self.a * self.l - self.i * self.d) + self.k * (self.e * self.d - self.a * self.h));
-            tmp.l = d * (self.d * (self.i * self.f - self.e * self.j) + self.h * (self.a * self.j - self.i * self.b) + self.l * (self.e * self.b - self.a * self.f));
-            tmp.p = d * (self.a * (self.f * self.k - self.j * self.g) + self.e * (self.j * self.c - self.b * self.k) + self.i * (self.b * self.g - self.f * self.c));
-
-        return tmp;
-        
-
-class Quaternion:
-    # All methods and naming conventions based off 
-    # http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions
-
-    # w is the real part, (x, y, z) are the imaginary parts
-    __slots__ = ['w', 'x', 'y', 'z']
-
-    def __init__(self, w=1, x=0, y=0, z=0):
-        self.w = w
-        self.x = x
-        self.y = y
-        self.z = z
-
-    def __copy__(self):
-        Q = Quaternion()
-        Q.w = self.w
-        Q.x = self.x
-        Q.y = self.y
-        Q.z = self.z
-        return Q
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Quaternion(real=%.2f, imag=<%.2f, %.2f, %.2f>)' % \
-            (self.w, self.x, self.y, self.z)
-
-    def __mul__(self, other):
-        if isinstance(other, Quaternion):
-            Ax = self.x
-            Ay = self.y
-            Az = self.z
-            Aw = self.w
-            Bx = other.x
-            By = other.y
-            Bz = other.z
-            Bw = other.w
-            Q = Quaternion()
-            Q.x =  Ax * Bw + Ay * Bz - Az * By + Aw * Bx    
-            Q.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By
-            Q.z =  Ax * By - Ay * Bx + Az * Bw + Aw * Bz
-            Q.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw
-            return Q
-        elif isinstance(other, Vector3):
-            w = self.w
-            x = self.x
-            y = self.y
-            z = self.z
-            Vx = other.x
-            Vy = other.y
-            Vz = other.z
-            return other.__class__(\
-               w * w * Vx + 2 * y * w * Vz - 2 * z * w * Vy + \
-               x * x * Vx + 2 * y * x * Vy + 2 * z * x * Vz - \
-               z * z * Vx - y * y * Vx,
-               2 * x * y * Vx + y * y * Vy + 2 * z * y * Vz + \
-               2 * w * z * Vx - z * z * Vy + w * w * Vy - \
-               2 * x * w * Vz - x * x * Vy,
-               2 * x * z * Vx + 2 * y * z * Vy + \
-               z * z * Vz - 2 * w * y * Vx - y * y * Vz + \
-               2 * w * x * Vy - x * x * Vz + w * w * Vz)
-        else:
-            other = other.copy()
-            other._apply_transform(self)
-            return other
-
-    def __imul__(self, other):
-        assert isinstance(other, Quaternion)
-        Ax = self.x
-        Ay = self.y
-        Az = self.z
-        Aw = self.w
-        Bx = other.x
-        By = other.y
-        Bz = other.z
-        Bw = other.w
-        self.x =  Ax * Bw + Ay * Bz - Az * By + Aw * Bx    
-        self.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By
-        self.z =  Ax * By - Ay * Bx + Az * Bw + Aw * Bz
-        self.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw
-        return self
-
-    def __abs__(self):
-        return math.sqrt(self.w ** 2 + \
-                         self.x ** 2 + \
-                         self.y ** 2 + \
-                         self.z ** 2)
-
-    magnitude = __abs__
-
-    def magnitude_squared(self):
-        return self.w ** 2 + \
-               self.x ** 2 + \
-               self.y ** 2 + \
-               self.z ** 2 
-
-    def identity(self):
-        self.w = 1
-        self.x = 0
-        self.y = 0
-        self.z = 0
-        return self
-
-    def rotate_axis(self, angle, axis):
-        self *= Quaternion.new_rotate_axis(angle, axis)
-        return self
-
-    def rotate_euler(self, heading, attitude, bank):
-        self *= Quaternion.new_rotate_euler(heading, attitude, bank)
-        return self
-
-    def rotate_matrix(self, m):
-        self *= Quaternion.new_rotate_matrix(m)
-        return self
-
-    def conjugated(self):
-        Q = Quaternion()
-        Q.w = self.w
-        Q.x = -self.x
-        Q.y = -self.y
-        Q.z = -self.z
-        return Q
-
-    def normalize(self):
-        d = self.magnitude()
-        if d != 0:
-            self.w /= d
-            self.x /= d
-            self.y /= d
-            self.z /= d
-        return self
-
-    def normalized(self):
-        d = self.magnitude()
-        if d != 0:
-            Q = Quaternion()
-            Q.w = self.w / d
-            Q.x = self.x / d
-            Q.y = self.y / d
-            Q.z = self.z / d
-            return Q
-        else:
-            return self.copy()
-
-    def get_angle_axis(self):
-        if self.w > 1:
-            self = self.normalized()
-        angle = 2 * math.acos(self.w)
-        s = math.sqrt(1 - self.w ** 2)
-        if s < 0.001:
-            return angle, Vector3(1, 0, 0)
-        else:
-            return angle, Vector3(self.x / s, self.y / s, self.z / s)
-
-    def get_euler(self):
-        t = self.x * self.y + self.z * self.w
-        if t > 0.4999:
-            heading = 2 * math.atan2(self.x, self.w)
-            attitude = math.pi / 2
-            bank = 0
-        elif t < -0.4999:
-            heading = -2 * math.atan2(self.x, self.w)
-            attitude = -math.pi / 2
-            bank = 0
-        else:
-            sqx = self.x ** 2
-            sqy = self.y ** 2
-            sqz = self.z ** 2
-            heading = math.atan2(2 * self.y * self.w - 2 * self.x * self.z,
-                                 1 - 2 * sqy - 2 * sqz)
-            attitude = math.asin(2 * t)
-            bank = math.atan2(2 * self.x * self.w - 2 * self.y * self.z,
-                              1 - 2 * sqx - 2 * sqz)
-        return heading, attitude, bank
-
-    def get_matrix(self):
-        xx = self.x ** 2
-        xy = self.x * self.y
-        xz = self.x * self.z
-        xw = self.x * self.w
-        yy = self.y ** 2
-        yz = self.y * self.z
-        yw = self.y * self.w
-        zz = self.z ** 2
-        zw = self.z * self.w
-        M = Matrix4()
-        M.a = 1 - 2 * (yy + zz)
-        M.b = 2 * (xy - zw)
-        M.c = 2 * (xz + yw)
-        M.e = 2 * (xy + zw)
-        M.f = 1 - 2 * (xx + zz)
-        M.g = 2 * (yz - xw)
-        M.i = 2 * (xz - yw)
-        M.j = 2 * (yz + xw)
-        M.k = 1 - 2 * (xx + yy)
-        return M
-
-    # Static constructors
-    def new_identity(cls):
-        return cls()
-    new_identity = classmethod(new_identity)
-
-    def new_rotate_axis(cls, angle, axis):
-        assert(isinstance(axis, Vector3))
-        axis = axis.normalized()
-        s = math.sin(angle / 2)
-        Q = cls()
-        Q.w = math.cos(angle / 2)
-        Q.x = axis.x * s
-        Q.y = axis.y * s
-        Q.z = axis.z * s
-        return Q
-    new_rotate_axis = classmethod(new_rotate_axis)
-
-    def new_rotate_euler(cls, heading, attitude, bank):
-        Q = cls()
-        c1 = math.cos(heading / 2)
-        s1 = math.sin(heading / 2)
-        c2 = math.cos(attitude / 2)
-        s2 = math.sin(attitude / 2)
-        c3 = math.cos(bank / 2)
-        s3 = math.sin(bank / 2)
-
-        Q.w = c1 * c2 * c3 - s1 * s2 * s3
-        Q.x = s1 * s2 * c3 + c1 * c2 * s3
-        Q.y = s1 * c2 * c3 + c1 * s2 * s3
-        Q.z = c1 * s2 * c3 - s1 * c2 * s3
-        return Q
-    new_rotate_euler = classmethod(new_rotate_euler)
-    
-    def new_rotate_matrix(cls, m):
-      if m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] > 0.00000001:
-        t = m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] + 1.0
-        s = 0.5/math.sqrt(t)
-        
-        return cls(
-          s*t,
-          (m[1*4 + 2] - m[2*4 + 1])*s,
-          (m[2*4 + 0] - m[0*4 + 2])*s,
-          (m[0*4 + 1] - m[1*4 + 0])*s
-          )
-        
-      elif m[0*4 + 0] > m[1*4 + 1] and m[0*4 + 0] > m[2*4 + 2]:
-        t = m[0*4 + 0] - m[1*4 + 1] - m[2*4 + 2] + 1.0
-        s = 0.5/math.sqrt(t)
-        
-        return cls(
-          (m[1*4 + 2] - m[2*4 + 1])*s,
-          s*t,
-          (m[0*4 + 1] + m[1*4 + 0])*s,
-          (m[2*4 + 0] + m[0*4 + 2])*s
-          )
-        
-      elif m[1*4 + 1] > m[2*4 + 2]:
-        t = -m[0*4 + 0] + m[1*4 + 1] - m[2*4 + 2] + 1.0
-        s = 0.5/math.sqrt(t)
-        
-        return cls(
-          (m[2*4 + 0] - m[0*4 + 2])*s,
-          (m[0*4 + 1] + m[1*4 + 0])*s,
-          s*t,
-          (m[1*4 + 2] + m[2*4 + 1])*s
-          )
-        
-      else:
-        t = -m[0*4 + 0] - m[1*4 + 1] + m[2*4 + 2] + 1.0
-        s = 0.5/math.sqrt(t)
-        
-        return cls(
-          (m[0*4 + 1] - m[1*4 + 0])*s,
-          (m[2*4 + 0] + m[0*4 + 2])*s,
-          (m[1*4 + 2] + m[2*4 + 1])*s,
-          s*t
-          )
-    new_rotate_matrix = classmethod(new_rotate_matrix)
-    
-    def new_interpolate(cls, q1, q2, t):
-        assert isinstance(q1, Quaternion) and isinstance(q2, Quaternion)
-        Q = cls()
-
-        costheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z
-        if costheta < 0.:
-            costheta = -costheta
-            q1 = q1.conjugated()
-        elif costheta > 1:
-            costheta = 1
-
-        theta = math.acos(costheta)
-        if abs(theta) < 0.01:
-            Q.w = q2.w
-            Q.x = q2.x
-            Q.y = q2.y
-            Q.z = q2.z
-            return Q
-
-        sintheta = math.sqrt(1.0 - costheta * costheta)
-        if abs(sintheta) < 0.01:
-            Q.w = (q1.w + q2.w) * 0.5
-            Q.x = (q1.x + q2.x) * 0.5
-            Q.y = (q1.y + q2.y) * 0.5
-            Q.z = (q1.z + q2.z) * 0.5
-            return Q
-
-        ratio1 = math.sin((1 - t) * theta) / sintheta
-        ratio2 = math.sin(t * theta) / sintheta
-
-        Q.w = q1.w * ratio1 + q2.w * ratio2
-        Q.x = q1.x * ratio1 + q2.x * ratio2
-        Q.y = q1.y * ratio1 + q2.y * ratio2
-        Q.z = q1.z * ratio1 + q2.z * ratio2
-        return Q
-    new_interpolate = classmethod(new_interpolate)
-
-# Geometry
-# Much maths thanks to Paul Bourke, http://astronomy.swin.edu.au/~pbourke
-# ---------------------------------------------------------------------------
-
-class Geometry:
-    def _connect_unimplemented(self, other):
-        raise AttributeError, 'Cannot connect %s to %s' % \
-            (self.__class__, other.__class__)
-
-    def _intersect_unimplemented(self, other):
-        raise AttributeError, 'Cannot intersect %s and %s' % \
-            (self.__class__, other.__class__)
-
-    _intersect_point2 = _intersect_unimplemented
-    _intersect_line2 = _intersect_unimplemented
-    _intersect_circle = _intersect_unimplemented
-    _connect_point2 = _connect_unimplemented
-    _connect_line2 = _connect_unimplemented
-    _connect_circle = _connect_unimplemented
-
-    _intersect_point3 = _intersect_unimplemented
-    _intersect_line3 = _intersect_unimplemented
-    _intersect_sphere = _intersect_unimplemented
-    _intersect_plane = _intersect_unimplemented
-    _connect_point3 = _connect_unimplemented
-    _connect_line3 = _connect_unimplemented
-    _connect_sphere = _connect_unimplemented
-    _connect_plane = _connect_unimplemented
-
-    def intersect(self, other):
-        raise NotImplementedError
-
-    def connect(self, other):
-        raise NotImplementedError
-
-    def distance(self, other):
-        c = self.connect(other)
-        if c:
-            return c.length
-        return 0.0
-
-def _intersect_point2_circle(P, C):
-    return abs(P - C.c) <= C.r
-    
-def _intersect_line2_line2(A, B):
-    d = B.v.y * A.v.x - B.v.x * A.v.y
-    if d == 0:
-        return None
-
-    dy = A.p.y - B.p.y
-    dx = A.p.x - B.p.x
-    ua = (B.v.x * dy - B.v.y * dx) / d
-    if not A._u_in(ua):
-        return None
-    ub = (A.v.x * dy - A.v.y * dx) / d
-    if not B._u_in(ub):
-        return None
-
-    return Point2(A.p.x + ua * A.v.x,
-                  A.p.y + ua * A.v.y)
-
-def _intersect_line2_circle(L, C):
-    a = L.v.magnitude_squared()
-    b = 2 * (L.v.x * (L.p.x - C.c.x) + \
-             L.v.y * (L.p.y - C.c.y))
-    c = C.c.magnitude_squared() + \
-        L.p.magnitude_squared() - \
-        2 * C.c.dot(L.p) - \
-        C.r ** 2
-    det = b ** 2 - 4 * a * c
-    if det < 0:
-        return None
-    sq = math.sqrt(det)
-    u1 = (-b + sq) / (2 * a)
-    u2 = (-b - sq) / (2 * a)
-    if not L._u_in(u1):
-        u1 = max(min(u1, 1.0), 0.0)
-    if not L._u_in(u2):
-        u2 = max(min(u2, 1.0), 0.0)
-
-    # Tangent
-    if u1 == u2:
-        return Point2(L.p.x + u1 * L.v.x,
-                      L.p.y + u1 * L.v.y)
-
-    return LineSegment2(Point2(L.p.x + u1 * L.v.x,
-                               L.p.y + u1 * L.v.y),
-                        Point2(L.p.x + u2 * L.v.x,
-                               L.p.y + u2 * L.v.y))
-
-def _connect_point2_line2(P, L):
-    d = L.v.magnitude_squared()
-    assert d != 0
-    u = ((P.x - L.p.x) * L.v.x + \
-         (P.y - L.p.y) * L.v.y) / d
-    if not L._u_in(u):
-        u = max(min(u, 1.0), 0.0)
-    return LineSegment2(P, 
-                        Point2(L.p.x + u * L.v.x,
-                               L.p.y + u * L.v.y))
-
-def _connect_point2_circle(P, C):
-    v = P - C.c
-    v.normalize()
-    v *= C.r
-    return LineSegment2(P, Point2(C.c.x + v.x, C.c.y + v.y))
-
-def _connect_line2_line2(A, B):
-    d = B.v.y * A.v.x - B.v.x * A.v.y
-    if d == 0:
-        # Parallel, connect an endpoint with a line
-        if isinstance(B, Ray2) or isinstance(B, LineSegment2):
-            p1, p2 = _connect_point2_line2(B.p, A)
-            return p2, p1
-        # No endpoint (or endpoint is on A), possibly choose arbitrary point
-        # on line.
-        return _connect_point2_line2(A.p, B)
-
-    dy = A.p.y - B.p.y
-    dx = A.p.x - B.p.x
-    ua = (B.v.x * dy - B.v.y * dx) / d
-    if not A._u_in(ua):
-        ua = max(min(ua, 1.0), 0.0)
-    ub = (A.v.x * dy - A.v.y * dx) / d
-    if not B._u_in(ub):
-        ub = max(min(ub, 1.0), 0.0)
-
-    return LineSegment2(Point2(A.p.x + ua * A.v.x, A.p.y + ua * A.v.y),
-                        Point2(B.p.x + ub * B.v.x, B.p.y + ub * B.v.y))
-
-def _connect_circle_line2(C, L):
-    d = L.v.magnitude_squared()
-    assert d != 0
-    u = ((C.c.x - L.p.x) * L.v.x + (C.c.y - L.p.y) * L.v.y) / d
-    if not L._u_in(u):
-        u = max(min(u, 1.0), 0.0)
-    point = Point2(L.p.x + u * L.v.x, L.p.y + u * L.v.y)
-    v = (point - C.c)
-    v.normalize()
-    v *= C.r
-    return LineSegment2(Point2(C.c.x + v.x, C.c.y + v.y), point)
-
-def _connect_circle_circle(A, B):
-    v = B.c - A.c
-    v.normalize()
-    return LineSegment2(Point2(A.c.x + v.x * A.r, A.c.y + v.y * A.r),
-                        Point2(B.c.x - v.x * B.r, B.c.y - v.y * B.r))
-
-
-class Point2(Vector2, Geometry):
-    def __repr__(self):
-        return 'Point2(%.2f, %.2f)' % (self.x, self.y)
-
-    def intersect(self, other):
-        return other._intersect_point2(self)
-
-    def _intersect_circle(self, other):
-        return _intersect_point2_circle(self, other)
-
-    def connect(self, other):
-        return other._connect_point2(self)
-
-    def _connect_point2(self, other):
-        return LineSegment2(other, self)
-    
-    def _connect_line2(self, other):
-        c = _connect_point2_line2(self, other)
-        if c:
-            return c._swap()
-
-    def _connect_circle(self, other):
-        c = _connect_point2_circle(self, other)
-        if c:
-            return c._swap()
-
-class Line2(Geometry):
-    __slots__ = ['p', 'v']
-
-    def __init__(self, *args):
-        if len(args) == 3:
-            assert isinstance(args[0], Point2) and \
-                   isinstance(args[1], Vector2) and \
-                   type(args[2]) == float
-            self.p = args[0].copy()
-            self.v = args[1] * args[2] / abs(args[1])
-        elif len(args) == 2:
-            if isinstance(args[0], Point2) and isinstance(args[1], Point2):
-                self.p = args[0].copy()
-                self.v = args[1] - args[0]
-            elif isinstance(args[0], Point2) and isinstance(args[1], Vector2):
-                self.p = args[0].copy()
-                self.v = args[1].copy()
-            else:
-                raise AttributeError, '%r' % (args,)
-        elif len(args) == 1:
-            if isinstance(args[0], Line2):
-                self.p = args[0].p.copy()
-                self.v = args[0].v.copy()
-            else:
-                raise AttributeError, '%r' % (args,)
-        else:
-            raise AttributeError, '%r' % (args,)
-        
-        if not self.v:
-            raise AttributeError, 'Line has zero-length vector'
-
-    def __copy__(self):
-        return self.__class__(self.p, self.v)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Line2(<%.2f, %.2f> + u<%.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.v.x, self.v.y)
-
-    p1 = property(lambda self: self.p)
-    p2 = property(lambda self: Point2(self.p.x + self.v.x, 
-                                      self.p.y + self.v.y))
-
-    def _apply_transform(self, t):
-        self.p = t * self.p
-        self.v = t * self.v
-
-    def _u_in(self, u):
-        return True
-
-    def intersect(self, other):
-        return other._intersect_line2(self)
-
-    def _intersect_line2(self, other):
-        return _intersect_line2_line2(self, other)
-
-    def _intersect_circle(self, other):
-        return _intersect_line2_circle(self, other)
-
-    def connect(self, other):
-        return other._connect_line2(self)
-
-    def _connect_point2(self, other):
-        return _connect_point2_line2(other, self)
-
-    def _connect_line2(self, other):
-        return _connect_line2_line2(other, self)
-
-    def _connect_circle(self, other):
-        return _connect_circle_line2(other, self)
-
-class Ray2(Line2):
-    def __repr__(self):
-        return 'Ray2(<%.2f, %.2f> + u<%.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.v.x, self.v.y)
-
-    def _u_in(self, u):
-        return u >= 0.0
-
-class LineSegment2(Line2):
-    def __repr__(self):
-        return 'LineSegment2(<%.2f, %.2f> to <%.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.p.x + self.v.x, self.p.y + self.v.y)
-
-    def _u_in(self, u):
-        return u >= 0.0 and u <= 1.0
-
-    def __abs__(self):
-        return abs(self.v)
-
-    def magnitude_squared(self):
-        return self.v.magnitude_squared()
-
-    def _swap(self):
-        # used by connect methods to switch order of points
-        self.p = self.p2
-        self.v *= -1
-        return self
-
-    length = property(lambda self: abs(self.v))
-
-class Circle(Geometry):
-    __slots__ = ['c', 'r']
-
-    def __init__(self, center, radius):
-        assert isinstance(center, Vector2) and type(radius) == float
-        self.c = center.copy()
-        self.r = radius
-
-    def __copy__(self):
-        return self.__class__(self.c, self.r)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Circle(<%.2f, %.2f>, radius=%.2f)' % \
-            (self.c.x, self.c.y, self.r)
-
-    def _apply_transform(self, t):
-        self.c = t * self.c
-
-    def intersect(self, other):
-        return other._intersect_circle(self)
-
-    def _intersect_point2(self, other):
-        return _intersect_point2_circle(other, self)
-
-    def _intersect_line2(self, other):
-        return _intersect_line2_circle(other, self)
-
-    def connect(self, other):
-        return other._connect_circle(self)
-
-    def _connect_point2(self, other):
-        return _connect_point2_circle(other, self)
-
-    def _connect_line2(self, other):
-        c = _connect_circle_line2(self, other)
-        if c:
-            return c._swap()
-
-    def _connect_circle(self, other):
-        return _connect_circle_circle(other, self)
-
-# 3D Geometry
-# -------------------------------------------------------------------------
-
-def _connect_point3_line3(P, L):
-    d = L.v.magnitude_squared()
-    assert d != 0
-    u = ((P.x - L.p.x) * L.v.x + \
-         (P.y - L.p.y) * L.v.y + \
-         (P.z - L.p.z) * L.v.z) / d
-    if not L._u_in(u):
-        u = max(min(u, 1.0), 0.0)
-    return LineSegment3(P, Point3(L.p.x + u * L.v.x,
-                                  L.p.y + u * L.v.y,
-                                  L.p.z + u * L.v.z))
-
-def _connect_point3_sphere(P, S):
-    v = P - S.c
-    v.normalize()
-    v *= S.r
-    return LineSegment3(P, Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z))
-
-def _connect_point3_plane(p, plane):
-    n = plane.n.normalized()
-    d = p.dot(plane.n) - plane.k
-    return LineSegment3(p, Point3(p.x - n.x * d, p.y - n.y * d, p.z - n.z * d))
-
-def _connect_line3_line3(A, B):
-    assert A.v and B.v
-    p13 = A.p - B.p
-    d1343 = p13.dot(B.v)
-    d4321 = B.v.dot(A.v)
-    d1321 = p13.dot(A.v)
-    d4343 = B.v.magnitude_squared()
-    denom = A.v.magnitude_squared() * d4343 - d4321 ** 2
-    if denom == 0:
-        # Parallel, connect an endpoint with a line
-        if isinstance(B, Ray3) or isinstance(B, LineSegment3):
-            return _connect_point3_line3(B.p, A)._swap()
-        # No endpoint (or endpoint is on A), possibly choose arbitrary
-        # point on line.
-        return _connect_point3_line3(A.p, B)
-
-    ua = (d1343 * d4321 - d1321 * d4343) / denom
-    if not A._u_in(ua):
-        ua = max(min(ua, 1.0), 0.0)
-    ub = (d1343 + d4321 * ua) / d4343
-    if not B._u_in(ub):
-        ub = max(min(ub, 1.0), 0.0)
-    return LineSegment3(Point3(A.p.x + ua * A.v.x,
-                               A.p.y + ua * A.v.y,
-                               A.p.z + ua * A.v.z),
-                        Point3(B.p.x + ub * B.v.x,
-                               B.p.y + ub * B.v.y,
-                               B.p.z + ub * B.v.z))
-
-def _connect_line3_plane(L, P):
-    d = P.n.dot(L.v)
-    if not d:
-        # Parallel, choose an endpoint
-        return _connect_point3_plane(L.p, P)
-    u = (P.k - P.n.dot(L.p)) / d
-    if not L._u_in(u):
-        # intersects out of range, choose nearest endpoint
-        u = max(min(u, 1.0), 0.0)
-        return _connect_point3_plane(Point3(L.p.x + u * L.v.x,
-                                            L.p.y + u * L.v.y,
-                                            L.p.z + u * L.v.z), P)
-    # Intersection
-    return None
-
-def _connect_sphere_line3(S, L):
-    d = L.v.magnitude_squared()
-    assert d != 0
-    u = ((S.c.x - L.p.x) * L.v.x + \
-         (S.c.y - L.p.y) * L.v.y + \
-         (S.c.z - L.p.z) * L.v.z) / d
-    if not L._u_in(u):
-        u = max(min(u, 1.0), 0.0)
-    point = Point3(L.p.x + u * L.v.x, L.p.y + u * L.v.y, L.p.z + u * L.v.z)
-    v = (point - S.c)
-    v.normalize()
-    v *= S.r
-    return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), 
-                        point)
-
-def _connect_sphere_sphere(A, B):
-    v = B.c - A.c
-    v.normalize()
-    return LineSegment3(Point3(A.c.x + v.x * A.r,
-                               A.c.y + v.y * A.r,
-                               A.c.x + v.z * A.r),
-                        Point3(B.c.x + v.x * B.r,
-                               B.c.y + v.y * B.r,
-                               B.c.x + v.z * B.r))
-
-def _connect_sphere_plane(S, P):
-    c = _connect_point3_plane(S.c, P)
-    if not c:
-        return None
-    p2 = c.p2
-    v = p2 - S.c
-    v.normalize()
-    v *= S.r
-    return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), 
-                        p2)
-
-def _connect_plane_plane(A, B):
-    if A.n.cross(B.n):
-        # Planes intersect
-        return None
-    else:
-        # Planes are parallel, connect to arbitrary point
-        return _connect_point3_plane(A._get_point(), B)
-
-def _intersect_point3_sphere(P, S):
-    return abs(P - S.c) <= S.r
-    
-def _intersect_line3_sphere(L, S):
-    a = L.v.magnitude_squared()
-    b = 2 * (L.v.x * (L.p.x - S.c.x) + \
-             L.v.y * (L.p.y - S.c.y) + \
-             L.v.z * (L.p.z - S.c.z))
-    c = S.c.magnitude_squared() + \
-        L.p.magnitude_squared() - \
-        2 * S.c.dot(L.p) - \
-        S.r ** 2
-    det = b ** 2 - 4 * a * c
-    if det < 0:
-        return None
-    sq = math.sqrt(det)
-    u1 = (-b + sq) / (2 * a)
-    u2 = (-b - sq) / (2 * a)
-    if not L._u_in(u1):
-        u1 = max(min(u1, 1.0), 0.0)
-    if not L._u_in(u2):
-        u2 = max(min(u2, 1.0), 0.0)
-    return LineSegment3(Point3(L.p.x + u1 * L.v.x,
-                               L.p.y + u1 * L.v.y,
-                               L.p.z + u1 * L.v.z),
-                        Point3(L.p.x + u2 * L.v.x,
-                               L.p.y + u2 * L.v.y,
-                               L.p.z + u2 * L.v.z))
-
-def _intersect_line3_plane(L, P):
-    d = P.n.dot(L.v)
-    if not d:
-        # Parallel
-        return None
-    u = (P.k - P.n.dot(L.p)) / d
-    if not L._u_in(u):
-        return None
-    return Point3(L.p.x + u * L.v.x,
-                  L.p.y + u * L.v.y,
-                  L.p.z + u * L.v.z)
-
-def _intersect_plane_plane(A, B):
-    n1_m = A.n.magnitude_squared()
-    n2_m = B.n.magnitude_squared()
-    n1d2 = A.n.dot(B.n)
-    det = n1_m * n2_m - n1d2 ** 2
-    if det == 0:
-        # Parallel
-        return None
-    c1 = (A.k * n2_m - B.k * n1d2) / det
-    c2 = (B.k * n1_m - A.k * n1d2) / det
-    return Line3(Point3(c1 * A.n.x + c2 * B.n.x,
-                        c1 * A.n.y + c2 * B.n.y,
-                        c1 * A.n.z + c2 * B.n.z), 
-                 A.n.cross(B.n))
-
-class Point3(Vector3, Geometry):
-    def __repr__(self):
-        return 'Point3(%.2f, %.2f, %.2f)' % (self.x, self.y, self.z)
-
-    def intersect(self, other):
-        return other._intersect_point3(self)
-
-    def _intersect_sphere(self, other):
-        return _intersect_point3_sphere(self, other)
-
-    def connect(self, other):
-        return other._connect_point3(self)
-
-    def _connect_point3(self, other):
-        if self != other:
-            return LineSegment3(other, self)
-        return None
-
-    def _connect_line3(self, other):
-        c = _connect_point3_line3(self, other)
-        if c:
-            return c._swap()
-        
-    def _connect_sphere(self, other):
-        c = _connect_point3_sphere(self, other)
-        if c:
-            return c._swap()
-
-    def _connect_plane(self, other):
-        c = _connect_point3_plane(self, other)
-        if c:
-            return c._swap()
-
-class Line3:
-    __slots__ = ['p', 'v']
-
-    def __init__(self, *args):
-        if len(args) == 3:
-            assert isinstance(args[0], Point3) and \
-                   isinstance(args[1], Vector3) and \
-                   type(args[2]) == float
-            self.p = args[0].copy()
-            self.v = args[1] * args[2] / abs(args[1])
-        elif len(args) == 2:
-            if isinstance(args[0], Point3) and isinstance(args[1], Point3):
-                self.p = args[0].copy()
-                self.v = args[1] - args[0]
-            elif isinstance(args[0], Point3) and isinstance(args[1], Vector3):
-                self.p = args[0].copy()
-                self.v = args[1].copy()
-            else:
-                raise AttributeError, '%r' % (args,)
-        elif len(args) == 1:
-            if isinstance(args[0], Line3):
-                self.p = args[0].p.copy()
-                self.v = args[0].v.copy()
-            else:
-                raise AttributeError, '%r' % (args,)
-        else:
-            raise AttributeError, '%r' % (args,)
-        
-        # XXX This is annoying.
-        #if not self.v:
-        #    raise AttributeError, 'Line has zero-length vector'
-
-    def __copy__(self):
-        return self.__class__(self.p, self.v)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Line3(<%.2f, %.2f, %.2f> + u<%.2f, %.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.p.z, self.v.x, self.v.y, self.v.z)
-
-    p1 = property(lambda self: self.p)
-    p2 = property(lambda self: Point3(self.p.x + self.v.x, 
-                                      self.p.y + self.v.y,
-                                      self.p.z + self.v.z))
-
-    def _apply_transform(self, t):
-        self.p = t * self.p
-        self.v = t * self.v
-
-    def _u_in(self, u):
-        return True
-
-    def intersect(self, other):
-        return other._intersect_line3(self)
-
-    def _intersect_sphere(self, other):
-        return _intersect_line3_sphere(self, other)
-
-    def _intersect_plane(self, other):
-        return _intersect_line3_plane(self, other)
-
-    def connect(self, other):
-        return other._connect_line3(self)
-
-    def _connect_point3(self, other):
-        return _connect_point3_line3(other, self)
-
-    def _connect_line3(self, other):
-        return _connect_line3_line3(other, self)
-
-    def _connect_sphere(self, other):
-        return _connect_sphere_line3(other, self)
-
-    def _connect_plane(self, other):
-        c = _connect_line3_plane(self, other)
-        if c:
-            return c
-
-class Ray3(Line3):
-    def __repr__(self):
-        return 'Ray3(<%.2f, %.2f, %.2f> + u<%.2f, %.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.p.z, self.v.x, self.v.y, self.v.z)
-
-    def _u_in(self, u):
-        return u >= 0.0
-
-class LineSegment3(Line3):
-    def __repr__(self):
-        return 'LineSegment3(<%.2f, %.2f, %.2f> to <%.2f, %.2f, %.2f>)' % \
-            (self.p.x, self.p.y, self.p.z,
-             self.p.x + self.v.x, self.p.y + self.v.y, self.p.z + self.v.z)
-
-    def _u_in(self, u):
-        return u >= 0.0 and u <= 1.0
-
-    def __abs__(self):
-        return abs(self.v)
-
-    def magnitude_squared(self):
-        return self.v.magnitude_squared()
-
-    def _swap(self):
-        # used by connect methods to switch order of points
-        self.p = self.p2
-        self.v *= -1
-        return self
-
-    length = property(lambda self: abs(self.v))
-
-class Sphere:
-    __slots__ = ['c', 'r']
-
-    def __init__(self, center, radius):
-        assert isinstance(center, Vector3) and type(radius) == float
-        self.c = center.copy()
-        self.r = radius
-
-    def __copy__(self):
-        return self.__class__(self.c, self.r)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Sphere(<%.2f, %.2f, %.2f>, radius=%.2f)' % \
-            (self.c.x, self.c.y, self.c.z, self.r)
-
-    def _apply_transform(self, t):
-        self.c = t * self.c
-
-    def intersect(self, other):
-        return other._intersect_sphere(self)
-
-    def _intersect_point3(self, other):
-        return _intersect_point3_sphere(other, self)
-
-    def _intersect_line3(self, other):
-        return _intersect_line3_sphere(other, self)
-
-    def connect(self, other):
-        return other._connect_sphere(self)
-
-    def _connect_point3(self, other):
-        return _connect_point3_sphere(other, self)
-
-    def _connect_line3(self, other):
-        c = _connect_sphere_line3(self, other)
-        if c:
-            return c._swap()
-
-    def _connect_sphere(self, other):
-        return _connect_sphere_sphere(other, self)
-
-    def _connect_plane(self, other):
-        c = _connect_sphere_plane(self, other)
-        if c:
-            return c
-
-class Plane:
-    # n.p = k, where n is normal, p is point on plane, k is constant scalar
-    __slots__ = ['n', 'k']
-
-    def __init__(self, *args):
-        if len(args) == 3:
-            assert isinstance(args[0], Point3) and \
-                   isinstance(args[1], Point3) and \
-                   isinstance(args[2], Point3)
-            self.n = (args[1] - args[0]).cross(args[2] - args[0])
-            self.n.normalize()
-            self.k = self.n.dot(args[0])
-        elif len(args) == 2:
-            if isinstance(args[0], Point3) and isinstance(args[1], Vector3):
-                self.n = args[1].normalized()
-                self.k = self.n.dot(args[0])
-            elif isinstance(args[0], Vector3) and type(args[1]) == float:
-                self.n = args[0].normalized()
-                self.k = args[1]
-            else:
-                raise AttributeError, '%r' % (args,)
-
-        else:
-            raise AttributeError, '%r' % (args,)
-        
-        if not self.n:
-            raise AttributeError, 'Points on plane are colinear'
-
-    def __copy__(self):
-        return self.__class__(self.n, self.k)
-
-    copy = __copy__
-
-    def __repr__(self):
-        return 'Plane(<%.2f, %.2f, %.2f>.p = %.2f)' % \
-            (self.n.x, self.n.y, self.n.z, self.k)
-
-    def _get_point(self):
-        # Return an arbitrary point on the plane
-        if self.n.z:
-            return Point3(0., 0., self.k / self.n.z)
-        elif self.n.y:
-            return Point3(0., self.k / self.n.y, 0.)
-        else:
-            return Point3(self.k / self.n.x, 0., 0.)
-
-    def _apply_transform(self, t):
-        p = t * self._get_point()
-        self.n = t * self.n
-        self.k = self.n.dot(p)
-
-    def intersect(self, other):
-        return other._intersect_plane(self)
-
-    def _intersect_line3(self, other):
-        return _intersect_line3_plane(other, self)
-
-    def _intersect_plane(self, other):
-        return _intersect_plane_plane(self, other)
-
-    def connect(self, other):
-        return other._connect_plane(self)
-
-    def _connect_point3(self, other):
-        return _connect_point3_plane(other, self)
-
-    def _connect_line3(self, other):
-        return _connect_line3_plane(other, self)
-
-    def _connect_sphere(self, other):
-        return _connect_sphere_plane(other, self)
-
-    def _connect_plane(self, other):
-        return _connect_plane_plane(other, self)
-

Deleted: trunk/lib/py/eudoxos.py
===================================================================
--- trunk/lib/py/eudoxos.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/eudoxos.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,244 +0,0 @@
-# encoding: utf-8
-# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
-#
-# I doubt there functions will be useful for anyone besides me.
-#
-from yade.wrapper import *
-from math import *
-from yade._eudoxos import * ## c++ implementations
-
-def estimateStress(strain,cutoff=0.):
-	"""Use summed stored energy in contacts to compute macroscopic stress over the same volume, provided known strain."""
-	# E=(1/2)σεAl # global stored energy
-	# σ=EE/(.5εAl)=EE/(.5εV)
-	from yade import utils
-	dim=utils.aabbDim(cutoff,centers=False)
-	return utils.elasticEnergy(utils.aabbExtrema(cutoff))/(.5*strain*dim[0]*dim[1]*dim[2])
-
-def estimatePoissonYoung(principalAxis,stress=0,plot=False,cutoff=0.):
-	"""Estimate Poisson's ration given the "principal" axis of straining.
-	For every base direction, homogenized strain is computed
-	(slope in linear regression on discrete function particle coordinate →
-	→ particle displacement	in the same direction as returned by
-	utils.coordsAndDisplacements) and, (if axis '0' is the strained 
-	axis) the poisson's ratio is given as -½(ε₁+ε₂)/ε₀.
-
-	Young's modulus is computed as σ/ε₀; if stress σ is not given (default 0),
-	the result is 0.
-
-	cutoff, if > 0., will take only smaller part (centered) or the specimen into account
-	"""
-	dd=[] # storage for linear regression parameters
-	import pylab,numpy,stats
-	from yade import utils
-	if cutoff>0: cut=utils.fractionalBox(fraction=1-cutoff)
-	for axis in [0,1,2]:
-		if cutoff>0:
-			w,dw=utils.coordsAndDisplacements(axis,AABB=cut)
-		else:
-			w,dw=utils.coordsAndDisplacements(axis)
-		l,ll=stats.linregress(w,dw)[0:2] # use only tangent and section
-		dd.append((l,ll,min(w),max(w)))
-		if plot: pylab.plot(w,dw,'.',label='xyz'[axis])
-	if plot:
-		for axis in [0,1,2]:
-			dist=dd[axis][-1]-dd[axis][-2]
-			c=numpy.linspace(dd[axis][-2]-.2*dist,dd[axis][-1]+.2*dist)
-			d=[dd[axis][0]*cc+dd[axis][1] for cc in c]
-			pylab.plot(c,d,label='interp '+'xyz'[axis])
-		pylab.legend()
-		pylab.show()
-	otherAxes=(principalAxis+1)%3,(principalAxis+2)%3
-	avgTransHomogenizedStrain=.5*(dd[otherAxes[0]][0]+dd[otherAxes[1]][0])
-	principalHomogenizedStrain=dd[principalAxis][0]
-	return -avgTransHomogenizedStrain/principalHomogenizedStrain,stress/principalHomogenizedStrain
-
-
-def oofemTextExport(fName):
-	"""Export simulation data in text format 
-	
-	The format is line-oriented as follows:
-		# 3 lines of material parameters:
-		1. E G # elastic
-		2. epsCrackOnset relDuctility xiShear transStrainCoeff #tension; epsFr=epsCrackOnset*relDuctility
-		3. cohesionT tanPhi # shear
-		4. [number of spheres] [number of links]
-		5. id x y z r -1/0/1[on negative/no/positive boundary] # spheres
-		…
-		n. id1 id2 contact_point_x cp_y cp_z A # interactions """
-	from yade.wrapper import Omega
-	material,bodies,interactions=[],[],[]
-	o=Omega()
-
-	f=open(fName,'w') # fail early on I/O problem
-
-	ph=o.interactions.nth(0).phys # some params are the same everywhere
-	material.append("%g %g"%(ph['E'],ph['G']))
-	material.append("%g %g %g %g"%(ph['epsCrackOnset'],ph['epsFracture'],1e50,0.0))
-	material.append("%g %g"%(ph['undamagedCohesion'],ph['tanFrictionAngle']))
-
-	# need strainer for getting bodies in positive/negative boundary
-	strainers=[e for e in o.engines if e.name=='UniaxialStrainer']
-	if len(strainers)>0: strainer=strainers[0]
-	else: strainer=None
-
-	for b in o.bodies:
-		if strainer and b.id in strainer['negIds']: boundary=-1
-		elif strainer and b.id in strainer['posIds']: boundary=1
-		else: boundary=0
-		bodies.append("%d %g %g %g %g %d"%(b.id,b.phys['se3'][0],b.phys['se3'][1],b.phys['se3'][2],b.shape['radius'],boundary))
-
-	for i in o.interactions:
-		if not i.geom or not i.phys: continue
-		cp=i.geom['contactPoint']
-		interactions.append("%d %d %g %g %g %g"%(i.id1,i.id2,cp[0],cp[1],cp[2],i.phys['crossSection']))
-	
-	f.write('\n'.join(material+["%d %d"%(len(bodies),len(interactions))]+bodies+interactions))
-	f.close()
-
-def oofemPrescribedDisplacementsExport(fileName):
-	f=open(fileName,'w')
-	f.write(fileName+'.out\n'+'''All Yade displacements prescribed as boundary conditions
-NonLinearStatic nsteps 2 contextOutputStep 1 rtolv 1.e-2 stiffMode 2 maxiter 50 controllmode 1 nmodules 0
-domain 3dshell
-OutputManager tstep_all dofman_all element_all
-''')
-	inters=[i for i in O.interactions if (i.geom and i.phys)]
-	f.write("ndofman %d nelem %d ncrosssect 1 nmat 1 nbc %d nic 0 nltf 1 nbarrier 0\n"%(len(O.bodies),len(inters),len(O.bodies)*6))
-	bcMax=0; bcMap={}
-	for b in O.bodies:
-		mf=' '.join([str(a) for a in list(O.actions.f(b.id))+list(O.actions.m(b.id))])
-		f.write("## #%d: forces %s\n"%(b.id+1,mf))
-		f.write("Particle %d coords 3 %.15e %.15e %.15e rad %g"%(b.id+1,b.phys['se3'][0],b.phys['se3'][1],b.phys['se3'][2],b.shape['radius']))
-		bcMap[b.id]=tuple([bcMax+i for i in [1,2,3,4,5,6]])
-		bcMax+=6
-		f.write(' bc '+' '.join([str(i) for i in bcMap[b.id]])+'\n')
-	for j,i in enumerate(inters):
-		epsTNorm=sqrt(sum([e**2 for e in i.phys['epsT']]))
-		epsT='epsT [ %g %g %g ] %g'%(i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm)
-		en=i.phys['epsN']; n=i.geom['normal']
-		epsN='epsN [ %g %g %g ] %g'%(en*n[0],en*n[1],en*n[2],en)
-		Fn='Fn [ %g %g %g ] %g'%(i.phys['normalForce'][0],i.phys['normalForce'][1],i.phys['normalForce'][2],i.phys['Fn'])
-		Fs='Fs [ %lf %lf %lf ] %lf'%(i.phys['shearForce'][0],i.phys['shearForce'][1],i.phys['shearForce'][2],sqrt(sum([fs**2 for fs in i.phys['shearForce']])))
-		f.write('## #%d #%d: %s %s %s %s\n'%(i.id1+1,i.id2+1,epsN,epsT,Fn,Fs))
-		f.write('CohSur3d %d nodes 2 %d %d mat 1 crossSect 1 area %g\n'%(j+1,i.id1+1,i.id2+1,i.phys['crossSection']))
-	# crosssection
-	f.write("SimpleCS 1 thick 1.0 width 1.0\n")
-	# material
-	ph=inters[0].phys
-	f.write("CohInt 1 kn %g ks %g e0 %g ef %g c 0. ksi %g coh %g tanphi %g d 1.0 conf 0.0 maxdist 0.0\n"%(ph['E'],ph['G'],ph['epsCrackOnset'],ph['epsFracture'],ph['xiShear'],ph['undamagedCohesion'],ph['tanFrictionAngle']))
-	# boundary conditions
-	for b in O.bodies:
-		displ=b.phys.displ; rot=b.phys.rot
-		dofs=[displ[0],displ[1],displ[2],rot[0],rot[1],rot[2]]
-		f.write('# particle %d\n'%b.id)
-		for dof in range(6):
-			f.write('BoundaryCondition %d loadTimeFunction 1 prescribedvalue %.15e\n'%(bcMap[b.id][dof],dofs[dof]))
-	#f.write('PiecewiseLinFunction 1 npoints 3 t 3 0. 10. 1000.  f(t) 3 0. 10. 1000.\n')
-	f.write('ConstantFunction 1 f(t) 1.0\n')
-
-
-
-
-def oofemDirectExport(fileBase,title=None,negIds=[],posIds=[]):
-	from yade.wrapper import Omega
-	material,bodies,interactions=[],[],[]
-	o=Omega()
-	strainers=[e for e in o.engines if e.name=='UniaxialStrainer']
-	if len(strainers)>0:
-		strainer=strainers[0]
-		posIds,negIds=strainer['posIds'],strainer['negIds']
-	else: strainer=None
-	f=open(fileBase+'.in','w')
-	# header
-	f.write(fileBase+'.out\n')
-	f.write((title if title else 'Yade simulation for '+fileBase)+'\n')
-	f.write("NonLinearStatic nsteps 2 contextOutputStep 1 rtolv 1.e-2 stiffMode 2 maxiter 50 controllmode 1 nmodules 0\n")
-	f.write("domain 3dShell\n")
-	f.write("OutputManager tstep_all dofman_all element_all\n")
-	inters=[i for i in o.interactions if (i.geom and i.phys)]
-	f.write("ndofman %d nelem %d ncrosssect 1 nmat 1 nbc 2 nic 0 nltf 1 nbarrier 0\n"%(len(o.bodies)+2,len(inters)))
-	# elements
-	f.write("Node 1 coords 3 0.0 0.0 0.0 bc 6 1 1 1 1 1 1\n")
-	f.write("Node 2 coords 3 0.0 0.0 0.0 bc 6 1 2 1 1 1 1\n")
-	for b in o.bodies:
-		f.write("Particle %d coords 3 %g %g %g rad %g"%(b.id+3,b.phys.refPos[0],b.phys.refPos[1],b.phys.refPos[2],b.shape['radius']))
-		if b.id in negIds: f.write(" dofType 6 1 1 1 1 1 1 masterMask 6 0 1 0 0 0 0 ")
-		elif b.id in posIds: f.write(" dofType 6 1 1 1 1 1 1 masterMask 6 0 2 0 0 0 0 0")
-		f.write('\n')
-	j=1
-	for i in inters:
-		f.write('CohSur3d %d nodes 2 %d %d mat 1 crossSect 1 area %g\n'%(j,i.id1+3,i.id2+3,i.phys['crossSection']))
-		j+=1
-	# crosssection
-	f.write("SimpleCS 1 thick 1.0 width 1.0\n")
-	# material
-	ph=inters[0].phys
-	f.write("CohInt 1 kn %g ks %g e0 %g ef %g c 0. ksi %g coh %g tanphi %g damchartime %g damrateexp %g plchartime %g plrateexp %g d 1.0\n"%(ph['E'],ph['G'],ph['epsCrackOnset'],ph['epsFracture'],0.0,ph['undamagedCohesion'],ph['tanFrictionAngle'],ph['dmgTau'],ph['dmgRateExp'],ph['plTau'],ph['plRateExp']))
-	# boundary conditions
-	f.write('BoundaryCondition 1 loadTimeFunction 1 prescribedvalue 0.0\n')
-	f.write('BoundaryCondition 2 loadTimeFunction 1 prescribedvalue 1.e-4\n')
-	f.write('PiecewiseLinFunction 1 npoints 3 t 3 0. 10. 1000.  f(t) 3 0. 10. 1000.\n')
-
-
-def displacementsInteractionsExport(fName):
-	f=open(fName,'w')
-	print "Writing body displacements and interaction strains."
-	o=Omega()
-	for b in o.bodies:
-		x0,y0,z0=b.phys['refSe3'][0:3]; x,y,z=b.phys.pos
-		rx,ry,rz,rr=b.phys['se3'][3:7]
-		f.write('%d xyz [ %g %g %g ] dxyz [ %g %g %g ] rxyz [ %g %g %g ] \n'%(b.id,x0,y0,z0,x-x0,y-y0,z-z0,rr*rx,rr*ry,rr*rz))
-	f.write('\n')
-	for i in o.interactions:
-		if not i['isReal']:continue
-		epsTNorm=sqrt(sum([e**2 for e in i.phys['epsT']]))
-		epsT='epsT [ %g %g %g ] %g'%(i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm)
-		en=i.phys['epsN']; n=i.geom['normal']
-		epsN='epsN [ %g %g %g ] %g'%(en*n[0],en*n[1],en*n[2],en)
-		Fn='Fn [ %g %g %g ] %g'%(i.phys['normalForce'][0],i.phys['normalForce'][1],i.phys['normalForce'][2],i.phys['Fn'])
-		Fs='Fs [ %lf %lf %lf ] %lf'%(i.phys['shearForce'][0],i.phys['shearForce'][1],i.phys['shearForce'][2],sqrt(sum([fs**2 for fs in i.phys['shearForce']])))
-		f.write('%d %d %s %s %s %s\n'%(i.id1,i.id2,epsN,epsT,Fn,Fs))
-		# f.write('%d %d %g %g %g %g %g\n'%(i.id1,i.id2,i.phys['epsN'],i.phys['epsT'][0],i.phys['epsT'][1],i.phys['epsT'][2],epsTNorm))
-	f.close()
-
-
-
-
-
-def eliminateJumps(eps,sigma,numSteep=10,gapWidth=5,movWd=40):
-	from matplotlib.mlab import movavg
-	from numpy import diff,abs
-	import numpy
-	# get histogram of 'derivatives'
-	ds=abs(diff(sigma))
-	n,bins=numpy.histogram(ds)
-	i=1; sum=0
-	# numSteep steepest pieces will be discarded
-	while sum<numSteep:
-		#print n[-i],bins[-i]
-		sum+=n[-i]; i+=1
-	#print n[-i],bins[-i]
-	threshold=bins[-i]
-	# old algo: replace with nan's
-	##rEps,rSigma=eps[:],sigma[:]; nan=float('nan')
-	##indices=where(ds>threshold)[0]
-	##for i in indices:
-	##	for ii in range(max(0,i-gapWidth),min(len(rEps),i+gapWidth+1)): rEps[ii]=rSigma[ii]=nan
-
-	## doesn't work with older numpy (?)
-	# indices1=where(ds>threshold)[0]
-	indices1=[]
-	for i in range(len(ds)):
-		if ds[i]>threshold: indices1.append(i)
-	indices=[]
-	for i in indices1:
-		for ii in range(i-gapWidth,i+gapWidth+1): indices.append(ii)
-	#print indices1, indices
-	rEps=[eps[i] for i in range(0,len(eps)) if i not in indices]
-	rSigma=[sigma[i] for i in range(0,len(sigma)) if i not in indices]
-	# apply moving average to the result
-	rSigma=movavg(rSigma,movWd)
-	rEps=rEps[movWd/2:-movWd/2+1]
-	return rEps,rSigma.flatten().tolist()
-

Deleted: trunk/lib/py/linterpolation.py
===================================================================
--- trunk/lib/py/linterpolation.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/linterpolation.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,99 +0,0 @@
-# encoding: utf-8
-#
-# © 2009 Václav Šmilauer <eudoxos@xxxxxxxx>
-#
-
-"""
-Module for rudimentary support of manipulation with piecewise-linear
-functions (which are usually interpolations of higher-order functions,
-whence the module name). Interpolation is always given as two lists
-of the same length, where the x-list must be increasing.
-
-Periodicity is supported by supposing that the interpolation
-can wrap from the last x-value to the first x-value (which
-should be 0 for meaningful results).
-
-Non-periodic interpolation can be converted to periodic one
-by padding the interpolation with constant head and tail using
-the sanitizeInterpolation function.
-
-There is a c++ template function for interpolating on such sequences in
-pkg/common/Engine/DeusExMachina/LinearInterpolate.hpp (stateful, therefore
-fast for sequential reads).
-
-TODO: Interpolating from within python is not (yet) supported.
-"""
-
-def revIntegrateLinear(I,x0,y0,x1,y1):
-	"""Helper function, returns value of integral variable x for
-	linear function f passing through (x0,y0),(x1,y1) such that
-	1. x∈[x0,x1]
-	2. ∫_x0^x f dx=I
-	and raise exception if such number doesn't exist or the solution
-	is not unique (possible?)
-	"""
-	from math import sqrt
-	dx,dy=x1-x0,y1-y0
-	if dy==0: # special case, degenerates to linear equation
-		return (x0*y0+I)/y0
-	a=dy/dx; b=2*(y0-x0*dy/dx); c=x0**2*dy/dx-2*x0*y0-2*I
-	det=b**2-4*a*c; assert(det>0)
-	p,q=(-b+sqrt(det))/(2*a),(-b-sqrt(det))/(2*a)
-	pOK,qOK=x0<=p<=x1,x0<=q<=x1
-	if pOK and qOK: raise ValueError("Both radices within interval!?")
-	if not pOK and not qOK: raise ValueError("No radix in given interval!")
-	return p if pOK else q
-
-def integral(x,y):
-	"""Return integral of piecewise-linear function given by points
-	x0,x1,… and y0,y1,… """
-	assert(len(x)==len(y))
-	sum=0
-	for i in range(1,len(x)): sum+=(x[i]-x[i-1])*.5*(y[i]+y[i-1])
-	return sum
-
-def xFractionalFromIntegral(integral,x,y):
-	"""Return x within range x0…xn such that ∫_x0^x f dx==integral.
-	Raises error if the integral value is not reached within the x-range.
-	"""
-	sum=0
-	for i in range(1,len(x)):
-		diff=(x[i]-x[i-1])*.5*(y[i]+y[i-1])
-		if sum+diff>integral:
-			return revIntegrateLinear(integral-sum,x[i-1],y[i-1],x[i],y[i])
-		else: sum+=diff
-	raise "Integral not reached within the interpolation range!"
-
-
-def xFromIntegral(integralValue,x,y):
-	"""Return x such that ∫_x0^x f dx==integral.
-	x wraps around at xn. For meaningful results, therefore, x0 should == 0 """
-	from math import floor
-	period=x[-1]-x[0]
-	periodIntegral=integral(x,y)
-	numPeriods=floor(integralValue/periodIntegral)
-	xFrac=xFractionalFromIntegral(integralValue-numPeriods*periodIntegral,x,y)
-	#print '### wanted _%g; period=%g; periodIntegral=_%g (numPeriods=%g); rests _%g (xFrac=%g)'%(integralValue,period,periodIntegral,numPeriods,integralValue-numPeriods*periodIntegral,xFrac)
-	#print '### returning %g*%g+%g=%g'%(period,numPeriods,xFrac,period*numPeriods+xFrac)
-	return period*numPeriods+xFrac
-
-def sanitizeInterpolation(x,y,x0,x1):
-	"""Extends piecewise-linear function in such way that it spans at least
-	the x0…x1 interval, by adding constant padding at the beginning (using y0)
-	and/or at the end (using y1) or not at all."""
-	xx,yy=[],[]
-	if x0<x[0]:
-		xx+=[x0]; yy+=[y[0]]
-	xx+=x; yy+=y
-	if x1>x[-1]:
-		xx+=[x1]; yy+=[y[-1]]
-	return xx,yy
-
-if __name__=="main":
-	xx,yy=sanitizeInterpolation([1,2,3],[1,1,2],0,4)
-	print xx,yy
-	print integral(xx,yy) # 5.5
-	print revIntegrateLinear(.625,1,1,2,2) # 1.5
-	print xFractionalFromIntegral(1.625,xx,yy) # 1.625
-	print xFractionalFromIntegral(2.625,xx,yy) # 2.5
-

Deleted: trunk/lib/py/log.cpp
===================================================================
--- trunk/lib/py/log.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/log.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,55 +0,0 @@
-#include<boost/python.hpp>
-#include<string>
-#include<yade/lib-base/Logging.hpp>
-using namespace boost;
-enum{ll_TRACE,ll_DEBUG,ll_INFO,ll_WARN,ll_ERROR,ll_FATAL};
-
-#ifdef LOG4CXX
-	#include<log4cxx/logmanager.h>
-
-	void logSetLevel(std::string loggerName,int level){
-		std::string fullName(loggerName.empty()?"yade":("yade."+loggerName));
-		if(!log4cxx::LogManager::exists(fullName)) throw std::invalid_argument("No logger named `"+fullName+"'");
-		log4cxx::LevelPtr l;
-		switch(level){
-			#ifdef LOG4CXX_TRACE
-				case ll_TRACE: l=log4cxx::Level::getTrace(); break;
-				case ll_DEBUG: l=log4cxx::Level::getDebug(); break;
-				case ll_INFO:  l=log4cxx::Level::getInfo(); break;
-				case ll_WARN:  l=log4cxx::Level::getWarn(); break;
-				case ll_ERROR: l=log4cxx::Level::getError(); break;
-				case ll_FATAL: l=log4cxx::Level::getFatal(); break;
-			#else
-				case ll_TRACE: l=log4cxx::Level::DEBUG; break;
-				case ll_DEBUG: l=log4cxx::Level::DEBUG; break;
-				case ll_INFO:  l=log4cxx::Level::INFO; break;
-				case ll_WARN:  l=log4cxx::Level::WARN; break;
-				case ll_ERROR: l=log4cxx::Level::ERROR; break;
-				case ll_FATAL: l=log4cxx::Level::FATAL; break;
-			#endif
-			default: throw std::invalid_argument("Unrecognized logging level "+lexical_cast<std::string>(level));
-		}
-		log4cxx::LogManager::getLogger("yade."+loggerName)->setLevel(l);
-	}
-#else
-	bool warnedOnce=false;
-	void logSetLevel(std::string loggerName, int level){
-		// better somehow python's raise RuntimeWarning, but not sure how to do that from c++
-		// it shouldn't be trapped by boost::python's exception translator, just print warning
-		// Do like this for now.
-		if(!warnedOnce){
-			LOG_WARN("Yade was compiled without log4cxx support. Setting log levels from python will have no effect (warn once).");
-			warnedOnce=true;
-		}
-	}
-#endif
-
-BOOST_PYTHON_MODULE(log){
-	python::def("setLevel",logSetLevel,"Set minimum severity level (constants TRACE,DEBUG,INFO,WARN,ERROR,FATAL) for given logger\nleading 'yade.' will be appended automatically to the logger name; if logger is '', the root logger 'yade' will be operated on.");
-	python::scope().attr("TRACE")=(int)ll_TRACE;
-	python::scope().attr("DEBUG")=(int)ll_DEBUG;
-	python::scope().attr("INFO")= (int)ll_INFO;
-	python::scope().attr("WARN")= (int)ll_WARN;
-	python::scope().attr("ERROR")=(int)ll_ERROR;
-	python::scope().attr("FATAL")=(int)ll_FATAL;
-}

Deleted: trunk/lib/py/pack.py
===================================================================
--- trunk/lib/py/pack.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/pack.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,249 +0,0 @@
-# encoding: utf-8
-#
-import itertools,warnings
-from numpy import arange
-from math import sqrt
-from yade import utils
-
-# for now skip the import, but try in inGtsSurface constructor again, to give error if we really use it
-try:
-	import gts
-except ImportError: pass
-
-# make c++ predicates available in this module
-from _packPredicates import *
-# import SpherePack
-from _packSpheres import *
-
-class inGtsSurface_py(Predicate):
-	"""This class was re-implemented in c++, but should stay here to serve as reference for implementing
-	Predicates in pure python code. C++ allows us to play dirty tricks in GTS which are not accessible
-	through pygts itself; the performance penalty of pygts comes from fact that if constructs and destructs
-	bb tree for the surface at every invocation of gts.Point().is_inside(). That is cached in the c++ code,
-	provided that the surface is not manipulated with during lifetime of the object (user's responsibility).
-
-	---
-	
-	Predicate for GTS surfaces. Constructed using an already existing surfaces, which must be closed.
-
-		import gts
-		surf=gts.read(open('horse.gts'))
-		inGtsSurface(surf)
-
-	Note: padding is optionally supported by testing 6 points along the axes in the pad distance. This
-	must be enabled in the ctor by saying doSlowPad=True. If it is not enabled and pad is not zero,
-	warning is issued.
-	"""
-	def __init__(self,surf,noPad=False):
-		# call base class ctor; necessary for virtual methods to work as expected.
-		# see comments in _packPredicates.cpp for struct PredicateWrap.
-		super(inGtsSurface,self).__init__()
-		if not surf.is_closed(): raise RuntimeError("Surface for inGtsSurface predicate must be closed.")
-		self.surf=surf
-		self.noPad=noPad
-		inf=float('inf')
-		mn,mx=[inf,inf,inf],[-inf,-inf,-inf]
-		for v in surf.vertices():
-			c=v.coords()
-			mn,mx=[min(mn[i],c[i]) for i in 0,1,2],[max(mx[i],c[i]) for i in 0,1,2]
-		self.mn,self.mx=tuple(mn),tuple(mx)
-		import gts
-	def aabb(self): return self.mn,self.mx
-	def __call__(self,_pt,pad=0.):
-		p=gts.Point(*_pt)
-		if self.noPad:
-			if pad!=0: warnings.warn("Padding disabled in ctor, using 0 instead.")
-			return p.is_inside(self.surf)
-		pp=[gts.Point(_pt[0]-pad,_pt[1],_pt[2]),gts.Point(_pt[0]+pad,_pt[1],_pt[2]),gts.Point(_pt[0],_pt[1]-pad,_pt[2]),gts.Point(_pt[0],_pt[1]+pad,_pt[2]),gts.Point(_pt[0],_pt[1],_pt[2]-pad),gts.Point(_pt[0],_pt[1],_pt[2]+pad)]
-		return p.is_inside(self.surf) and pp[0].is_inside(self.surf) and pp[1].is_inside(self.surf) and pp[2].is_inside(self.surf) and pp[3].is_inside(self.surf) and pp[4].is_inside(self.surf) and pp[5].is_inside(self.surf)
-
-class inSpace(Predicate):
-	"""Predicate returning True for any points, with infinite bounding box."""
-	def aabb(self):
-		inf=float('inf'); return [-inf,-inf,-inf],[inf,inf,inf]
-	def __call__(self,pt): return True
-
-#####
-## surface construction and manipulation
-#####
-
-def gtsSurface2Facets(surf,**kw):
-	"""Construct facets from given GTS surface. **kw is passed to utils.facet."""
-	return [utils.facet([v.coords() for v in face.vertices()],**kw) for face in surf]
-
-def sweptPolylines2gtsSurface(pts,threshold=0,capStart=False,capEnd=False):
-	"""Create swept suface (as GTS triangulation) given same-length sequences of points (as 3-tuples).
-	If threshold is given (>0), gts.Surface().cleanup(threshold) will be called before returning, which
-	removes vertices mutually closer than threshold. Can be used to create closed swept surface (revolved), as
-	we don't check for coincident vertices otherwise.
-	"""
-	if not len(set([len(pts1) for pts1 in pts]))==1: raise RuntimeError("Polylines must be all of the same length!")
-	vtxs=[[gts.Vertex(x,y,z) for x,y,z in pts1] for pts1 in pts]
-	sectEdges=[[gts.Edge(vtx[i],vtx[i+1]) for i in xrange(0,len(vtx)-1)] for vtx in vtxs]
-	interSectEdges=[[] for i in range(0,len(vtxs)-1)]
-	for i in range(0,len(vtxs)-1):
-		for j in range(0,len(vtxs[i])):
-			interSectEdges[i].append(gts.Edge(vtxs[i][j],vtxs[i+1][j]))
-			if j<len(vtxs[i])-1: interSectEdges[i].append(gts.Edge(vtxs[i][j],vtxs[i+1][j+1]))
-	surf=gts.Surface()
-	for i in range(0,len(vtxs)-1):
-		for j in range(0,len(vtxs[i])-1):
-			surf.add(gts.Face(interSectEdges[i][2*j+1],sectEdges[i+1][j],interSectEdges[i][2*j]))
-			surf.add(gts.Face(sectEdges[i][j],interSectEdges[i][2*j+2],interSectEdges[i][2*j+1]))
-	def doCap(vtx,edg,start):
-		ret=[]
-		eFan=[edg[0]]+[gts.Edge(vtx[i],vtx[0]) for i in range(2,len(vtx))]
-		for i in range(1,len(edg)):
-			ret+=[gts.Face(eFan[i-1],eFan[i],edg[i]) if start else gts.Face(eFan[i-1],edg[i],eFan[i])]
-		return ret
-	caps=[]
-	if capStart: caps+=doCap(vtxs[0],sectEdges[0],start=True)
-	if capEnd: caps+=doCap(vtxs[-1],sectEdges[-1],start=False)
-	for cap in caps: surf.add(cap)
-	if threshold>0: surf.cleanup(threshold)
-	return surf
-
-import euclid
-
-def revolutionSurfaceMeridians(sects,angles,origin=euclid.Vector3(0,0,0),orientation=euclid.Quaternion()):
-	"""Revolution surface given sequences of 2d points and sequence of corresponding angles,
-	returning sequences of 3d points representing meridian sections of the revolution surface.
-	The 2d sections are turned around z-axis, but they can be transformed
-	using the origin and orientation arguments to give arbitrary orientation."""
-	import math
-	def toGlobal(x,y,z):
-		return tuple(origin+orientation*(euclid.Vector3(x,y,z)))
-	return [[toGlobal(x2d*math.cos(angles[i]),x2d*math.sin(angles[i]),y2d) for x2d,y2d in sects[i]] for i in range(0,len(sects))]
-
-########
-## packing generators
-########
-
-
-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()
-	if(max([mx[i]-mn[i] for i in 0,1,2])==float('inf')): raise ValueError("AABB of the predicate must not be infinite (didn't you use union | instead of intersection & for unbounded predicate such as notInNotch?");
-	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]
-	if(max(dim)==float('inf')): raise ValueError("AABB of the predicate must not be infinite (didn't you use union | instead of intersection & for unbounded predicate such as notInNotch?");
-	ii,jj,kk=[range(0,int(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
-
-def filterSpherePack(predicate,spherePack,**kw):
-	"""Using given SpherePack instance, return spheres the satisfy predicate.
-	The packing will be recentered to match the predicate and warning is given if the predicate
-	is larger than the packing."""
-	mn,mx=predicate.aabb()
-	dimP,centP=predicate.dim(),predicate.center()
-	dimS,centS=spherePack.dim(),spherePack.center()
-	if dimP[0]>dimS[0] or dimP[1]>dimS[1] or dimP[2]>dimS[2]: warnings.warn("Packing's dimension (%s) doesn't fully contain dimension of the predicate (%s)."%(dimS,dimP))
-	spherePack.translate(tuple([centP[i]-centS[i] for i in 0,1,2]))
-	ret=[]
-	for s in spherePack:
-		if predicate(s[0],s[1]): ret+=[utils.sphere(s[0],radius=s[1],**kw)]
-	return ret
-
-def triaxialPack(predicate,radius,dim=None,cropLayers=1,radiusStDev=0.,assumedFinalDensity=.6,memoizeDb=None,**kw):
-	"""Generator of triaxial packing, using TriaxialTest. Radius is radius of spheres, radiusStDev is its standard deviation.
-	By default, all spheres are of the same radius. cropLayers is how many layer of spheres will be added to the computed
-	dimension of the box so that there no (or not so much, at least) boundary effects at the boundaris of the predicate.
-	assumedFinalDensity should be OK as it is, it is used to compute necessary number of spheres for the packing.
-
-	The memoizeDb parameter can be passed a file (existent or nonexistent). If the file exists, it will be first looked
-	for a suitable packing that was previously saved already (known as memoization). Saved packings will be scaled to
-	requested sphere radius; those that are smaller are distcarded as well as those with different radiusStDev. From
-	the remaining ones, the one with the least spheres will be loaded and returned. If no suitable packing is found, it
-	is generated as usually, but saved into the database for later use.
-
-	O.switchWorld() magic is used to have clean simulation for TriaxialTest without deleting the original simulation.
-	This function therefore should never run in parallel with some code accessing your simulation.
-	"""
-	import sqlite3, os.path, cPickle, time, sys
-	from yade import log
-	from math import pi
-	if not dim: dim=predicate.dim()
-	if max(dim)==float('inf'): raise RuntimeError("Infinite predicate and no dimension of packing requested.")
-	fullDim=tuple([dim[i]+4*cropLayers*radius for i in 0,1,2])
-	if(memoizeDb and os.path.exists(memoizeDb)):
-		# find suitable packing and return it directly
-		conn=sqlite3.connect(memoizeDb); c=conn.cursor();
-		c.execute('select radius,radiusStDev,dimx,dimy,dimz,N,timestamp from packings order by N')
-		for row in c:
-			R,rDev,X,Y,Z,NN,timestamp=row[0:7]; scale=radius/R
-			rDev*=scale; X*=scale; Y*=scale; Z*=scale
-			if (radiusStDev==0 and rDev!=0) or (radiusStDev==0 and rDev!=0) or (radiusStDev!=0 and abs((rDev-radiusStDev)/radiusStDev)>1e-2): continue # not suitable, standard deviation differs too much
-			if X<fullDim[0] or Y<fullDim[1] or Z<fullDim[2]: continue # not suitable, not large enough
-			print "Found suitable packing in database (radius=%g±%g,N=%g,dim=%g×%g×%g,scale=%g), created %s"%(R,rDev,NN,X,Y,Z,scale,time.asctime(time.gmtime(timestamp)))
-			c.execute('select pack from packings where timestamp=?',(timestamp,))
-			sp=SpherePack(cPickle.loads(str(c.fetchone()[0])))
-			sp.scale(scale)
-			return filterSpherePack(predicate,sp,**kw)
-		print "No suitable packing in database found, running triaxial"
-		sys.stdout.flush()
-	V=(4/3)*pi*radius**3; N=assumedFinalDensity*fullDim[0]*fullDim[1]*fullDim[2]/V;
-	##
-	O.switchWorld()
-	##
-	TriaxialTest(
-		numberOfGrains=int(N),
-		radiusMean=radius,
-		# this is just size ratio if radiusMean is specified
-		# if you comment out the line above, it will be the corner (before compaction) and radiusMean will be set accordingly
-		upperCorner=fullDim,
-		radiusStdDev=radiusStDev,
-		## no need to touch any the following, I think
-		noFiles=True,
-		lowerCorner=[0,0,0],
-		sigmaIsoCompaction=1e7,
-		sigmaLateralConfinement=1e3,
-		StabilityCriterion=.05,
-		strainRate=.2,
-		fast=True,
-		thickness=-1, # will be set to sphere radius if negative
-		maxWallVelocity=.1,
-		wallOversizeFactor=1.5,
-		autoUnload=True, # unload after isotropic compaction
-		autoCompressionActivation=False # stop once unloaded
-	).load()
-	log.setLevel('TriaxialCompressionEngine',log.WARN)
-	O.run(); O.wait()
-	sp=SpherePack(); sp.fromSimulation()
-	##
-	O.switchWorld()
-	##
-	if(memoizeDb):
-		if os.path.exists(memoizeDb):
-			conn=sqlite3.connect(memoizeDb)
-		else:
-			conn=sqlite3.connect(memoizeDb)
-			c=conn.cursor()
-			c.execute('create table packings (radius real, radiusStDev real, dimx real, dimy real, dimz real, N integer, timestamp real, pack blob)')
-		c=conn.cursor()
-		packBlob=buffer(cPickle.dumps(sp.toList(),cPickle.HIGHEST_PROTOCOL))
-		c.execute('insert into packings values (?,?,?,?,?,?,?,?)',(radius,radiusStDev,fullDim[0],fullDim[1],fullDim[2],len(sp),time.time(),packBlob,))
-		c.close()
-		conn.commit()
-		print "Packing saved to the database",memoizeDb
-	return filterSpherePack(predicate,sp,**kw)
-
-
-

Deleted: trunk/lib/py/plot.py
===================================================================
--- trunk/lib/py/plot.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/plot.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,190 +0,0 @@
-# encoding: utf-8
-# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
-"""
-Module containing utility functions for plotting inside yade.
-
-Experimental, interface may change (even drastically).
-
-"""
-import matplotlib
-matplotlib.use('TkAgg')
-#matplotlib.use('GTKCairo')
-#matplotlib.use('QtAgg')
-matplotlib.rc('axes',grid=True) # put grid in all figures
-import pylab
-
-
-data={} # global, common for all plots: {'name':[value,...],...}
-plots={} # dictionary x-name -> (yspec,...), where yspec is either y-name or (y-name,'line-specification')
-plotsFilled={} # same as plots but with standalone plot specs filled to tuples (used internally only)
-plotLines={} # dictionary x-name -> Line2d objects (that hopefully still correspond to yspec in plots)
-needsFullReplot=True
-
-def reset():
-	global data, plots, plotsFilled, plotLines, needsFullReplot
-	data={}; plots={}; plotsFilled={}; plotLines={}; needsFullReplot=True; 
-	pylab.close('all')
-
-# we could have a yplot class, that would hold: (yspec,...), (Line2d,Line2d,...) ?
-
-
-plotDataCollector=None
-import yade.wrapper
-o=yade.wrapper.Omega()
-
-maxDataLen=1000
-
-def reduceData(l):
-	"""If we have too much data, take every second value and double the step for DateGetterEngine. This will keep the samples equidistant.
-	"""
-	if l>maxDataLen:
-		global plotDataCollector
-		if not plotDataCollector: plotDataCollector=o.labeledEngine('plotDataCollector') # will raise RuntimeError if not found
-		if plotDataCollector['mayStretch']: # may we double the period without getting over limits?
-			plotDataCollector['stretchFactor']=2. # just to make sure
-			print "Reducing data: %d > %d"%(l,maxDataLen)
-			for d in data: data[d]=data[d][::2]
-			for attr in ['virtPeriod','realPeriod','iterPeriod']:
-				if(plotDataCollector[attr]>0): plotDataCollector[attr]=2*plotDataCollector[attr]
-
-def splitData():
-	"Make all plots discontinuous at this point (adds nan's to all data fields)"
-	addData({})
-
-
-def reverseData():
-	for k in data: data[k].reverse()
-
-def addData(d):
-	"""Add data from argument {'name':value,...} to yade.plot.data.
-
-	New data will be left-padded with nan's, unspecified data will be nan.
-	This way, equal length of all data is assured so that they can be plotted one against any other.
-
-	Nan's don't appear in graphs."""
-	import numpy
-	if len(data)>0: numSamples=len(data[data.keys()[0]])
-	else: numSamples=0
-	reduceData(numSamples)
-	nan=float('nan')
-	for name in d:
-		if not name in data.keys():
-			data[name]=[nan for i in range(numSamples)] #numpy.array([nan for i in range(numSamples)])
-	for name in data:
-		if name in d: data[name].append(d[name]) #numpy.append(data[name],[d[name]],1)
-		else: data[name].append(nan)
-
-def fillNonSequence(o):
-	if o.__class__==tuple().__class__ or o.__class__==list().__class__: return o
-	else: return (o,'')
-
-def show(): plot()
-
-def plot():
-	pylab.ion() ## # no interactive mode (hmmm, I don't know why actually...)
-	for p in plots:
-		pylab.figure()
-		plots_p=[fillNonSequence(o) for o in plots[p]]
-		plotsFilled[p]=plots_p
-		plotLines[p]=pylab.plot(*sum([[data[p],data[d[0]],d[1]] for d in plots_p],[]))
-		pylab.legend([_p[0] for _p in plots_p])
-		pylab.xlabel(p)
-		if 'title' in O.tags.keys(): pylab.title(O.tags['title'])
-	pylab.show()
-updatePeriod=0
-def periodicUpdate(period):
-	import time
-	global updatePeriod
-	while updatePeriod>0:
-		doUpdate()
-		time.sleep(updatePeriod)
-def startUpdate(period=10):
-	global updatePeriod
-	updatePeriod=period
-	import threading
-	threading.Thread(target=periodicUpdate,args=(period,),name='Thread-update').start()
-def stopUpdate():
-	global updatePeriod
-	updatePeriod=0
-def doUpdate():
-	pylab.close('all')
-	plot()
-
-
-def saveGnuplot(baseName,term='wxt',extension=None,timestamp=False,comment=None,title=None,varData=False):
-	"""baseName: used for creating baseName.gnuplot (command file for gnuplot),
-			associated baseName.data (data) and output files (if applicable) in the form baseName.[plot number].extension
-		term: specify the gnuplot terminal;
-			defaults to x11, in which case gnuplot will draw persistent windows to screen and terminate
-			other useful terminals are 'png', 'cairopdf' and so on
-		extension: defaults to terminal name
-			fine for png for example; if you use 'cairopdf', you should also say extension='pdf' however
-		timestamp: append numeric time to the basename
-		varData: whether file to plot will be declared as variable or be in-place in the plot expression
-		comment: a user comment (may be multiline) that will be embedded in the control file
-
-		Returns name fo the gnuplot file created.
-	"""
-	import time,bz2
-	vars=data.keys(); vars.sort()
-	lData=len(data[vars[0]])
-	if timestamp: baseName+=time.strftime('_%Y%m%d_%H:%M')
-	baseNameNoPath=baseName.split('/')[-1]
-	fData=bz2.BZ2File(baseName+".data.bz2",'w');
-	fData.write("# "+"\t\t".join(vars)+"\n")
-	for i in range(lData):
-		fData.write("\t".join([str(data[var][i]) for var in vars])+"\n")
-	fData.close()
-	fPlot=file(baseName+".gnuplot",'w')
-	fPlot.write('#!/usr/bin/env gnuplot\n#\n# created '+time.asctime()+' ('+time.strftime('%Y%m%d_%H:%M')+')\n#\n')
-	if comment: fPlot.write('# '+comment.replace('\n','\n# ')+'#\n')
-	dataFile='"< bzcat %s.data.bz2"'%(baseNameNoPath)
-	if varData:
-		fPlot.write('dataFile=%s'%dataFile); dataFile='dataFile'
-	if not extension: extension=term
-	i=0
-	for p in plots:
-		# print p
-		plots_p=[fillNonSequence(o) for o in plots[p]]
-		if term in ['wxt','x11']: fPlot.write("set term %s %d persist\n"%(term,i))
-		else: fPlot.write("set term %s; set output '%s.%d.%s'\n"%(term,baseNameNoPath,i,extension))
-		fPlot.write("set xlabel '%s'\n"%p)
-		fPlot.write("set grid\n")
-		fPlot.write("set datafile missing 'nan'\n")
-		if title: fPlot.write("set title '%s'\n"%title)
-		fPlot.write("plot "+",".join([" %s using %d:%d title '%s(%s)' with lines"%(dataFile,vars.index(p)+1,vars.index(pp[0])+1,pp[0],p) for pp in plots_p])+"\n")
-		i+=1
-	fPlot.close()
-	return baseName+'.gnuplot'
-
-
-	
-import random
-if __name__ == "__main__":
-	for i in range(10):
-		addData({'a':random.random(),'b':random.random(),'t':i*.001,'i':i})
-	print data
-	for i in range(15):
-		addData({'a':random.random(),'c':random.random(),'d':random.random(),'one':1,'t':(i+10)*.001,'i':i+10})
-	print data
-	# all lists must have the same length
-	l=set([len(data[n]) for n in data])
-	print l
-	assert(len(l)==1)
-	plots={'t':('a',('b','g^'),'d'),'i':('a',('one','g^'))}
-	fullPlot()
-	print "PLOT DONE!"
-	fullPlot()
-	plots['t']=('a',('b','r^','d'))
-	print "FULL PLOT DONE!"
-	for i in range(20):
-		addData({'d':.1,'a':.5,'c':.6,'c':random.random(),'t':(i+25)*0.001,'i':i+25})
-	updatePlot()
-	print "UPDATED!"
-	print data['d']
-	import time
-	#time.sleep(60)
-	killPlots()
-	#pylab.clf()
-
-

Deleted: trunk/lib/py/timing.py
===================================================================
--- trunk/lib/py/timing.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/timing.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,64 +0,0 @@
-# encoding: utf-8
-def _resetEngine(e):
-	if e.timingDeltas: e.timingDeltas.reset()
-	if e.__class__.__name__=='EngineUnit': return
-	if e.__class__.__name__=='MetaEngine':
-		for f in e.functors: _resetEngine(f)
-	elif e.__class__.__name__=='ParallelEngine':
-		for s in e.slaves: _resetEngine(s)
-	e.execTime,e.execCount=0,0
-
-def reset():
-	for e in O.engines: _resetEngine(e)
-
-_statCols={'label':40,'count':20,'time':20,'relTime':20}
-_maxLev=3
-
-def _formatLine(label,time,count,totalTime,level):
-	sp,negSp=' '*level*2,' '*(_maxLev-level)*2
-	raw=[]
-	raw.append(label)
-	raw.append(str(count) if count>=0 else '')
-	raw.append((str(time/1000)+u'us') if time>=0 else '')
-	raw.append(('%6.2f%%'%(time*100./totalTime)) if totalTime>0 else '')
-	return u' '.join([
-		(sp+raw[0]).ljust(_statCols['label']),
-		(raw[1]+negSp).rjust(_statCols['count']),
-		(raw[2]+negSp).rjust(_statCols['time']),
-		(raw[3]+negSp).rjust(_statCols['relTime']),
-	])
-
-def _delta_stats(deltas,totalTime,level):
-	ret=0
-	deltaTime=sum([d[1] for d in deltas.data])
-	for d in deltas.data:
-		print _formatLine(d[0],d[1],d[2],totalTime,level); ret+=1
-	if len(deltas.data)>1:
-		print _formatLine('TOTAL',deltaTime,-1,totalTime,level); ret+=1
-	return ret
-
-def _engines_stats(engines,totalTime,level):
-	lines=0; hereLines=0
-	for e in engines:
-		if e.__class__.__name__!='EngineUnit': print _formatLine(u'"'+e['label']+'"' if e['label'] else e.name,e.execTime,e.execCount,totalTime,level); lines+=1; hereLines+=1
-		if e.timingDeltas: 
-			if e.__class__.__name__=='EngineUnit':
-				print _formatLine(e.name,-1,-1,-1,level); lines+=1; hereLines+=1
-				execTime=sum([d[1] for d in e.timingDeltas.data])
-			else: execTime=e.execTime
-			lines+=_delta_stats(e.timingDeltas,execTime,level+1)
-		if e.__class__.__name__=='MetaEngine': lines+=_engines_stats(e.functors,e.execTime,level+1)
-		if e.__class__.__name__=='InteractionDispatcher':
-			lines+=_engines_stats(e.geomDispatcher.functors,e.execTime,level+1)
-			lines+=_engines_stats(e.physDispatcher.functors,e.execTime,level+1)
-			lines+=_engines_stats(e.constLawDispatcher.functors,e.execTime,level+1)
-		elif e.__class__.__name__=='ParallelEngine': lines+=_engines_stats(e.slave,e.execTime,level+1)
-	if hereLines>1:
-		print _formatLine('TOTAL',totalTime,-1,totalTime,level); lines+=1
-	return lines
-
-def stats():
-	print 'Name'.ljust(_statCols['label'])+' '+'Count'.rjust(_statCols['count'])+' '+'Time'.rjust(_statCols['time'])+' '+'Rel. time'.rjust(_statCols['relTime'])
-	print '-'*(sum([_statCols[k] for k in _statCols])+len(_statCols)-1)
-	_engines_stats(O.engines,sum([e.execTime for e in O.engines]),0)
-	print

Deleted: trunk/lib/py/utils.py
===================================================================
--- trunk/lib/py/utils.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/lib/py/utils.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -1,452 +0,0 @@
-# encoding: utf-8
-#
-# utility functions for yade
-#
-# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
-
-import math,random
-from yade.wrapper import *
-try: # use psyco if available
-	import psyco
-	psyco.full()
-except ImportError: pass
-
-# c++ implementations for performance reasons
-from yade._utils import *
-
-def saveVars(mark='',loadNow=False,**kw):
-	"""Save passed variables into the simulation so that it can be recovered when the simulation is loaded again.
-
-	For example, variables a=5, b=66 and c=7.5e-4 are defined. To save those, use
-
-	 utils.saveVars(a=a,b=b,c=c)
-
-	those variables will be save in the .xml file, when the simulation itself is saved. To recover those variables once
-	the .xml is loaded again, use
-
-	 utils.loadVars(mark)
-
-	and they will be defined in the __builtin__ namespace (i.e. available from anywhere in the python code).
-
-	If loadParam==True, variables will be loaded immediately after saving. That effectively makes **kw available in builtin namespace.
-	"""
-	import cPickle
-	Omega().tags['pickledPythonVariablesDictionary'+mark]=cPickle.dumps(kw)
-	if loadNow: loadVars(mark)
-
-def loadVars(mark=None):
-	"""Load variables from saveVars, which are saved inside the simulation.
-	If mark==None, all save variables are loaded. Otherwise only those with
-	the mark passed."""
-	import cPickle
-	import __builtin__
-	if mark!=None:
-		d=cPickle.loads(Omega().tags['pickledPythonVariablesDictionary'+mark])
-		for k in d: __builtin__.__dict__[k]=d[k]
-	else: # load everything one by one
-		for m in Omega().tags.keys():
-			if m.startswith('pickledPythonVariablesDictionary'):
-				loadVars(m[len('pickledPythonVariableDictionary')+1:])
-
-
-def SpherePWaveTimeStep(radius,density,young):
-	"""Compute P-wave critical timestep for a single sphere.
-	If you want to compute minimum critical timestep for all spheres in the simulation, use utils.PWaveTimeStep() instead."""
-	from math import sqrt
-	return radius/sqrt(young/density)
-
-def randomColor(): return [random.random(),random.random(),random.random()]
-
-def typedEngine(name): return [e for e in Omega().engines if e.name==name][0]
-
-def downCast(obj,newClassName):
-	"""Cast given object to class deriving from the same yade root class and copy all parameters from given object.
-	Obj should be up in the inheritance tree, otherwise some attributes may not be defined in the new class."""
-	return obj.__class__(newClassName,dict([ (key,obj[key]) for key in obj.keys() ]))
-
-def sphere(center,radius,density=1,young=30e9,poisson=.3,frictionAngle=0.5236,dynamic=True,wire=False,color=None,physParamsClass='BodyMacroParameters',physParamsAttr={},velocity=[0,0,0]):
-	"""Create default sphere, with given parameters. Physical properties such as mass and inertia are calculated automatically."""
-	s=Body()
-	if not color: color=randomColor()
-	s.shape=GeometricalModel('Sphere',{'radius':radius,'diffuseColor':color,'wire':wire})
-	s.mold=InteractingGeometry('InteractingSphere',{'radius':radius,'diffuseColor':color})
-	V=(4./3)*math.pi*radius**3
-	inert=(2./5.)*V*density*radius**2
-	pp={'se3':[center[0],center[1],center[2],1,0,0,0],'refSe3':[center[0],center[1],center[2],1,0,0,0],'mass':V*density,'inertia':[inert,inert,inert],'young':young,'poisson':poisson,'frictionAngle':frictionAngle, 'velocity':[velocity[0],velocity[1],velocity[2]]}
-	pp.update(physParamsAttr)
-	s.phys=PhysicalParameters(physParamsClass)
-	for k in [attr for attr in pp.keys() if attr in s.phys.keys()]:
-		s.phys[k]=pp[k]
-	s.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
-	s['isDynamic']=dynamic
-	return s
-
-def box(center,extents,orientation=[1,0,0,0],density=1,young=30e9,poisson=.3,frictionAngle=0.5236,dynamic=True,wire=False,color=None,physParamsClass='BodyMacroParameters',physParamsAttr={}):
-	"""Create default box (cuboid), with given parameters. Physical properties such as mass and inertia are calculated automatically."""
-	b=Body()
-	if not color: color=randomColor()
-	b.shape=GeometricalModel('Box',{'extents':extents,'diffuseColor':color,'wire':wire})
-	b.mold=InteractingGeometry('InteractingBox',{'extents':extents,'diffuseColor':color})
-	mass=8*extents[0]*extents[1]*extents[2]*density
-
-	V=extents[0]*extents[1]*extents[2]
-	
-	Pp={'se3':[center[0],center[1],center[2],orientation[0],orientation[1],orientation[2],orientation[3]],'refSe3':[center[0],center[1],center[2],orientation[0],orientation[1],orientation[2],orientation[3]],'mass':V*density,'inertia':[mass*4*(extents[1]**2+extents[2]**2),mass*4*(extents[0]**2+extents[2]**2),mass*4*(extents[0]**2+extents[1]**2)],'young':young,'poisson':poisson,'frictionAngle':frictionAngle}
-
-	b.phys=PhysicalParameters(physParamsClass)
-	for k in [attr for attr in Pp.keys() if attr in b.phys.keys()]:
-		b.phys[k]=Pp[k]
-
-	b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
-	b['isDynamic']=dynamic
-	return b
-
-def alignedFacetBox(center,extents,wallMask=63,**kw):
-	"""Create axis-aligned box composed of facets, with given center and extents. wallMask determines which walls will be created,
-	in the order -x (1), +x (2), -y (4), +y (8), -z (16), +z (32). The numbers are ANDed; the default 63 means to create all walls.
-	Remaining **kw arguments are passed to utils.facet. The facets are oriented outwards from the box."""
-	mn,mx=[center[i]-extents[i] for i in 0,1,2],[center[i]+extents[i] for i in 0,1,2]
-	def doWall(a,b,c,d):
-		return [facet((a,b,c),**kw),facet((a,c,d),**kw)]
-	ret=[]
-	A,B,C,D=(mn[0],mn[1],mn[2]),(mx[0],mn[1],mn[2]),(mx[0],mx[1],mn[2]),(mn[0],mx[1],mn[2])
-	E,F,G,H=(mn[0],mn[1],mx[2]),(mx[0],mn[1],mx[2]),(mx[0],mx[1],mx[2]),(mn[0],mx[1],mx[2])
-	if wallMask&1:  ret+=doWall(A,D,H,E)
-	if wallMask&2:  ret+=doWall(B,C,G,F)
-	if wallMask&4:  ret+=doWall(A,B,F,E)
-	if wallMask&8:  ret+=doWall(D,H,G,C)
-	if wallMask&16: ret+=doWall(A,D,C,B)
-	if wallMask&32: ret+=doWall(E,F,G,H)
-	return ret
-
-def facet(vertices,young=30e9,poisson=.3,frictionAngle=0.5236,dynamic=False,wire=True,color=None,physParamsClass='BodyMacroParameters',physParamsAttr={}):
-	"""Create default facet with given parameters. Vertices are given as sequence of 3 3-tuple and they, all in global coordinates."""
-	b=Body()
-	if not color: color=randomColor()
-	b.shape=GeometricalModel('Facet',{'diffuseColor':color,'wire':wire})
-	b.mold=InteractingGeometry('InteractingFacet',{'diffuseColor':color})
-	center=inscribedCircleCenter(list(vertices[0]),list(vertices[1]),list(vertices[2]))
-	vertices=map(lambda a,b:map(lambda x,y:x-y,a,b),vertices,[center,center,center]) 
-	vStr='['+' '.join(['{%g %g %g}'%(v[0],v[1],v[2]) for v in vertices])+']'
-	b.shape.setRaw('vertices',vStr)
-	b.mold.setRaw('vertices',vStr)
-	pp={'se3':[center[0],center[1],center[2],1,0,0,0],'refSe3':[center[0],center[1],center[2],1,0,0,0],'young':young,'poisson':poisson,'frictionAngle':frictionAngle,'inertia':[0,0,0]}
-	pp.update(physParamsAttr)
-	b.phys=PhysicalParameters(physParamsClass)
-	for k in [attr for attr in pp.keys() if attr in b.phys.keys()]:
-		b.phys[k]=pp[k]
-	b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
-	b['isDynamic']=dynamic
-	b.mold.postProcessAttributes()
-	return b
-
-def aabbWalls(extrema=None,thickness=None,oversizeFactor=1.5,**kw):
-	"""return 6 walls that will wrap existing packing;
-	extrema are extremal points of the AABB of the packing (will be calculated if not specified)
-	thickness is wall thickness (will be 1/10 of the X-dimension if not specified)
-	Walls will be enlarged in their plane by oversizeFactor.
-	returns list of 6 wall Bodies enclosing the packing, in the order minX,maxX,minY,maxY,minZ,maxZ.
-	"""
-	walls=[]
-	if not extrema: extrema=aabbExtrema()
-	if not thickness: thickness=(extrema[1][0]-extrema[0][0])/10.
-	for axis in [0,1,2]:
-		mi,ma=extrema
-		center=[(mi[i]+ma[i])/2. for i in range(3)]
-		extents=[.5*oversizeFactor*(ma[i]-mi[i]) for i in range(3)]
-		extents[axis]=thickness/2.
-		for j in [0,1]:
-			center[axis]=extrema[j][axis]+(j-.5)*thickness
-			walls.append(box(center=center,extents=extents,dynamic=False,**kw))
-			walls[-1].shape['wire']=True
-	return walls
-
-
-def aabbDim(cutoff=0.,centers=False):
-	"""return dimensions of the bounding box, optionally cut."""
-	a=aabbExtrema(cutoff,centers)
-	return (a[1][0]-a[0][0],a[1][1]-a[0][1],a[1][2]-a[0][2])
-
-def aabbExtrema2d(pts):
-	"""return 2d bounding box for a sequence of 2-tuples"""
-	inf=float('inf')
-	min,max=[inf,inf],[-inf,-inf]
-	for pt in pts:
-		if pt[0]<min[0]: min[0]=pt[0]
-		elif pt[0]>max[0]: max[0]=pt[0]
-		if pt[1]<min[1]: min[1]=pt[1]
-		elif pt[1]>max[1]: max[1]=pt[1]
-	return tuple(min),tuple(max)
-
-def perpendicularArea(axis):
-	"""return area perpendicular to given axis (0=x,1=y,2=z) generated by bodies
-	for which the function consider returns True (defaults to returning True always)
-	and which is of the type "Sphere"
-	"""
-	ext=aabbExtrema()
-	other=((axis+1)%3,(axis+2)%3)
-	return (ext[1][other[0]]-ext[0][other[0]])*(ext[1][other[1]]-ext[0][other[1]])
-
-def fractionalBox(fraction=1.,minMax=None):
-	"""retrurn (min,max) that is the original minMax box (or aabb of the whole simulation if not specified)
-	linearly scaled around its center to the fraction factor"""
-	if not minMax: minMax=aabbExtrema()
-	half=[.5*(minMax[1][i]-minMax[0][i]) for i in [0,1,2]]
-	return (tuple([minMax[0][i]+(1-fraction)*half[i] for i in [0,1,2]]),tuple([minMax[1][i]-(1-fraction)*half[i] for i in [0,1,2]]))
-
-
-def randomizeColors(onShapes=True,onMolds=False,onlyDynamic=False):
-	"""Assign random colors to shape's (GeometricalModel) and/or mold's (InteractingGeometry) diffuseColor.
-	
-	onShapes and onMolds turn on/off operating on the respective colors.
-	If onlyDynamic is true, only dynamic bodies will have the color changed.
-	"""
-	if not onShapes and not onMolds: return
-	o=Omega()
-	for b in o.bodies:
-		color=(random.random(),random.random(),random.random())
-		if onShapes and (b['isDynamic'] or not onlyDynamic): b.shape['diffuseColor']=color
-		if onMolds  and (b['isDynamic'] or not onlyDynamic): b.mold['diffuseColor']=color
-
-
-def spheresFromFile(filename,scale=1.,wenjieFormat=False,**kw):
-	"""Load sphere coordinates from file, create spheres, insert them to the simulation.
-
-	filename is the file holding ASCII numbers (at least 4 colums that hold x_center, y_center, z_center, radius).
-	All remaining arguments are passed the the yade.utils.sphere function that creates the bodies.
-
-	wenjieFormat will skip all lines that have exactly 5 numbers and where the 4th one is exactly 1.0 -
-	this was used by a fellow developer called Wenjie to mark box elements.
-	
-	Returns list of body ids that were inserted into simulation."""
-	o=Omega()
-	ret=[]
-	for l in open(filename):
-		ss=[float(i) for i in l.split()]
-		if wenjieFormat and len(ss)==5 and ss[4]==1.0: continue
-		id=o.bodies.append(sphere([scale*ss[0],scale*ss[1],scale*ss[2]],scale*ss[3],**kw))
-		ret.append(id)
-	return ret
-
-def spheresToFile(filename,consider=lambda id: True):
-	"""Save sphere coordinates into ASCII file; the format of the line is: x y z r.
-	Non-spherical bodies are silently skipped.
-	
-	Returns number of spheres that were written."""
-	o=Omega()
-	out=open(filename,'w')
-	count=0
-	for b in o.bodies:
-		if not b.shape or not b.shape.name=='Sphere' or not consider(b.id): continue
-		out.write('%g\t%g\t%g\t%g\n'%(b.phys['se3'][0],b.phys['se3'][1],b.phys['se3'][2],b.shape['radius']))
-		count+=1
-	out.close()
-	return count
-
-def avgNumInteractions(cutoff=0.):
-	nums,counts=bodyNumInteractionsHistogram(aabbExtrema(cutoff))
-	return sum([nums[i]*counts[i] for i in range(len(nums))])/(1.*sum(counts))
-
-def plotNumInteractionsHistogram(cutoff=0.):
-	nums,counts=bodyNumInteractionsHistogram(aabbExtrema(cutoff))
-	import pylab
-	pylab.bar(nums,counts)
-	pylab.title('Number of interactions histogram, average %g (cutoff=%g)'%(avgNumInteractions(cutoff),cutoff))
-	pylab.xlabel('Number of interactions')
-	pylab.ylabel('Body count')
-	pylab.show()
-
-def plotDirections(aabb=(),mask=0,bins=20,numHist=True):
-	"""Plot 3 histograms for distribution of interaction directions, in yz,xz and xy planes and
-	(optional but default) histogram of number of interactions per body."""
-	import pylab,math
-	from yade import utils
-	for axis in [0,1,2]:
-		d=utils.interactionAnglesHistogram(axis,mask=mask,bins=bins,aabb=aabb)
-		fc=[0,0,0]; fc[axis]=1.
-		subp=pylab.subplot(220+axis+1,polar=True);
-		# 1.1 makes small gaps between values (but the column is a bit decentered)
-		pylab.bar(d[0],d[1],width=math.pi/(1.1*bins),fc=fc,alpha=.7,label=['yz','xz','xy'][axis])
-		#pylab.title(['yz','xz','xy'][axis]+' plane')
-		pylab.text(.5,.25,['yz','xz','xy'][axis],horizontalalignment='center',verticalalignment='center',transform=subp.transAxes,fontsize='xx-large')
-	if numHist:
-		pylab.subplot(224,polar=False)
-		nums,counts=utils.bodyNumInteractionsHistogram(aabb if len(aabb)>0 else utils.aabbExtrema())
-		avg=sum([nums[i]*counts[i] for i in range(len(nums))])/(1.*sum(counts))
-		pylab.bar(nums,counts,fc=[1,1,0],alpha=.7,align='center')
-		pylab.xlabel('Interactions per body (avg. %g)'%avg)
-		pylab.axvline(x=avg,linewidth=3,color='r')
-		pylab.ylabel('Body count')
-	pylab.show()
-
-
-def import_stl_geometry(file, young=30e9,poisson=.3,color=[0,1,0],frictionAngle=0.5236,wire=True,noBoundingVolume=False,noInteractingGeometry=False,physParamsClass='BodyMacroParameters',physParamsAttr={}):
-	""" Import geometry from stl file, create facets and return list of their ids."""
-	imp = STLImporter()
-	imp.wire = wire
-	imp.open(file)
-	o=Omega()
-	begin=len(o.bodies)
-	for i in xrange(imp.number_of_facets):
-		b=Body()
-		b['isDynamic']=False
-		pp={'se3':[0,0,0,1,0,0,0],'young':young,'poisson':poisson,'frictionAngle':frictionAngle}
-		pp.update(physParamsAttr)
-		b.phys=PhysicalParameters(physParamsClass)
-		for k in [attr for attr in pp.keys() if attr in b.phys.keys()]:
-			b.phys[k]=pp[k]
-		if not noBoundingVolume:
-			b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
-		o.bodies.append(b)
-	imp.import_geometry(o.bodies,begin,noInteractingGeometry)
-	imported=range(begin,begin+imp.number_of_facets)
-	for i in imported:
-		if not noInteractingGeometry:
-			o.bodies[i].mold.postProcessAttributes()
-		o.bodies[i].shape['diffuseColor']=color
-	return imported
-
-def encodeVideoFromFrames(wildcard,out,renameNotOverwrite=True,fps=24):
-	import pygst,sys,gobject,os
-	pygst.require("0.10")
-	import gst
-	if renameNotOverwrite and os.path.exists(out):
-		i=0;
-		while(os.path.exists(out+"~%d"%i)): i+=1
-		os.rename(out,out+"~%d"%i); print "Output file `%s' already existed, old file renamed to `%s'"%(out,out+"~%d"%i)
-	print "Encoding video from %s to %s"%(wildcard,out)
-	pipeline=gst.parse_launch('multifilesrc location="%s" index=0 caps="image/png,framerate=\(fraction\)%d/1" ! pngdec ! ffmpegcolorspace ! theoraenc sharpness=2 quality=63 ! oggmux ! filesink location="%s"'%(wildcard,fps,out))
-	bus=pipeline.get_bus()
-	bus.add_signal_watch()
-	mainloop=gobject.MainLoop();
-	bus.connect("message::eos",lambda bus,msg: mainloop.quit())
-	pipeline.set_state(gst.STATE_PLAYING)
-	mainloop.run()
-	pipeline.set_state(gst.STATE_NULL); pipeline.get_state()
-
-def readParamsFromTable(tableFileLine=None,noTableOk=False,unknownOk=False,**kw):
-	"""
-	Read parameters from a file and assign them to __builtin__ variables.
-
-	tableFile is a text file (with one value per blank-separated columns)
-	tableLine is number of line where to get the values from
-
-		The format of the file is as follows (commens starting with # and empty lines allowed)
-		
-		name1 name2 … # 0th line
-		val1  val2  … # 1st line
-		val2  val2  … # 2nd line
-		…
-
-	The name `description' is special and is assigned to Omega().tags['description']
-
-	assigns Omega().tags['params']="name1=val1,name2=val2,…"
-	
-	assigns Omega().tags['defaultParams']="unassignedName1=defaultValue1,…"
-
-	saves all parameters (default as well as settable) using saveVars('table')
-
-	return value is the number of assigned parameters.
-	"""
-	o=Omega()
-	tagsParams=[]
-	dictDefaults,dictParams={},{}
-	import os, __builtin__
-	if not tableFileLine and not os.environ.has_key('PARAM_TABLE'):
-		if not noTableOk: raise EnvironmentError("PARAM_TABLE is not defined in the environment")
-		o.tags['line']='l!'
-	else:
-		if not tableFileLine: tableFileLine=os.environ['PARAM_TABLE']
-		env=tableFileLine.split(':')
-		tableDesc=None
-		tableFile,tableLine=env[0],env[1]
-		if len(env)>2: tableDesc=env[3]
-		o.tags['line']='l'+tableLine
-		ll=[l.split('#')[0] for l in ['']+open(tableFile).readlines()]; names=ll[1].split(); values=ll[int(tableLine)].split()
-		assert(len(names)==len(values))
-		if 'description' in names: O.tags['description']=values[names.index('description')]
-		else:
-			bangCols=[i for i,h in enumerate(names) if h[-1]=='!']
-			if len(bangCols)==0: bangCols=range(len(names))
-			for i in range(len(names)):
-				if names[i][-1]=='!': names[i]=names[i][:-1] # strip trailing !
-			O.tags['description']=','.join(names[col]+'='+('%g'%values[col] if isinstance(values[col],float) else str(values[col])) for col in bangCols).replace("'",'').replace('"','')
-		for i in range(len(names)):
-			if names[i]=='description': continue
-			if names[i] not in kw.keys():
-				if (not unknownOk) and names[i][0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
-			else: kw.pop(names[i])
-			if names[i][0]!='!':
-				exec('%s=%s'%(names[i],values[i])) in __builtins__; tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
-	defaults=[]
-	for k in kw.keys():
-		exec("%s=%s"%(k,repr(kw[k]))) in __builtins__
-		defaults+=["%s=%s"%(k,kw[k])]; dictDefaults[k]=kw[k]
-	o.tags['defaultParams']=",".join(defaults)
-	o.tags['params']=",".join(tagsParams)
-	dictParams.update(dictDefaults); saveVars('table',**dictParams)
-	return len(tagsParams)
-
-def ColorizedVelocityFilter(isFilterActivated=True,autoScale=True,minValue=0,maxValue=0,posX=0,posY=0.2,width=0.05,height=0.5,title='Velocity, m/s'):
-    f = DeusExMachina('ColorizedVelocityFilter',{'isFilterActivated':isFilterActivated,'autoScale':autoScale,'minValue':minValue,'maxValue':maxValue,'posX':posX,'posY':posY,'width':width,'height':height,'title':title})
-    O.engines+=[f]
-    return f
-
-def ColorizedTimeFilter(point=[0,0,0],normal=[0,1,0],isFilterActivated=True,autoScale=True,minValue=0,maxValue=0,posX=0,posY=0.2,width=0.05,height=0.5,title='Time, m/s'):
-    f = DeusExMachina('ColorizedTimeFilter',{'point':point,'normal':normal,'isFilterActivated':isFilterActivated,'autoScale':autoScale,'minValue':minValue,'maxValue':maxValue,'posX':posX,'posY':posY,'width':width,'height':height,'title':title})
-    O.engines+=[f]
-    return f
-
-def PythonRunnerFilter(command='pass',isFilterActivated=True):
-    f = DeusExMachina('PythonRunnerFilter',{'command':command,'isFilterActivated':isFilterActivated})
-    O.engines+=[f]
-    return f
-
-def replaceCollider(colliderEngine):
-	"""Replaces collider (Collider) engine with the engine supplied. Raises error if no collider is in engines."""
-	colliderIdx=-1
-	for i,e in enumerate(O.engines):
-		if O.isChildClassOf(e.name,"Collider"):
-			colliderIdx=i
-			break
-	if colliderIdx<0: raise RuntimeError("No Collider found within O.engines.")
-	O.engines=O.engines[:colliderIdx]+[colliderEngine]+O.engines[colliderIdx+1:]
-
-
-def procStatus(name):
-	import os
-	for l in open('/proc/%d/status'%os.getpid()):
-		if l.split(':')[0]==name: return l
-	raise "No such line in /proc/[pid]/status: "+name
-def vmData():
-	l=procStatus('VmData'); ll=l.split(); assert(ll[2]=='kB')
-	return int(ll[1])
-
-def spheresFromFileUniaxial(filename,areaSections=10,**kw):
-	"""Load spheres from file, but do some additional work useful for uniaxial test:
-	
-	1. Find the dimensions that is the longest (uniaxial loading axis)
-	2. Find the minimum cross-section area of the speciment by examining several (areaSections)
-		sections perpendicular to axis, computing area of the convex hull for each one. This will
-		work also for non-prismatic specimen.
-	3. Find the bodies that are on the negative/positive boundary, to which the straining condition
-		should be applied.
-
-	Returns dictionary with keys 'negIds', 'posIds', 'axis', 'area'.
-	"""
-	ids=spheresFromFile(filename,**kw)
-	mm,mx=aabbExtrema()
-	dim=aabbDim(); axis=dim.index(max(dim))
-	import numpy
-	areas=[approxSectionArea(coord,axis) for coord in numpy.linspace(mm[axis],mx[axis],num=10)[1:-1]]
-	negIds,posIds=negPosExtremeIds(axis=axis,distFactor=2.2)
-	return {'negIds':negIds,'posIds':posIds,'axis':axis,'area':min(areas)}
-
-def NormalRestitution2DampingRate(en):
-        """Compute the normal damping rate as a function of the normal coefficient of restitution.
-        """
-	if en == 0.0: return 0.999999999
-	if en == 1.0: return 0.0
-	from math import sqrt,log,pi
-	ln_en = math.log(en)
-	return (-ln_en/math.sqrt((math.pow(ln_en,2) + math.pi*math.pi)))

Copied: trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.cpp (from rev 1834, trunk/gui/py/PeriodicPythonRunner.cpp)
===================================================================
--- trunk/gui/py/PeriodicPythonRunner.cpp	2009-07-02 15:26:02 UTC (rev 1834)
+++ trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,2 @@
+#include<yade/pkg-common/PeriodicPythonRunner.hpp>
+YADE_PLUGIN();


Property changes on: trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.cpp
___________________________________________________________________
Name: svn:mergeinfo
   + 

Copied: trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.hpp (from rev 1834, trunk/gui/py/PeriodicPythonRunner.hpp)


Property changes on: trunk/pkg/common/Engine/StandAloneEngine/PeriodicPythonRunner.hpp
___________________________________________________________________
Name: svn:mergeinfo
   + 

Modified: trunk/pkg/common/SConscript
===================================================================
--- trunk/pkg/common/SConscript	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/common/SConscript	2009-07-07 11:54:14 UTC (rev 1841)
@@ -4,6 +4,7 @@
 if 'EMBED_PYTHON' in env['CPPDEFINES']:
 	env.Install("$PREFIX/lib/yade$SUFFIX/pkg-common",[
 		env.SharedLibrary('PythonRunnerFilter',['Engine/FilterEngine/PythonRunnerFilter.cpp'],LIBS=env['LIBS']+['FilterEngine']),
+		env.SharedLibrary('PeriodicPythonRunner',['Engine/StandAloneEngine/PeriodicPythonRunner.cpp']),
 	])
 
 

Modified: trunk/pkg/dem/DataClass/InteractionGeometry/SpheresContactGeometry.hpp
===================================================================
--- trunk/pkg/dem/DataClass/InteractionGeometry/SpheresContactGeometry.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/DataClass/InteractionGeometry/SpheresContactGeometry.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -39,11 +39,11 @@
 		#endif
 
 
-		SpheresContactGeometry():contactPoint(Vector3r::ZERO),radius1(0.),radius2(0.){createIndex();
+		SpheresContactGeometry():contactPoint(Vector3r::ZERO),radius1(0.),radius2(0.)
 		#ifdef SCG_SHEAR
-			shear=Vector3r::ZERO; prevNormal=Vector3r::ZERO /*initialized to proper value by geom functor*/;
-		#endif	
-		}
+			,shear(Vector3r::ZERO), prevNormal(Vector3r::ZERO) /*initialized to proper value by geom functor*/
+		#endif
+			{ createIndex(); }
 		virtual ~SpheresContactGeometry();
 
 		void updateShearForce(Vector3r& shearForce, Real ks, const Vector3r& prevNormal, const RigidBodyParameters* rbp1, const RigidBodyParameters* rbp2, Real dt, bool avoidGranularRatcheting=true);

Modified: trunk/pkg/dem/Engine/DeusExMachina/TriaxialStressController.cpp
===================================================================
--- trunk/pkg/dem/Engine/DeusExMachina/TriaxialStressController.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/Engine/DeusExMachina/TriaxialStressController.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -77,7 +77,7 @@
 	height0 = 0;
 	width0 = 0;
 	depth0 = 0;
-	thickness = 0;
+	thickness = -1;
 	
 	//UnbalancedForce = 0;
 	
@@ -219,7 +219,7 @@
 	
 	
 
-	if(thickness<=0) thickness=YADE_PTR_CAST<InteractingBox>(Body::byId(wall_bottom_id,ncb)->interactingGeometry)->extents.Y();
+	if(thickness<0) thickness=YADE_PTR_CAST<InteractingBox>(Body::byId(wall_bottom_id,ncb)->interactingGeometry)->extents.Y();
 
 	PhysicalParameters* p_bottom = static_cast<PhysicalParameters*>((*bodies)[wall_bottom_id]->physicalParameters.get());
 	PhysicalParameters* p_top   =	 static_cast<PhysicalParameters*>((*bodies)[wall_top_id]->physicalParameters.get());

Modified: trunk/pkg/dem/Engine/EngineUnit/InteractingSphere2InteractingSphere4SpheresContactGeometry.cpp
===================================================================
--- trunk/pkg/dem/Engine/EngineUnit/InteractingSphere2InteractingSphere4SpheresContactGeometry.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/Engine/EngineUnit/InteractingSphere2InteractingSphere4SpheresContactGeometry.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -31,17 +31,16 @@
 		if(c->interactionGeometry) scm=YADE_PTR_CAST<SpheresContactGeometry>(c->interactionGeometry);
 		else { scm=shared_ptr<SpheresContactGeometry>(new SpheresContactGeometry()); c->interactionGeometry=scm; }
 
-		#ifdef SCG_SHEAR
-			if(isNew) scm->prevNormal=normal; 
-			else scm->prevNormal=scm->normal;
-		#endif
-
 		Real penetrationDepth=s1->radius+s2->radius-normal.Normalize(); /* Normalize() works in-place and returns length before normalization; from here, normal is unit vector */
 		scm->contactPoint=se31.position+(s1->radius-0.5*penetrationDepth)*normal;//0.5*(pt1+pt2);
 		scm->normal=normal;
 		scm->penetrationDepth=penetrationDepth;
 		scm->radius1=s1->radius;
 		scm->radius2=s2->radius;
+		#ifdef SCG_SHEAR
+			if(isNew) scm->prevNormal=normal; 
+			else scm->prevNormal=scm->normal;
+		#endif
 		// keep this for reference on how to compute bending and torsion from relative orientation; parts in SpheresContactGeometry header
 		#if 0
 			scm->initRelOri12=se31.orientation.Conjugate()*se32.orientation;

Modified: trunk/pkg/dem/Engine/StandAloneEngine/ElasticContactLaw.cpp
===================================================================
--- trunk/pkg/dem/Engine/StandAloneEngine/ElasticContactLaw.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/Engine/StandAloneEngine/ElasticContactLaw.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -121,6 +121,7 @@
 	phys->normalForce=phys->kn*displN*geom->normal;
 	Real maxFsSq=phys->normalForce.SquaredLength()*pow(phys->tangensOfFrictionAngle,2);
 	Vector3r trialFs=phys->ks*geom->displacementT();
-	if(trialFs.SquaredLength()>maxFsSq){ geom->slipToDisplacementTMax(sqrt(maxFsSq)); trialFs*=sqrt(maxFsSq/(trialFs.SquaredLength()));} 
+	if(trialFs.SquaredLength()>maxFsSq){ geom->slipToDisplacementTMax(sqrt(maxFsSq)); trialFs*=sqrt(maxFsSq/(trialFs.SquaredLength()));}
+	phys->shearForce=trialFs;
 	applyForceAtContactPoint(phys->normalForce+trialFs,geom->contactPoint,contact->getId1(),geom->se31.position,contact->getId2(),geom->se32.position,rootBody);
 }

Modified: trunk/pkg/dem/PreProcessor/TriaxialTest.cpp
===================================================================
--- trunk/pkg/dem/PreProcessor/TriaxialTest.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/PreProcessor/TriaxialTest.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -32,6 +32,7 @@
 #include<yade/pkg-common/Box.hpp>
 #include<yade/pkg-common/AABB.hpp>
 #include<yade/pkg-common/Sphere.hpp>
+#include<yade/pkg-common/Facet.hpp>
 #include<yade/core/MetaBody.hpp>
 #include<yade/pkg-common/PersistentSAPCollider.hpp>
 #include<yade/pkg-common/InsertionSortCollider.hpp>
@@ -53,6 +54,7 @@
 #include<yade/core/Body.hpp>
 #include<yade/pkg-common/InteractingBox.hpp>
 #include<yade/pkg-common/InteractingSphere.hpp>
+#include<yade/pkg-common/InteractingFacet.hpp>
 
 #include<yade/pkg-common/PhysicalActionContainerReseter.hpp>
 
@@ -162,6 +164,7 @@
 	
 	fast=false;
 	noFiles=false;
+	facetWalls=false;
 
 	
 	
@@ -234,6 +237,7 @@
 	REGISTER_ATTRIBUTE(fixedBoxDims);
 	REGISTER_ATTRIBUTE(fast);
 	REGISTER_ATTRIBUTE(noFiles);
+	REGISTER_ATTRIBUTE(facetWalls);
 }
 
 
@@ -247,6 +251,10 @@
 		message="Biaxial test can be generated only if Z size is more than 8 times smaller than X size";
 		return false;
 	}
+	if(facetWalls && !fast){
+		LOG_WARN("Turning TriaxialTest::fast on, since facetWalls were selected.");
+		fast=true;
+	}
 	
 	rootBody = shared_ptr<MetaBody>(new MetaBody);
 	positionRootBody(rootBody);
@@ -289,6 +297,7 @@
 	}
 
 	if(thickness<0) thickness=radiusMean;
+	if(facetWalls) thickness=0;
 	
 	if(boxWalls)
 	{
@@ -465,47 +474,56 @@
 void TriaxialTest::createBox(shared_ptr<Body>& body, Vector3r position, Vector3r extents, bool wire)
 {
 	body = shared_ptr<Body>(new Body(body_id_t(0),2));
-	shared_ptr<BodyMacroParameters> physics(new BodyMacroParameters);
+	body->isDynamic			= false;
+
+
 	shared_ptr<AABB> aabb(new AABB);
-	shared_ptr<Box> gBox(new Box);
-	shared_ptr<InteractingBox> iBox(new InteractingBox);
+	aabb->diffuseColor		= Vector3r(1,0,0);
+	body->boundingVolume		= aabb;
 	
-	Quaternionr q;
-	q.FromAxisAngle( Vector3r(0,0,1),0);
-
-	body->isDynamic			= false;
-	
-	physics->angularVelocity	= Vector3r(0,0,0);
-	physics->velocity		= Vector3r(0,0,0);
-	physics->mass			= 0; 
+	shared_ptr<BodyMacroParameters> physics(new BodyMacroParameters);
 	//physics->mass			= extents[0]*extents[1]*extents[2]*density*2; 
 	physics->inertia		= Vector3r(
 							  physics->mass*(extents[1]*extents[1]+extents[2]*extents[2])/3
 							, physics->mass*(extents[0]*extents[0]+extents[2]*extents[2])/3
 							, physics->mass*(extents[1]*extents[1]+extents[0]*extents[0])/3
 						);
-//	physics->mass			= 0;
+	physics->mass			= 0;
 //	physics->inertia		= Vector3r(0,0,0);
-	physics->se3			= Se3r(position,q);
+	physics->se3.position=position;
 
 	physics->young			= boxYoungModulus;
 	physics->poisson		= boxPoissonRatio;
 	physics->frictionAngle		= boxFrictionDeg * Mathr::PI/180.0;
+	body->physicalParameters	= physics;
 
-	aabb->diffuseColor		= Vector3r(1,0,0);
 
-	gBox->extents			= extents;
-	gBox->diffuseColor		= Vector3r(1,1,1);
-	gBox->wire			= wire;
-	gBox->shadowCaster		= false;
-	
-	iBox->extents			= extents;
-	iBox->diffuseColor		= Vector3r(1,1,1);
+	if(!facetWalls){
+		shared_ptr<Box> gBox(new Box);
+		gBox->extents			= extents;
+		gBox->diffuseColor		= Vector3r(1,1,1);
+		gBox->wire			= wire;
+		gBox->shadowCaster		= false;
+		body->geometricalModel		= gBox;
 
-	body->boundingVolume		= aabb;
-	body->interactingGeometry	= iBox;
-	body->geometricalModel		= gBox;
-	body->physicalParameters	= physics;
+		shared_ptr<InteractingBox> iBox(new InteractingBox);
+		iBox->extents			= extents;
+		iBox->diffuseColor		= Vector3r(1,1,1);
+		body->interactingGeometry	= iBox;
+	} else {
+		int ax0 = extents[0]==0 ? 0 : (extents[1]==0 ? 1 : 2); int ax1=(ax0+1)%3, ax2=(ax0+2)%3;
+		Vector3r corner=position-extents; // "lower right" corner, with 90 degrees
+		Vector3r side1(Vector3r::ZERO); side1[ax1]=4*extents[ax1]; Vector3r side2(Vector3r::ZERO); side2[ax2]=4*extents[ax2];
+		Vector3r v[3]; v[0]=corner; v[1]=corner+side1; v[2]=corner+side2;
+		Vector3r cog=Shop::inscribedCircleCenter(v[0],v[1],v[2]);
+		shared_ptr<InteractingFacet> iFacet(new InteractingFacet);
+		shared_ptr<Facet> facet(new Facet);
+		for(int i=0; i<3; i++){ iFacet->vertices.push_back(v[i]-cog); facet->vertices.push_back(v[i]-cog);}
+		iFacet->diffuseColor=facet->diffuseColor=Vector3r(1,1,1);
+		facet->wire=true;
+		body->geometricalModel=facet;
+		body->interactingGeometry=iFacet;
+	}
 }
 
 
@@ -513,19 +531,23 @@
 {
 	
 	shared_ptr<InteractionGeometryMetaEngine> interactionGeometryDispatcher(new InteractionGeometryMetaEngine);
-	interactionGeometryDispatcher->add("InteractingSphere2InteractingSphere4SpheresContactGeometry");
-	interactionGeometryDispatcher->add("InteractingBox2InteractingSphere4SpheresContactGeometry");
+	if(!facetWalls){
+		interactionGeometryDispatcher->add("InteractingSphere2InteractingSphere4SpheresContactGeometry");
+		interactionGeometryDispatcher->add("InteractingBox2InteractingSphere4SpheresContactGeometry");
+	} else {
+		interactionGeometryDispatcher->add("ef2_Sphere_Sphere_Dem3DofGeom");
+		interactionGeometryDispatcher->add("ef2_Facet_Sphere_Dem3DofGeom");
+	}
 
+
 	shared_ptr<InteractionPhysicsMetaEngine> interactionPhysicsDispatcher(new InteractionPhysicsMetaEngine);
-//	interactionPhysicsDispatcher->add("SimpleElasticRelationships");
-// Unhandled exception: St13runtime_error : Class `SimpleElasticRelationships' could not be cast to required 2D EngineUnit	
 	shared_ptr<InteractionPhysicsEngineUnit> ss(new SimpleElasticRelationships);
 	interactionPhysicsDispatcher->add(ss);
 	
 		
 	shared_ptr<BoundingVolumeMetaEngine> boundingVolumeDispatcher	= shared_ptr<BoundingVolumeMetaEngine>(new BoundingVolumeMetaEngine);
 	boundingVolumeDispatcher->add("InteractingSphere2AABB");
-	boundingVolumeDispatcher->add("InteractingBox2AABB");
+	boundingVolumeDispatcher->add(facetWalls ? "InteractingFacet2AABB" : "InteractingBox2AABB");
 	boundingVolumeDispatcher->add("MetaInteractingGeometry2AABB");
 		
 	shared_ptr<GravityEngine> gravityCondition(new GravityEngine);
@@ -615,11 +637,15 @@
 			ids->geomDispatcher=interactionGeometryDispatcher;
 			ids->physDispatcher=interactionPhysicsDispatcher;
 			ids->constLawDispatcher=shared_ptr<ConstitutiveLawDispatcher>(new ConstitutiveLawDispatcher);
-			shared_ptr<ef2_Spheres_Elastic_ElasticLaw> see(new ef2_Spheres_Elastic_ElasticLaw);
-				see->sdecGroupMask=2;
-			ids->constLawDispatcher->add(see);
+			if(!facetWalls){
+				shared_ptr<ef2_Spheres_Elastic_ElasticLaw> see(new ef2_Spheres_Elastic_ElasticLaw); see->sdecGroupMask=2;
+				ids->constLawDispatcher->add(see);
+			} else {
+				ids->constLawDispatcher->add(shared_ptr<Law2_Dem3Dof_Elastic_Elastic>(new Law2_Dem3Dof_Elastic_Elastic));
+			}
 		rootBody->engines.push_back(ids);
 	} else {
+		assert(!facetWalls);
 		rootBody->engines.push_back(interactionGeometryDispatcher);
 		rootBody->engines.push_back(interactionPhysicsDispatcher);
 		shared_ptr<ElasticContactLaw> elasticContactLaw(new ElasticContactLaw);
@@ -696,7 +722,7 @@
 	long tries = 1000; //nb of tries for positionning the next sphere
 	Vector3r dimensions = upperCorner - lowerCorner;
 
-	LOG_INFO("Generating aggregates ...");
+	LOG_INFO("Generating "<<number<<" aggregates ...");
 	
 	long t, i;
 	for (i=0; i<number; ++i) {

Modified: trunk/pkg/dem/PreProcessor/TriaxialTest.hpp
===================================================================
--- trunk/pkg/dem/PreProcessor/TriaxialTest.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/PreProcessor/TriaxialTest.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -107,6 +107,8 @@
 				,isotropicCompaction
 				//! do not create any files during run (.xml, .spheres, wall stress records)
 				,noFiles
+				// use Facets instead of Boxes for the walls
+				,facetWalls
 				;
 
 				//! Generate faster simulation: use InsertionSortCollider and InteractionDispatchers

Modified: trunk/pkg/dem/SConscript
===================================================================
--- trunk/pkg/dem/SConscript	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/pkg/dem/SConscript	2009-07-07 11:54:14 UTC (rev 1841)
@@ -652,6 +652,7 @@
 			'ElasticCriterionTimeStepper',
 			'InteractingSphere',
 			'InteractingBox',
+			'InteractingFacet',
 			'NewtonsDampedLaw',
 			'MetaInteractingGeometry',
 			'GravityEngines',
@@ -665,6 +666,7 @@
 			'yade-multimethods',
 			'Box',
 			'Sphere',
+			'Facet',
 			'AABB',
 			'PersistentSAPCollider',
 			'InsertionSortCollider',

Copied: trunk/py/_eudoxos.cpp (from rev 1834, trunk/lib/py/_eudoxos.cpp)

Copied: trunk/py/_packPredicates.cpp (from rev 1834, trunk/lib/py/_packPredicates.cpp)

Copied: trunk/py/_packSpheres.cpp (from rev 1834, trunk/lib/py/_packSpheres.cpp)
===================================================================
--- trunk/lib/py/_packSpheres.cpp	2009-07-02 15:26:02 UTC (rev 1834)
+++ trunk/py/_packSpheres.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,143 @@
+// 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<yade/lib-base/yadeWm3Extra.hpp>
+// #include<yade/gui-py/_utils.hpp> // will be: yade/lib-py/_utils.hpp> at some point
+#include<Wm3Vector3.h>
+
+#include<yade/core/Omega.hpp>
+#include<yade/core/MetaBody.hpp>
+#include<yade/pkg-common/InteractingSphere.hpp>
+
+#include<yade/extra/Shop.hpp>
+
+using namespace boost;
+using namespace std;
+#ifdef LOG4CXX
+	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.pack.spheres");
+#endif
+
+// copied from _packPredicates.cpp :-(
+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])());}
+Vector3r tuple2vec(python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
+
+struct SpherePack{
+	struct Sph{ Vector3r c; Real r; Sph(const Vector3r& _c, Real _r): c(_c), r(_r){};
+		python::tuple asTuple() const { return python::make_tuple(c,r); }
+	};
+	vector<Sph> pack;
+	SpherePack(){};
+	SpherePack(const python::list& l){ fromList(l); }
+	// add single sphere
+	void add(const Vector3r& c, Real r){ pack.push_back(Sph(c,r)); }
+	// I/O
+	void fromList(const python::list& l);
+	python::list toList() const;
+	void fromFile(const string file);
+	void toFile(const string file) const;
+	void fromSimulation();
+	// spatial characteristics
+	Vector3r dim() const {Vector3r mn,mx; aabb(mn,mx); return mx-mn;}
+	python::tuple aabb_py() const { Vector3r mn,mx; aabb(mn,mx); return python::make_tuple(mn,mx); }
+	void aabb(Vector3r& mn, Vector3r& mx) const {
+		Real inf=std::numeric_limits<Real>::infinity(); mn=Vector3r(inf,inf,inf); mx=Vector3r(-inf,-inf,-inf);
+		FOREACH(const Sph& s, pack){ Vector3r r(s.r,s.r,s.r); mn=componentMinVector(mn,s.c-r); mx=componentMaxVector(mx,s.c+r);}
+	}
+	Vector3r midPt() const {Vector3r mn,mx; aabb(mn,mx); return .5*(mn+mx);}
+	Real relDensity() const {
+		Real sphVol=0; Vector3r dd=dim();
+		FOREACH(const Sph& s, pack) sphVol+=pow(s.r,3);
+		sphVol*=(4/3.)*Mathr::PI;
+		return sphVol/(dd[0]*dd[1]*dd[2]);
+	}
+	// transformations
+	void translate(const Vector3r& shift){ FOREACH(Sph& s, pack) s.c+=shift; }
+	void rotate(const Vector3r& axis, Real angle){ Vector3r mid=midPt(); Quaternionr q(axis,angle); FOREACH(Sph& s, pack) s.c=q*(s.c-mid)+mid; }
+	void scale(Real scale){ Vector3r mid=midPt(); FOREACH(Sph& s, pack) {s.c=scale*(s.c-mid)+mid; s.r*=abs(scale); } }
+	// iteration 
+	size_t len() const{ return pack.size(); }
+	python::tuple getitem(size_t idx){ if(idx<0 || idx>=pack.size()) throw runtime_error("Index "+lexical_cast<string>(idx)+" out of range 0.."+lexical_cast<string>(pack.size()-1)); return pack[idx].asTuple(); }
+	struct iterator{
+		const SpherePack& sPack; size_t pos;
+		iterator(const SpherePack& _sPack): sPack(_sPack), pos(0){}
+		iterator iter(){ return *this;}
+		python::tuple next(){
+			if(pos==sPack.pack.size()-1){ PyErr_SetNone(PyExc_StopIteration); python::throw_error_already_set(); }
+			return sPack.pack[pos++].asTuple();
+		}
+	};
+	SpherePack::iterator getIterator() const{ return SpherePack::iterator(*this);};
+};
+
+void SpherePack::fromList(const python::list& l){
+	pack.clear();
+	size_t len=python::len(l);
+	for(size_t i=0; i<len; i++){
+		const python::tuple& t=python::extract<python::tuple>(l[i]);
+		const python::tuple& t1=python::extract<python::tuple>(t[0]);
+		pack.push_back(Sph(tuple2vec(t1),python::extract<double>(t[1])));
+	}
+};
+
+python::list SpherePack::toList() const {
+	python::list ret;
+	FOREACH(const Sph& s, pack) ret.append(python::make_tuple(vec2tuple(s.c),s.r));
+	return ret;
+};
+
+void SpherePack::fromFile(string file) {
+	typedef pair<Vector3r,Real> pairVector3rReal;
+	vector<pairVector3rReal> ss;
+	Vector3r mn,mx;
+	ss=Shop::loadSpheresFromFile(file,mn,mx);
+	pack.clear();
+	FOREACH(const pairVector3rReal& s, ss) pack.push_back(Sph(s.first,s.second));
+}
+
+void SpherePack::toFile(const string fname) const {
+	ofstream f(fname.c_str());
+	if(!f.good()) throw runtime_error("Unable to open file `"+fname+"'");
+	FOREACH(const Sph& s, pack) f<<s.c[0]<<" "<<s.c[1]<<" "<<s.c[2]<<" "<<s.r<<endl;
+	f.close();
+};
+
+void SpherePack::fromSimulation() {
+	pack.clear();
+	MetaBody* rootBody=Omega::instance().getRootBody().get();
+	FOREACH(const shared_ptr<Body>& b, *rootBody->bodies){
+		shared_ptr<InteractingSphere>	intSph=dynamic_pointer_cast<InteractingSphere>(b->interactingGeometry);
+		if(!intSph) continue;
+		pack.push_back(Sph(b->physicalParameters->se3.position,intSph->radius));
+	}
+}
+
+
+
+BOOST_PYTHON_MODULE(_packSpheres){
+	python::class_<SpherePack>("SpherePack","Set of spheres as centers and radii",python::init<python::optional<python::list> >(python::args("list"),"Empty constructor, optionally taking list [ ((cx,cy,cz),r), … ] for initial data." ))
+		.def("add",&SpherePack::add,"Add single sphere to packing, given center as 3-tuple and radius")
+		.def("toList",&SpherePack::toList,"Return packing data as python list.")
+		.def("fromList",&SpherePack::fromList,"Make packing from given list, same format as for constructor. Discards current data.")
+		.def("load",&SpherePack::fromFile,"Load packing from external text file (current data will be discarded).")
+		.def("save",&SpherePack::toFile,"Save packing to external text file (will be overwritten).")
+		.def("fromSimulation",&SpherePack::fromSimulation,"Make packing corresponding to the current simulation. Discards current data.")
+		.def("aabb",&SpherePack::aabb_py,"Get axis-aligned bounding box coordinates, as 2 3-tuples.")
+		.def("dim",&SpherePack::dim,"Return dimensions of the packing in terms of aabb(), as a 3-tuple.")
+		.def("center",&SpherePack::midPt,"Return coordinates of the bounding box center.")
+		.def("relDensity",&SpherePack::relDensity,"Relative packing density, measured as sum of spheres' volumes / aabb volume.\n(Sphere overlaps are ignored.)")
+		.def("translate",&SpherePack::translate,"Translate all spheres by given vector.")
+		.def("rotate",&SpherePack::rotate,"Rotate all spheres around packing center (in terms of aabb()), given axis and angle of the rotation.")
+		.def("scale",&SpherePack::scale,"Scale the packing around its center (in terms of aabb()) by given factor (may be negative).")
+		.def("__len__",&SpherePack::len,"Get number of spheres in the packing")
+		.def("__getitem__",&SpherePack::getitem,"Get entry at given index, as tuple of center and radius.")
+		.def("__iter__",&SpherePack::getIterator,"Return iterator over spheres.")
+		;
+	python::class_<SpherePack::iterator>("SpherePackIterator",python::init<SpherePack::iterator&>())
+		.def("__iter__",&SpherePack::iterator::iter)
+		.def("next",&SpherePack::iterator::next)
+	;
+}
+


Property changes on: trunk/py/_packSpheres.cpp
___________________________________________________________________
Name: svn:mergeinfo
   + 

Copied: trunk/py/_utils.cpp (from rev 1834, trunk/lib/py/_utils.cpp)

Copied: trunk/py/euclid.py (from rev 1834, trunk/lib/py/euclid.py)


Property changes on: trunk/py/euclid.py
___________________________________________________________________
Name: svn:mergeinfo
   + 

Copied: trunk/py/eudoxos.py (from rev 1834, trunk/lib/py/eudoxos.py)

Copied: trunk/py/linterpolation.py (from rev 1834, trunk/lib/py/linterpolation.py)

Copied: trunk/py/log.cpp (from rev 1834, trunk/lib/py/log.cpp)

Added: trunk/py/miniWm3Wrap/README
===================================================================
--- trunk/py/miniWm3Wrap/README	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/README	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,13 @@
+This thin wrapper created using py++ wraps Vector2, Vector3 and Quaternion classes of Wm3
+(with real numbers, i.e. Vector2r, Vector3r, Quaternionr).
+
+* Matrix2 and Matrix3 are not wrapped (although they could be) as they are not used much in yade
+  (use numpy.Matrix if you need them in pure python code).
+* X(), Y(), Z(), W() methods are not supported, use the [] indexing to acces/update elements.
+* Methods taking or returning unwrapped types (like Quaternion().ToRotationMatrix()) are not
+  wrapped either.
+* This wrapper only works for typeof(Real)==typeof(double)
+
+The following files are generated by py++, but included so that you don't have to install py++:
+__call_policies.pypp.hpp, __convenience.pypp.hpp, miniWm3Wrap.cpp
+

Added: trunk/py/miniWm3Wrap/__call_policies.pypp.hpp
===================================================================
--- trunk/py/miniWm3Wrap/__call_policies.pypp.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/__call_policies.pypp.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,173 @@
+// This file has been generated by Py++.
+
+// Copyright 2004-2008 Roman Yakovenko.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef call_policies_pyplusplus_hpp__
+#define call_policies_pyplusplus_hpp__
+
+#include "boost/python.hpp"
+#include "boost/cstdint.hpp"
+#include "boost/mpl/int.hpp"
+#include "boost/function.hpp"
+#include "boost/utility/addressof.hpp"
+#include "boost/type_traits/is_same.hpp"
+#include "boost/python/object/class_detail.hpp"
+
+namespace pyplusplus{ namespace call_policies{
+
+namespace bpl = boost::python;
+
+namespace memory_managers{
+
+    struct none{
+        template< typename T>
+        static void deallocate_array(T*){}
+    };
+
+    struct delete_{
+        template< typename T>
+        static void deallocate_array(T* arr){
+            delete[] arr;
+        }
+    };
+
+}/*memory_managers*/
+
+
+namespace detail{
+
+struct make_value_holder{
+
+    template <class T>
+    static PyObject* execute(T* p){
+        if (p == 0){
+            return bpl::detail::none();
+        }
+        else{
+            bpl::object p_value( *p );
+            return bpl::incref( p_value.ptr() );
+        }
+    }
+};
+
+template <class R>
+struct return_pointee_value_requires_a_pointer_return_type
+# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__)
+{}
+# endif
+;
+
+struct make_addressof_holder{
+
+    template <class T>
+    static PyObject* execute(T* p){
+        if (p == 0){
+            return bpl::detail::none();
+        }
+        else{
+            boost::uint32_t addressof_p = reinterpret_cast< boost::uint32_t >( p );
+            bpl::object p_address( addressof_p );
+            return bpl::incref( p_address.ptr() );
+        }
+    }
+
+};
+
+template <class R>
+struct return_addressof_value_requires_a_pointer_return_type
+# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__)
+{}
+# endif
+;
+
+
+} //detail
+
+struct return_pointee_value{
+
+    template <class T>
+    struct apply{
+
+        BOOST_STATIC_CONSTANT( bool, ok = boost::is_pointer<T>::value );
+
+        typedef typename boost::mpl::if_c<
+            ok
+            , bpl::to_python_indirect<T, detail::make_value_holder>
+            , detail::return_pointee_value_requires_a_pointer_return_type<T>
+        >::type type;
+
+    };
+
+};
+
+struct return_addressof{
+
+    template <class T>
+    struct apply{
+
+        BOOST_STATIC_CONSTANT( bool, ok = boost::is_pointer<T>::value );
+
+        typedef typename boost::mpl::if_c<
+            ok
+            , bpl::to_python_indirect<T, detail::make_addressof_holder>
+            , detail::return_addressof_value_requires_a_pointer_return_type<T>
+        >::type type;
+
+    };
+
+};
+
+template< typename CallPolicies, class T >
+bpl::object make_object( T x ){
+    //constructs object using CallPolicies result_converter
+    typedef BOOST_DEDUCED_TYPENAME CallPolicies::result_converter:: template apply< T >::type result_converter_t;
+    result_converter_t rc;
+    return bpl::object( bpl::handle<>( rc( x ) ) );
+}
+
+namespace arrays{
+
+namespace details{
+
+template< unsigned int size, typename MemoryManager, typename CallPolicies>
+struct as_tuple_impl{
+
+    template <class U>
+    inline PyObject* operator()(U const* arr) const{
+        if( !arr ){
+            return bpl::incref( bpl::tuple().ptr() );
+        }
+        bpl::list list_;
+        for( unsigned int i = 0; i < size; ++i ){
+            list_.append( make_object< CallPolicies>( arr[i] ) );
+        }
+        MemoryManager::deallocate_array( arr );
+        return bpl::incref( bpl::tuple( list_ ).ptr() );
+    }
+};
+
+}
+
+template< unsigned int size, typename MemoryManager, typename MakeObjectCallPolicies=bpl::default_call_policies>
+struct as_tuple{
+public:
+
+    template <class T>
+    struct apply{
+        BOOST_STATIC_CONSTANT( bool, ok = boost::is_pointer<T>::value );
+        typedef details::as_tuple_impl<size, MemoryManager, MakeObjectCallPolicies> type;
+    };
+
+};
+
+} /*arrays*/
+
+} /*pyplusplus*/ } /*call_policies*/
+
+
+#endif//call_policies_pyplusplus_hpp__
+
+

Added: trunk/py/miniWm3Wrap/__convenience.pypp.hpp
===================================================================
--- trunk/py/miniWm3Wrap/__convenience.pypp.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/__convenience.pypp.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,185 @@
+// This file has been generated by Py++.
+
+// Copyright 2004-2008 Roman Yakovenko.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef __convenience_pyplusplus_hpp__
+#define __convenience_pyplusplus_hpp__
+
+#include "boost/python.hpp"
+
+namespace pyplusplus{ namespace convenience{
+
+//TODO: Replace index_type with Boost.Python defined ssize_t type.
+//      This should be done by checking Python and Boost.Python version.
+typedef int index_type;
+
+inline void
+raise_error( PyObject *exception, const char *message ){
+   PyErr_SetString(exception, message);
+   boost::python::throw_error_already_set();
+}
+
+inline index_type sequence_len(boost::python::object const& obj){
+    if( !PySequence_Check( obj.ptr() ) ){
+        raise_error( PyExc_TypeError, "Sequence expected" );
+    }
+
+    index_type result = PyObject_Length( obj.ptr() );
+    if( PyErr_Occurred() ){
+        boost::python::throw_error_already_set();
+    }
+    return result;
+}
+
+inline void
+ensure_sequence( boost::python::object seq, index_type expected_length=-1 ){
+    index_type length = sequence_len( seq );
+    if( expected_length != -1 && length != expected_length ){
+        std::stringstream err;
+        err << "Expected sequence length is " << expected_length << ". "
+            << "Actual sequence length is " << length << ".";
+        raise_error( PyExc_ValueError, err.str().c_str() );
+    }
+}
+
+template< class ExpectedType >
+void ensure_uniform_sequence( boost::python::object seq, index_type expected_length=-1 ){
+    ensure_sequence( seq, expected_length );
+
+    index_type length = sequence_len( seq );
+    for( index_type index = 0; index < length; ++index ){
+        boost::python::object item = seq[index];
+
+        boost::python::extract<ExpectedType> type_checker( item );
+        if( !type_checker.check() ){
+            std::string expected_type_name( boost::python::type_id<ExpectedType>().name() );
+
+            std::string item_type_name("different");
+            PyObject* item_impl = item.ptr();
+            if( item_impl && item_impl->ob_type && item_impl->ob_type->tp_name ){
+                item_type_name = std::string( item_impl->ob_type->tp_name );
+            }
+
+            std::stringstream err;
+            err << "Sequence should contain only items with type \"" << expected_type_name << "\". "
+                << "Item at position " << index << " has \"" << item_type_name << "\" type.";
+            raise_error( PyExc_ValueError, err.str().c_str() );
+        }
+    }
+}
+
+template< class Iterator, class Inserter >
+void copy_container( Iterator begin, Iterator end, Inserter inserter ){
+    for( Iterator index = begin; index != end; ++index )
+        inserter( *index );
+}
+
+template< class Inserter >
+void copy_sequence( boost::python::object const& seq, Inserter inserter ){
+    index_type length = sequence_len( seq );
+    for( index_type index = 0; index < length; ++index ){
+        inserter = seq[index];
+    }
+}
+
+template< class Inserter, class TItemType >
+void copy_sequence( boost::python::object const& seq, Inserter inserter, boost::type< TItemType > ){
+    index_type length = sequence_len( seq );
+    for( index_type index = 0; index < length; ++index ){
+        boost::python::object item = seq[index];
+        inserter = boost::python::extract< TItemType >( item );
+    }
+}
+
+struct list_inserter{
+    list_inserter( boost::python::list& py_list )
+    : m_py_list( py_list )
+    {}
+    
+    template< class T >
+    void operator()( T const & value ){
+        m_py_list.append( value );
+    }
+private:
+    boost::python::list& m_py_list;
+};
+
+template < class T >
+struct array_inserter_t{
+    array_inserter_t( T* array, index_type size )
+    : m_array( array )
+      , m_curr_pos( 0 )
+      , m_size( size )
+    {}
+
+    void insert( const T& item ){
+        if( m_size <= m_curr_pos ){
+            std::stringstream err;
+            err << "Index out of range. Array size is" << m_size << ", "
+                << "current position is" << m_curr_pos << ".";
+            raise_error( PyExc_ValueError, err.str().c_str() );
+        }
+        m_array[ m_curr_pos ] = item;
+        m_curr_pos += 1;
+    }
+
+    array_inserter_t<T>& 
+    operator=( boost::python::object const & item ){
+        insert( boost::python::extract< T >( item ) );
+        return *this;
+    }
+    
+private:
+    T* m_array;
+    index_type m_curr_pos;
+    const index_type m_size;
+};
+
+template< class T>
+array_inserter_t<T> array_inserter( T* array, index_type size ){
+    return array_inserter_t<T>( array, size );
+}
+
+inline boost::python::object 
+get_out_argument( boost::python::object result, const char* arg_name ){
+    if( !PySequence_Check( result.ptr() ) ){
+        return result;
+    }    
+    boost::python::object cls = boost::python::getattr( result, "__class__" );
+    boost::python::object cls_name = boost::python::getattr( cls, "__name__" );
+    std::string name = boost::python::extract< std::string >( cls_name );
+    if( "named_tuple" == name ){
+        return boost::python::getattr( result, arg_name );    
+    }
+    else{
+        return result;
+    }
+    
+}
+
+inline boost::python::object 
+get_out_argument( boost::python::object result, index_type index ){
+    if( !PySequence_Check( result.ptr() ) ){
+        return result;
+    }    
+    boost::python::object cls = boost::python::getattr( result, "__class__" );
+    boost::python::object cls_name = boost::python::getattr( cls, "__name__" );
+    std::string name = boost::python::extract< std::string >( cls_name );
+    if( "named_tuple" == name ){
+        return result[ index ];    
+    }
+    else{
+        return result;
+    }
+}
+
+} /*pyplusplus*/ } /*convenience*/
+
+namespace pyplus_conv = pyplusplus::convenience;
+
+#endif//__convenience_pyplusplus_hpp__
+
+

Added: trunk/py/miniWm3Wrap/miniWm3Wrap-funcs.ipp
===================================================================
--- trunk/py/miniWm3Wrap/miniWm3Wrap-funcs.ipp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/miniWm3Wrap-funcs.ipp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,36 @@
+#include<boost/lexical_cast.hpp>
+#include<string>
+#include<stdexcept>
+
+#define IDX_CHECK(i,MAX){ if(i<0 || i>=MAX) { PyErr_SetString(PyExc_IndexError, "Index out of range 0.." #MAX); boost::python::throw_error_already_set(); } }
+void Vector2_set_item(Vector2r & self, int idx, Real value){ IDX_CHECK(idx,2); self[idx]=value; }
+void Vector3_set_item(Vector3r & self, int idx, Real value){ IDX_CHECK(idx,3); self[idx]=value; }
+void Quaternion_set_item(Quaternionr & self, int idx, Real value){ IDX_CHECK(idx,4); self[idx]=value; }
+Real Vector2_get_item(const Vector2r & self, int idx){ IDX_CHECK(idx,2); return self[idx]; }
+Real Vector3_get_item(const Vector3r & self, int idx){ IDX_CHECK(idx,3); return self[idx]; }
+Real Quaternion_get_item(const Quaternionr & self, int idx){ IDX_CHECK(idx,4); return self[idx]; }
+std::string Vector2_str(const Vector2r & self){ return std::string("Vector2(")+boost::lexical_cast<std::string>(self[0])+","+boost::lexical_cast<std::string>(self[1])+")";}
+std::string Vector3_str(const Vector3r & self){ return std::string("Vector3(")+boost::lexical_cast<std::string>(self[0])+","+boost::lexical_cast<std::string>(self[1])+","+boost::lexical_cast<std::string>(self[2])+")";}
+std::string Quaternion_str(const Quaternionr & self){ Vector3r axis; Real angle; self.ToAxisAngle(axis,angle); return std::string("Quaternion((")+boost::lexical_cast<std::string>(axis[0])+","+boost::lexical_cast<std::string>(axis[1])+","+boost::lexical_cast<std::string>(axis[2])+"),"+boost::lexical_cast<std::string>(angle)+")";}
+int Vector2_len(){return 2;}
+int Vector3_len(){return 3;}
+int Quaternion_len(){return 4;}
+#undef IDX_CHECK
+
+// automagic converter from tuple to Vector3r
+struct custom_Vector3r_from_tuple{
+	custom_Vector3r_from_tuple(){
+		boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<Vector3r>());
+	}
+	static void* convertible(PyObject* obj_ptr){
+		if(!PySequence_Check(obj_ptr) || PySequence_Size(obj_ptr)!=3) return 0;
+		return obj_ptr;
+	}
+	static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data){
+		void* storage=((boost::python::converter::rvalue_from_python_storage<Vector3r>*)(data))->storage.bytes;
+		new (storage) Vector3r(boost::python::extract<double>(PySequence_GetItem(obj_ptr,0)),boost::python::extract<double>(PySequence_GetItem(obj_ptr,1)),boost::python::extract<double>(PySequence_GetItem(obj_ptr,2)));
+		data->convertible=storage;
+	}
+};
+
+

Added: trunk/py/miniWm3Wrap/miniWm3Wrap-generate.py
===================================================================
--- trunk/py/miniWm3Wrap/miniWm3Wrap-generate.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/miniWm3Wrap-generate.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,66 @@
+import os, os.path
+from pyplusplus import module_builder
+from pyplusplus.decl_wrappers import *
+from pyplusplus.module_builder import call_policies
+from pyplusplus import function_transformers as FT
+
+
+
+
+#Creating an instance of class that will help you to expose your declarations
+mb = module_builder.module_builder_t( [os.path.abspath("miniWm3Wrap-toExpose.hpp")]
+                                      , working_directory=r"."
+                                      , include_paths=['../../lib','../../lib/miniWm3','/usr/include']
+                                      , define_symbols=['USE_MINIWM3'] )
+# exclude exerything first
+mb.decls().exclude()
+# include what is in Wm3
+mb.decls(lambda d: ('Wm3' in d.location.file_name) if d.location else False).include()
+
+
+## things we want to exclude:
+# we don't need system things from wm3
+mb.decls(lambda decl: 'System' in decl.name).exclude() 
+# do not expose instances we created just to instantiate templates
+mb.decls(lambda decl: decl.name.startswith('noexpose')).exclude() 
+# exclude casting operators
+mb.casting_operators().exclude()
+# exclude functions&operators returning double& (not representable)
+mb.member_functions(return_type='double &').exclude()
+mb.global_ns.operators(return_type='double &').exclude() # global_ns is a bug workaround: http://www.mail-archive.com/cplusplus-sig@xxxxxxxxxx/msg00730.html
+# exclude everything (member functions, operators, ...) taking or returning types we do not wrap
+mb.decls(lambda d: 'Matrix3' in str(d) or 'Matrix2' in str(d) or 'Vector4' in str(d)).exclude()
+# exclude operator[] since we implement __getitem__/__setitem__ by ourselves
+mb.member_operators(lambda o: o.symbol=='[]').exclude()
+
+# register manual wraps
+v2,v3,q4=mb.class_(lambda d: d.name=='Vector2<double>'),mb.class_(lambda d: d.name=='Vector3<double>'),mb.class_(lambda d: d.name=='Quaternion<double>')
+v2.add_registration_code('def("__len__",&::Vector2_len)   .staticmethod("__len__").def("__setitem__",&::Vector2_set_item)   .def("__getitem__",&::Vector2_get_item)   .def("__str__",&::Vector2_str)   .def("__repr__",&::Vector2_str)')
+v3.add_registration_code('def("__len__",&::Vector3_len)   .staticmethod("__len__").def("__setitem__",&::Vector3_set_item)   .def("__getitem__",&::Vector3_get_item)   .def("__str__",&::Vector3_str)   .def("__repr__",&::Vector3_str)')
+q4.add_registration_code('def("__len__",&::Quaternion_len).staticmethod("__len__").def("__setitem__",&::Quaternion_set_item).def("__getitem__",&::Quaternion_get_item).def("__str__",&::Quaternion_str).def("__repr__",&::Quaternion_str)')
+
+# arg0 and arg1 will be returned as tuple, instead of the references ToAxisAngle takes
+mb.member_functions('ToAxisAngle').add_transformation(FT.output(0),FT.output(1))
+# quaternion operations returning itself
+mb.member_functions(lambda mf: 'Quaternion<double> &' in str(mf.return_type)).call_policies=call_policies.return_self()
+
+mb.add_registration_code("custom_Vector3r_from_tuple();")
+
+if 0:
+	#mb.member_functions(return_type='Wm3::Matrix3<double> &').call_policies=call_policies.return_self
+	mb.member_functions('ToAngle').add_transformation(FT.output(0)) # Matrix2
+	mb.member_functions(lambda mf: 'Matrix3<double> &' in str(mf.return_type)).call_policies=call_policies.return_self()
+	mb.decls(lambda decl: decl.name.startswith('ToEulerAngles')).add_transformation(FT.output(0),FT.output(1),FT.output(2))
+
+mb.build_code_creator(module_name='miniWm3Wrap')
+mb.code_creator.add_include("miniWm3Wrap-funcs.ipp")
+mb.write_module('miniWm3Wrap.cpp')
+
+# cleaup unneeded generated files
+toClean='exposed_decl.pypp.txt','miniWm3Wrap.cpp~','named_tuple.py'
+for f in toClean:
+	if os.path.exists(f):
+		try:
+			os.remove(f)
+		except IOError: pass
+

Added: trunk/py/miniWm3Wrap/miniWm3Wrap-toExpose.hpp
===================================================================
--- trunk/py/miniWm3Wrap/miniWm3Wrap-toExpose.hpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/miniWm3Wrap-toExpose.hpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,22 @@
+#include<base/yadeWm3.hpp>
+#include<base/yadeWm3Extra.hpp>
+#include<boost/lexical_cast.hpp>
+#include<string>
+
+// define python names for these classes
+// see http://language-binding.net/pyplusplus/documentation/how_to/hints.html#pyplusplus-aliases-namespace
+namespace pyplusplus{ namespace aliases {
+	typedef Vector3r Vector3;
+	typedef Vector2r Vector2;
+	typedef Quaternionr Quaternion;
+	typedef Matrix3r Matrix3;
+	typedef Matrix2r Matrix2;
+}}
+// instantiate those types so that they are exposed
+// but name them noexpose_*, as we ask for exclusion of such things in the wrapper script
+Vector3r noexpose_v3r;
+Vector2r noexpose_v2r;
+Quaternionr noexpose_qr;
+//Matrix3r noexpose_m3r;
+//Matrix2r noexpose_m2r;
+

Added: trunk/py/miniWm3Wrap/miniWm3Wrap.cpp
===================================================================
--- trunk/py/miniWm3Wrap/miniWm3Wrap.cpp	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/py/miniWm3Wrap/miniWm3Wrap.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,848 @@
+// This file has been generated by Py++.
+
+#include "boost/python.hpp"
+
+#include "__convenience.pypp.hpp"
+
+#include "__call_policies.pypp.hpp"
+
+#include "/home/vaclav/yade/trunk/py/miniWm3Wrap/miniWm3Wrap-toExpose.hpp"
+
+#include "miniWm3Wrap-funcs.ipp"
+
+namespace bp = boost::python;
+
+static boost::python::tuple ToAxisAngle_2c4febac34e606b4a98de72d9f8161c9( ::Wm3::Quaternion< double > const & inst ){
+    Wm3::Vector3<double> rkAxis2;
+    double rfAngle2;
+    inst.ToAxisAngle(rkAxis2, rfAngle2);
+    return bp::make_tuple( rkAxis2, rfAngle2 );
+}
+
+BOOST_PYTHON_MODULE(miniWm3Wrap){
+    { //::Wm3::Quaternion< double >
+        typedef bp::class_< Wm3::Quaternion< double > > Quaternion_exposer_t;
+        Quaternion_exposer_t Quaternion_exposer = Quaternion_exposer_t( "Quaternion", bp::init< >() );
+        bp::scope Quaternion_scope( Quaternion_exposer );
+        Quaternion_exposer.def( bp::init< double, double, double, double >(( bp::arg("fW"), bp::arg("fX"), bp::arg("fY"), bp::arg("fZ") )) );
+        Quaternion_exposer.def( bp::init< Wm3::Quaternion< double > const & >(( bp::arg("rkQ") )) );
+        Quaternion_exposer.def( bp::init< Wm3::Vector3< double > const &, double >(( bp::arg("rkAxis"), bp::arg("fAngle") )) );
+        Quaternion_exposer.def( bp::init< Wm3::Vector3< double > const * >(( bp::arg("akRotColumn") )) );
+        bp::implicitly_convertible< Wm3::Vector3< double > const *, Wm3::Quaternion< double > >();
+        { //::Wm3::Quaternion< double >::Align
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*Align_function_type )( ::Wm3::Vector3< double > const &,::Wm3::Vector3< double > const & ) ;
+            
+            Quaternion_exposer.def( 
+                "Align"
+                , Align_function_type( &::Wm3::Quaternion< double >::Align )
+                , ( bp::arg("rkV1"), bp::arg("rkV2") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::Conjugate
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > ( exported_class_t::*Conjugate_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Conjugate"
+                , Conjugate_function_type( &::Wm3::Quaternion< double >::Conjugate ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::DecomposeSwingTimesTwist
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef void ( exported_class_t::*DecomposeSwingTimesTwist_function_type )( ::Wm3::Vector3< double > const &,::Wm3::Quaternion< double > &,::Wm3::Quaternion< double > & ) ;
+            
+            Quaternion_exposer.def( 
+                "DecomposeSwingTimesTwist"
+                , DecomposeSwingTimesTwist_function_type( &::Wm3::Quaternion< double >::DecomposeSwingTimesTwist )
+                , ( bp::arg("rkV1"), bp::arg("rkSwing"), bp::arg("rkTwist") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::DecomposeTwistTimesSwing
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef void ( exported_class_t::*DecomposeTwistTimesSwing_function_type )( ::Wm3::Vector3< double > const &,::Wm3::Quaternion< double > &,::Wm3::Quaternion< double > & ) ;
+            
+            Quaternion_exposer.def( 
+                "DecomposeTwistTimesSwing"
+                , DecomposeTwistTimesSwing_function_type( &::Wm3::Quaternion< double >::DecomposeTwistTimesSwing )
+                , ( bp::arg("rkV1"), bp::arg("rkTwist"), bp::arg("rkSwing") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Dot
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*Dot_function_type )( ::Wm3::Quaternion< double > const & ) const;
+            
+            Quaternion_exposer.def( 
+                "Dot"
+                , Dot_function_type( &::Wm3::Quaternion< double >::Dot )
+                , ( bp::arg("rkQ") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Exp
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > ( exported_class_t::*Exp_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Exp"
+                , Exp_function_type( &::Wm3::Quaternion< double >::Exp ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::FromAxisAngle
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*FromAxisAngle_function_type )( ::Wm3::Vector3< double > const &,double ) ;
+            
+            Quaternion_exposer.def( 
+                "FromAxisAngle"
+                , FromAxisAngle_function_type( &::Wm3::Quaternion< double >::FromAxisAngle )
+                , ( bp::arg("rkAxis"), bp::arg("fAngle") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::FromRotationMatrix
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*FromRotationMatrix_function_type )( ::Wm3::Vector3< double > const * ) ;
+            
+            Quaternion_exposer.def( 
+                "FromRotationMatrix"
+                , FromRotationMatrix_function_type( &::Wm3::Quaternion< double >::FromRotationMatrix )
+                , ( bp::arg("akRotColumn") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::Intermediate
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*Intermediate_function_type )( ::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const & ) ;
+            
+            Quaternion_exposer.def( 
+                "Intermediate"
+                , Intermediate_function_type( &::Wm3::Quaternion< double >::Intermediate )
+                , ( bp::arg("rkQ0"), bp::arg("rkQ1"), bp::arg("rkQ2") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::Inverse
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > ( exported_class_t::*Inverse_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Inverse"
+                , Inverse_function_type( &::Wm3::Quaternion< double >::Inverse ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Length
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*Length_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Length"
+                , Length_function_type( &::Wm3::Quaternion< double >::Length ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Log
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > ( exported_class_t::*Log_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Log"
+                , Log_function_type( &::Wm3::Quaternion< double >::Log ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Normalize
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*Normalize_function_type )(  ) ;
+            
+            Quaternion_exposer.def( 
+                "Normalize"
+                , Normalize_function_type( &::Wm3::Quaternion< double >::Normalize ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Rotate
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Vector3< double > ( exported_class_t::*Rotate_function_type )( ::Wm3::Vector3< double > const & ) const;
+            
+            Quaternion_exposer.def( 
+                "Rotate"
+                , Rotate_function_type( &::Wm3::Quaternion< double >::Rotate )
+                , ( bp::arg("rkVector") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Slerp
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*Slerp_function_type )( double,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const & ) ;
+            
+            Quaternion_exposer.def( 
+                "Slerp"
+                , Slerp_function_type( &::Wm3::Quaternion< double >::Slerp )
+                , ( bp::arg("fT"), bp::arg("rkP"), bp::arg("rkQ") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::SlerpExtraSpins
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*SlerpExtraSpins_function_type )( double,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const &,int ) ;
+            
+            Quaternion_exposer.def( 
+                "SlerpExtraSpins"
+                , SlerpExtraSpins_function_type( &::Wm3::Quaternion< double >::SlerpExtraSpins )
+                , ( bp::arg("fT"), bp::arg("rkP"), bp::arg("rkQ"), bp::arg("iExtraSpins") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::Squad
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*Squad_function_type )( double,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const &,::Wm3::Quaternion< double > const & ) ;
+            
+            Quaternion_exposer.def( 
+                "Squad"
+                , Squad_function_type( &::Wm3::Quaternion< double >::Squad )
+                , ( bp::arg("fT"), bp::arg("rkQ0"), bp::arg("rkA0"), bp::arg("rkA1"), bp::arg("rkQ1") )
+                , bp::return_self< >() );
+        
+        }
+        { //::Wm3::Quaternion< double >::SquaredLength
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*SquaredLength_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "SquaredLength"
+                , SquaredLength_function_type( &::Wm3::Quaternion< double >::SquaredLength ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::ToAxisAngle
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef boost::python::tuple ( *ToAxisAngle_function_type )( ::Wm3::Quaternion<double> const & );
+            
+            Quaternion_exposer.def( 
+                "ToAxisAngle"
+                , ToAxisAngle_function_type( &ToAxisAngle_2c4febac34e606b4a98de72d9f8161c9 )
+                , ( bp::arg("inst") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::ToRotationMatrix
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef void ( exported_class_t::*ToRotationMatrix_function_type )( ::Wm3::Vector3< double > * ) const;
+            
+            Quaternion_exposer.def( 
+                "ToRotationMatrix"
+                , ToRotationMatrix_function_type( &::Wm3::Quaternion< double >::ToRotationMatrix )
+                , ( bp::arg("akRotColumn") ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::W
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*W_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "W"
+                , W_function_type( &::Wm3::Quaternion< double >::W ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::X
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*X_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "X"
+                , X_function_type( &::Wm3::Quaternion< double >::X ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Y
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*Y_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Y"
+                , Y_function_type( &::Wm3::Quaternion< double >::Y ) );
+        
+        }
+        { //::Wm3::Quaternion< double >::Z
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef double ( exported_class_t::*Z_function_type )(  ) const;
+            
+            Quaternion_exposer.def( 
+                "Z"
+                , Z_function_type( &::Wm3::Quaternion< double >::Z ) );
+        
+        }
+        Quaternion_exposer.def( bp::self != bp::self );
+        Quaternion_exposer.def( bp::self * bp::self );
+        Quaternion_exposer.def( bp::self * bp::other< double >() );
+        Quaternion_exposer.def( bp::self *= bp::other< double >() );
+        Quaternion_exposer.def( bp::self + bp::self );
+        Quaternion_exposer.def( bp::self += bp::self );
+        Quaternion_exposer.def( bp::self - bp::self );
+        Quaternion_exposer.def( -bp::self );
+        Quaternion_exposer.def( bp::self -= bp::self );
+        Quaternion_exposer.def( bp::self / bp::other< double >() );
+        Quaternion_exposer.def( bp::self /= bp::other< double >() );
+        Quaternion_exposer.def( bp::self < bp::self );
+        Quaternion_exposer.def( bp::self <= bp::self );
+        { //::Wm3::Quaternion< double >::operator=
+        
+            typedef Wm3::Quaternion< double > exported_class_t;
+            typedef ::Wm3::Quaternion< double > & ( exported_class_t::*assign_function_type )( ::Wm3::Quaternion< double > const & ) ;
+            
+            Quaternion_exposer.def( 
+                "assign"
+                , assign_function_type( &::Wm3::Quaternion< double >::operator= )
+                , ( bp::arg("rkQ") )
+                , bp::return_self< >() );
+        
+        }
+        Quaternion_exposer.def( bp::self == bp::self );
+        Quaternion_exposer.def( bp::self > bp::self );
+        Quaternion_exposer.def( bp::self >= bp::self );
+        Quaternion_exposer.def_readonly( "IDENTITY", Wm3::Quaternion< double >::IDENTITY );
+        Quaternion_exposer.def_readonly( "ZERO", Wm3::Quaternion< double >::ZERO );
+        Quaternion_exposer.def( bp::self * bp::other< Wm3::Vector3< double > >() );
+        Quaternion_exposer.def( bp::self_ns::str( bp::self ) );
+        Quaternion_exposer.def("__len__",&::Quaternion_len).staticmethod("__len__").def("__setitem__",&::Quaternion_set_item).def("__getitem__",&::Quaternion_get_item).def("__str__",&::Quaternion_str).def("__repr__",&::Quaternion_str);
+    }
+
+    { //::Wm3::Vector2< double >
+        typedef bp::class_< Wm3::Vector2< double > > Vector2_exposer_t;
+        Vector2_exposer_t Vector2_exposer = Vector2_exposer_t( "Vector2", bp::init< >() );
+        bp::scope Vector2_scope( Vector2_exposer );
+        Vector2_exposer.def( bp::init< double, double >(( bp::arg("fX"), bp::arg("fY") )) );
+        Vector2_exposer.def( bp::init< double const * >(( bp::arg("afTuple") )) );
+        bp::implicitly_convertible< double const *, Wm3::Vector2< double > >();
+        Vector2_exposer.def( bp::init< Wm3::Vector2< double > const & >(( bp::arg("rkV") )) );
+        { //::Wm3::Vector2< double >::ComputeExtremes
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef void ( *ComputeExtremes_function_type )( int,::Wm3::Vector2< double > const *,::Wm3::Vector2< double > &,::Wm3::Vector2< double > & );
+            
+            Vector2_exposer.def( 
+                "ComputeExtremes"
+                , ComputeExtremes_function_type( &::Wm3::Vector2< double >::ComputeExtremes )
+                , ( bp::arg("iVQuantity"), bp::arg("akPoint"), bp::arg("rkMin"), bp::arg("rkMax") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Dot
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*Dot_function_type )( ::Wm3::Vector2< double > const & ) const;
+            
+            Vector2_exposer.def( 
+                "Dot"
+                , Dot_function_type( &::Wm3::Vector2< double >::Dot )
+                , ( bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::DotPerp
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*DotPerp_function_type )( ::Wm3::Vector2< double > const & ) const;
+            
+            Vector2_exposer.def( 
+                "DotPerp"
+                , DotPerp_function_type( &::Wm3::Vector2< double >::DotPerp )
+                , ( bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::GenerateOrthonormalBasis
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef void ( *GenerateOrthonormalBasis_function_type )( ::Wm3::Vector2< double > &,::Wm3::Vector2< double > &,bool );
+            
+            Vector2_exposer.def( 
+                "GenerateOrthonormalBasis"
+                , GenerateOrthonormalBasis_function_type( &::Wm3::Vector2< double >::GenerateOrthonormalBasis )
+                , ( bp::arg("rkU"), bp::arg("rkV"), bp::arg("bUnitLengthV") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::GetBarycentrics
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef void ( exported_class_t::*GetBarycentrics_function_type )( ::Wm3::Vector2< double > const &,::Wm3::Vector2< double > const &,::Wm3::Vector2< double > const &,double * ) const;
+            
+            Vector2_exposer.def( 
+                "GetBarycentrics"
+                , GetBarycentrics_function_type( &::Wm3::Vector2< double >::GetBarycentrics )
+                , ( bp::arg("rkV0"), bp::arg("rkV1"), bp::arg("rkV2"), bp::arg("afBary") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Length
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*Length_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "Length"
+                , Length_function_type( &::Wm3::Vector2< double >::Length ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Normalize
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*Normalize_function_type )(  ) ;
+            
+            Vector2_exposer.def( 
+                "Normalize"
+                , Normalize_function_type( &::Wm3::Vector2< double >::Normalize ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Orthonormalize
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef void ( *Orthonormalize_function_type )( ::Wm3::Vector2< double > &,::Wm3::Vector2< double > & );
+            
+            Vector2_exposer.def( 
+                "Orthonormalize"
+                , Orthonormalize_function_type( &::Wm3::Vector2< double >::Orthonormalize )
+                , ( bp::arg("rkU"), bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Perp
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef ::Wm3::Vector2< double > ( exported_class_t::*Perp_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "Perp"
+                , Perp_function_type( &::Wm3::Vector2< double >::Perp ) );
+        
+        }
+        { //::Wm3::Vector2< double >::SquaredLength
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*SquaredLength_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "SquaredLength"
+                , SquaredLength_function_type( &::Wm3::Vector2< double >::SquaredLength ) );
+        
+        }
+        { //::Wm3::Vector2< double >::UnitPerp
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef ::Wm3::Vector2< double > ( exported_class_t::*UnitPerp_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "UnitPerp"
+                , UnitPerp_function_type( &::Wm3::Vector2< double >::UnitPerp ) );
+        
+        }
+        { //::Wm3::Vector2< double >::X
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*X_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "X"
+                , X_function_type( &::Wm3::Vector2< double >::X ) );
+        
+        }
+        { //::Wm3::Vector2< double >::Y
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef double ( exported_class_t::*Y_function_type )(  ) const;
+            
+            Vector2_exposer.def( 
+                "Y"
+                , Y_function_type( &::Wm3::Vector2< double >::Y ) );
+        
+        }
+        Vector2_exposer.def( bp::self != bp::self );
+        Vector2_exposer.def( bp::self * bp::other< double >() );
+        Vector2_exposer.def( bp::self *= bp::other< double >() );
+        Vector2_exposer.def( bp::self + bp::self );
+        Vector2_exposer.def( bp::self += bp::self );
+        Vector2_exposer.def( bp::self - bp::self );
+        Vector2_exposer.def( -bp::self );
+        Vector2_exposer.def( bp::self -= bp::self );
+        Vector2_exposer.def( bp::self / bp::other< double >() );
+        Vector2_exposer.def( bp::self /= bp::other< double >() );
+        Vector2_exposer.def( bp::self < bp::self );
+        Vector2_exposer.def( bp::self <= bp::self );
+        { //::Wm3::Vector2< double >::operator=
+        
+            typedef Wm3::Vector2< double > exported_class_t;
+            typedef ::Wm3::Vector2< double > & ( exported_class_t::*assign_function_type )( ::Wm3::Vector2< double > const & ) ;
+            
+            Vector2_exposer.def( 
+                "assign"
+                , assign_function_type( &::Wm3::Vector2< double >::operator= )
+                , ( bp::arg("rkV") )
+                , bp::return_self< >() );
+        
+        }
+        Vector2_exposer.def( bp::self == bp::self );
+        Vector2_exposer.def( bp::self > bp::self );
+        Vector2_exposer.def( bp::self >= bp::self );
+        Vector2_exposer.def_readonly( "ONE", Wm3::Vector2< double >::ONE );
+        Vector2_exposer.def_readonly( "UNIT_X", Wm3::Vector2< double >::UNIT_X );
+        Vector2_exposer.def_readonly( "UNIT_Y", Wm3::Vector2< double >::UNIT_Y );
+        Vector2_exposer.def_readonly( "ZERO", Wm3::Vector2< double >::ZERO );
+        Vector2_exposer.staticmethod( "ComputeExtremes" );
+        Vector2_exposer.staticmethod( "GenerateOrthonormalBasis" );
+        Vector2_exposer.staticmethod( "Orthonormalize" );
+        Vector2_exposer.def( bp::other< Real >() * bp::self );
+        Vector2_exposer.def("__len__",&::Vector2_len)   .staticmethod("__len__").def("__setitem__",&::Vector2_set_item)   .def("__getitem__",&::Vector2_get_item)   .def("__str__",&::Vector2_str)   .def("__repr__",&::Vector2_str);
+    }
+
+    { //::Wm3::Vector3< double >
+        typedef bp::class_< Wm3::Vector3< double > > Vector3_exposer_t;
+        Vector3_exposer_t Vector3_exposer = Vector3_exposer_t( "Vector3", bp::init< >() );
+        bp::scope Vector3_scope( Vector3_exposer );
+        Vector3_exposer.def( bp::init< double, double, double >(( bp::arg("fX"), bp::arg("fY"), bp::arg("fZ") )) );
+        Vector3_exposer.def( bp::init< double const * >(( bp::arg("afTuple") )) );
+        bp::implicitly_convertible< double const *, Wm3::Vector3< double > >();
+        Vector3_exposer.def( bp::init< Wm3::Vector3< double > const & >(( bp::arg("rkV") )) );
+        { //::Wm3::Vector3< double >::ComputeExtremes
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef void ( *ComputeExtremes_function_type )( int,::Wm3::Vector3< double > const *,::Wm3::Vector3< double > &,::Wm3::Vector3< double > & );
+            
+            Vector3_exposer.def( 
+                "ComputeExtremes"
+                , ComputeExtremes_function_type( &::Wm3::Vector3< double >::ComputeExtremes )
+                , ( bp::arg("iVQuantity"), bp::arg("akPoint"), bp::arg("rkMin"), bp::arg("rkMax") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Cross
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef ::Wm3::Vector3< double > ( exported_class_t::*Cross_function_type )( ::Wm3::Vector3< double > const & ) const;
+            
+            Vector3_exposer.def( 
+                "Cross"
+                , Cross_function_type( &::Wm3::Vector3< double >::Cross )
+                , ( bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Dot
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*Dot_function_type )( ::Wm3::Vector3< double > const & ) const;
+            
+            Vector3_exposer.def( 
+                "Dot"
+                , Dot_function_type( &::Wm3::Vector3< double >::Dot )
+                , ( bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::GenerateOrthonormalBasis
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef void ( *GenerateOrthonormalBasis_function_type )( ::Wm3::Vector3< double > &,::Wm3::Vector3< double > &,::Wm3::Vector3< double > &,bool );
+            
+            Vector3_exposer.def( 
+                "GenerateOrthonormalBasis"
+                , GenerateOrthonormalBasis_function_type( &::Wm3::Vector3< double >::GenerateOrthonormalBasis )
+                , ( bp::arg("rkU"), bp::arg("rkV"), bp::arg("rkW"), bp::arg("bUnitLengthW") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::GetBarycentrics
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef void ( exported_class_t::*GetBarycentrics_function_type )( ::Wm3::Vector3< double > const &,::Wm3::Vector3< double > const &,::Wm3::Vector3< double > const &,::Wm3::Vector3< double > const &,double * ) const;
+            
+            Vector3_exposer.def( 
+                "GetBarycentrics"
+                , GetBarycentrics_function_type( &::Wm3::Vector3< double >::GetBarycentrics )
+                , ( bp::arg("rkV0"), bp::arg("rkV1"), bp::arg("rkV2"), bp::arg("rkV3"), bp::arg("afBary") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Length
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*Length_function_type )(  ) const;
+            
+            Vector3_exposer.def( 
+                "Length"
+                , Length_function_type( &::Wm3::Vector3< double >::Length ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Normalize
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*Normalize_function_type )(  ) ;
+            
+            Vector3_exposer.def( 
+                "Normalize"
+                , Normalize_function_type( &::Wm3::Vector3< double >::Normalize ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Orthonormalize
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef void ( *Orthonormalize_function_type )( ::Wm3::Vector3< double > &,::Wm3::Vector3< double > &,::Wm3::Vector3< double > & );
+            
+            Vector3_exposer.def( 
+                "Orthonormalize"
+                , Orthonormalize_function_type( &::Wm3::Vector3< double >::Orthonormalize )
+                , ( bp::arg("rkU"), bp::arg("rkV"), bp::arg("rkW") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Orthonormalize
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef void ( *Orthonormalize_function_type )( ::Wm3::Vector3< double > * );
+            
+            Vector3_exposer.def( 
+                "Orthonormalize"
+                , Orthonormalize_function_type( &::Wm3::Vector3< double >::Orthonormalize )
+                , ( bp::arg("akV") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::SquaredLength
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*SquaredLength_function_type )(  ) const;
+            
+            Vector3_exposer.def( 
+                "SquaredLength"
+                , SquaredLength_function_type( &::Wm3::Vector3< double >::SquaredLength ) );
+        
+        }
+        { //::Wm3::Vector3< double >::UnitCross
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef ::Wm3::Vector3< double > ( exported_class_t::*UnitCross_function_type )( ::Wm3::Vector3< double > const & ) const;
+            
+            Vector3_exposer.def( 
+                "UnitCross"
+                , UnitCross_function_type( &::Wm3::Vector3< double >::UnitCross )
+                , ( bp::arg("rkV") ) );
+        
+        }
+        { //::Wm3::Vector3< double >::X
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*X_function_type )(  ) const;
+            
+            Vector3_exposer.def( 
+                "X"
+                , X_function_type( &::Wm3::Vector3< double >::X ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Y
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*Y_function_type )(  ) const;
+            
+            Vector3_exposer.def( 
+                "Y"
+                , Y_function_type( &::Wm3::Vector3< double >::Y ) );
+        
+        }
+        { //::Wm3::Vector3< double >::Z
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef double ( exported_class_t::*Z_function_type )(  ) const;
+            
+            Vector3_exposer.def( 
+                "Z"
+                , Z_function_type( &::Wm3::Vector3< double >::Z ) );
+        
+        }
+        Vector3_exposer.def( bp::self != bp::self );
+        Vector3_exposer.def( bp::self * bp::other< double >() );
+        Vector3_exposer.def( bp::self *= bp::other< double >() );
+        Vector3_exposer.def( bp::self + bp::self );
+        Vector3_exposer.def( bp::self += bp::self );
+        Vector3_exposer.def( bp::self - bp::self );
+        Vector3_exposer.def( -bp::self );
+        Vector3_exposer.def( bp::self -= bp::self );
+        Vector3_exposer.def( bp::self / bp::other< double >() );
+        Vector3_exposer.def( bp::self /= bp::other< double >() );
+        Vector3_exposer.def( bp::self < bp::self );
+        Vector3_exposer.def( bp::self <= bp::self );
+        { //::Wm3::Vector3< double >::operator=
+        
+            typedef Wm3::Vector3< double > exported_class_t;
+            typedef ::Wm3::Vector3< double > & ( exported_class_t::*assign_function_type )( ::Wm3::Vector3< double > const & ) ;
+            
+            Vector3_exposer.def( 
+                "assign"
+                , assign_function_type( &::Wm3::Vector3< double >::operator= )
+                , ( bp::arg("rkV") )
+                , bp::return_self< >() );
+        
+        }
+        Vector3_exposer.def( bp::self == bp::self );
+        Vector3_exposer.def( bp::self > bp::self );
+        Vector3_exposer.def( bp::self >= bp::self );
+        Vector3_exposer.def_readonly( "ONE", Wm3::Vector3< double >::ONE );
+        Vector3_exposer.def_readonly( "UNIT_X", Wm3::Vector3< double >::UNIT_X );
+        Vector3_exposer.def_readonly( "UNIT_Y", Wm3::Vector3< double >::UNIT_Y );
+        Vector3_exposer.def_readonly( "UNIT_Z", Wm3::Vector3< double >::UNIT_Z );
+        Vector3_exposer.def_readonly( "ZERO", Wm3::Vector3< double >::ZERO );
+        Vector3_exposer.staticmethod( "ComputeExtremes" );
+        Vector3_exposer.staticmethod( "GenerateOrthonormalBasis" );
+        Vector3_exposer.staticmethod( "Orthonormalize" );
+        Vector3_exposer.def( bp::self_ns::str( bp::self ) );
+        Vector3_exposer.def("__len__",&::Vector3_len)   .staticmethod("__len__").def("__setitem__",&::Vector3_set_item)   .def("__getitem__",&::Vector3_get_item)   .def("__str__",&::Vector3_str)   .def("__repr__",&::Vector3_str);
+    }
+
+    { //::componentMaxVector
+    
+        typedef ::Wm3::Vector3r ( *componentMaxVector_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "componentMaxVector"
+            , componentMaxVector_function_type( &::componentMaxVector )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::componentMaxVector
+    
+        typedef ::Wm3::Vector2r ( *componentMaxVector_function_type )( ::Wm3::Vector2r const &,::Wm3::Vector2r const & );
+        
+        bp::def( 
+            "componentMaxVector"
+            , componentMaxVector_function_type( &::componentMaxVector )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::componentMinVector
+    
+        typedef ::Wm3::Vector3r ( *componentMinVector_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "componentMinVector"
+            , componentMinVector_function_type( &::componentMinVector )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::componentMinVector
+    
+        typedef ::Wm3::Vector2r ( *componentMinVector_function_type )( ::Wm3::Vector2r const &,::Wm3::Vector2r const & );
+        
+        bp::def( 
+            "componentMinVector"
+            , componentMinVector_function_type( &::componentMinVector )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::componentSum
+    
+        typedef ::Real ( *componentSum_function_type )( ::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "componentSum"
+            , componentSum_function_type( &::componentSum )
+            , ( bp::arg("v") ) );
+    
+    }
+
+    { //::diagDiv
+    
+        typedef ::Wm3::Vector3r ( *diagDiv_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "diagDiv"
+            , diagDiv_function_type( &::diagDiv )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::diagMult
+    
+        typedef ::Wm3::Vector3r ( *diagMult_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "diagMult"
+            , diagMult_function_type( &::diagMult )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::diagMult
+    
+        typedef ::Wm3::Vector2r ( *diagMult_function_type )( ::Wm3::Vector2r const &,::Wm3::Vector2r const & );
+        
+        bp::def( 
+            "diagMult"
+            , diagMult_function_type( &::diagMult )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+
+    { //::quaternionFromAxes
+    
+        typedef ::Wm3::Quaternionr ( *quaternionFromAxes_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "quaternionFromAxes"
+            , quaternionFromAxes_function_type( &::quaternionFromAxes )
+            , ( bp::arg("axis1"), bp::arg("axis2"), bp::arg("axis3") ) );
+    
+    }
+
+    { //::quaternionToAxes
+    
+        typedef void ( *quaternionToAxes_function_type )( ::Wm3::Quaternionr const &,::Wm3::Vector3r &,::Wm3::Vector3r &,::Wm3::Vector3r & );
+        
+        bp::def( 
+            "quaternionToAxes"
+            , quaternionToAxes_function_type( &::quaternionToAxes )
+            , ( bp::arg("q"), bp::arg("axis1"), bp::arg("axis2"), bp::arg("axis3") ) );
+    
+    }
+
+    { //::quaternionToEulerAngles
+    
+        typedef void ( *quaternionToEulerAngles_function_type )( ::Wm3::Quaternionr const &,::Wm3::Vector3r &,::Real );
+        
+        bp::def( 
+            "quaternionToEulerAngles"
+            , quaternionToEulerAngles_function_type( &::quaternionToEulerAngles )
+            , ( bp::arg("q"), bp::arg("eulerAngles"), bp::arg("threshold")=9.999999974752427078783512115478515625e-7f ) );
+    
+    }
+
+    { //::quaterniontoGLMatrix
+    
+        typedef void ( *quaterniontoGLMatrix_function_type )( ::Wm3::Quaternionr const &,::Real * );
+        
+        bp::def( 
+            "quaterniontoGLMatrix"
+            , quaterniontoGLMatrix_function_type( &::quaterniontoGLMatrix )
+            , ( bp::arg("q"), bp::arg("m") ) );
+    
+    }
+
+    custom_Vector3r_from_tuple();
+
+    { //::unitVectorsAngle
+    
+        typedef ::Real ( *unitVectorsAngle_function_type )( ::Wm3::Vector3r const &,::Wm3::Vector3r const & );
+        
+        bp::def( 
+            "unitVectorsAngle"
+            , unitVectorsAngle_function_type( &::unitVectorsAngle )
+            , ( bp::arg("a"), bp::arg("rkV") ) );
+    
+    }
+}

Copied: trunk/py/pack.py (from rev 1834, trunk/lib/py/pack.py)
===================================================================
--- trunk/lib/py/pack.py	2009-07-02 15:26:02 UTC (rev 1834)
+++ trunk/py/pack.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,249 @@
+# encoding: utf-8
+#
+import itertools,warnings
+from numpy import arange
+from math import sqrt
+from yade import utils
+
+# for now skip the import, but try in inGtsSurface constructor again, to give error if we really use it
+try:
+	import gts
+except ImportError: pass
+
+# make c++ predicates available in this module
+from _packPredicates import *
+# import SpherePack
+from _packSpheres import *
+
+class inGtsSurface_py(Predicate):
+	"""This class was re-implemented in c++, but should stay here to serve as reference for implementing
+	Predicates in pure python code. C++ allows us to play dirty tricks in GTS which are not accessible
+	through pygts itself; the performance penalty of pygts comes from fact that if constructs and destructs
+	bb tree for the surface at every invocation of gts.Point().is_inside(). That is cached in the c++ code,
+	provided that the surface is not manipulated with during lifetime of the object (user's responsibility).
+
+	---
+	
+	Predicate for GTS surfaces. Constructed using an already existing surfaces, which must be closed.
+
+		import gts
+		surf=gts.read(open('horse.gts'))
+		inGtsSurface(surf)
+
+	Note: padding is optionally supported by testing 6 points along the axes in the pad distance. This
+	must be enabled in the ctor by saying doSlowPad=True. If it is not enabled and pad is not zero,
+	warning is issued.
+	"""
+	def __init__(self,surf,noPad=False):
+		# call base class ctor; necessary for virtual methods to work as expected.
+		# see comments in _packPredicates.cpp for struct PredicateWrap.
+		super(inGtsSurface,self).__init__()
+		if not surf.is_closed(): raise RuntimeError("Surface for inGtsSurface predicate must be closed.")
+		self.surf=surf
+		self.noPad=noPad
+		inf=float('inf')
+		mn,mx=[inf,inf,inf],[-inf,-inf,-inf]
+		for v in surf.vertices():
+			c=v.coords()
+			mn,mx=[min(mn[i],c[i]) for i in 0,1,2],[max(mx[i],c[i]) for i in 0,1,2]
+		self.mn,self.mx=tuple(mn),tuple(mx)
+		import gts
+	def aabb(self): return self.mn,self.mx
+	def __call__(self,_pt,pad=0.):
+		p=gts.Point(*_pt)
+		if self.noPad:
+			if pad!=0: warnings.warn("Padding disabled in ctor, using 0 instead.")
+			return p.is_inside(self.surf)
+		pp=[gts.Point(_pt[0]-pad,_pt[1],_pt[2]),gts.Point(_pt[0]+pad,_pt[1],_pt[2]),gts.Point(_pt[0],_pt[1]-pad,_pt[2]),gts.Point(_pt[0],_pt[1]+pad,_pt[2]),gts.Point(_pt[0],_pt[1],_pt[2]-pad),gts.Point(_pt[0],_pt[1],_pt[2]+pad)]
+		return p.is_inside(self.surf) and pp[0].is_inside(self.surf) and pp[1].is_inside(self.surf) and pp[2].is_inside(self.surf) and pp[3].is_inside(self.surf) and pp[4].is_inside(self.surf) and pp[5].is_inside(self.surf)
+
+class inSpace(Predicate):
+	"""Predicate returning True for any points, with infinite bounding box."""
+	def aabb(self):
+		inf=float('inf'); return [-inf,-inf,-inf],[inf,inf,inf]
+	def __call__(self,pt): return True
+
+#####
+## surface construction and manipulation
+#####
+
+def gtsSurface2Facets(surf,**kw):
+	"""Construct facets from given GTS surface. **kw is passed to utils.facet."""
+	return [utils.facet([v.coords() for v in face.vertices()],**kw) for face in surf]
+
+def sweptPolylines2gtsSurface(pts,threshold=0,capStart=False,capEnd=False):
+	"""Create swept suface (as GTS triangulation) given same-length sequences of points (as 3-tuples).
+	If threshold is given (>0), gts.Surface().cleanup(threshold) will be called before returning, which
+	removes vertices mutually closer than threshold. Can be used to create closed swept surface (revolved), as
+	we don't check for coincident vertices otherwise.
+	"""
+	if not len(set([len(pts1) for pts1 in pts]))==1: raise RuntimeError("Polylines must be all of the same length!")
+	vtxs=[[gts.Vertex(x,y,z) for x,y,z in pts1] for pts1 in pts]
+	sectEdges=[[gts.Edge(vtx[i],vtx[i+1]) for i in xrange(0,len(vtx)-1)] for vtx in vtxs]
+	interSectEdges=[[] for i in range(0,len(vtxs)-1)]
+	for i in range(0,len(vtxs)-1):
+		for j in range(0,len(vtxs[i])):
+			interSectEdges[i].append(gts.Edge(vtxs[i][j],vtxs[i+1][j]))
+			if j<len(vtxs[i])-1: interSectEdges[i].append(gts.Edge(vtxs[i][j],vtxs[i+1][j+1]))
+	surf=gts.Surface()
+	for i in range(0,len(vtxs)-1):
+		for j in range(0,len(vtxs[i])-1):
+			surf.add(gts.Face(interSectEdges[i][2*j+1],sectEdges[i+1][j],interSectEdges[i][2*j]))
+			surf.add(gts.Face(sectEdges[i][j],interSectEdges[i][2*j+2],interSectEdges[i][2*j+1]))
+	def doCap(vtx,edg,start):
+		ret=[]
+		eFan=[edg[0]]+[gts.Edge(vtx[i],vtx[0]) for i in range(2,len(vtx))]
+		for i in range(1,len(edg)):
+			ret+=[gts.Face(eFan[i-1],eFan[i],edg[i]) if start else gts.Face(eFan[i-1],edg[i],eFan[i])]
+		return ret
+	caps=[]
+	if capStart: caps+=doCap(vtxs[0],sectEdges[0],start=True)
+	if capEnd: caps+=doCap(vtxs[-1],sectEdges[-1],start=False)
+	for cap in caps: surf.add(cap)
+	if threshold>0: surf.cleanup(threshold)
+	return surf
+
+import euclid
+
+def revolutionSurfaceMeridians(sects,angles,origin=euclid.Vector3(0,0,0),orientation=euclid.Quaternion()):
+	"""Revolution surface given sequences of 2d points and sequence of corresponding angles,
+	returning sequences of 3d points representing meridian sections of the revolution surface.
+	The 2d sections are turned around z-axis, but they can be transformed
+	using the origin and orientation arguments to give arbitrary orientation."""
+	import math
+	def toGlobal(x,y,z):
+		return tuple(origin+orientation*(euclid.Vector3(x,y,z)))
+	return [[toGlobal(x2d*math.cos(angles[i]),x2d*math.sin(angles[i]),y2d) for x2d,y2d in sects[i]] for i in range(0,len(sects))]
+
+########
+## packing generators
+########
+
+
+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()
+	if(max([mx[i]-mn[i] for i in 0,1,2])==float('inf')): raise ValueError("AABB of the predicate must not be infinite (didn't you use union | instead of intersection & for unbounded predicate such as notInNotch?");
+	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]
+	if(max(dim)==float('inf')): raise ValueError("AABB of the predicate must not be infinite (didn't you use union | instead of intersection & for unbounded predicate such as notInNotch?");
+	ii,jj,kk=[range(0,int(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
+
+def filterSpherePack(predicate,spherePack,**kw):
+	"""Using given SpherePack instance, return spheres the satisfy predicate.
+	The packing will be recentered to match the predicate and warning is given if the predicate
+	is larger than the packing."""
+	mn,mx=predicate.aabb()
+	dimP,centP=predicate.dim(),predicate.center()
+	dimS,centS=spherePack.dim(),spherePack.center()
+	if dimP[0]>dimS[0] or dimP[1]>dimS[1] or dimP[2]>dimS[2]: warnings.warn("Packing's dimension (%s) doesn't fully contain dimension of the predicate (%s)."%(dimS,dimP))
+	spherePack.translate(centP-centS)
+	ret=[]
+	for s in spherePack:
+		if predicate(s[0],s[1]): ret+=[utils.sphere(s[0],radius=s[1],**kw)]
+	return ret
+
+def triaxialPack(predicate,radius,dim=None,cropLayers=0,radiusStDev=0.,assumedFinalDensity=.6,memoizeDb=None,**kw):
+	"""Generator of triaxial packing, using TriaxialTest. Radius is radius of spheres, radiusStDev is its standard deviation.
+	By default, all spheres are of the same radius. cropLayers is how many layers of spheres will be added to the computed
+	dimension of the box so that there no (or not so much, at least) boundary effects at the boundaries of the predicate.
+	assumedFinalDensity should be OK as it is, it is used to compute necessary number of spheres for the packing.
+
+	The memoizeDb parameter can be passed a file (existent or nonexistent). If the file exists, it will be first looked
+	for a suitable packing that was previously saved already (known as memoization). Saved packings will be scaled to
+	requested sphere radius; those that are smaller are distcarded as well as those with different radiusStDev. From
+	the remaining ones, the one with the least spheres will be loaded and returned. If no suitable packing is found, it
+	is generated as usually, but saved into the database for later use.
+
+	O.switchWorld() magic is used to have clean simulation for TriaxialTest without deleting the original simulation.
+	This function therefore should never run in parallel with some code accessing your simulation.
+	"""
+	import sqlite3, os.path, cPickle, time, sys
+	from yade import log
+	from math import pi
+	if not dim: dim=predicate.dim()
+	if max(dim)==float('inf'): raise RuntimeError("Infinite predicate and no dimension of packing requested.")
+	fullDim=tuple([dim[i]+4*cropLayers*radius for i in 0,1,2])
+	if(memoizeDb and os.path.exists(memoizeDb)):
+		# find suitable packing and return it directly
+		conn=sqlite3.connect(memoizeDb); c=conn.cursor();
+		c.execute('select radius,radiusStDev,dimx,dimy,dimz,N,timestamp from packings order by N')
+		for row in c:
+			R,rDev,X,Y,Z,NN,timestamp=row[0:7]; scale=radius/R
+			rDev*=scale; X*=scale; Y*=scale; Z*=scale
+			if (radiusStDev==0 and rDev!=0) or (radiusStDev==0 and rDev!=0) or (radiusStDev!=0 and abs((rDev-radiusStDev)/radiusStDev)>1e-2): continue # not suitable, standard deviation differs too much
+			if X<fullDim[0] or Y<fullDim[1] or Z<fullDim[2]: continue # not suitable, not large enough
+			print "Found suitable packing in database (radius=%g±%g,N=%g,dim=%g×%g×%g,scale=%g), created %s"%(R,rDev,NN,X,Y,Z,scale,time.asctime(time.gmtime(timestamp)))
+			c.execute('select pack from packings where timestamp=?',(timestamp,))
+			sp=SpherePack(cPickle.loads(str(c.fetchone()[0])))
+			sp.scale(scale)
+			return filterSpherePack(predicate,sp,**kw)
+		print "No suitable packing in database found, running triaxial"
+		sys.stdout.flush()
+	V=(4/3)*pi*radius**3; N=assumedFinalDensity*fullDim[0]*fullDim[1]*fullDim[2]/V;
+	##
+	O.switchWorld()
+	##
+	TriaxialTest(
+		numberOfGrains=int(N),
+		radiusMean=radius,
+		# this is just size ratio if radiusMean is specified
+		# if you comment out the line above, it will be the corner (before compaction) and radiusMean will be set accordingly
+		upperCorner=fullDim,
+		radiusStdDev=radiusStDev,
+		## no need to touch any the following, I think
+		noFiles=True,
+		lowerCorner=[0,0,0],
+		sigmaIsoCompaction=1e7,
+		sigmaLateralConfinement=1e3,
+		StabilityCriterion=.05,
+		strainRate=.2,
+		fast=True,
+		thickness=-1, # will be set to sphere radius if negative
+		maxWallVelocity=.1,
+		wallOversizeFactor=1.5,
+		autoUnload=True, # unload after isotropic compaction
+		autoCompressionActivation=False # stop once unloaded
+	).load()
+	log.setLevel('TriaxialCompressionEngine',log.WARN)
+	O.run(); O.wait()
+	sp=SpherePack(); sp.fromSimulation()
+	##
+	O.switchWorld()
+	##
+	if(memoizeDb):
+		if os.path.exists(memoizeDb):
+			conn=sqlite3.connect(memoizeDb)
+		else:
+			conn=sqlite3.connect(memoizeDb)
+			c=conn.cursor()
+			c.execute('create table packings (radius real, radiusStDev real, dimx real, dimy real, dimz real, N integer, timestamp real, pack blob)')
+		c=conn.cursor()
+		packBlob=buffer(cPickle.dumps(sp.toList(),cPickle.HIGHEST_PROTOCOL))
+		c.execute('insert into packings values (?,?,?,?,?,?,?,?)',(radius,radiusStDev,fullDim[0],fullDim[1],fullDim[2],len(sp),time.time(),packBlob,))
+		c.close()
+		conn.commit()
+		print "Packing saved to the database",memoizeDb
+	return filterSpherePack(predicate,sp,**kw)
+
+
+

Copied: trunk/py/plot.py (from rev 1834, trunk/lib/py/plot.py)

Copied: trunk/py/timing.py (from rev 1834, trunk/lib/py/timing.py)

Copied: trunk/py/utils.py (from rev 1834, trunk/lib/py/utils.py)
===================================================================
--- trunk/lib/py/utils.py	2009-07-02 15:26:02 UTC (rev 1834)
+++ trunk/py/utils.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,454 @@
+# encoding: utf-8
+#
+# utility functions for yade
+#
+# 2008 © Václav Šmilauer <eudoxos@xxxxxxxx>
+
+import math,random
+from yade.wrapper import *
+from miniWm3Wrap import *
+try: # use psyco if available
+	import psyco
+	psyco.full()
+except ImportError: pass
+
+# c++ implementations for performance reasons
+from yade._utils import *
+
+def saveVars(mark='',loadNow=False,**kw):
+	"""Save passed variables into the simulation so that it can be recovered when the simulation is loaded again.
+
+	For example, variables a=5, b=66 and c=7.5e-4 are defined. To save those, use
+
+	 utils.saveVars(a=a,b=b,c=c)
+
+	those variables will be save in the .xml file, when the simulation itself is saved. To recover those variables once
+	the .xml is loaded again, use
+
+	 utils.loadVars(mark)
+
+	and they will be defined in the __builtin__ namespace (i.e. available from anywhere in the python code).
+
+	If loadParam==True, variables will be loaded immediately after saving. That effectively makes **kw available in builtin namespace.
+	"""
+	import cPickle
+	Omega().tags['pickledPythonVariablesDictionary'+mark]=cPickle.dumps(kw)
+	if loadNow: loadVars(mark)
+
+def loadVars(mark=None):
+	"""Load variables from saveVars, which are saved inside the simulation.
+	If mark==None, all save variables are loaded. Otherwise only those with
+	the mark passed."""
+	import cPickle
+	import __builtin__
+	if mark!=None:
+		d=cPickle.loads(Omega().tags['pickledPythonVariablesDictionary'+mark])
+		for k in d: __builtin__.__dict__[k]=d[k]
+	else: # load everything one by one
+		for m in Omega().tags.keys():
+			if m.startswith('pickledPythonVariablesDictionary'):
+				loadVars(m[len('pickledPythonVariableDictionary')+1:])
+
+
+def SpherePWaveTimeStep(radius,density,young):
+	"""Compute P-wave critical timestep for a single sphere.
+	If you want to compute minimum critical timestep for all spheres in the simulation, use utils.PWaveTimeStep() instead."""
+	from math import sqrt
+	return radius/sqrt(young/density)
+
+def randomColor(): return [random.random(),random.random(),random.random()]
+
+def typedEngine(name): return [e for e in Omega().engines if e.name==name][0]
+
+def downCast(obj,newClassName):
+	"""Cast given object to class deriving from the same yade root class and copy all parameters from given object.
+	Obj should be up in the inheritance tree, otherwise some attributes may not be defined in the new class."""
+	return obj.__class__(newClassName,dict([ (key,obj[key]) for key in obj.keys() ]))
+
+bodiesPhysDefaults={'young':30e9,'poisson':.3,'frictionAngle':.5236}
+
+def sphere(center,radius,dynamic=True,wire=False,color=None,density=1,physParamsClass='BodyMacroParameters',**physParamsAttr):
+	"""Create default sphere, with given parameters. Physical properties such as mass and inertia are calculated automatically."""
+	s=Body()
+	if not color: color=randomColor()
+	pp=bodiesPhysDefaults.copy(); pp.update(physParamsAttr);
+	s.shape=GeometricalModel('Sphere',{'radius':radius,'diffuseColor':color,'wire':wire})
+	s.mold=InteractingGeometry('InteractingSphere',{'radius':radius,'diffuseColor':color})
+	V=(4./3)*math.pi*radius**3
+	inert=(2./5.)*V*density*radius**2
+	pp.update({'se3':[center[0],center[1],center[2],1,0,0,0],'refSe3':[center[0],center[1],center[2],1,0,0,0],'mass':V*density,'inertia':[inert,inert,inert]})
+	s.phys=PhysicalParameters(physParamsClass,pp)
+	s.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
+	s['isDynamic']=dynamic
+	return s
+
+def box(center,extents,orientation=[1,0,0,0],dynamic=True,wire=False,color=None,density=1,physParamsClass='BodyMacroParameters',**physParamsAttr):
+	"""Create default box (cuboid), with given parameters. Physical properties such as mass and inertia are calculated automatically."""
+	b=Body()
+	if not color: color=randomColor()
+	pp=bodiesPhysDefaults.copy(); pp.update(physParamsAttr);
+	b.shape=GeometricalModel('Box',{'extents':extents,'diffuseColor':color,'wire':wire})
+	b.mold=InteractingGeometry('InteractingBox',{'extents':extents,'diffuseColor':color})
+	mass=8*extents[0]*extents[1]*extents[2]*density
+	V=extents[0]*extents[1]*extents[2]
+	pp.update({'se3':[center[0],center[1],center[2],orientation[0],orientation[1],orientation[2],orientation[3]],'refSe3':[center[0],center[1],center[2],orientation[0],orientation[1],orientation[2],orientation[3]],'mass':V*density,'inertia':[mass*4*(extents[1]**2+extents[2]**2),mass*4*(extents[0]**2+extents[2]**2),mass*4*(extents[0]**2+extents[1]**2)]})
+	b.phys=PhysicalParameters(physParamsClass,pp)
+	b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
+	b['isDynamic']=dynamic
+	return b
+
+def facet(vertices,dynamic=False,wire=True,color=None,density=1,physParamsClass='BodyMacroParameters',**physParamsAttr):
+	"""Create default facet with given parameters. Vertices are given as sequence of 3 3-tuple and they, all in global coordinates."""
+	b=Body()
+	if not color: color=randomColor()
+	pp=bodiesPhysDefaults.copy(); pp.update(physParamsAttr);
+	b.shape=GeometricalModel('Facet',{'diffuseColor':color,'wire':wire})
+	b.mold=InteractingGeometry('InteractingFacet',{'diffuseColor':color})
+	center=inscribedCircleCenter(vertices[0],vertices[1],vertices[2])
+	vertices=Vector3(vertices[0])-center,Vector3(vertices[1])-center,Vector3(vertices[2])-center
+	vStr='['+' '.join(['{%g %g %g}'%(v[0],v[1],v[2]) for v in vertices])+']'
+	b.shape.setRaw('vertices',vStr)
+	b.mold.setRaw('vertices',vStr)
+	pp.update({'se3':[center[0],center[1],center[2],1,0,0,0],'refSe3':[center[0],center[1],center[2],1,0,0,0],'inertia':[0,0,0]})
+	b.phys=PhysicalParameters(physParamsClass,pp)
+	b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
+	b['isDynamic']=dynamic
+	b.mold.postProcessAttributes()
+	return b
+
+def alignedFacetBox(center,extents,wallMask=63,**kw):
+	"""Create axis-aligned box composed of facets, with given center and extents. wallMask determines which walls will be created,
+	in the order -x (1), +x (2), -y (4), +y (8), -z (16), +z (32). The numbers are ANDed; the default 63 means to create all walls.
+	Remaining **kw arguments are passed to utils.facet. The facets are oriented outwards from the box."""
+	mn,mx=[center[i]-extents[i] for i in 0,1,2],[center[i]+extents[i] for i in 0,1,2]
+	def doWall(a,b,c,d):
+		return [facet((a,b,c),**kw),facet((a,c,d),**kw)]
+	ret=[]
+	A,B,C,D=(mn[0],mn[1],mn[2]),(mx[0],mn[1],mn[2]),(mx[0],mx[1],mn[2]),(mn[0],mx[1],mn[2])
+	E,F,G,H=(mn[0],mn[1],mx[2]),(mx[0],mn[1],mx[2]),(mx[0],mx[1],mx[2]),(mn[0],mx[1],mx[2])
+	if wallMask&1:  ret+=doWall(A,D,H,E)
+	if wallMask&2:  ret+=doWall(B,C,G,F)
+	if wallMask&4:  ret+=doWall(A,B,F,E)
+	if wallMask&8:  ret+=doWall(D,H,G,C)
+	if wallMask&16: ret+=doWall(A,D,C,B)
+	if wallMask&32: ret+=doWall(E,F,G,H)
+	return ret
+
+
+def aabbWalls(extrema=None,thickness=None,oversizeFactor=1.5,**kw):
+	"""return 6 walls that will wrap existing packing;
+	extrema are extremal points of the AABB of the packing (will be calculated if not specified)
+	thickness is wall thickness (will be 1/10 of the X-dimension if not specified)
+	Walls will be enlarged in their plane by oversizeFactor.
+	returns list of 6 wall Bodies enclosing the packing, in the order minX,maxX,minY,maxY,minZ,maxZ.
+	"""
+	walls=[]
+	if not extrema: extrema=aabbExtrema()
+	if not thickness: thickness=(extrema[1][0]-extrema[0][0])/10.
+	for axis in [0,1,2]:
+		mi,ma=extrema
+		center=[(mi[i]+ma[i])/2. for i in range(3)]
+		extents=[.5*oversizeFactor*(ma[i]-mi[i]) for i in range(3)]
+		extents[axis]=thickness/2.
+		for j in [0,1]:
+			center[axis]=extrema[j][axis]+(j-.5)*thickness
+			walls.append(box(center=center,extents=extents,dynamic=False,**kw))
+			walls[-1].shape['wire']=True
+	return walls
+
+
+def aabbDim(cutoff=0.,centers=False):
+	"""return dimensions of the bounding box, optionally cut."""
+	a=aabbExtrema(cutoff,centers)
+	return (a[1][0]-a[0][0],a[1][1]-a[0][1],a[1][2]-a[0][2])
+
+def aabbExtrema2d(pts):
+	"""return 2d bounding box for a sequence of 2-tuples"""
+	inf=float('inf')
+	min,max=[inf,inf],[-inf,-inf]
+	for pt in pts:
+		if pt[0]<min[0]: min[0]=pt[0]
+		elif pt[0]>max[0]: max[0]=pt[0]
+		if pt[1]<min[1]: min[1]=pt[1]
+		elif pt[1]>max[1]: max[1]=pt[1]
+	return tuple(min),tuple(max)
+
+def perpendicularArea(axis):
+	"""return area perpendicular to given axis (0=x,1=y,2=z) generated by bodies
+	for which the function consider returns True (defaults to returning True always)
+	and which is of the type "Sphere"
+	"""
+	ext=aabbExtrema()
+	other=((axis+1)%3,(axis+2)%3)
+	return (ext[1][other[0]]-ext[0][other[0]])*(ext[1][other[1]]-ext[0][other[1]])
+
+def fractionalBox(fraction=1.,minMax=None):
+	"""retrurn (min,max) that is the original minMax box (or aabb of the whole simulation if not specified)
+	linearly scaled around its center to the fraction factor"""
+	if not minMax: minMax=aabbExtrema()
+	half=[.5*(minMax[1][i]-minMax[0][i]) for i in [0,1,2]]
+	return (tuple([minMax[0][i]+(1-fraction)*half[i] for i in [0,1,2]]),tuple([minMax[1][i]-(1-fraction)*half[i] for i in [0,1,2]]))
+
+
+def randomizeColors(onShapes=True,onMolds=False,onlyDynamic=False):
+	"""Assign random colors to shape's (GeometricalModel) and/or mold's (InteractingGeometry) diffuseColor.
+	
+	onShapes and onMolds turn on/off operating on the respective colors.
+	If onlyDynamic is true, only dynamic bodies will have the color changed.
+	"""
+	if not onShapes and not onMolds: return
+	o=Omega()
+	for b in o.bodies:
+		color=(random.random(),random.random(),random.random())
+		if onShapes and (b['isDynamic'] or not onlyDynamic): b.shape['diffuseColor']=color
+		if onMolds  and (b['isDynamic'] or not onlyDynamic): b.mold['diffuseColor']=color
+
+
+def spheresFromFile(filename,scale=1.,wenjieFormat=False,**kw):
+	"""Load sphere coordinates from file, create spheres, insert them to the simulation.
+
+	filename is the file holding ASCII numbers (at least 4 colums that hold x_center, y_center, z_center, radius).
+	All remaining arguments are passed the the yade.utils.sphere function that creates the bodies.
+
+	wenjieFormat will skip all lines that have exactly 5 numbers and where the 4th one is exactly 1.0 -
+	this was used by a fellow developer called Wenjie to mark box elements.
+	
+	Returns list of body ids that were inserted into simulation."""
+	o=Omega()
+	ret=[]
+	for l in open(filename):
+		ss=[float(i) for i in l.split()]
+		if wenjieFormat and len(ss)==5 and ss[4]==1.0: continue
+		id=o.bodies.append(sphere([scale*ss[0],scale*ss[1],scale*ss[2]],scale*ss[3],**kw))
+		ret.append(id)
+	return ret
+
+def spheresToFile(filename,consider=lambda id: True):
+	"""Save sphere coordinates into ASCII file; the format of the line is: x y z r.
+	Non-spherical bodies are silently skipped.
+	
+	Returns number of spheres that were written."""
+	o=Omega()
+	out=open(filename,'w')
+	count=0
+	for b in o.bodies:
+		if not b.shape or not b.shape.name=='Sphere' or not consider(b.id): continue
+		out.write('%g\t%g\t%g\t%g\n'%(b.phys['se3'][0],b.phys['se3'][1],b.phys['se3'][2],b.shape['radius']))
+		count+=1
+	out.close()
+	return count
+
+def avgNumInteractions(cutoff=0.):
+	nums,counts=bodyNumInteractionsHistogram(aabbExtrema(cutoff))
+	return sum([nums[i]*counts[i] for i in range(len(nums))])/(1.*sum(counts))
+
+def plotNumInteractionsHistogram(cutoff=0.):
+	nums,counts=bodyNumInteractionsHistogram(aabbExtrema(cutoff))
+	import pylab
+	pylab.bar(nums,counts)
+	pylab.title('Number of interactions histogram, average %g (cutoff=%g)'%(avgNumInteractions(cutoff),cutoff))
+	pylab.xlabel('Number of interactions')
+	pylab.ylabel('Body count')
+	pylab.show()
+
+def plotDirections(aabb=(),mask=0,bins=20,numHist=True):
+	"""Plot 3 histograms for distribution of interaction directions, in yz,xz and xy planes and
+	(optional but default) histogram of number of interactions per body."""
+	import pylab,math
+	from yade import utils
+	for axis in [0,1,2]:
+		d=utils.interactionAnglesHistogram(axis,mask=mask,bins=bins,aabb=aabb)
+		fc=[0,0,0]; fc[axis]=1.
+		subp=pylab.subplot(220+axis+1,polar=True);
+		# 1.1 makes small gaps between values (but the column is a bit decentered)
+		pylab.bar(d[0],d[1],width=math.pi/(1.1*bins),fc=fc,alpha=.7,label=['yz','xz','xy'][axis])
+		#pylab.title(['yz','xz','xy'][axis]+' plane')
+		pylab.text(.5,.25,['yz','xz','xy'][axis],horizontalalignment='center',verticalalignment='center',transform=subp.transAxes,fontsize='xx-large')
+	if numHist:
+		pylab.subplot(224,polar=False)
+		nums,counts=utils.bodyNumInteractionsHistogram(aabb if len(aabb)>0 else utils.aabbExtrema())
+		avg=sum([nums[i]*counts[i] for i in range(len(nums))])/(1.*sum(counts))
+		pylab.bar(nums,counts,fc=[1,1,0],alpha=.7,align='center')
+		pylab.xlabel('Interactions per body (avg. %g)'%avg)
+		pylab.axvline(x=avg,linewidth=3,color='r')
+		pylab.ylabel('Body count')
+	pylab.show()
+
+
+def import_stl_geometry(file, young=30e9,poisson=.3,color=[0,1,0],frictionAngle=0.5236,wire=True,noBoundingVolume=False,noInteractingGeometry=False,physParamsClass='BodyMacroParameters',physParamsAttr={}):
+	""" Import geometry from stl file, create facets and return list of their ids."""
+	imp = STLImporter()
+	imp.wire = wire
+	imp.open(file)
+	o=Omega()
+	begin=len(o.bodies)
+	for i in xrange(imp.number_of_facets):
+		b=Body()
+		b['isDynamic']=False
+		pp={'se3':[0,0,0,1,0,0,0],'young':young,'poisson':poisson,'frictionAngle':frictionAngle}
+		pp.update(physParamsAttr)
+		b.phys=PhysicalParameters(physParamsClass)
+		for k in [attr for attr in pp.keys() if attr in b.phys.keys()]:
+			b.phys[k]=pp[k]
+		if not noBoundingVolume:
+			b.bound=BoundingVolume('AABB',{'diffuseColor':[0,1,0]})
+		o.bodies.append(b)
+	imp.import_geometry(o.bodies,begin,noInteractingGeometry)
+	imported=range(begin,begin+imp.number_of_facets)
+	for i in imported:
+		if not noInteractingGeometry:
+			o.bodies[i].mold.postProcessAttributes()
+		o.bodies[i].shape['diffuseColor']=color
+	return imported
+
+def encodeVideoFromFrames(wildcard,out,renameNotOverwrite=True,fps=24):
+	import pygst,sys,gobject,os
+	pygst.require("0.10")
+	import gst
+	if renameNotOverwrite and os.path.exists(out):
+		i=0;
+		while(os.path.exists(out+"~%d"%i)): i+=1
+		os.rename(out,out+"~%d"%i); print "Output file `%s' already existed, old file renamed to `%s'"%(out,out+"~%d"%i)
+	print "Encoding video from %s to %s"%(wildcard,out)
+	pipeline=gst.parse_launch('multifilesrc location="%s" index=0 caps="image/png,framerate=\(fraction\)%d/1" ! pngdec ! ffmpegcolorspace ! theoraenc sharpness=2 quality=63 ! oggmux ! filesink location="%s"'%(wildcard,fps,out))
+	bus=pipeline.get_bus()
+	bus.add_signal_watch()
+	mainloop=gobject.MainLoop();
+	bus.connect("message::eos",lambda bus,msg: mainloop.quit())
+	pipeline.set_state(gst.STATE_PLAYING)
+	mainloop.run()
+	pipeline.set_state(gst.STATE_NULL); pipeline.get_state()
+
+def readParamsFromTable(tableFileLine=None,noTableOk=False,unknownOk=False,**kw):
+	"""
+	Read parameters from a file and assign them to __builtin__ variables.
+
+	tableFile is a text file (with one value per blank-separated columns)
+	tableLine is number of line where to get the values from
+
+		The format of the file is as follows (commens starting with # and empty lines allowed)
+		
+		name1 name2 … # 0th line
+		val1  val2  … # 1st line
+		val2  val2  … # 2nd line
+		…
+
+	The name `description' is special and is assigned to Omega().tags['description']
+
+	assigns Omega().tags['params']="name1=val1,name2=val2,…"
+	
+	assigns Omega().tags['defaultParams']="unassignedName1=defaultValue1,…"
+
+	saves all parameters (default as well as settable) using saveVars('table')
+
+	return value is the number of assigned parameters.
+	"""
+	o=Omega()
+	tagsParams=[]
+	dictDefaults,dictParams={},{}
+	import os, __builtin__
+	if not tableFileLine and not os.environ.has_key('PARAM_TABLE'):
+		if not noTableOk: raise EnvironmentError("PARAM_TABLE is not defined in the environment")
+		o.tags['line']='l!'
+	else:
+		if not tableFileLine: tableFileLine=os.environ['PARAM_TABLE']
+		env=tableFileLine.split(':')
+		tableDesc=None
+		tableFile,tableLine=env[0],env[1]
+		if len(env)>2: tableDesc=env[3]
+		o.tags['line']='l'+tableLine
+		ll=[l.split('#')[0] for l in ['']+open(tableFile).readlines()]; names=ll[1].split(); values=ll[int(tableLine)].split()
+		assert(len(names)==len(values))
+		if 'description' in names: O.tags['description']=values[names.index('description')]
+		else:
+			bangCols=[i for i,h in enumerate(names) if h[-1]=='!']
+			if len(bangCols)==0: bangCols=range(len(names))
+			for i in range(len(names)):
+				if names[i][-1]=='!': names[i]=names[i][:-1] # strip trailing !
+			O.tags['description']=','.join(names[col]+'='+('%g'%values[col] if isinstance(values[col],float) else str(values[col])) for col in bangCols).replace("'",'').replace('"','')
+		for i in range(len(names)):
+			if names[i]=='description': continue
+			if names[i] not in kw.keys():
+				if (not unknownOk) and names[i][0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
+			else: kw.pop(names[i])
+			if names[i][0]!='!':
+				exec('%s=%s'%(names[i],values[i])) in __builtins__; tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
+	defaults=[]
+	for k in kw.keys():
+		exec("%s=%s"%(k,repr(kw[k]))) in __builtins__
+		defaults+=["%s=%s"%(k,kw[k])]; dictDefaults[k]=kw[k]
+	o.tags['defaultParams']=",".join(defaults)
+	o.tags['params']=",".join(tagsParams)
+	dictParams.update(dictDefaults); saveVars('table',**dictParams)
+	return len(tagsParams)
+
+def ColorizedVelocityFilter(isFilterActivated=True,autoScale=True,minValue=0,maxValue=0,posX=0,posY=0.2,width=0.05,height=0.5,title='Velocity, m/s'):
+    f = DeusExMachina('ColorizedVelocityFilter',{'isFilterActivated':isFilterActivated,'autoScale':autoScale,'minValue':minValue,'maxValue':maxValue,'posX':posX,'posY':posY,'width':width,'height':height,'title':title})
+    O.engines+=[f]
+    return f
+
+def ColorizedTimeFilter(point=[0,0,0],normal=[0,1,0],isFilterActivated=True,autoScale=True,minValue=0,maxValue=0,posX=0,posY=0.2,width=0.05,height=0.5,title='Time, m/s'):
+    f = DeusExMachina('ColorizedTimeFilter',{'point':point,'normal':normal,'isFilterActivated':isFilterActivated,'autoScale':autoScale,'minValue':minValue,'maxValue':maxValue,'posX':posX,'posY':posY,'width':width,'height':height,'title':title})
+    O.engines+=[f]
+    return f
+
+def PythonRunnerFilter(command='pass',isFilterActivated=True):
+    f = DeusExMachina('PythonRunnerFilter',{'command':command,'isFilterActivated':isFilterActivated})
+    O.engines+=[f]
+    return f
+
+def replaceCollider(colliderEngine):
+	"""Replaces collider (Collider) engine with the engine supplied. Raises error if no collider is in engines."""
+	colliderIdx=-1
+	for i,e in enumerate(O.engines):
+		if O.isChildClassOf(e.name,"Collider"):
+			colliderIdx=i
+			break
+	if colliderIdx<0: raise RuntimeError("No Collider found within O.engines.")
+	O.engines=O.engines[:colliderIdx]+[colliderEngine]+O.engines[colliderIdx+1:]
+
+
+def procStatus(name):
+	import os
+	for l in open('/proc/%d/status'%os.getpid()):
+		if l.split(':')[0]==name: return l
+	raise "No such line in /proc/[pid]/status: "+name
+def vmData():
+	l=procStatus('VmData'); ll=l.split(); assert(ll[2]=='kB')
+	return int(ll[1])
+
+def spheresFromFileUniaxial(filename,areaSections=10,**kw):
+	"""Load spheres from file, but do some additional work useful for uniaxial test:
+	
+	1. Find the dimensions that is the longest (uniaxial loading axis)
+	2. Find the minimum cross-section area of the speciment by examining several (areaSections)
+		sections perpendicular to axis, computing area of the convex hull for each one. This will
+		work also for non-prismatic specimen.
+	3. Find the bodies that are on the negative/positive boundary, to which the straining condition
+		should be applied.
+
+	Returns dictionary with keys 'negIds', 'posIds', 'axis', 'area'.
+	"""
+	ids=spheresFromFile(filename,**kw)
+	mm,mx=aabbExtrema()
+	dim=aabbDim(); axis=dim.index(max(dim))
+	import numpy
+	areas=[approxSectionArea(coord,axis) for coord in numpy.linspace(mm[axis],mx[axis],num=10)[1:-1]]
+	negIds,posIds=negPosExtremeIds(axis=axis,distFactor=2.2)
+	return {'negIds':negIds,'posIds':posIds,'axis':axis,'area':min(areas)}
+
+def NormalRestitution2DampingRate(en):
+        """Compute the normal damping rate as a function of the normal coefficient of restitution.
+        """
+	if en == 0.0: return 0.999999999
+	if en == 1.0: return 0.0
+	from math import sqrt,log,pi
+	ln_en = math.log(en)
+	return (-ln_en/math.sqrt((math.pow(ln_en,2) + math.pi*math.pi)))
+
+def xMirror(half):
+	"""Mirror a sequence of 2d points around the x axis (changing sign on the y coord).
+	The sequence should start up and then it will wrap from y downwards (or vice versa).
+	If the last point's x coord is zero, it will not be duplicated."""
+	return list(half)+[(x,-y) for x,y in reversed(half[:-1] if half[-1][0]==0 else half)]
+

Copied: trunk/py/yadeWrapper/yadeWrapper.cpp (from rev 1834, trunk/gui/py/yadeControl.cpp)
===================================================================
--- trunk/gui/py/yadeControl.cpp	2009-07-02 15:26:02 UTC (rev 1834)
+++ trunk/py/yadeWrapper/yadeWrapper.cpp	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,849 @@
+// 2007,2008 © Václav Šmilauer <eudoxos@xxxxxxxx> 
+
+#include<sstream>
+#include<map>
+#include<vector>
+#include<unistd.h>
+#include<list>
+
+
+#include<boost/python.hpp>
+#include<boost/python/suite/indexing/vector_indexing_suite.hpp>
+#include<boost/bind.hpp>
+#include<boost/thread/thread.hpp>
+#include<boost/filesystem/operations.hpp>
+#include<boost/date_time/posix_time/posix_time.hpp>
+#include<boost/any.hpp>
+#include<boost/python.hpp>
+#include<boost/foreach.hpp>
+#include<boost/algorithm/string.hpp>
+
+// [boost1.34] #include<boost/python/stl_iterator.hpp>
+
+#include<yade/lib-base/Logging.hpp>
+#include<yade/lib-serialization-xml/XMLFormatManager.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/core/FileGenerator.hpp>
+
+#include<yade/lib-import/STLImporter.hpp>
+
+#include<yade/core/MetaEngine.hpp>
+#include<yade/core/MetaEngine1D.hpp>
+#include<yade/core/MetaEngine2D.hpp>
+#include<yade/core/StandAloneEngine.hpp>
+#include<yade/core/DeusExMachina.hpp>
+#include<yade/core/EngineUnit.hpp>
+#include<yade/pkg-common/ParallelEngine.hpp>
+#include<yade/core/EngineUnit1D.hpp>
+#include<yade/core/EngineUnit2D.hpp>
+
+#include<yade/pkg-common/BoundingVolumeMetaEngine.hpp>
+#include<yade/pkg-common/GeometricalModelMetaEngine.hpp>
+#include<yade/pkg-common/InteractingGeometryMetaEngine.hpp>
+#include<yade/pkg-common/InteractionGeometryMetaEngine.hpp>
+#include<yade/pkg-common/InteractionPhysicsMetaEngine.hpp>
+#include<yade/pkg-common/PhysicalParametersMetaEngine.hpp>
+#include<yade/pkg-common/ConstitutiveLawDispatcher.hpp>
+#include<yade/pkg-common/InteractionDispatchers.hpp>
+#include<yade/pkg-common/PhysicalActionDamper.hpp>
+#include<yade/pkg-common/PhysicalActionApplier.hpp>
+#include<yade/pkg-common/MetaInteractingGeometry.hpp>
+#include<yade/pkg-common/AABB.hpp>
+#include<yade/pkg-common/ParticleParameters.hpp>
+
+#include<yade/pkg-common/BoundingVolumeEngineUnit.hpp>
+#include<yade/pkg-common/GeometricalModelEngineUnit.hpp>
+#include<yade/pkg-common/InteractingGeometryEngineUnit.hpp>
+#include<yade/pkg-common/InteractionGeometryEngineUnit.hpp>
+#include<yade/pkg-common/InteractionPhysicsEngineUnit.hpp>
+#include<yade/pkg-common/PhysicalParametersEngineUnit.hpp>
+#include<yade/pkg-common/PhysicalActionDamperUnit.hpp>
+#include<yade/pkg-common/PhysicalActionApplierUnit.hpp>
+#include<yade/pkg-common/ConstitutiveLaw.hpp>
+
+#include<yade/extra/Shop.hpp>
+#include<yade/pkg-dem/Clump.hpp>
+
+using namespace boost;
+using namespace std;
+
+#include<yade/gui-py/pyAttrUtils.hpp>
+#include<yade/extra/boost_python_len.hpp>
+
+class RenderingEngine;
+
+/*!
+	
+	A regular class (not Omega) is instantiated like this:
+
+		RootClass('optional class name as quoted string',{optional dictionary of attributes})
+		
+	if class name is not given, the RootClass itself is instantiated
+
+		p=PhysicalParameters() # p is now instance of PhysicalParameters
+		p=PhysicalParameters('RigidBodyParameters') # p is now instance of RigidBodyParameters, which has PhysicalParameters as the "root" class
+		p=PhysicalParameters('RigidBodyParameters',{'mass':100,'se3':[1,1,2,1,0,0,0]}) # convenience constructor
+
+	The last statement is equivalent to:
+
+		p=PhysicalParameters('RigidBodyParameters')
+		p['mass']=100; 
+		p['se3']=[1,1,2,1,0,0,0]
+
+	Class attributes are those that are registered as serializable, are accessed using the [] operator and are always read-write (be careful)
+
+		p['se3'] # this will show you the se3 attribute inside p
+		p['se3']=[1,2,3,1,0,0,0] # this sets se3 of p
+
+	Those attributes that are not fundamental types (strings, numbers, booleans, se3, vectors, quaternions, arrays of numbers, arrays of strings) can be accessed only through explicit python data members, for example:
+		
+		b=Body()
+		b.mold=InteractingGeometry("InteractingSphere",{'radius':1})
+		b.shape=GeometricalModel("Sphere",{'radius':1})
+		b.mold # will give you the interactingGeometry of body
+	
+	Instances can be queried about attributes and data members they have:
+
+		b.keys() # serializable attributes, accessible via b['attribute']
+		dir(b) # python data members, accessible via b.attribute; the __something__ attributes are python internal attributes/metods -- methods are just callable members
+
+	MetaEngine class has special constructor (for convenience):
+
+		m=MetaEngine('class name as string',[list of engine units])
+
+	and it is equivalent to
+
+		m=MetaEntine('class name as string')
+		m.functors=[list of engine units]
+
+	It is your responsibility to pass the right engineUnits, otherwise crash will results. There is currently no way I know of to prevent that. 
+
+*/
+
+/*
+TODO:
+	1. PhysicalActionContainer (constructor with actionName) with iteration
+	2. from yadeControl import Omega as _Omega, inherit from that and add other convenience functions
+*/
+
+#ifdef LOG4CXX
+	log4cxx::LoggerPtr logger=log4cxx::Logger::getLogger("yade.python");
+#endif
+
+BASIC_PY_PROXY(pyGeneric,Serializable);
+
+BASIC_PY_PROXY(pyInteractionGeometry,InteractionGeometry);
+BASIC_PY_PROXY(pyInteractionPhysics,InteractionPhysics);
+
+BASIC_PY_PROXY(pyGeometricalModel,GeometricalModel);
+BASIC_PY_PROXY_HEAD(pyPhysicalParameters,PhysicalParameters)
+	python::list blockedDOFs_get(){
+		python::list ret;
+		#define _SET_DOF(DOF_ANY,str) if((proxee->blockedDOFs & PhysicalParameters::DOF_ANY)!=0) ret.append(str);
+		_SET_DOF(DOF_X,"x"); _SET_DOF(DOF_Y,"y"); _SET_DOF(DOF_Z,"z"); _SET_DOF(DOF_RX,"rx"); _SET_DOF(DOF_RY,"ry"); _SET_DOF(DOF_RZ,"rz");
+		#undef _SET_DOF
+		return ret;
+	}
+	void blockedDOFs_set(python::list l){
+		proxee->blockedDOFs=PhysicalParameters::DOF_NONE;
+		int len=python::len(l);
+		for(int i=0; i<len; i++){
+			string s=python::extract<string>(l[i])();
+			#define _GET_DOF(DOF_ANY,str) if(s==str) { proxee->blockedDOFs|=PhysicalParameters::DOF_ANY; continue; }
+			_GET_DOF(DOF_X,"x"); _GET_DOF(DOF_Y,"y"); _GET_DOF(DOF_Z,"z"); _GET_DOF(DOF_RX,"rx"); _GET_DOF(DOF_RY,"ry"); _GET_DOF(DOF_RZ,"rz");
+			#undef _GET_DOF
+			throw std::invalid_argument("Invalid  DOF specification `"+s+"', must be ∈{x,y,z,rx,ry,rz}.");
+		}
+	}
+	Vector3r displ_get(){return proxee->se3.position-proxee->refSe3.position;}
+	python::tuple rot_get(){Quaternionr relRot=proxee->refSe3.orientation.Conjugate()*proxee->se3.orientation; Vector3r axis; Real angle; relRot.ToAxisAngle(axis,angle); axis*=angle; return python::make_tuple(axis[0],axis[1],axis[2]); }
+	Vector3r pos_get(){return proxee->se3.position;}
+	Vector3r refPos_get(){return proxee->refSe3.position;}
+	python::tuple ori_get(){Vector3r axis; Real angle; proxee->se3.orientation.ToAxisAngle(axis,angle); return python::make_tuple(axis[0],axis[1],axis[2],angle);}
+	void pos_set(const Vector3r& p){ proxee->se3.position=p; }
+	void refPos_set(const Vector3r& p){ proxee->refSe3.position=p;}
+	void ori_set(python::list l){if(python::len(l)!=4) throw invalid_argument("Wrong number of quaternion elements "+lexical_cast<string>(python::len(l))+", should be 4"); proxee->se3.orientation=Quaternionr(Vector3r(python::extract<double>(l[0])(),python::extract<double>(l[1])(),python::extract<double>(l[2])()),python::extract<double>(l[3])());}
+BASIC_PY_PROXY_TAIL;
+
+BASIC_PY_PROXY(pyBoundingVolume,BoundingVolume);
+BASIC_PY_PROXY(pyInteractingGeometry,InteractingGeometry);
+
+struct pyTimingDeltas{
+	shared_ptr<TimingDeltas> proxee;
+	pyTimingDeltas(shared_ptr<TimingDeltas> td){proxee=td;}
+	python::list data_get(){
+		python::list ret;
+		for(size_t i=0; i<proxee->data.size(); i++){
+			ret.append(python::make_tuple(proxee->labels[i],proxee->data[i].nsec,proxee->data[i].nExec));
+		}
+		return ret;
+	}
+	void reset(){proxee->data.clear(); proxee->labels.clear();}
+};
+
+#define PY_PROXY_TIMING \
+	TimingInfo::delta execTime_get(void){return proxee->timingInfo.nsec;} void execTime_set(TimingInfo::delta t){proxee->timingInfo.nsec=t;} \
+	long execCount_get(void){return proxee->timingInfo.nExec;} void execCount_set(long n){proxee->timingInfo.nExec=n;} \
+	python::object timingDeltas_get(void){return proxee->timingDeltas?python::object(pyTimingDeltas(proxee->timingDeltas)):python::object();}
+
+
+BASIC_PY_PROXY_HEAD(pyDeusExMachina,DeusExMachina)
+	PY_PROXY_TIMING
+BASIC_PY_PROXY_TAIL;
+
+BASIC_PY_PROXY_HEAD(pyStandAloneEngine,StandAloneEngine)
+	PY_PROXY_TIMING
+BASIC_PY_PROXY_TAIL;
+	
+
+python::list anyEngines_get(const vector<shared_ptr<Engine> >&);
+void anyEngines_set(vector<shared_ptr<Engine> >&, python::object);
+
+BASIC_PY_PROXY_HEAD(pyParallelEngine,ParallelEngine)
+	pyParallelEngine(python::list slaves){init("ParallelEngine"); slaves_set(slaves);}
+	void slaves_set(python::list slaves){
+		ensureAcc(); shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)");
+		int len=python::len(slaves);
+		me->slaves=ParallelEngine::slaveContainer(); // empty the container
+		for(int i=0; i<len; i++){
+			python::extract<python::list> grpMaybe(slaves[i]);
+			python::list grpList;
+			if(grpMaybe.check()){ grpList=grpMaybe(); }
+			else{ /* we got a standalone thing; let's wrap it in list */ grpList.append(slaves[i]); }
+			vector<shared_ptr<Engine> > grpVec;
+			anyEngines_set(grpVec,grpList);
+			me->slaves.push_back(grpVec);
+		}
+	}
+	python::list slaves_get(void){	
+		ensureAcc(); shared_ptr<ParallelEngine> me=dynamic_pointer_cast<ParallelEngine>(proxee); if(!me) throw runtime_error("Proxied class not a ParallelEngine. (WTF?)");
+		python::list ret;
+		FOREACH(vector<shared_ptr<Engine > >& grp, me->slaves){
+			python::list rret=anyEngines_get(grp);
+			if(python::len(rret)==1){ ret.append(rret[0]); } else ret.append(rret);
+		}
+		return ret;
+	}
+BASIC_PY_PROXY_TAIL;
+
+
+BASIC_PY_PROXY_HEAD(pyEngineUnit,EngineUnit)
+	python::list bases_get(void){ python::list ret; vector<string> t=proxee->getFunctorTypes(); for(size_t i=0; i<t.size(); i++) ret.append(t[i]); return ret; }
+	python::object timingDeltas_get(){return proxee->timingDeltas?python::object(pyTimingDeltas(proxee->timingDeltas)):python::object();}
+BASIC_PY_PROXY_TAIL;
+
+BASIC_PY_PROXY_HEAD(pyMetaEngine,MetaEngine)
+		// additional constructor
+		pyMetaEngine(string clss, python::list functors){init(clss); functors_set(functors);}
+		python::list functors_get(void){
+			ensureAcc(); shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(proxee); if(!me) throw runtime_error("Proxied class not a MetaEngine (?!)"); python::list ret;
+			/* garbage design: functorArguments are instances of EngineUnits, but they may not be present; therefore, only use them if they exist; our pyMetaEngine, however, will always have both names and EnguneUnit objects in the same count */
+			for(size_t i=0; i<me->functorNames.size(); i++){
+				shared_ptr<EngineUnit> eu;
+				string functorName(*(me->functorNames[i].rbegin()));
+				if(i<=me->functorArguments.size()){ /* count i-th list member */ size_t j=0;
+					for(list<shared_ptr<EngineUnit> >::iterator I=me->functorArguments.begin(); I!=me->functorArguments.end(); I++, j++) { if(j==i) { eu=(*I); break;}}
+				}
+				if(!eu) /* either list was shorter or empty pointer in the functorArguments list */ { eu=dynamic_pointer_cast<EngineUnit>(ClassFactory::instance().createShared(functorName)); if(!eu) throw runtime_error("Unable to construct `"+string(*(me->functorNames[i].rbegin()))+"' EngineUnit"); }
+				assert(eu);
+				ret.append(pyEngineUnit(eu));
+			}
+			return ret;
+		}
+		void functors_set(python::list ftrs){
+			ensureAcc(); shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(proxee); if(!me) throw runtime_error("Proxied class not a MetaEngine. (?!)");
+			me->clear(); int len=python::len(ftrs);
+			for(int i=0; i<len; i++){
+				python::extract<pyEngineUnit> euEx(ftrs[i]); if(!euEx.check()) throw invalid_argument("Unable to extract type EngineUnit from sequence.");
+				bool ok=false;
+				/* FIXME: casting engine unit to the right type via dynamic_cast doesn't work (always unusuccessful),
+				 * do static_cast and if the EngineUnit is of wrong type, it will crash badly immediately. */
+				#define TRY_ADD_FUNCTOR(P,Q) {shared_ptr<P> p(dynamic_pointer_cast<P>(me)); shared_ptr<EngineUnit> eu(euEx().proxee); if(p&&eu){p->add(static_pointer_cast<Q>(eu)); ok=true; }}
+				// shared_ptr<Q> q(dynamic_pointer_cast<Q>(eu)); cerr<<#P<<" "<<#Q<<":"<<(bool)p<<" "<<(bool)q<<endl;
+				TRY_ADD_FUNCTOR(BoundingVolumeMetaEngine,BoundingVolumeEngineUnit);
+				TRY_ADD_FUNCTOR(GeometricalModelMetaEngine,GeometricalModelEngineUnit);
+				TRY_ADD_FUNCTOR(InteractingGeometryMetaEngine,InteractingGeometryEngineUnit);
+				TRY_ADD_FUNCTOR(InteractionGeometryMetaEngine,InteractionGeometryEngineUnit);
+				TRY_ADD_FUNCTOR(InteractionPhysicsMetaEngine,InteractionPhysicsEngineUnit);
+				TRY_ADD_FUNCTOR(PhysicalParametersMetaEngine,PhysicalParametersEngineUnit);
+				TRY_ADD_FUNCTOR(PhysicalActionDamper,PhysicalActionDamperUnit);
+				TRY_ADD_FUNCTOR(PhysicalActionApplier,PhysicalActionApplierUnit);
+				TRY_ADD_FUNCTOR(ConstitutiveLawDispatcher,ConstitutiveLaw);
+				if(!ok) throw runtime_error(string("Unable to cast to suitable MetaEngine type when adding functor (MetaEngine: ")+me->getClassName()+", functor: "+euEx().proxee->getClassName()+")");
+				#undef TRY_ADD_FUNCTOR
+			}
+		}
+	PY_PROXY_TIMING
+BASIC_PY_PROXY_TAIL;
+
+BASIC_PY_PROXY_HEAD(pyInteractionDispatchers,InteractionDispatchers)
+	pyInteractionDispatchers(python::list geomFunctors, python::list physFunctors, python::list constLawFunctors){
+		init("InteractionDispatchers");
+		pyMetaEngine(proxee->geomDispatcher).functors_set(geomFunctors);
+		pyMetaEngine(proxee->physDispatcher).functors_set(physFunctors);
+		pyMetaEngine(proxee->constLawDispatcher).functors_set(constLawFunctors);
+	}
+	pyMetaEngine geomDispatcher_get(void){ return pyMetaEngine(proxee->geomDispatcher);}
+	pyMetaEngine physDispatcher_get(void){ return pyMetaEngine(proxee->physDispatcher);}
+	pyMetaEngine constLawDispatcher_get(void){ return pyMetaEngine(proxee->constLawDispatcher);}
+	PY_PROXY_TIMING
+BASIC_PY_PROXY_TAIL;
+
+python::list anyEngines_get(const vector<shared_ptr<Engine> >& engContainer){
+	python::list ret; 
+	FOREACH(const shared_ptr<Engine>& eng, engContainer){
+		#define APPEND_ENGINE_IF_POSSIBLE(engineType,pyEngineType) { shared_ptr<engineType> e=dynamic_pointer_cast<engineType>(eng); if(e) { ret.append(pyEngineType(e)); continue; } }
+		APPEND_ENGINE_IF_POSSIBLE(InteractionDispatchers,pyInteractionDispatchers); APPEND_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine); APPEND_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine); APPEND_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina); APPEND_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine); 
+		throw std::runtime_error("Unknown engine type: `"+eng->getClassName()+"' (only MetaEngine, StandAloneEngine, DeusExMachina and ParallelEngine are supported)");
+	}
+	return ret;
+}
+
+void anyEngines_set(vector<shared_ptr<Engine> >& engContainer, python::object egs){
+	int len=python::len(egs);
+	//const shared_ptr<MetaBody>& rootBody=OMEGA.getRootBody(); rootBody->engines.clear();
+	engContainer.clear();
+	for(int i=0; i<len; i++){
+		#define PUSH_BACK_ENGINE_IF_POSSIBLE(pyEngineType) if(python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)).check()){ pyEngineType e=python::extract<pyEngineType>(PySequence_GetItem(egs.ptr(),i)); engContainer.push_back(e.proxee); /* cerr<<"added "<<e.pyStr()<<", a "<<#pyEngineType<<endl; */ continue; }
+		PUSH_BACK_ENGINE_IF_POSSIBLE(pyStandAloneEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyMetaEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyDeusExMachina); PUSH_BACK_ENGINE_IF_POSSIBLE(pyParallelEngine); PUSH_BACK_ENGINE_IF_POSSIBLE(pyInteractionDispatchers);
+		throw std::runtime_error("Encountered unknown engine type (unable to extract from python object)");
+	}
+}
+
+
+
+BASIC_PY_PROXY_HEAD(pyInteraction,Interaction)
+	NONPOD_ATTRIBUTE_ACCESS(geom,pyInteractionGeometry,interactionGeometry);
+	NONPOD_ATTRIBUTE_ACCESS(phys,pyInteractionPhysics,interactionPhysics);
+	/* shorthands */ unsigned id1_get(void){ensureAcc(); return proxee->getId1();} unsigned id2_get(void){ensureAcc(); return proxee->getId2();}
+	bool isReal_get(void){ensureAcc(); return proxee->isReal(); }
+BASIC_PY_PROXY_TAIL;
+
+BASIC_PY_PROXY_HEAD(pyBody,Body)
+	NONPOD_ATTRIBUTE_ACCESS(shape,pyGeometricalModel,geometricalModel);
+	NONPOD_ATTRIBUTE_ACCESS(mold,pyInteractingGeometry,interactingGeometry);
+	NONPOD_ATTRIBUTE_ACCESS(bound,pyBoundingVolume,boundingVolume);
+	NONPOD_ATTRIBUTE_ACCESS(phys,pyPhysicalParameters,physicalParameters);
+	unsigned id_get(){ensureAcc(); return proxee->getId();}
+	int mask_get(){ensureAcc(); return proxee->groupMask;}
+	void mask_set(int m){ensureAcc(); proxee->groupMask=m;}
+	bool dynamic_get(){ensureAcc(); return proxee->isDynamic;} void dynamic_set(bool dyn){ensureAcc(); proxee->isDynamic=dyn;}
+	bool isStandalone(){ensureAcc(); return proxee->isStandalone();} bool isClumpMember(){ensureAcc(); return proxee->isClumpMember();} bool isClump(){ensureAcc(); return proxee->isClump();}
+BASIC_PY_PROXY_TAIL;
+
+class pyBodyContainer{
+	public:
+	const shared_ptr<BodyContainer> proxee;
+	pyBodyContainer(const shared_ptr<BodyContainer>& _proxee): proxee(_proxee){}
+	pyBody pyGetitem(unsigned id){
+		if(id>=proxee->size()){ PyErr_SetString(PyExc_IndexError, "Body id out of range."); python::throw_error_already_set(); /* make compiler happy; never reached */ return pyBody(); }
+		else return pyBody(proxee->operator[](id));
+	}
+	body_id_t insert(pyBody b){return proxee->insert(b.proxee);}
+	python::list insertList(python::list bb){python::list ret; for(int i=0; i<len(bb); i++){ret.append(insert(python::extract<pyBody>(bb[i])()));} return ret;}
+		python::tuple insertClump(python::list bb){/*clump: first add constitutents, then add clump, then add constitutents to the clump, then update clump props*/
+		python::list ids=insertList(bb);
+		shared_ptr<Clump> clump=shared_ptr<Clump>(new Clump());
+		shared_ptr<Body> clumpAsBody=static_pointer_cast<Body>(clump);
+		clump->isDynamic=true;
+		proxee->insert(clumpAsBody);
+		for(int i=0; i<len(ids); i++){clump->add(python::extract<body_id_t>(ids[i])());}
+		clump->updateProperties(false);
+		return python::make_tuple(clump->getId(),ids);
+	}
+	python::list replace(python::list bb){proxee->clear(); return insertList(bb);}
+	long length(){return proxee->size();}
+	void clear(){proxee->clear();}
+};
+
+
+class pyTags{
+	public:
+		pyTags(const shared_ptr<MetaBody> _mb): mb(_mb){}
+		const shared_ptr<MetaBody> mb;
+		bool hasKey(string key){ FOREACH(string val, mb->tags){ if(algorithm::starts_with(val,key+"=")){ return true;} } return false; }
+		string getItem(string key){
+			FOREACH(string& val, mb->tags){
+				if(algorithm::starts_with(val,key+"=")){ string val1(val); algorithm::erase_head(val1,key.size()+1); algorithm::replace_all(val1,"~"," "); return val1;}
+			}
+			PyErr_SetString(PyExc_KeyError, "Invalid key.");
+			python::throw_error_already_set(); /* make compiler happy; never reached */ return string();
+		}
+		void setItem(string key,string newVal){
+			string item=algorithm::replace_all_copy(key+"="+newVal," ","~");
+			FOREACH(string& val, mb->tags){if(algorithm::starts_with(val,key+"=")){ val=item; return; } }
+			mb->tags.push_back(item);
+			}
+		python::list keys(){
+			python::list ret;
+			FOREACH(string val, mb->tags){
+				size_t i=val.find("=");
+				if(i==string::npos) throw runtime_error("Tags must be in the key=value format");
+				algorithm::erase_tail(val,val.size()-i); ret.append(val);
+			}
+			return ret;
+		}
+};
+
+class pyInteractionIterator{
+	InteractionContainer::iterator I, Iend;
+	public:
+	pyInteractionIterator(const shared_ptr<InteractionContainer>& ic){ I=ic->begin(); Iend=ic->end(); }
+	pyInteractionIterator pyIter(){return *this;}
+	pyInteraction pyNext(){ if(!(I!=Iend)){ PyErr_SetNone(PyExc_StopIteration); python::throw_error_already_set(); }
+		InteractionContainer::iterator ret=I; ++I; return pyInteraction(*ret); }
+};
+
+class pyInteractionContainer{
+	public:
+		const shared_ptr<InteractionContainer> proxee;
+		pyInteractionContainer(const shared_ptr<InteractionContainer>& _proxee): proxee(_proxee){}
+		pyInteractionIterator pyIter(){return pyInteractionIterator(proxee);}
+		pyInteraction pyGetitem(python::object id12){
+			if(!PySequence_Check(id12.ptr())) throw invalid_argument("Key must be a tuple");
+			if(python::len(id12)!=2) throw invalid_argument("Key must be a 2-tuple: id1,id2.");
+			python::extract<body_id_t> id1_(PySequence_GetItem(id12.ptr(),0)), id2_(PySequence_GetItem(id12.ptr(),1));
+			if(!id1_.check()) throw invalid_argument("Could not extract id1");
+			if(!id2_.check()) throw invalid_argument("Could not extract id2");
+			shared_ptr<Interaction> i=proxee->find(id1_(),id2_());
+			if(i) return pyInteraction(i); else throw invalid_argument("No such interaction.");
+		}
+		/* return nth _real_ iteration from the container (0-based index); this is to facilitate picking random interaction */
+		pyInteraction pyNth(long n){
+			long i=0; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(!I->isReal()) continue; if(i++==n) return pyInteraction(I); }
+			throw invalid_argument(string("Interaction number out of range (")+lexical_cast<string>(n)+">="+lexical_cast<string>(i)+").");
+		}
+		long len(){return proxee->size();}
+		void clear(){proxee->clear();}
+		python::list withBody(long id){ python::list ret; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(I->isReal() && (I->getId1()==id || I->getId2()==id)) ret.append(pyInteraction(I));} return ret;}
+		python::list withBodyAll(long id){ python::list ret; FOREACH(const shared_ptr<Interaction>& I, *proxee){ if(I->getId1()==id || I->getId2()==id) ret.append(pyInteraction(I));} return ret; }
+};
+
+Vector3r tuple2vec(const python::tuple& t){return Vector3r(python::extract<double>(t[0])(),python::extract<double>(t[1])(),python::extract<double>(t[2])());}
+
+class pyBexContainer{
+	public:
+		pyBexContainer(){}
+		python::tuple force_get(long id){  MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r f=rb->bex.getForce(id); return python::make_tuple(f[0],f[1],f[2]); }
+		python::tuple torque_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getTorque(id); return python::make_tuple(m[0],m[1],m[2]);}
+		python::tuple move_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getMove(id); return python::make_tuple(m[0],m[1],m[2]);}
+		python::tuple rot_get(long id){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.sync(); Vector3r m=rb->bex.getRot(id); return python::make_tuple(m[0],m[1],m[2]);}
+		void force_add(long id, const Vector3r& f){  MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addForce (id,f); }
+		void torque_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addTorque(id,t);}
+		void move_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addMove(id,t);}
+		void rot_add(long id, const Vector3r& t){ MetaBody* rb=Omega::instance().getRootBody().get(); rb->bex.addRot(id,t);}
+};
+
+class pyOmega{
+	private:
+		// can be safely removed now, since pyOmega makes an empty rootBody in the constructor, if there is none
+		void assertRootBody(){if(!OMEGA.getRootBody()) throw std::runtime_error("No root body."); }
+		Omega& OMEGA;
+	public:
+	pyOmega(): OMEGA(Omega::instance()){
+		shared_ptr<MetaBody> rb=OMEGA.getRootBody();
+		assert(rb);
+		if(!rb->physicalParameters){rb->physicalParameters=shared_ptr<PhysicalParameters>(new ParticleParameters);} /* PhysicalParameters crashes PhysicalParametersMetaEngine... why? */
+		if(!rb->boundingVolume){rb->boundingVolume=shared_ptr<AABB>(new AABB);}
+		// initialized in constructor now: rb->boundingVolume->diffuseColor=Vector3r(1,1,1); 
+		if(!rb->interactingGeometry){rb->interactingGeometry=shared_ptr<MetaInteractingGeometry>(new MetaInteractingGeometry);}
+		//if(!OMEGA.getRootBody()){shared_ptr<MetaBody> mb=Shop::rootBody(); OMEGA.setRootBody(mb);}
+		/* this is not true if another instance of Omega is created; flag should be stored inside the Omega singleton for clean solution. */
+		if(!OMEGA.hasSimulationLoop()){
+			OMEGA.createSimulationLoop();
+		}
+	};
+	/* Create variables in python's __builtin__ namespace that correspond to labeled objects. At this moment, only engines can be labeled. */
+	void mapLabeledEntitiesToVariables(){
+		FOREACH(const shared_ptr<Engine>& e, OMEGA.getRootBody()->engines){
+			if(!e->label.empty()){
+				PyGILState_STATE gstate; gstate = PyGILState_Ensure();
+				PyRun_SimpleString(("__builtins__."+e->label+"=Omega().labeledEngine('"+e->label+"')").c_str());
+				PyGILState_Release(gstate);
+			}
+			if(isChildClassOf(e->getClassName(),"MetaEngine")){
+				shared_ptr<MetaEngine> ee=dynamic_pointer_cast<MetaEngine>(e);
+				FOREACH(const shared_ptr<EngineUnit>& f, ee->functorArguments){
+					if(!f->label.empty()){
+						PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyRun_SimpleString(("__builtins__."+f->label+"=Omega().labeledEngine('"+f->label+"')").c_str()); PyGILState_Release(gstate);
+					}
+				}
+			}
+			if(e->getClassName()=="InteractionDispatchers"){
+				shared_ptr<InteractionDispatchers> ee=dynamic_pointer_cast<InteractionDispatchers>(e);
+				list<shared_ptr<EngineUnit> > eus;
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->geomDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->physDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->constLawDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,eus){
+					if(!eu->label.empty()){
+						PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyRun_SimpleString(("__builtins__."+eu->label+"=Omega().labeledEngine('"+eu->label+"')").c_str()); PyGILState_Release(gstate);
+					}
+				}
+			}
+		}
+	}
+
+	long iter(){ return OMEGA.getCurrentIteration();}
+	double simulationTime(){return OMEGA.getSimulationTime();}
+	double realTime(){ return OMEGA.getComputationTime(); }
+	// long realTime(){return OMEGA(get...);}
+	double dt_get(){return OMEGA.getTimeStep();}
+	void dt_set(double dt){OMEGA.skipTimeStepper(true); OMEGA.setTimeStep(dt);}
+
+	long stopAtIter_get(){return OMEGA.getRootBody()->stopAtIteration; }
+	void stopAtIter_set(long s){OMEGA.getRootBody()->stopAtIteration=s; }
+
+	bool usesTimeStepper_get(){return OMEGA.timeStepperActive();}
+	void usesTimeStepper_set(bool use){OMEGA.skipTimeStepper(!use);}
+
+	bool timingEnabled_get(){return TimingInfo::enabled;}
+	void timingEnabled_set(bool enabled){TimingInfo::enabled=enabled;}
+	unsigned long bexSyncCount_get(){ return OMEGA.getRootBody()->bex.syncCount;}
+	void bexSyncCount_set(unsigned long count){ OMEGA.getRootBody()->bex.syncCount=count;}
+
+	void run(long int numIter=-1,bool doWait=false){
+		if(numIter>0) OMEGA.getRootBody()->stopAtIteration=OMEGA.getCurrentIteration()+numIter;
+		OMEGA.startSimulationLoop();
+		LOG_DEBUG("RUN"<<((OMEGA.getRootBody()->stopAtIteration-OMEGA.getCurrentIteration())>0?string(" ("+lexical_cast<string>(OMEGA.getRootBody()->stopAtIteration-OMEGA.getCurrentIteration())+" to go)"):string(""))<<"!");
+		if(doWait) wait();
+	}
+	void pause(){Py_BEGIN_ALLOW_THREADS; OMEGA.stopSimulationLoop(); Py_END_ALLOW_THREADS; LOG_DEBUG("PAUSE!");}
+	void step() { LOG_DEBUG("STEP!"); run(1); wait();  }
+	void wait(){ if(OMEGA.isRunning()){LOG_DEBUG("WAIT!");} else return; timespec t1,t2; t1.tv_sec=0; t1.tv_nsec=40000000; /* 40 ms */ Py_BEGIN_ALLOW_THREADS; while(OMEGA.isRunning()) nanosleep(&t1,&t2); Py_END_ALLOW_THREADS; }
+
+	void load(std::string fileName) {
+		Py_BEGIN_ALLOW_THREADS; OMEGA.joinSimulationLoop(); Py_END_ALLOW_THREADS; 
+		OMEGA.setSimulationFileName(fileName);
+		OMEGA.loadSimulation();
+		OMEGA.createSimulationLoop();
+		mapLabeledEntitiesToVariables();
+		LOG_DEBUG("LOAD!");
+	}
+	void reload(){	load(OMEGA.getSimulationFileName());}
+	void saveTmp(string mark=""){ save(":memory:"+mark);}
+	void loadTmp(string mark=""){ load(":memory:"+mark);}
+	void tmpToFile(string mark, string filename){
+		if(OMEGA.memSavedSimulations.count(":memory:"+mark)==0) throw runtime_error("No memory-saved simulation named "+mark);
+		iostreams::filtering_ostream out;
+		if(boost::algorithm::ends_with(filename,".bz2")) out.push(iostreams::bzip2_compressor());
+		out.push(iostreams::file_sink(filename));
+		if(!out.good()) throw runtime_error("Error while opening file `"+filename+"' for writing.");
+		LOG_INFO("Saving :memory:"<<mark<<" to "<<filename);
+		out<<OMEGA.memSavedSimulations[":memory:"+mark];
+	}
+
+
+
+	void reset(){Py_BEGIN_ALLOW_THREADS; OMEGA.reset(); Py_END_ALLOW_THREADS; }
+	void resetTime(){ OMEGA.getRootBody()->currentIteration=0; OMEGA.getRootBody()->simulationTime=0; OMEGA.timeInit(); }
+	void switchWorld(){ std::swap(OMEGA.rootBody,OMEGA.rootBodyAnother); }
+
+	void save(std::string fileName){
+		assertRootBody();
+		OMEGA.saveSimulation(fileName);
+		OMEGA.setSimulationFileName(fileName);
+		LOG_DEBUG("SAVE!");
+	}
+
+	void saveSpheres(std::string fileName){ Shop::saveSpheresToFile(fileName); }
+
+	python::list miscParams_get(){
+		python::list ret;
+		FOREACH(shared_ptr<Serializable>& s, OMEGA.getRootBody()->miscParams){
+			ret.append(pyGeneric(s));
+		}
+		return ret;
+	}
+
+	void miscParams_set(python::list l){
+		int len=python::len(l);
+		vector<shared_ptr<Serializable> >& miscParams=OMEGA.getRootBody()->miscParams;
+		miscParams.clear();
+		for(int i=0; i<len; i++){
+			if(python::extract<pyGeneric>(PySequence_GetItem(l.ptr(),i)).check()){ pyGeneric g=python::extract<pyGeneric>(PySequence_GetItem(l.ptr(),i)); miscParams.push_back(g.proxee); }
+			else throw std::invalid_argument("Unable to extract `Generic' from item #"+lexical_cast<string>(i)+".");
+		}
+	}
+
+	python::list engines_get(void){assertRootBody(); return anyEngines_get(OMEGA.getRootBody()->engines);}
+	void engines_set(python::object egs){assertRootBody(); anyEngines_set(OMEGA.getRootBody()->engines,egs); mapLabeledEntitiesToVariables(); }
+	python::list initializers_get(void){assertRootBody(); return anyEngines_get(OMEGA.getRootBody()->initializers);}
+	void initializers_set(python::object egs){assertRootBody(); anyEngines_set(OMEGA.getRootBody()->initializers,egs); OMEGA.getRootBody()->needsInitializers=true; }
+
+	python::object labeled_engine_get(string label){
+		FOREACH(const shared_ptr<Engine>& eng, OMEGA.getRootBody()->engines){
+			if(eng->label==label){
+				#define RETURN_ENGINE_IF_POSSIBLE(engineType,pyEngineType) { shared_ptr<engineType> e=dynamic_pointer_cast<engineType>(eng); if(e) return python::object(pyEngineType(e)); }
+				RETURN_ENGINE_IF_POSSIBLE(MetaEngine,pyMetaEngine);
+				RETURN_ENGINE_IF_POSSIBLE(StandAloneEngine,pyStandAloneEngine);
+				RETURN_ENGINE_IF_POSSIBLE(DeusExMachina,pyDeusExMachina);
+				RETURN_ENGINE_IF_POSSIBLE(ParallelEngine,pyParallelEngine);
+				throw std::runtime_error("Unable to cast engine to MetaEngine, StandAloneEngine, DeusExMachina or ParallelEngine? ??");
+			}
+			shared_ptr<MetaEngine> me=dynamic_pointer_cast<MetaEngine>(eng);
+			if(me){
+				FOREACH(const shared_ptr<EngineUnit>& eu, me->functorArguments){
+					if(eu->label==label) return python::object(pyEngineUnit(eu));
+				}
+			}
+			shared_ptr<InteractionDispatchers> ee=dynamic_pointer_cast<InteractionDispatchers>(eng);
+			if(ee){
+				list<shared_ptr<EngineUnit> > eus;
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->geomDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->physDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,ee->constLawDispatcher->functorArguments) eus.push_back(eu);
+				FOREACH(const shared_ptr<EngineUnit>& eu,eus){
+					if(eu->label==label) return python::object(pyEngineUnit(eu));
+				}
+			}
+		}
+		throw std::invalid_argument(string("No engine labeled `")+label+"'");
+	}
+	
+	pyBodyContainer bodies_get(void){assertRootBody(); return pyBodyContainer(OMEGA.getRootBody()->bodies); }
+	pyInteractionContainer interactions_get(void){assertRootBody(); return pyInteractionContainer(OMEGA.getRootBody()->interactions); }
+	
+	pyBexContainer bex_get(void){return pyBexContainer();}
+	
+
+	boost::python::list listChildClasses(const string& base){
+		boost::python::list ret;
+		for(map<string,DynlibDescriptor>::const_iterator di=Omega::instance().getDynlibsDescriptor().begin();di!=Omega::instance().getDynlibsDescriptor().end();++di) if (Omega::instance().isInheritingFrom((*di).first,base)) ret.append(di->first);
+		return ret;
+	}
+
+	bool isChildClassOf(const string& child, const string& base){
+		return (Omega::instance().isInheritingFrom(child,base));
+	}
+
+	boost::python::list plugins_get(){
+		const map<string,DynlibDescriptor>& plugins=Omega::instance().getDynlibsDescriptor();
+		std::pair<string,DynlibDescriptor> p; boost::python::list ret;
+		FOREACH(p, plugins) ret.append(p.first);
+		return ret;
+	}
+
+	pyTags tags_get(void){assertRootBody(); return pyTags(OMEGA.getRootBody());}
+
+	void interactionContainer_set(string clss){
+		MetaBody* rb=OMEGA.getRootBody().get();
+		if(rb->interactions->size()>0) throw std::runtime_error("Interaction container not empty, will not change its class.");
+		shared_ptr<InteractionContainer> ic=dynamic_pointer_cast<InteractionContainer>(ClassFactory::instance().createShared(clss));
+		rb->interactions=ic;
+	}
+	string interactionContainer_get(string clss){ return OMEGA.getRootBody()->interactions->getClassName(); }
+
+	void bodyContainer_set(string clss){
+		MetaBody* rb=OMEGA.getRootBody().get();
+		if(rb->bodies->size()>0) throw std::runtime_error("Body container not empty, will not change its class.");
+		shared_ptr<BodyContainer> bc=dynamic_pointer_cast<BodyContainer>(ClassFactory::instance().createShared(clss));
+		rb->bodies=bc;
+	}
+	string bodyContainer_get(string clss){ return OMEGA.getRootBody()->bodies->getClassName(); }
+	#ifdef YADE_OPENMP
+		int numThreads_get(){ return omp_get_max_threads();}
+		void numThreads_set(int n){ int bcn=OMEGA.getRootBody()->bex.getNumAllocatedThreads(); if(bcn<n) LOG_WARN("BexContainer has only "<<bcn<<" threads allocated. Changing thread number to on "<<bcn<<" instead of "<<n<<" requested."); omp_set_num_threads(min(n,bcn)); LOG_WARN("BUG: Omega().numThreads=n doesn't work as expected (number of threads is not changed globally). Set env var OMP_NUM_THREADS instead."); }
+	#else
+		int numThreads_get(){return 1;}
+		void numThreads_set(int n){ LOG_WARN("Yade was compiled without openMP support, changing number of threads will have no effect."); }
+	#endif
+
+};
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_run_overloads,run,0,2);
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_saveTmp_overloads,saveTmp,0,1);
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(omega_loadTmp_overloads,loadTmp,0,1);
+
+BASIC_PY_PROXY_HEAD(pyFileGenerator,FileGenerator)
+	void generate(string outFile){ensureAcc(); proxee->setFileName(outFile); proxee->setSerializationLibrary("XMLFormatManager"); bool ret=proxee->generateAndSave(); LOG_INFO((ret?"SUCCESS:\n":"FAILURE:\n")<<proxee->message); if(ret==false) throw runtime_error("Generator reported error: "+proxee->message); };
+	void load(){ ensureAcc(); char tmpnam_str [L_tmpnam]; char* result=tmpnam(tmpnam_str); if(result!=tmpnam_str) throw runtime_error(__FILE__ ": tmpnam(char*) failed!");  string xml(tmpnam_str+string(".xml.bz2")); LOG_DEBUG("Using temp file "<<xml); this->generate(xml); pyOmega().load(xml); }
+BASIC_PY_PROXY_TAIL;
+
+class pySTLImporter : public STLImporter {
+    public:
+	void py_import(pyBodyContainer bc, unsigned int begin=0, bool noInteractingGeometry=false) { import(bc.proxee,begin,noInteractingGeometry); }
+};
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(STLImporter_import_overloads,py_import,1,3);
+
+
+// automatic conversion of Vector3r and 3-tuple
+// this doesn't create any new class in python
+struct custom_Vector3r_to_tuple{
+	static PyObject* convert(Vector3r const& v){
+		return python::incref(python::make_tuple(v[0],v[1],v[2]).ptr());
+	}
+};
+struct custom_Vector3r_from_tuple{
+	custom_Vector3r_from_tuple(){
+		python::converter::registry::push_back(&convertible,&construct,python::type_id<Vector3r>());
+	}
+	static void* convertible(PyObject* obj_ptr){
+		if(!PySequence_Check(obj_ptr) || PySequence_Size(obj_ptr)!=3) return 0;
+		return obj_ptr;
+	}
+	static void construct(PyObject* obj_ptr, python::converter::rvalue_from_python_stage1_data* data){
+		void* storage=((python::converter::rvalue_from_python_storage<Vector3r>*)(data))->storage.bytes;
+		new (storage) Vector3r(python::extract<double>(PySequence_GetItem(obj_ptr,0)),python::extract<double>(PySequence_GetItem(obj_ptr,1)),python::extract<double>(PySequence_GetItem(obj_ptr,2)));
+		data->convertible=storage;
+	}
+};
+
+
+BOOST_PYTHON_MODULE(wrapper)
+{
+
+	//python::to_python_converter<Vector3r,custom_Vector3r_to_tuple>();
+	//custom_Vector3r_from_tuple();
+
+	boost::python::class_<pyOmega>("Omega")
+		.add_property("iter",&pyOmega::iter)
+		.add_property("stopAtIter",&pyOmega::stopAtIter_get,&pyOmega::stopAtIter_set)
+		.add_property("time",&pyOmega::simulationTime)
+		.add_property("realtime",&pyOmega::realTime)
+		.add_property("dt",&pyOmega::dt_get,&pyOmega::dt_set)
+		.add_property("usesTimeStepper",&pyOmega::usesTimeStepper_get,&pyOmega::usesTimeStepper_set)
+		.def("load",&pyOmega::load)
+		.def("reload",&pyOmega::reload)
+		.def("save",&pyOmega::save)
+		.def("loadTmp",&pyOmega::loadTmp,omega_loadTmp_overloads(python::args("mark")))
+		.def("saveTmp",&pyOmega::saveTmp,omega_saveTmp_overloads(python::args("mark")))
+		.def("tmpToFile",&pyOmega::tmpToFile)
+		.def("saveSpheres",&pyOmega::saveSpheres)
+		.def("run",&pyOmega::run,omega_run_overloads())
+		.def("pause",&pyOmega::pause)
+		.def("step",&pyOmega::step)
+		.def("wait",&pyOmega::wait)
+		.def("reset",&pyOmega::reset)
+		.def("switchWorld",&pyOmega::switchWorld)
+		.def("labeledEngine",&pyOmega::labeled_engine_get)
+		.def("resetTime",&pyOmega::resetTime)
+		.def("plugins",&pyOmega::plugins_get)
+		.add_property("engines",&pyOmega::engines_get,&pyOmega::engines_set)
+		.add_property("miscParams",&pyOmega::miscParams_get,&pyOmega::miscParams_set)
+		.add_property("initializers",&pyOmega::initializers_get,&pyOmega::initializers_set)
+		.add_property("bodies",&pyOmega::bodies_get)
+		.add_property("interactions",&pyOmega::interactions_get)
+		.add_property("actions",&pyOmega::bex_get)
+		.add_property("bex",&pyOmega::bex_get)
+		.add_property("tags",&pyOmega::tags_get)
+		.def("childClasses",&pyOmega::listChildClasses)
+		.def("isChildClassOf",&pyOmega::isChildClassOf)
+		.add_property("bodyContainer",&pyOmega::bodyContainer_get,&pyOmega::bodyContainer_set)
+		.add_property("interactionContainer",&pyOmega::interactionContainer_get,&pyOmega::interactionContainer_set)
+		.add_property("timingEnabled",&pyOmega::timingEnabled_get,&pyOmega::timingEnabled_set)
+		.add_property("bexSyncCount",&pyOmega::bexSyncCount_get,&pyOmega::bexSyncCount_set)
+		.add_property("numThreads",&pyOmega::numThreads_get,&pyOmega::numThreads_set)
+		;
+	boost::python::class_<pyTags>("TagsWrapper",python::init<pyTags&>())
+		.def("__getitem__",&pyTags::getItem)
+		.def("__setitem__",&pyTags::setItem)
+		.def("keys",&pyTags::keys);
+	
+	boost::python::class_<pyBodyContainer>("BodyContainer",python::init<pyBodyContainer&>())
+		.def("__getitem__",&pyBodyContainer::pyGetitem)
+		.def("__len__",&pyBodyContainer::length)
+		.def("append",&pyBodyContainer::insert)
+		.def("append",&pyBodyContainer::insertList)
+		.def("appendClumped",&pyBodyContainer::insertClump)
+		.def("clear", &pyBodyContainer::clear)
+		.def("replace",&pyBodyContainer::replace);
+	boost::python::class_<pyInteractionContainer>("InteractionContainer",python::init<pyInteractionContainer&>())
+		.def("__iter__",&pyInteractionContainer::pyIter)
+		.def("__getitem__",&pyInteractionContainer::pyGetitem)
+		.def("__len__",&pyInteractionContainer::len)
+		.def("nth",&pyInteractionContainer::pyNth)
+		.def("withBody",&pyInteractionContainer::withBody)
+		.def("withBodyAll",&pyInteractionContainer::withBodyAll)
+		.def("nth",&pyInteractionContainer::pyNth)
+		.def("clear",&pyInteractionContainer::clear);
+	boost::python::class_<pyInteractionIterator>("InteractionIterator",python::init<pyInteractionIterator&>())
+		.def("__iter__",&pyInteractionIterator::pyIter)
+		.def("next",&pyInteractionIterator::pyNext);
+
+	boost::python::class_<pyBexContainer>("BexContainer",python::init<pyBexContainer&>())
+		.def("f",&pyBexContainer::force_get)
+		.def("t",&pyBexContainer::torque_get)
+		.def("m",&pyBexContainer::torque_get) // for compatibility with ActionContainer
+		.def("move",&pyBexContainer::move_get)
+		.def("rot",&pyBexContainer::rot_get)
+		.def("addF",&pyBexContainer::force_add)
+		.def("addT",&pyBexContainer::torque_add)
+		.def("addMove",&pyBexContainer::move_add)
+		.def("addRot",&pyBexContainer::rot_add);
+
+	boost::python::class_<pyTimingDeltas>("TimingDeltas",python::init<pyTimingDeltas&>())
+		.def("reset",&pyTimingDeltas::reset)
+		.add_property("data",&pyTimingDeltas::data_get);
+
+	#define TIMING_PROPS(class) .add_property("execTime",&class::execTime_get,&class::execTime_set).add_property("execCount",&class::execCount_get,&class::execCount_set).add_property("timingDeltas",&class::timingDeltas_get)
+
+	BASIC_PY_PROXY_WRAPPER(pyStandAloneEngine,"StandAloneEngine")
+		TIMING_PROPS(pyStandAloneEngine);
+	BASIC_PY_PROXY_WRAPPER(pyMetaEngine,"MetaEngine")
+		.add_property("functors",&pyMetaEngine::functors_get,&pyMetaEngine::functors_set)
+		TIMING_PROPS(pyMetaEngine)
+		.def(python::init<string,python::list>());
+	BASIC_PY_PROXY_WRAPPER(pyParallelEngine,"ParallelEngine")
+		.add_property("slaves",&pyParallelEngine::slaves_get,&pyParallelEngine::slaves_set)
+		.def(python::init<python::list>());
+	BASIC_PY_PROXY_WRAPPER(pyDeusExMachina,"DeusExMachina")
+		TIMING_PROPS(pyDeusExMachina);
+	BASIC_PY_PROXY_WRAPPER(pyInteractionDispatchers,"InteractionDispatchers")
+		.def(python::init<python::list,python::list,python::list>())
+		.add_property("geomDispatcher",&pyInteractionDispatchers::geomDispatcher_get)
+		.add_property("physDispatcher",&pyInteractionDispatchers::physDispatcher_get)
+		.add_property("constLawDispatcher",&pyInteractionDispatchers::constLawDispatcher_get)
+		TIMING_PROPS(pyInteractionDispatchers);
+	BASIC_PY_PROXY_WRAPPER(pyEngineUnit,"EngineUnit")
+		.add_property("timingDeltas",&pyEngineUnit::timingDeltas_get)
+		.add_property("bases",&pyEngineUnit::bases_get);
+
+	#undef TIMING_PROPS
+
+	BASIC_PY_PROXY_WRAPPER(pyGeometricalModel,"GeometricalModel");
+	BASIC_PY_PROXY_WRAPPER(pyInteractingGeometry,"InteractingGeometry");
+	BASIC_PY_PROXY_WRAPPER(pyPhysicalParameters,"PhysicalParameters")	
+		.add_property("blockedDOFs",&pyPhysicalParameters::blockedDOFs_get,&pyPhysicalParameters::blockedDOFs_set)
+		.add_property("pos",&pyPhysicalParameters::pos_get,&pyPhysicalParameters::pos_set)
+		.add_property("ori",&pyPhysicalParameters::ori_get,&pyPhysicalParameters::ori_set)
+		.add_property("refPos",&pyPhysicalParameters::refPos_get,&pyPhysicalParameters::refPos_set)
+		.add_property("displ",&pyPhysicalParameters::displ_get)
+		.add_property("rot",&pyPhysicalParameters::rot_get)
+		;
+	BASIC_PY_PROXY_WRAPPER(pyBoundingVolume,"BoundingVolume");
+	BASIC_PY_PROXY_WRAPPER(pyInteractionGeometry,"InteractionGeometry");
+	BASIC_PY_PROXY_WRAPPER(pyInteractionPhysics,"InteractionPhysics");
+
+	BASIC_PY_PROXY_WRAPPER(pyGeneric,"Generic");
+
+	BASIC_PY_PROXY_WRAPPER(pyBody,"Body")
+		.add_property("shape",&pyBody::shape_get,&pyBody::shape_set)
+		.add_property("mold",&pyBody::mold_get,&pyBody::mold_set)
+		.add_property("bound",&pyBody::bound_get,&pyBody::bound_set)
+		.add_property("phys",&pyBody::phys_get,&pyBody::phys_set)
+		.add_property("dynamic",&pyBody::dynamic_get,&pyBody::dynamic_set)
+		.add_property("id",&pyBody::id_get)
+		.add_property("mask",&pyBody::mask_get,&pyBody::mask_set)
+		.add_property("isStandalone",&pyBody::isStandalone)
+		.add_property("isClumpMember",&pyBody::isClumpMember)
+		.add_property("isClump",&pyBody::isClump);
+
+	BASIC_PY_PROXY_WRAPPER(pyInteraction,"Interaction")
+		.add_property("phys",&pyInteraction::phys_get,&pyInteraction::phys_set)
+		.add_property("geom",&pyInteraction::geom_get,&pyInteraction::geom_set)
+		.add_property("id1",&pyInteraction::id1_get)
+		.add_property("id2",&pyInteraction::id2_get)
+		.add_property("isReal",&pyInteraction::isReal_get);
+
+	BASIC_PY_PROXY_WRAPPER(pyFileGenerator,"Preprocessor")
+		.def("generate",&pyFileGenerator::generate)
+		.def("load",&pyFileGenerator::load);
+
+	boost::python::class_<pySTLImporter>("STLImporter")
+	    .def("open",&pySTLImporter::open)
+	    .add_property("number_of_facets",&pySTLImporter::number_of_facets)
+	    .def_readwrite("wire",&pySTLImporter::wire)
+	    .def("import_geometry",&pySTLImporter::py_import,STLImporter_import_overloads());
+	
+}
+


Property changes on: trunk/py/yadeWrapper/yadeWrapper.cpp
___________________________________________________________________
Name: svn:mergeinfo
   + 

Modified: trunk/scripts/test/gts-triax-pack.py
===================================================================
--- trunk/scripts/test/gts-triax-pack.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/scripts/test/gts-triax-pack.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -32,7 +32,7 @@
 # parameters (or parameters that can be scaled to the same one),
 # it will load the packing instead of running the triaxial compaction again.
 # Try running for the second time to see the speed difference!
-O.bodies.append(pack.triaxialPack(pack.inGtsSurface(surf),radius=5e-4,radiusStDev=1e-4,memoizeDb='/tmp/gts-triax-packings.sqlite'))
+O.bodies.append(pack.triaxialPack(pack.inGtsSurface(surf),radius=15e-4,radiusStDev=1e-4,memoizeDb='/tmp/gts-triax-packings.sqlite'))
 # We could also fill the horse with triaxial packing, but have nice approximation, the triaxial would run terribly long,
 # since horse discard most volume of its bounding box
 # Here, we would use a very crude one, however

Modified: trunk/scripts/test/shear.py
===================================================================
--- trunk/scripts/test/shear.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/scripts/test/shear.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -8,21 +8,13 @@
 	utils.sphere([0,0,1],.5000001,dynamic=True,color=(0,0,1))
 ])
 O.engines=[
-	MetaEngine('BoundingVolumeMetaEngine',[
-		EngineUnit('InteractingSphere2AABB'),
-		EngineUnit('InteractingFacet2AABB'),
-	]),
-	StandAloneEngine('PersistentSAPCollider',{'haveDistantTransient':True}),
-	MetaEngine('InteractionGeometryMetaEngine',[
-		EngineUnit('InteractingSphere2InteractingSphere4SpheresContactGeometry'),
-	]),
-	MetaEngine('InteractionPhysicsMetaEngine',[
-		EngineUnit('SimpleElasticRelationships')
-	]),
-	DeusExMachina('RotationEngine',{'rotationAxis':[1,1,0],'angularVelocity':.001,'subscribedBodies':[1]}),
-	StandAloneEngine('ElasticContactLaw',{'useShear':False}),
-	StandAloneEngine('PeriodicPythonRunner',{'iterPeriod':10000,'command':'interInfo()'}),
-	#DeusExMachina('NewtonsDampedLaw',{'damping':0})
+	BoundingVolumeMetaEngine([InteractingSphere2AABB(),InteractingFacet2AABB()]),
+	InsertionSortCollider(),
+	InteractionGeometryMetaEngine([InteractingSphere2InteractingSphere4SpheresContactGeometry()]),
+	InteractionPhysicsMetaEngine([SimpleElasticRelationships()]),
+	RotationEngine(rotationAxis=[1,1,0],angularVelocity=.001,subscribedBodies=[1]),
+	ElasticContactLaw(useShear=False,label='elasticLaw'),
+	PeriodicPythonRunner(iterPeriod=10000,command='interInfo()'),
 ]
 O.miscParams=[
 	Generic('GLDrawSphere',{'glutUse':True})
@@ -47,7 +39,6 @@
 nIter=O.iter
 print '============= shear ============='
 O.loadTmp('init')
-ecl=[e for e in O.engines if e.name=='ElasticContactLaw'][0]
-ecl['useShear']=True
+elasticLaw['useShear']=True
 O.run(nIter,True)
 quit()

Modified: trunk/scripts/test/triax-identical-results.py
===================================================================
--- trunk/scripts/test/triax-identical-results.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/scripts/test/triax-identical-results.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -15,10 +15,10 @@
 inSph='%s-in.spheres'%sph
 if exists(inSph): print "Using existing initial configuration",inSph
 else:
-	Preprocessor('TriaxialTest').load()
+	TriaxialTest(noFiles=True).load()
 	print "Using new initial configuration in",inSph
 	utils.spheresToFile(inSph)
-Preprocessor('TriaxialTest',{'importFilename':inSph}).load()
+TriaxialTest(importFilename=inSph,noFiles=True).load()
 O.usesTimeStepper=False
 O.dt=utils.PWaveTimeStep()
 #
@@ -26,7 +26,10 @@
 #
 [e for e in O.engines if e.name=='ElasticContactLaw'][0]['useShear']=True
 if 1:
+	#for i in range(0,100):
+	#	#	O.save('/tmp/a.%03d.xml'%O.iter)
+	#	O.step()
 	O.run(2000,True)
 	utils.spheresToFile(outSph)
 	print "Results saved to",outSph
-	quit()
+	#quit()

Added: trunk/scripts/test/wm3-wrap.py
===================================================================
--- trunk/scripts/test/wm3-wrap.py	2009-07-06 15:22:39 UTC (rev 1840)
+++ trunk/scripts/test/wm3-wrap.py	2009-07-07 11:54:14 UTC (rev 1841)
@@ -0,0 +1,27 @@
+# constructors, constants as static objects
+x,y,z,one=Vector3.UNIT_X,Vector3.UNIT_Y,Vector3.UNIT_Z,Vector3.ONE
+x2=Vector3(x)
+# conversions to sequence types
+list(x2)
+tuple(x2)
+# operations and operators
+x+y+z==one
+x.Dot(y)==z
+x.Cross(y)==0
+# methods
+one.Length()
+
+# quaternions
+# construction (implicit conversion of 3-tuple or list of length 3 to Vector3)
+q1=Quaternion((0,0,1),pi/2)
+q2=Quaternion(Vector3(0,0,1),pi/2)
+q1==q2
+# rotating vector
+q1*x==y # almost, due to rounding 
+# rotation composition
+q1*q1*x 
+# inverse rotation
+q1.Conjugate()
+# convert to axis-angle representation
+axis,angle=q1.ToAxisAngle()
+