Index: /cpp/README
===================================================================
--- /cpp/README	(revision 109)
+++ /cpp/README	(revision 109)
@@ -0,0 +1,22 @@
+=== Building GDK test applications ===
+
+make -f frams/Makefile-GDK gdk_tests
+
+=== Source locations ===
+
+  +- common          - general compatibility layer & utilities
+  +- frams           - framsticks modules
+      +- _demos      - GDK test applications
+      +- config      - GDK elements declarations
+      +- errmgr      - error manager (logging/messaging)
+      +- genetic     - genetic module
+      |  +- oper     - genetic operators
+      |  +- conv     - genetic converters
+      +- model       - parts, joints, neurons
+      +- neuro       - neural net simulation
+      |   +- impl    - neuron classes implementations
+      +- param       - c++/properties access objects
+      +- util        - framsticks-related utilities
+      +- virtfile    - object oriented FILE* replacement
+      +- vm
+          +- classes - virtual machine c++ interfaces
Index: /cpp/common/2d.h
===================================================================
--- /cpp/common/2d.h	(revision 109)
+++ /cpp/common/2d.h	(revision 109)
@@ -0,0 +1,119 @@
+#ifndef _2D_H_
+#define _2D_H_
+
+#include "nonstd_stl.h"
+#include <math.h>
+
+//unifikacja starych GUIXY i Pt2D
+template <typename T> class XY
+{
+public:
+T x,y;
+XY() {}
+XY(T _x,T _y):x(_x),y(_y) {}
+template <typename Q> XY(const Q& other):x(other.x),y(other.y) {}
+template <typename Q> const XY& operator=(const Q& other) {x=other.x; y=other.y; return *this;}
+XY operator+(const XY&p) const  {return XY(x+p.x,y+p.y);}
+XY operator-(const XY&p) const {return XY(x-p.x,y-p.y);}
+XY operator+=(const XY&p) {x+=p.x; y+=p.y; return *this;}
+XY operator-=(const XY&p) {x-=p.x; y-=p.y; return *this;}
+XY operator-() const {return XY(-x,-y);}
+XY operator*=(T q) {x*=q; y*=q; return *this;}
+XY operator/=(T q) {x/=q; y/=q; return *this;}
+XY operator/(T q) {return XY(x/q,y/q);}
+XY operator*(T q) const  {return XY(q*x,q*y);}
+void set(T _x,T _y) {x=_x; y=_y;}
+void add(T _x,T _y) {x+=_x; y+=_y;}
+void sub(T _x,T _y) {x-=_x; y-=_y;}
+bool operator==(const XY& p) const {return (fabs(double(x-p.x))<1e-20)&&(fabs(double(y-p.y))<1e-20);}
+T distanceTo(const XY& p) const {return sqrt(double((p.x-x)*(p.x-x)+(p.y-y)*(p.y-y)));}
+T magnitude() const {return sqrt(x*x+y*y);}
+T length() const {return sqrt(x*x+y*y);}
+T lengthSq() const { return x*x + y*y; }
+T dotProduct(const XY& v) const {return x*v.x + y*v.y;}
+T crossProduct(const XY& v) const {return x*v.y - y*v.x;}
+void normalize() { operator/=(length()); } // length becomes 1
+static XY average(const XY& v1,const XY& v2) { return XY((v1.x+v2.x)*0.5,(v1.y+v2.y)*0.5); }
+double getDirection() const {return atan2(y,x);}
+static XY interpolate(const XY& v1, const XY& v2,double t) {return v1+(v2-v1)*t;}
+XY toInt() const {return XY(int(x),int(y));}
+static const XY& zero() {static XY t(0,0); return t;}
+static const XY& one() {static XY t(1,1); return t;}
+};
+
+template <typename T> XY<T> xymin(const XY<T>& a, const XY<T>& b) {return XY<T>(min(a.x,b.x),min(a.y,b.y));}
+template <typename T> XY<T> xymax(const XY<T>& a, const XY<T>& b) {return XY<T>(max(a.x,b.x),max(a.y,b.y));}
+
+typedef XY<int> IntXY;
+
+template <typename T>
+class XYRect
+{
+public:
+XY<T> p,size;
+XYRect() {}
+XYRect(const XY<T>& p1,const XY<T>& s):p(p1),size(s) {}
+XYRect(T _x,T _y,T _w,T _h):p(_x,_y),size(_w,_h) {}
+static XYRect<T> centeredAt(const XY<T>& p,XY<T> s) {return XYRect<T>(p-s*0.5,s);}
+
+bool isEmpty() const {return (size.x<0)||(size.y<0);}
+XYRect toInt() const {return XYRect(int(p.x),int(p.y),int(p.x+size.x)-int(p.x),int(p.y+size.y)-int(p.y));}
+bool operator==(const XYRect& r) const {return (p==r.p) && (size==r.size);}
+
+bool intersects(const XYRect& r) const
+{
+if (r.p.x >= (p.x+size.x)) return false;
+if (r.p.y >= (p.y+size.y)) return false;
+if ((r.p.x+r.size.x) <= p.x) return false;
+if ((r.p.y+r.size.y) <= p.y) return false;
+return true;
+}
+
+bool contains(const XY<T>& n) const
+{
+if (n.x<p.x) return false;
+if (n.x>(p.x+size.x)) return false;
+if (n.y<p.y) return false;
+if (n.y>(p.y+size.y)) return false;
+return true;
+}
+
+void add(const XY<T>& n)
+{
+if (n.x<p.x) {size.x+=p.x-n.x; p.x=n.x;}
+else if (n.x>(p.x+size.x)) size.x=n.x-p.x;
+if (n.y<p.y) {size.y+=p.y-n.y; p.y=n.y;}
+else if (n.y>(p.y+size.y)) size.y=n.y-p.y;
+}
+
+void extend(const XY<T>& border_size)
+{
+size+=border_size*2; p-=border_size;
+}
+
+XYRect intersection(const XYRect& r) const
+{
+XYRect i;
+XY<T> p2=p+size;
+XY<T> rp2=r.p+r.size;
+i.p.x=max(p.x,r.p.x);
+i.p.y=max(p.y,r.p.y);
+i.size.x=min(p2.x,rp2.x)-i.p.x;
+i.size.y=min(p2.y,rp2.y)-i.p.y;
+return i;
+}
+
+T distanceTo(const XY<T>& n) const
+{
+XY<T> tp=n;
+if (n.x<p.x) tp.x=p.x; else if (n.x>=(p.x+size.x)) tp.x=p.x+size.x;
+if (n.y<p.y) tp.y=p.y; else if (n.y>=(p.y+size.y)) tp.y=p.y+size.y;
+ return tp.distanceTo(n);
+}
+
+static const XYRect& zero() {static XYRect t(0,0,0,0); return t;}
+static const XYRect& one() {static XYRect t(0,0,1,1); return t;}
+};
+
+
+#endif
Index: /cpp/common/Convert.cpp
===================================================================
--- /cpp/common/Convert.cpp	(revision 109)
+++ /cpp/common/Convert.cpp	(revision 109)
@@ -0,0 +1,119 @@
+#include "Convert.h"
+
+#include <sstream>
+
+#if defined __ANDROID__ || defined __BORLANDC__
+ #include <ctype.h> //toupper, tolower
+#endif
+
+#ifdef SHP
+ #include <cstdlib>
+#else
+ #include <stdlib.h>
+#endif
+
+#include <stdio.h>
+
+
+int Convert::toInt(string s) {return atoi(s.c_str());}
+float Convert::toFloat(string s) {return (float)atof(s.c_str());}
+#ifndef __BORLANDC__ //szok: borland kompiluje ten plik w GUI i CLI ale nie w teatrze. Z jakichs powodow nie potrafi znalezc ::tolower i ::toupper, moze chodzi o pewne definy w <cctype> ale czemu tylko w teatrze?!
+string Convert::toLowerCase(string s) {std::transform(s.begin(), s.end(), s.begin(), ::tolower);  return s;}
+string Convert::toUpperCase(string s) {std::transform(s.begin(), s.end(), s.begin(), ::toupper);  return s;}
+#endif
+char Convert::toLowerCase(char c) {return (char)tolower(c);}
+char Convert::toUpperCase(char c) {return (char)toupper(c);}
+
+template<class T> const char* printf_format_for(const T& value) {return "unknown type";}
+template<> const char* printf_format_for(const unsigned int& value) {return "%u";}
+template<> const char* printf_format_for(const int& value) {return "%d";}
+template<> const char* printf_format_for(const short& value) {return "%d";}
+template<> const char* printf_format_for(const float& value) {return "%g";}
+template<> const char* printf_format_for(const double& value) {return "%g";}
+
+template<class T> string Convert::_toString(const T& value)
+{
+char buf[30];
+sprintf(buf,printf_format_for(value),value);
+return string(buf);
+/*
+#ifndef MULTITHREADED
+	static
+#endif
+	std::ostringstream oss; //pod VS tworzenie go trwa dlugo nawet w wersji release (szczegolnie jak np konwertuje sie cos setki tysiecy razy)
+	//dlatego robimy go raz (static) i potem tylko czyscimy
+	//ciekawostka: kiedy nie byl static, czasy wykonania bogatego w konwersje kawa³ka kodu oscylowa³y w trybie debug
+	//(5.5s lub 55s) a w release zawsze 57s. Po uzyciu static czas tego samego kodu jest zawsze debug: 0.72s release: 0.33s
+	oss.clear(); //clear error flag
+	oss.str(""); //set empty string
+  oss << value;
+  return oss.str();
+*/
+}
+
+string Convert::toString(unsigned int v) {return _toString(v);}
+string Convert::toString(int v) {return _toString(v);}
+string Convert::toString(short v) {return _toString(v);}
+string Convert::toString(float v) {return _toString(v);}
+string Convert::toString(double v) {return _toString(v);}
+
+#ifdef MULTITHREADED
+//jezeli jest tu a nie jako static w funkcji, to inicjalizacja
+//nastapi na samym poczatku (w nieprzewidywalnym momencie, ale nie szkodzi(?))
+//gdyby byla w funkcji to teoretycznie dwa watki moglyby wejsc
+//do niej rownoczesnie i zaczac inicjalizacje po czym jeden korzystalby
+//z mutexa gdy drugi dalej by go inicjalizowal
+#include "threads.h"
+static pthread_mutex_t fix_unsafe_mutex=PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+struct tm Convert::localtime(const time_t &timep)
+{
+#ifndef MULTITHREADED
+
+return *::localtime(&timep);
+
+#else
+
+struct tm ret;
+
+#if defined LINUX // || android?
+return *::localtime_r(&timep,&ret);
+#elif defined _WIN32 && !defined __BORLANDC__ // and not bada?
+::localtime_s(&ret,&timep);
+return ret;
+#else //borland?
+pthread_mutex_lock(&fix_unsafe_mutex);
+ret=*::localtime(&timep);
+pthread_mutex_unlock(&fix_unsafe_mutex);
+return ret;
+#endif
+
+#endif
+}
+
+string Convert::asctime(const struct tm &tm)
+{
+char *ret;
+#ifndef MULTITHREADED
+
+ret=::asctime(&tm);
+
+#else //MULTITHREADED
+
+char buf[26];
+#if defined LINUX // || android?
+ret=::asctime_r(&tm,buf);
+#elif defined _WIN32 && !defined __BORLANDC__ // and not bada?
+asctime_s(buf,sizeof(buf),&tm);
+ret=buf;
+#else //borland?
+pthread_mutex_lock(&fix_unsafe_mutex);
+strcpy(buf,::asctime(&tm));
+ret=buf;
+pthread_mutex_unlock(&fix_unsafe_mutex);
+#endif
+#endif
+
+return string(ret,24); //24 znaki z pominieciem ostatniego \n
+}
Index: /cpp/common/Convert.h
===================================================================
--- /cpp/common/Convert.h	(revision 109)
+++ /cpp/common/Convert.h	(revision 109)
@@ -0,0 +1,74 @@
+#ifndef _CONVERT_H_
+#define _CONVERT_H_
+
+#include "nonstd.h"
+#include "nonstd_math.h"
+#include "nonstd_stl.h"
+#include "2d.h"
+
+typedef XY<double> Pt2D;
+
+
+struct Convert
+{
+public:
+  static int toInt(string s);
+  static float toFloat(string s);
+  static string toLowerCase(string s);
+  static string toUpperCase(string s);
+  static char toLowerCase(char c);
+  static char toUpperCase(char c);
+  template<class T> static string _toString(const T& value);
+  static string toString(unsigned int v);
+  static string toString(int v);
+  static string toString(short v);
+  static string toString(float v);
+  static string toString(double v);// ze niby w badzie ma nie byc? ale w frams na pewno ma byc bo tu same double
+  static string zeroPad(string s,int l) {while((int)s.length()<l) s=string("0")+s; return s;}
+
+  static double toRadians(double kat) {return kat*M_PI/180;}
+  static double toDegrees(double kat) {return kat/M_PI*180;}
+  static double atan_2(double y,double x) {if (x==0 && y==0) return 0; else return atan2(y,x);}
+
+	static double odleglosc_sq(double x1,double y1,double x2,double y2) //odleglosc do kwadratu, wystarczy do porownywania
+	{double dx=x2-x1, dy=y2-y1; return dx*dx+dy*dy;}
+	static double odleglosc_sq(const Pt2D& p1,const Pt2D& p2) //odleglosc do kwadratu
+	{return odleglosc_sq(p1.x,p1.y,p2.x,p2.y);}
+
+	static double odleglosc(double x1,double y1,double x2,double y2)	{return sqrt(odleglosc_sq(x1,y1,x2,y2));}
+	static double odleglosc(const Pt2D& p1,const Pt2D& p2)
+	{return sqrt(odleglosc_sq(p1,p2));}
+	
+  //static float odleglosc(int x1,int y1,int x2,int y2) {float dx=x1-x2; float dy=y1-y2; return sqrt(dx*dx+dy*dy);}
+  //static float odleglosc(float x1,float y1,float x2,float y2) {return sqrt(odleglosc_sq(x1,y1,x2,y2));}
+  //static float odleglosc_sq(float x1,float y1,float x2,float y2) {float dx=x1-x2; float dy=y1-y2; return dx*dx+dy*dy;}
+
+  static struct tm localtime(const time_t &timep);//jak ::localtime ale zwraca strukture zamiast wskaznika, ref w parametrze dla wygodnego wywolywania
+  static string asctime(const struct tm &tm);//jak ::asctime ale thread safe i bez glupiego \n na koncu, ref w parametrze dla wygodnego wywolywania
+};
+
+
+
+struct Kat //znormalizowany k¹t w radianach [0,2pi) i stopniach [0,360) z obliczonymi sinus i cosinus oraz intem [0,359]
+{
+private:
+	double kat; //w radianach, read-only
+public:
+	double kat_stopnie; //read-only
+	int kat_stopnie_int; //read-only
+
+	Kat() {set(0);}
+	Kat(double k) {set(k);}
+	Kat(Kat &kt) {set(kt.get());}
+	Kat(double dy,double dx) {set(dy,dx);}
+	void set(double k) {k=fmod(k,M_PI*2); if (k<0) k+=M_PI*2; kat=k; sinus=sin(k); cosinus=cos(k); kat_stopnie=Convert::toDegrees(kat); kat_stopnie_int=roundToInt(kat_stopnie); kat_stopnie_int%=360; }
+	void set(double dy,double dx) {set(Convert::atan_2(dy,dx));}
+	void add(double dk) {set(kat+dk);}
+	void add(Kat &kt) {set(kat+kt.get());}
+	double get() {return kat;}
+	double sinus,cosinus;
+};
+
+
+
+#endif
Index: /cpp/common/framsg.cpp
===================================================================
--- /cpp/common/framsg.cpp	(revision 109)
+++ /cpp/common/framsg.cpp	(revision 109)
@@ -0,0 +1,28 @@
+#include "framsg.h"
+#include <common/nonstd_stdio.h>
+#include "Convert.h"
+
+char* MSG_LEVEL[]={"DEBUG","INFO","WARN","ERROR","CRITICAL"};
+
+void FMprintf_va(const char *o,const char *m,int w,const char *bl,va_list va)
+{
+	char buf[10000];
+	vsnprintf(buf,10000,bl,va);
+	FramMessage(o,m,buf,w);
+}
+
+void FMprintf(const char *o,const char *m,int w,const char *bl, ...)
+{
+	va_list argptr;
+	va_start(argptr,bl);
+	FMprintf_va(o,m,w,bl,argptr);
+	va_end(argptr);
+}
+
+void printFM(const char *bl,...)
+{
+	va_list argptr;
+	va_start(argptr,bl);
+	FMprintf_va("Message","printf",FMLV_INFO,bl,argptr);
+	va_end(argptr);
+}
Index: /cpp/common/framsg.h
===================================================================
--- /cpp/common/framsg.h	(revision 109)
+++ /cpp/common/framsg.h	(revision 109)
@@ -0,0 +1,28 @@
+#ifndef _FRAMSG_H_
+#define _FRAMSG_H_
+
+#include <stdarg.h>
+
+extern char* MSG_LEVEL[];
+
+void FMprintf(const char *o,const char *m,int w,const char *bl, ...);
+void FMprintf_va(const char *o,const char *m,int w,const char *bl,va_list va);//inna nazwa bo bez tego nie odroznia parametru "string" od va_list i wywoluje zla wersje
+void printFM(const char *bl,...); //dla leniwych - zastepuje printf (zmieniona nazwa chociaz tutaj "prawdopodobienstwo" konfliktu jest niewielkie, musialoby byc wywolanie malego printa z kilkoma parametrami odpowiednich typow zeby niechcacy wywolal sie duzy print)
+void FramMessage(const char *o,const char *m,const char *bl,int w);
+
+#define FMLV_DEBUG -1
+#define FMLV_INFO 0
+#define FMLV_WARN 1
+#define FMLV_ERROR 2
+#define FMLV_CRITICAL 3
+
+/*
+w: weight (importance) of a message
+-1:debugging information, not needed for final users
+0: information
+1: warning or corrected error
+2: uncorrected error. can cause malfunction
+3: serious error, causes side effects. user should save what can be saved and restart the application
+*/
+
+#endif
Index: /cpp/common/nonstd.h
===================================================================
--- /cpp/common/nonstd.h	(revision 109)
+++ /cpp/common/nonstd.h	(revision 109)
@@ -0,0 +1,113 @@
+#ifndef _NONSTD_H_
+#define _NONSTD_H_
+
+#define SAFEDELETE(p) {if (p) {delete p; p=NULL;}}
+#define SAFEDELETEARRAY(p) {if (p) {delete[] p; p=NULL;}}
+
+#define roundToInt(x) ((int)(floor((x)+0.5)))
+
+
+#define CPP_STR(s) CPP_XSTR(s)
+#define CPP_XSTR(s) #s
+
+
+#define DB(x) //output debug info. If needed, use #define DB(x) x
+//#define DB(x) x
+
+
+// ms visual c++
+#ifdef _MSC_VER
+ #include <stdio.h> //musi byc najpierw ten include zeby robic definy ponizej (albo bedzie '_vsnprintf': attributes inconsistent with previous declaration, stdio.h:358)
+ #define vsnprintf _vsnprintf
+ #define snprintf _snprintf
+#endif
+
+#ifdef MOBILE2D
+ #define strdup _strdup
+#endif
+
+/////////////////////////////////////////////////////// 64-bit int type and other macros
+#ifdef _WIN32
+	typedef __int64 LONGLONG;
+	#define PATH_SEPARATOR_CHAR '\\'
+	#define PATH_SEPARATOR_STRING "\\"
+	#define FPU_THROWS_EXCEPTIONS
+#else
+	#define LONGLONG long long int
+	#define PATH_SEPARATOR_CHAR '/'
+	#define PATH_SEPARATOR_STRING "/"
+#endif
+
+#define FOPEN_READ_BINARY "rb"
+// no FOPEN_READ_TEXT
+#define FOPEN_WRITE_BINARY "wb"
+#define FOPEN_APPEND_BINARY "ab"
+#define FOPEN_WRITE_TEXT "wt"
+#define FOPEN_APPEND_TEXT "at"
+
+#ifdef LINUX
+ #include <strings.h>
+ #define stricmp(a,b) strcasecmp(a,b)
+ #define _isnan(a) isnan(a) //do nonstd_math.h?
+#ifdef INITIAL_DIR_IS_HOME
+ #include "cwd.h"
+ #define GET_APP_HOME getAppHome()
+#else
+ #define GET_APP_HOME "./"
+#endif
+#ifdef INITIAL_DIR_IS_RES
+ #include "cwd.h"
+ #define GET_APP_RESOURCES getAppResources()
+#else
+ #define GET_APP_RESOURCES "./"
+#endif
+#endif
+
+#ifdef MACOS
+ #define stricmp(a,b) strcasecmp(a,b)
+#endif
+
+#ifdef IPHONE
+ #include <string>
+ #define _isnan(a) isnan(a) //do nonstd_math.h?
+ std::string getAppHome();
+ std::string getAppResources();
+ #define GET_APP_HOME getAppHome()
+ #define GET_APP_RESOURCES getAppResources()
+#endif
+
+#ifdef SHP
+ //#define __assert_func(a,b,c,d) 0 //Currently, we are sorry to inform you that assert() is not yet supported. We have considered your request for internal discussion. No ale na szczêcie w³asna (byle by by³a, bo i tak zak³adamy ze assert ktore przeciez dziala tylko w trybie debug nie jest potrzebne na bada) implementacja w "bada-assert.cpp"
+ #define GET_APP_HOME "/Home/"
+ #define GET_APP_RESOURCES "/Res/"
+#endif
+
+#ifdef TIZEN
+ #define GET_APP_HOME "/data/"
+ #define GET_APP_RESOURCES "/res/"
+#endif
+
+#ifdef __ANDROID__
+ #define GET_APP_HOME getAppHome()
+ #define GET_APP_RESOURCES "./" //w APK trzymamy w katalogu w "assets", bo w "res" nie ma podkatalogow ani odwolywania sie po nazwach
+#endif
+
+#if defined(_WIN32) && !defined(SHP)
+    #define GET_APP_HOME ".\\"
+    #define GET_APP_RESOURCES ".\\"
+#endif
+
+
+
+#ifdef MAIN_FRAMSTICKS
+ #define SCRIPTS_DIR GET_APP_RESOURCES "scripts" //katalogi sa na iphone std::string a gdzie indziej "const char*" i to sie wszedzie kompiluje?
+#endif
+
+
+
+//typedef unsigned char boolean; //niestety nie mozna uzyc 'bool' bo VC w rpcndr.h wlasnie tak definiuje booleana, jako unsigned char
+//typedef char byte; //rozne srodowiska c++ definiuja byte jako unsigned char! w javie jest inaczej -> trzeba i tak zmienic w portowanych zrodlach byte na char.
+
+
+#endif
+
Index: /cpp/common/nonstd_dir.h
===================================================================
--- /cpp/common/nonstd_dir.h	(revision 109)
+++ /cpp/common/nonstd_dir.h	(revision 109)
@@ -0,0 +1,15 @@
+#ifndef _NONSTD_DIR_H_
+#define _NONSTD_DIR_H_
+
+#if defined _WIN32 && !defined SHP && !defined __BORLANDC__
+#include "dirent_windows.h"
+#else
+#ifdef SHP
+#include "dirent_bada.h"
+#else
+#include <dirent.h>
+#endif
+#endif
+
+#endif
+
Index: /cpp/common/nonstd_math.cpp
===================================================================
--- /cpp/common/nonstd_math.cpp	(revision 109)
+++ /cpp/common/nonstd_math.cpp	(revision 109)
@@ -0,0 +1,107 @@
+#include "nonstd_math.h"
+
+RandomGenerator& rndGetInstance()
+{
+static RandomGenerator rnd(0);
+return rnd;
+}
+
+
+
+
+#ifdef _WIN32 //http://stackoverflow.com/questions/2170385/c-math-functions
+double round(double val)
+{    
+    return floor(val + 0.5);
+}
+#endif
+
+
+
+
+#if defined LINUX || defined TIZEN
+
+#include <fenv.h>
+
+void fpExceptInit()
+{}
+
+void fpExceptEnable()
+{
+feclearexcept(FE_DIVBYZERO | FE_OVERFLOW);
+feenableexcept(FE_DIVBYZERO | FE_OVERFLOW);
+}
+
+void fpExceptDisable()
+{
+fedisableexcept(FE_DIVBYZERO | FE_OVERFLOW);
+}
+
+#endif
+
+
+#ifdef IPHONE
+
+#include <fenv.h>
+
+void fpExceptInit()
+{}
+
+void fpExceptEnable()
+{
+feclearexcept(FE_DIVBYZERO | FE_OVERFLOW);
+//feenableexcept(FE_DIVBYZERO | FE_OVERFLOW);
+}
+
+void fpExceptDisable()
+{
+//fedisableexcept(FE_DIVBYZERO | FE_OVERFLOW);
+}
+#endif
+
+
+#ifdef __BORLANDC__
+
+#include "framsg.h"
+
+unsigned int fp_control_word_std;
+unsigned int fp_control_word_muted;
+
+void fpExceptInit()
+{
+	//unsigned int was=_clear87();
+	//FMprintf("","fpExceptInit",FMLV_INFO,"control87 status before clear was %08x", was);
+	fp_control_word_std=_control87(0, 0);             //4978 = 1001101110010
+	// Make the new fp env same as the old one, except for the changes we're going to make
+	fp_control_word_muted = fp_control_word_std | EM_INVALID | EM_DENORMAL | EM_ZERODIVIDE | EM_OVERFLOW | EM_UNDERFLOW | EM_INEXACT;  //4991 = 1001101111111
+}
+
+void fpExceptEnable()
+{
+	unsigned int was=_clear87(); //trzeba czyscic zeby nie bylo exception...
+	//FMprintf("","fpExceptEnable ",FMLV_INFO,"control87 status before clear was %08x", was);
+	_control87(fp_control_word_std, 0xffffffff);
+	//FMprintf("","fpExceptEnable ",FMLV_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo
+}
+
+void fpExceptDisable()
+{
+	unsigned int was=_clear87(); //trzeba czyscic zeby nie bylo exception...
+	//FMprintf("","fpExceptDisable",FMLV_INFO,"control87 status before clear was %08x", was);
+	_control87(fp_control_word_muted, 0xffffffff);
+	//FMprintf("","fpExceptDisable",FMLV_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo
+}
+
+#endif
+
+
+
+#ifdef _MSC_VER
+//Moznaby zrobic tak jak pod linuxem czyli wlaczyc exceptiony na poczatku i wylaczac na chwile przy dzieleniu w extvalue.
+//To by pozwoli³o na wy³apanie pod visualem z³ych sytuacji kiedy framsy licz¹ na NaN, INF itp.
+//http://stackoverflow.com/questions/2769814/how-do-i-use-try-catch-to-catch-floating-point-errors
+void fpExceptInit() {}
+void fpExceptEnable() {}
+void fpExceptDisable() {}
+#endif
+
Index: /cpp/common/nonstd_math.h
===================================================================
--- /cpp/common/nonstd_math.h	(revision 109)
+++ /cpp/common/nonstd_math.h	(revision 109)
@@ -0,0 +1,62 @@
+#ifndef _NONSTD_MATH_H_
+#define _NONSTD_MATH_H_
+
+
+#ifdef _MSC_VER
+ #define _USE_MATH_DEFINES //after this is defined, the next #include <math.h> or <cmath> will define M_PI etc.
+ #include <math.h> //w vc2008 dzia³a³o tu <cmath>, ale w vc2010 juz nie bo "co" (jaki inny .h stl'a?) includuje wczeniej <cmath> bez _USE_MATH_DEFINES, a <cmath> includuje <math.h> (ale tylko raz bo ma "include guards" jak kazdy .h)
+ #include <float.h>
+ #define isnan(x) _isnan(x)
+ #define finite(x) _finite(x)
+#else //m.in. __BORLANDC__
+ #include <math.h>
+#endif
+
+#ifdef _WIN32 //http://stackoverflow.com/questions/2170385/c-math-functions
+double round(double val);
+#endif
+
+
+//random number generator:
+#include "random.h"
+RandomGenerator& rndGetInstance();
+
+#define rnd01 (rndGetInstance().getDouble())
+#define rnd0N(limit) (rndGetInstance().getDouble()*(limit))
+#define randomN(limit) ((unsigned int)(rndGetInstance().getDouble()*(limit)))
+#define setRandomSeed(seed) rndGetInstance().setSeed(seed)
+#define setRandomRandomSeed() rndGetInstance().randomize()
+
+//floating point specific numbers
+#include "stdlib.h"
+
+#ifdef __BORLANDC__
+	#include <float.h>
+	#define isnan(x) _isnan(x) //http://stackoverflow.com/questions/570669/checking-if-a-double-or-float-is-nan-in-c
+	#define finite(x) _finite(x)
+#endif
+
+#ifdef IPHONE
+	#define finite(x) (!isinf(x))
+#endif
+
+
+#if defined SHP && !defined TIZEN
+ //#define __assert_func(a,b,c,d) 0 //Currently, we are sorry to inform you that assert() is not yet supported. We have considered your request for internal discussion. Na szczêcie jest w³asna (byle by by³a, bo i tak zak³adamy ze assert ktore przeciez dziala tylko w trybie debug nie jest potrzebne na bada) implementacja w "bada-assert.cpp"
+ #define isnan(x) false //isnan() sie nie linkuje
+ #define finite(x) true //j.w.
+ //#include <cstdlib> //RAND_MAX defined incorrectly
+ //#ifdef BADA_SIMULATOR //...but only in simulator libs
+ // #undef RAND_MAX
+ // #define RAND_MAX 32768 //...this is the actual value used by rand()
+ //#endif
+#endif
+
+
+//handling floating point exceptions
+void fpExceptInit(); //call once, before ...Enable/Disable
+void fpExceptEnable();
+void fpExceptDisable();
+
+#endif
+
Index: /cpp/common/nonstd_stdio.cpp
===================================================================
--- /cpp/common/nonstd_stdio.cpp	(revision 109)
+++ /cpp/common/nonstd_stdio.cpp	(revision 109)
@@ -0,0 +1,207 @@
+#include "nonstd_stdio.h"
+#if defined _WIN32 && !defined SHP
+//tu nie trzeba includów zeby dzialal unlink()
+#else
+#include <unistd.h>
+#endif
+#include "nonstd.h"
+
+bool fileExists(const char* path)
+{
+//lepiej gdyby uzywalo stat bo mfopen mogloby cos niepotrzebnie wczytywac przy otwarciu pliku ale mfopen wiadomo ze zadziala wszedzie tak samo
+MFILE *f=mfopen(path,FOPEN_READ_BINARY);
+if (f==NULL) return false;
+mfclose(f);
+return true;
+}
+
+bool removeFile(const char* path)
+{
+  return _unlink(path)==0; //VS: "The POSIX name is deprecated. Instead, use the ISO C++ conformant name: _unlink"
+}
+
+
+#if defined SHP && defined BADA_API_1
+
+MFILE *mfopen(const char *path, const char *mode)
+{
+	Osp::Io::File *f = new Osp::Io::File();
+	result r = f->Construct(path, mode);
+	if (IsFailed(r))
+	{
+		delete f;
+		f = NULL;
+	}
+	return f;
+}
+
+void mfclose(MFILE *f)
+{
+	delete f;
+}
+
+int mfread(void *ptr, int size, int count, MFILE *f)
+{
+	int bytes = size * count;
+	int przeczytane = f->Read(ptr, bytes);
+	return przeczytane != bytes ? przeczytane / size : count;
+}
+
+int mfwrite(const void *ptr, int size, int count, MFILE *f)
+{
+	result r = f->Write(ptr, size * count);
+	if (IsFailed(r))
+		return 0; //nie mozemy wykryc jesli udalo sie zapisac czêæ
+	else
+		return count;
+}
+
+int mfputs(const char *txt, MFILE *f)
+{
+	int len = strlen(txt);
+	int res = mfwrite(txt, len, 1, f);
+	return res == 1 ? 1 : EOF;
+}
+
+char* mfgets(char *str, int num, MFILE *f)
+{
+	bool err = false;
+	int przeczytane = 0;
+	num--; //zeby zawsze zostalo miejsce na wpisanie koncz¹cego NULL
+	do
+	{
+		err = f->Read(str, 1) != 1;
+		if (!err)
+		{
+			str++;
+			przeczytane++;
+		}
+	} while (!err && przeczytane<num && *str != '\n');
+	if (*str == '\n' && przeczytane<num)
+		*(str + 1) = 0;
+	return przeczytane == 0 ? NULL : str;
+}
+
+int mfeof(MFILE *f)
+{
+	//brzydkie obejscie zeby w bada wykryc czy FILE jest w stanie EOF
+	static char buf[1];
+	int pos = f->Tell();
+	int przeczytane = f->Read(&buf, 1);
+	f->Seek(Osp::Io::FILESEEKPOSITION_BEGIN,pos);
+	return przeczytane == 1 ? 0 : 1;
+}
+
+int mfseek(MFILE *f, long position, int type)
+{
+	result r;
+	if (type == SEEK_SET)
+		r = f->Seek(Osp::Io::FILESEEKPOSITION_BEGIN, position);
+	else if (type == SEEK_CUR)
+		r = f->Seek(Osp::Io::FILESEEKPOSITION_CURRENT, position);
+	else if (type == SEEK_END)
+		r = f->Seek(Osp::Io::FILESEEKPOSITION_END, position);
+	else
+		return 1;
+	return IsFailed(r) ? 1 : 0;
+}
+
+long mftell(MFILE *f)
+{
+	return f->Tell();
+}
+
+#endif
+
+
+
+
+
+#ifdef __ANDROID__
+//#include "framsg.h"
+#include "nonstd.h"
+MFILE *mfopen(const char *path, const char *mode)
+{
+	NvFile *rfile=NULL; //umie tylko czytac
+	FILE *rwfile=NULL;
+	if (strstr(path,GET_APP_RESOURCES)==path) //otwieramy resource! wiec uzywamy czytania z assets
+	{
+		if (path[0]=='.' && path[1]=='/') path+=2; //nie rozpoznaje sciezek "./costam", a w sailorze zrobiles tak ze nie moze byc pusty path bo to jest znacznik ze jest niezainicjowany, dlatego uzywasz "./" i musimy je tu obcinac
+		rfile=NvFOpen(path); //"mode" not supported! umie tylko czytac
+		if (rfile==NULL) return NULL;
+	} else //"normalny" dostep (do HOME)
+	{
+		rwfile=fopen(path,mode);
+		if (rwfile==NULL) return NULL;
+	}
+	MFILE *mfile=new MFILE;
+	mfile->rfile=rfile;
+	mfile->rwfile=rwfile;
+	return mfile;
+}
+
+void mfclose(MFILE *f)
+{
+	if (f->rfile)
+		NvFClose(f->rfile);
+	else
+		fclose(f->rwfile);
+
+	delete f;
+}
+
+int mfread(void *ptr, int size, int count, MFILE *f)
+{
+	if (f->rfile)
+		return size==0?0:NvFRead(ptr, size, count, f->rfile)/size; //blad nvidii w interpretacji zwracanej wartosci - zwraca liczbe bajtow zamiast liczbe porcji
+	else
+		return fread(ptr, size, count, f->rwfile);
+}
+
+int mfwrite(const void *ptr, int size, int count, MFILE *f)
+{
+	if (f->rfile)
+		return 0; //write not supported funkcjami nvidii
+	else
+		return fwrite(ptr, size, count, f->rwfile);
+}
+
+int mfputs(const char *txt, MFILE *f)
+{
+	int len = strlen(txt);
+	int res = mfwrite(txt, len, 1, f);
+	return res == 1 ? 1 : EOF;
+}
+
+char* mfgets(char *str, int num, MFILE *f)
+{
+	if (f->rfile)
+		return NvFGets(str, num, f->rfile);
+	else
+		return fgets(str,num,f->rwfile);
+}
+
+int mfeof(MFILE *f)
+{
+	if (f->rfile)
+		return NvFEOF(f->rfile);
+	else
+		return feof(f->rwfile);
+}
+
+int mfseek(MFILE *f, long position, int type)
+{
+	if (f->rfile)
+		return NvFSeek(f->rfile, position, type)==-1; // off_t AAsset_seek(AAsset* asset, off_t offset, int whence): Returns the new position on success, or (off_t) -1 on error.
+	else
+		return fseek(f->rwfile, position, type);
+}
+
+long mftell(MFILE *f)
+{
+	if (f->rfile)
+		return NvFTell(f->rfile);
+	else
+		return ftell(f->rwfile);
+}
+#endif
Index: /cpp/common/nonstd_stdio.h
===================================================================
--- /cpp/common/nonstd_stdio.h	(revision 109)
+++ /cpp/common/nonstd_stdio.h	(revision 109)
@@ -0,0 +1,92 @@
+#ifndef _NONSTD_STDIO_H_
+#define _NONSTD_STDIO_H_
+
+bool fileExists(const char* path);
+bool removeFile(const char* path);
+
+#ifdef _WIN32
+
+
+#ifndef _MSC_VER
+ #include <dir.h>
+#else
+ #ifndef MOBILE2D
+ #include <direct.h>
+ #endif
+ #define mkdir _mkdir
+#endif
+
+#ifndef MOBILE2D
+ #include <io.h> //borland compiler: include <io.h> before <dir.h> causes the SimWorld class in "simul.h" be unrecognized, for unknown reason :O moreover, this problem is only pertinent to the CLI project, not GUI. Maybe this is caused by global defines like NOVCL, NO_STRICT etc.?
+ #define makeDirectory(name) mkdir(name) 
+#endif
+
+
+#else
+
+ #include <unistd.h>
+ #include <sys/stat.h>
+ #define makeDirectory(name) mkdir(name,0777)
+ #define _unlink unlink //_unlink jest ISO-conformant, unlink jest POSIX-deprecated
+
+#endif
+
+
+#include <stdio.h>
+
+#if (defined SHP && defined BADA_API_1) || defined __ANDROID__
+
+#ifdef __ANDROID__
+#include <nv_file/nv_file.h>
+ struct rwFILE //jedno z dwoch pol jest zainicjowane w zaleznosci od tego gdzie jest plik
+ {
+	NvFile *rfile; //umie tylko czytac
+	FILE *rwfile;
+	rwFILE() {rfile=rwfile=NULL;}
+ };
+ typedef rwFILE MFILE;
+#else //SHP:
+ //z <stdio.h> wzielismy sprintfy i inne ktore dzia³aj¹...
+ #include <FIo.h>
+ // wklejone z sailora w ramach integracji frams+engine
+ // ale to nie sprawia ze framsy korzystaja z mfile - potrzebna jest implementacja virtfile dla bady! (patrz: stdiofile.h)
+ // i wtedy bedzie mozna mfile wywalic tez z sailora
+ typedef Osp::Io::File MFILE;
+#endif
+
+MFILE *mfopen(const char*path,const char*mode);
+void mfclose(MFILE *f);
+int mfread(void *ptr, int size, int n, MFILE *f);
+int mfwrite(const void *ptr, int size, int n, MFILE *f);
+int mfputs(const char *, MFILE *);
+int	mfseek(MFILE *, long, int);
+long mftell(MFILE *);
+char *mfgets(char *str, int num, MFILE *f);
+int mfeof(MFILE *f);
+
+//#define	SEEK_SET	0	/* set file offset to offset */
+//#define	SEEK_CUR	1	/* set file offset to current plus offset */
+//#define	SEEK_END	2	/* set file offset to EOF plus offset */
+//int	sprintf(char *, const char *, ...);
+//int	vsnprintf(char *,int, const char *, ...);
+
+#else
+ typedef FILE MFILE;
+ #define mfopen fopen
+ #define mfclose fclose
+ #define mfread fread
+ #define mfwrite fwrite
+ #define mfputs fputs
+ #define mfgets fgets
+ #define mfeof feof
+ #define mfseek fseek
+ #define mftell ftell
+#endif
+
+
+#ifndef _WIN32
+#define _strdup strdup //_strdup jest ISO-conformant, strdup jest POSIX deprecated
+#include <string.h> //strdup
+#endif
+
+#endif
Index: /cpp/common/nonstd_stl.h
===================================================================
--- /cpp/common/nonstd_stl.h	(revision 109)
+++ /cpp/common/nonstd_stl.h	(revision 109)
@@ -0,0 +1,46 @@
+#ifndef _NONSTD_STL_H_
+#define _NONSTD_STL_H_
+
+//stl jak sama nazwa glosi wcale nie jest nonstd
+
+#include <string>
+using std::string;
+#ifndef SHP //bada nie ma wstring
+using std::wstring;
+#endif
+
+#include <vector>
+using std::vector;
+
+#include <algorithm> //std::min,max,swap
+using std::min;
+using std::max;
+using std::swap;
+
+
+// ------------------- ARRAY_LENGTH -------------------
+
+//staromodne makro, niezabezpieczone przed uzyciem wskaznika w roli "x"
+//#define ARRAY_LENGTH(x) (sizeof(x)/sizeof((x)[0]))
+
+//hakerskie makro ktore wykrywa czesc pomy³kowych przypadkow uzycia
+//#define ARRAY_LENGTH(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+
+//szablonowa funkcja pisana przez sredniozaawansowanych, jak to funkcja - nie daje niestety sta³ej w czasie kompilacji
+//template<typename T, std::size_t N> inline std::size_t ARRAY_LENGTH( T(&)[N] ) { return N; } //"constexpr" dopiero w C++0x
+
+//szablony hakerskie: tablica bajtow o dlugosci N - tak dluga jak tablica o któr¹ pytamy...
+template <typename T, std::size_t N>
+char (&array_temp(T (&a)[N]))[N];
+
+// As litb noted in comments, you need this overload to handle array rvalues
+// correctly (e.g. when array is a member of a struct returned from function),
+// since they won't bind to non-const reference in the overload above.
+template <typename T, std::size_t N>
+char (&array_temp(const T (&a)[N]))[N];
+
+//...ktor¹ mozna potem uzyc normalnie w sizeof i dzieki temu mamy const w compile-time. tak uzyteczne jak staromodne makro ale z pelna kontrola bledow
+#define ARRAY_LENGTH(x) sizeof(array_temp(x))
+
+
+#endif
Index: /cpp/common/random.h
===================================================================
--- /cpp/common/random.h	(revision 109)
+++ /cpp/common/random.h	(revision 109)
@@ -0,0 +1,115 @@
+#include "nonstd.h" //LONGLONG
+#include <time.h> //time()
+#ifdef MULTITHREADED
+#include "threads.h"
+#endif
+#ifdef LINUX
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+//adapted from
+//http://en.wikipedia.org/wiki/Mersenne_twister#Pseudocode
+//http://my.opera.com/metrallik/blog/2013/04/19/c-class-for-random-generation-with-mersenne-twister-method
+
+class RandomGenerator
+{
+private:
+	static const unsigned int length=624;
+	static const unsigned int bitMask_32=0xffffffff;
+	static const unsigned int bitPow_31=1<<31;
+	static const unsigned int MAXVALUE=0xffffffff;
+	unsigned int counter; //only used in randomize(). uninitialized is OK
+#ifdef MULTITHREADED
+	pthread_mutex_t lock;
+#endif
+	unsigned int *mt;
+	unsigned int idx;
+public:
+
+	RandomGenerator(unsigned int seed)
+	{
+#ifdef MULTITHREADED
+		pthread_mutex_init(&lock,NULL);
+#endif
+		mt=new unsigned int[length];
+		setSeed(seed);
+	}
+
+	inline void setSeed(unsigned int seed)
+	{
+#ifdef MULTITHREADED
+		pthread_mutex_lock(&lock);
+#endif
+		idx=0;
+		mt[0]=seed;
+		for(unsigned int i=1;i<length;i++)
+			mt[i]=(1812433253*(mt[i-1]^(mt[i-1]>>30))+i)&bitMask_32;
+#ifdef MULTITHREADED
+		pthread_mutex_unlock(&lock);
+#endif
+	}
+
+	inline unsigned int randomize()
+	{
+		unsigned int seed;
+#ifdef LINUX
+		int fd=open("/dev/urandom",O_RDONLY);
+		if (fd>=0)
+		{
+			read(fd,&seed,sizeof(seed));
+			close(fd);
+		}
+		else
+#endif
+		{
+			counter++;
+			seed=time(NULL);              //time (seconds)
+			seed^=counter;                //incremented value, possibly randomly initialized
+			seed^=(unsigned int)&counter; //memory address
+		}
+		setSeed(seed);
+		return seed;
+	}
+
+	inline unsigned int getUint32()
+	{
+#ifdef MULTITHREADED
+		pthread_mutex_lock(&lock);
+#endif
+		if (idx==0) gen();
+		unsigned int y=mt[idx];
+		idx=(idx+1)%length;
+#ifdef MULTITHREADED
+		pthread_mutex_unlock(&lock);
+#endif
+		y^= y>>11;
+		y^=(y<< 7)&2636928640U;
+		y^=(y<<15)&4022730752U;
+		y^= y>>18;
+		return y;
+	}
+
+	inline double getDouble() // [0,1)
+	{
+		return double(getUint32())/((LONGLONG)(MAXVALUE)+1);
+	}
+
+	inline void gen()
+	{
+		for(unsigned int i=0;i<length;i++)
+		{
+			unsigned int y=(mt[i]&bitPow_31)+(mt[(i+1)%length]&(bitPow_31-1));
+			mt[i]=mt[(i+397)%length]^(y>>1);
+			if(y%2) mt[i]^=2567483615U;
+		}
+		return;
+	}
+
+	~RandomGenerator()
+	{
+		delete[] mt;
+	}
+};
+
Index: /cpp/common/threads.h
===================================================================
--- /cpp/common/threads.h	(revision 109)
+++ /cpp/common/threads.h	(revision 109)
@@ -0,0 +1,116 @@
+#ifndef _THREADS_H_
+#define _THREADS_H_
+
+#ifdef MULTITHREADED
+
+#include <pthread.h>
+
+int sysGetCPUCount();
+
+#ifdef LINUX
+//#define USE_CPP_TLS
+//#define CPP_TLS __thread
+#endif
+
+#ifdef __BORLANDC__ //zakladamy ze wszyscy uzywaja pthreadsowych, bo w tych wbudowanych w c++ w obecnym standardzie nie ma destrukcji obiektow (tylko proste struktury)
+//#define USE_CPP_TLS 
+//#define CPP_TLS __declspec(thread)
+#endif
+
+template<class T> class ThreadSingleton
+{
+pthread_key_t mt_key;
+
+  public:
+
+ThreadSingleton()
+ {
+ pthread_key_create(&mt_key,&destructor);
+ }
+
+~ThreadSingleton()
+ {
+ T* o=set(NULL);
+ if (o) delete o;
+ }
+
+static void destructor(void* o)
+ {
+ if (o)
+	 delete (T*)o;
+ }
+
+T* set(T* new_o)
+ {
+ T* o=(T*)pthread_getspecific(mt_key);
+ pthread_setspecific(mt_key,new_o);
+ return o;
+ }
+
+T* get()
+ {
+ T* o=(T*)pthread_getspecific(mt_key);
+ if (!o)
+	 {
+	 o=new T();
+	 pthread_setspecific(mt_key,o);
+	 }
+ return o;
+ }
+
+T& getref() {return *get();}
+};
+
+#else
+
+template<class T> class ThreadSingleton
+{
+T object;
+
+  public:
+
+T* get() {return &object;}
+T& getref() {return object;}
+};
+
+#endif
+
+////////////////////////////////////
+
+#ifdef USE_CPP_TLS
+
+// use c++ implementation (CPP_TLS must also be defined)
+
+#define THREAD_LOCAL_DECL(cls,var) CPP_TLS cls* var
+#define THREAD_LOCAL_DEF(cls,var) CPP_TLS cls* var=NULL
+
+template<class T> T* tlsGet(T*& var)
+{
+if (!var)
+	var=new T();
+return var;
+}
+
+#define tlsGetRef(var) (*tlsGet(var))
+
+template<class T> T* tlsSet(T*& var,T* new_o)
+{
+T* o=var;
+var=new_o;
+return o;
+}
+
+#else
+
+// use pthreads implementation
+
+#define THREAD_LOCAL_DECL(cls,var) ThreadSingleton<cls> var
+#define THREAD_LOCAL_DEF(cls,var) ThreadSingleton<cls> var
+#define tlsGet(var) var.get()
+#define tlsGetRef(var) var.getref()
+#define tlsSet(var,o) var.set(o)
+
+#endif
+
+
+#endif
Index: /cpp/frams/Makefile-GDK
===================================================================
--- /cpp/frams/Makefile-GDK	(revision 109)
+++ /cpp/frams/Makefile-GDK	(revision 109)
@@ -0,0 +1,53 @@
+#
+# Framsticks GDK makefile
+#
+
+include frams/Makefile-GDK-files
+
+GDK_TESTS=conv_test gdk_test geno_test neuro_test loader_test serial_test multiline_f0_test f0_variants_test full_props part_shapes
+
+gdk_tests: $(GDK_TESTS)
+
+GDK_BUILD_CONFIG= -include frams/config/gdk_build_config.h
+CXXFLAGS= -I$(CURDIR) -DLINUX $(GDK_BUILD_CONFIG) -Wno-parentheses -Wno-overloaded-virtual -Wno-format -g
+
+#############################################
+
+gdk_test: $(GDK_TEST_OBJS)
+	$(CXX) $(GDK_TEST_OBJS) -o $@
+
+conv_test: $(CONV_TEST_OBJS)
+	$(CXX) $(CONV_TEST_OBJS) -o $@
+
+geno_test: $(GENO_TEST_OBJS)
+	$(CXX) $(GENO_TEST_OBJS) -o $@
+
+neuro_test: $(NEURO_TEST_OBJS)
+	$(CXX) $(NEURO_TEST_OBJS) -o $@
+
+loader_test: $(LOADER_TEST_OBJS)
+	$(CXX) $(LOADER_TEST_OBJS) -o $@
+
+serial_test: $(SERIAL_TEST_OBJS)
+	$(CXX) $(SERIAL_TEST_OBJS) -o $@
+
+multiline_f0_test: $(MULTILINE_F0_OBJS)
+	$(CXX) $(MULTILINE_F0_OBJS) -o $@
+
+f0_variants_test: $(F0_VARIANTS_OBJS)
+	$(CXX) $(F0_VARIANTS_OBJS) -o $@
+
+full_props: $(FULL_PROPS_OBJS)
+	$(CXX) $(FULL_PROPS_OBJS) -o $@
+
+part_shapes: $(PART_SHAPES_OBJS)
+	$(CXX) $(PART_SHAPES_OBJS) -o $@
+
+#############################################
+
+ALL_OBJS=$(GDK_TEST_OBJS) $(CONV_TEST_OBJS) $(GENO_TEST_OBJS) $(NEURO_TEST_OBJS) $(LOADER_TEST_OBJS) $(SERIAL_TEST_OBJS) $(MULTILINE_F0_OBJS) $(F0_VARIANTS_OBJS) $(FULL_PROPS_OBJS) $(PART_SHAPES_OBJS)
+
+include frams/Makefile-maintain
+
+full_clean: clean
+	rm -f $(GDK_TESTS)
Index: /cpp/frams/Makefile-GDK-files
===================================================================
--- /cpp/frams/Makefile-GDK-files	(revision 109)
+++ /cpp/frams/Makefile-GDK-files	(revision 109)
@@ -0,0 +1,33 @@
+#### modules
+
+ALL_DIRS={common,frams,frams/config,frams/errmgr,frams/genetic,frams/genetic/conv,frams/genetic/oper,frams/model,frams/neuro,frams/neuro/impl,frams/param,frams/test,frams/util,frams/vm/classes}
+
+GENOCONV_GDK_OBJS=frams/genetic/conv/conv_f1.o
+
+GDK_OBJS=frams/util/list.o frams/util/advlist.o frams/param/param.o frams/util/sstring.o frams/util/sstringutils.o frams/util/3d.o frams/vm/classes/3dobject.o frams/model/model.o frams/model/modelparts.o frams/neuro/neurolibrary.o frams/genetic/geno.o frams/genetic/genoconv.o frams/util/extvalue.o frams/vm/classes/collectionobj.o frams/util/hashtable.o common/framsg.o frams/util/callbacks.o frams/param/syntparam.o frams/util/multirange.o frams/util/multimap.o frams/param/paramtabobj.o frams/genetic/defgenoconv.o frams/errmgr/errmanager.o frams/param/paramobj.o frams/genetic/oper/geno_fx.o common/nonstd_math.o frams/errmgr/stderrors.o common/Convert.o frams/util/rndutil.o
+
+
+#### gdk_tests
+
+GDK_TEST_OBJS= frams/_demos/gdk_test.o frams/errmgr/stdouterr.o frams/virtfile/virtfile.o  $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+MULTILINE_F0_OBJS=frams/_demos/multiline_f0_test.o frams/virtfile/stringfile.o frams/virtfile/virtfile.o frams/errmgr/stdouterr.o $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+F0_VARIANTS_OBJS=frams/_demos/f0_variants_test.o frams/virtfile/stringfile.o frams/virtfile/virtfile.o frams/errmgr/stdouterr.o   $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+LOADER_TEST_OBJS=frams/_demos/genotypeloader.o frams/_demos/loader_test.o frams/virtfile/virtfile.o frams/param/multiparamload.o frams/virtfile/stdiofile-autoinit.o frams/virtfile/stdiofile.o common/nonstd_stdio.o $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+CONV_TEST_OBJS= frams/_demos/conv_test.o frams/_demos/printconvmap.o frams/errmgr/stdouterr.o frams/virtfile/virtfile.o  $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+GENO_TEST_OBJS= frams/_demos/geno_test.o frams/virtfile/virtfile.o   $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+NEURO_TEST_OBJS= frams/_demos/neuro_test.o frams/errmgr/stdouterr.o frams/virtfile/virtfile.o \
+	frams/neuro/neuroimpl.o frams/neuro/neurofactory.o frams/neuro/impl/neuroimpl-simple.o frams/neuro/impl/neuroimpl-channels.o \
+	frams/neuro/impl/neuroimpl-fuzzy.o frams/neuro/impl/neuroimpl-fuzzy-f0.o  $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+FULL_PROPS_OBJS= frams/_demos/full_props.o frams/errmgr/stdouterr.o frams/virtfile/virtfile.o  frams/virtfile/stdiofile.o common/nonstd_stdio.o $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+SERIAL_TEST_OBJS= frams/_demos/serial_test.o frams/virtfile/virtfile.o  $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
+PART_SHAPES_OBJS= frams/_demos/part_shapes.o frams/virtfile/virtfile.o  $(GDK_OBJS) $(GENOCONV_GDK_OBJS)
+
Index: /cpp/frams/Makefile-maintain
===================================================================
--- /cpp/frams/Makefile-maintain	(revision 109)
+++ /cpp/frams/Makefile-maintain	(revision 109)
@@ -0,0 +1,19 @@
+
+clean:
+ifeq "$(ALL_DIRS)$(ALL_DIRS)" ""
+	@echo "Makefile-maintain clean: ALL_DIRS and/or EXTRA_CLEAN_FILES must be defined"
+else
+	rm -f $(EXTRA_CLEAN_FILES) $(ALL_DIRS)/*.{a,o,d}
+endif
+
+ALL_DEPS=$(ALL_OBJS:.o=.d)
+
+ifneq "$(MAKECMDGOALS)" "clean"
+-include $(ALL_DEPS)
+endif
+
+depend: $(ALL_DEPS)
+
+%.d: %.cpp
+	@set -e; rm -f $@; $(CXX) $(CXXFLAGS) -MM -MT $(<:.cpp=.o) $< > $@
+#	g++ $(CXXFLAGS) -MM -MT $(<:.o=.d) $< | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' > $@
Index: /cpp/frams/_demos/conv_test.cpp
===================================================================
--- /cpp/frams/_demos/conv_test.cpp	(revision 109)
+++ /cpp/frams/_demos/conv_test.cpp	(revision 109)
@@ -0,0 +1,159 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <ctype.h>
+#include <frams/genetic/genoconv.h>
+#include <frams/model/model.h>
+#include <frams/util/multimap.h>
+#include <frams/genetic/conv/conv_f1.h>
+
+#include "printconvmap.h"
+#include <frams/errmgr/stdouterr.h>
+
+StdoutErrorHandler err;
+
+/**
+ @file
+ Sample code: Genotype converter class
+*/
+
+/// Sample Geno converter not using Model class.
+/// (this converter generates the same output for each input).
+/// such a converter is responsible for doing valid
+/// f0 (or other format) output and storing temporary data
+class GenoConv_Test: public GenoConverter
+{
+public:
+GenoConv_Test()
+	{
+	name="Test Converter";
+	in_format='x';
+	}
+SString convert(SString &i,MultiMap *map) {return SString("after conversion...");}
+~GenoConv_Test() {}
+};
+
+/// Sample Geno converter using Model class.
+/// (this converter generates the same output for each input).
+class GenoConv_Test2: public GenoConverter
+{
+public:
+GenoConv_Test2()
+	{
+	name="Test Converter #2";
+	in_format='y';
+	}
+SString convert(SString &i,MultiMap *map)
+{
+Model mod;
+mod.open();
+mod.singleStepBuild("p:");
+mod.singleStepBuild("p:");
+mod.singleStepBuild("j:0,1");
+mod.getPart(1)->p=Pt3D(0,0.2,-1);
+mod.close();
+return mod.getGeno().getGene();
+}
+~GenoConv_Test2() {}
+};
+
+/// Sample Geno converter supporting conversion mapping.
+/// the conversion is very simple: any sequence of <digit><character>
+/// (but not inside neurons) is replaced by the repeated sequence of the character
+class GenoConv_Test3: public GenoConverter
+{
+public:
+GenoConv_Test3()
+	{
+	name="Test Converter #3";
+	in_format='z';
+	out_format='1';
+	mapsupport=1;
+	}
+SString convert(SString &in,MultiMap *map);
+~GenoConv_Test3() {}
+};
+
+/** main converting routine - most important: direct conversion map example */
+SString GenoConv_Test3::convert(SString &in,MultiMap *map)
+{
+SString dst;
+const char* src=in;
+const char* t;
+int insideneuron=0;
+int n;
+for(t=src;*t;t++)
+	{
+	if (insideneuron&&*t==']') insideneuron=0;
+	if (*t=='[') insideneuron=1;
+	if ((!insideneuron)&&isdigit(*t)&&t[1])
+		{ // special sequence detected!
+		n=*t-'0';
+		t++; // *t will be repeated 'n' times
+		for (int i=0;i<n;i++)
+			dst+=*t;
+		if (map) // fill in the map only if requested
+			map->add(t-src, t-src, dst.len()-n, dst.len()-1);
+		// meaning: source character (t-src) becomes (dst.len()-n ... dst.len()-1)
+		}
+	else
+		{
+		dst+=*t;
+		if (map)
+			map->add(t-src, t-src, dst.len()-1, dst.len()-1);
+		// meaning: map single to single character: (t-src) into (dst.len()-1)
+		}
+	}
+return dst;
+}
+
+
+///////////////////////////////////////////////
+
+void printGen(Geno &g)
+{
+printf("Genotype:\n%s\nFormat: %c\nValid: %s\nComment: %s\n",
+	(const char*)g.getGene(),g.getFormat(),g.isValid()?"yes":"no",(const char*)g.getComment());
+}
+
+int main(int argc,char *argv[])
+{
+GenoConvManager gcm;
+gcm.addConverter(new GenoConv_Test());
+gcm.addConverter(new GenoConv_Test2());
+gcm.addConverter(new GenoConv_Test3());
+gcm.addConverter(new GenoConv_F1());
+
+const char* src=(argc>1)?argv[1]:"X";
+char dst=(argc>2)?*argv[2]:'0';
+
+printf("*** source genotype:\n");
+Geno g1(src);
+printGen(g1);
+MultiMap m;
+Geno g2=g1.getConverted(dst,&m);
+printf("*** converted to f%c:\n",dst);
+printGen(g2);
+if (m.isEmpty())
+	printf("(conversion map not available)\n");
+else
+	{
+	printf("conversion map:\n");
+	m.print();
+	printConvMap(g1.getGene(),g2.getGene(),m);
+	printf("reverse conversion map:\n");
+	MultiMap rm;
+	rm.addReversed(m);
+	rm.print();
+	printConvMap(g2.getGene(),g1.getGene(),rm);
+	}
+
+Model mod1(g1,1);
+printf("\nmodel map for f%c genotype:\n",g1.getFormat());
+printModelMap(g1.getGene(),mod1.getMap());
+Model mod2(g2,1);
+printf("\nmodel map for f%c genotype:\n",g2.getFormat());
+printModelMap(g2.getGene(),mod2.getMap());
+return 0;
+}
Index: /cpp/frams/_demos/f0_variants_test.cpp
===================================================================
--- /cpp/frams/_demos/f0_variants_test.cpp	(revision 109)
+++ /cpp/frams/_demos/f0_variants_test.cpp	(revision 109)
@@ -0,0 +1,140 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <frams/virtfile/stdiofile.h>
+
+#include <frams/model/model.h>
+#include <frams/genetic/defgenoconv.h>
+#include <frams/errmgr/stdouterr.h>
+
+StdoutErrorHandler err; //redirect model-related errors to stdout
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+void save_as_f0(SString &gen,Model &m,bool omit_default_values)
+{
+// copied from Model::makeGeno() (with small changes)
+
+static Param modelparam(f0_model_paramtab);
+static Param partparam(f0_part_paramtab);
+static Param jointparam(f0_joint_paramtab);
+static Param neuroparam(f0_neuro_paramtab);
+static Param connparam(f0_neuroconn_paramtab);
+
+static Part defaultpart;
+static Joint defaultjoint;
+static Neuro defaultneuro;
+static Model defaultmodel;
+static NeuroConn defaultconn;
+
+modelparam.select(&m);
+gen+="m:";
+modelparam.save2(gen,omit_default_values ? &defaultmodel : NULL);
+
+Part *p;
+Joint *j;
+Neuro *n;
+
+for (int i=0;p=(Part*)m.getPart(i);i++)
+	{
+	partparam.select(p);
+	gen+="p:";
+	partparam.save2(gen,omit_default_values ? &defaultpart : NULL);
+	}
+for (int i=0;j=(Joint*)m.getJoint(i);i++)
+	{
+	jointparam.select(j);
+	jointparam.setParamTab(j->usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab);
+	gen+="j:";
+	jointparam.save2(gen,omit_default_values ? &defaultjoint : NULL);
+	}
+for (int i=0;n=(Neuro*)m.getNeuro(i);i++)
+	{
+	neuroparam.select(n);
+	gen+="n:";
+	neuroparam.save2(gen,omit_default_values ? &defaultneuro : NULL);
+	}
+for (int a=0;n=(Neuro*)m.getNeuro(a);a++)
+	{ // inputs
+	for (int b=0;b<n->getInputCount();b++)
+		{
+		double w;
+		NeuroConn nc;
+		Neuro* n2=n->getInput(b,w);
+		nc.n1_refno=n->refno; nc.n2_refno=n2->refno;
+		nc.weight=w;
+		nc.info=n->getInputInfo(b);
+		connparam.select(&nc);
+		gen+="c:";
+		connparam.save2(gen,omit_default_values ? &defaultconn : NULL);
+		}
+	}
+}
+
+int main(int argc,char*argv[])
+{
+SString gen(argc>1?argv[1]:"X[|G:1.23]");
+if (!strcmp(gen,"-"))
+	{
+	gen=0;
+	StdioFILEDontClose in(stdin);
+	loadSString(&in,gen);
+	}
+Geno g(gen);
+printf("\nSource genotype: '%s'\n",(const char*)g.getGene());
+printf("                  ( format %c %s)\n",
+       g.getFormat(), (const char*)g.getComment());
+
+Model m(g);//.getConverted('0'));
+
+if (!m.isValid())
+	{
+	printf("Cannot build Model from this genotype!\n");
+	return 2;	
+	}
+
+printf("\nthis example shows how to save a f0 genotype using low-level ParamInterface::save2() calls\n");
+
+SString f0_skipping_defaults;
+SString f0_no_skipping_defaults;
+
+save_as_f0(f0_skipping_defaults,m,true);
+save_as_f0(f0_no_skipping_defaults,m,false);
+
+printf("\n==== with defdata (skips default values) ======\n%s\n",(const char*)f0_skipping_defaults);
+printf("\n==== without defdata (saves all fields) ======\n%s\n",(const char*)f0_no_skipping_defaults);
+
+return 0;
+}
+
+/*********************** EXAMPLE OUTPUT *********************************
+
+Source genotype: 'X[|G:1.23]'
+                  ( format 1 )
+
+this example shows how to save a f0 genotype using low-level ParamInterface::save2() calls
+
+==== with defdata (skips default values) ======
+m:
+p:
+p:1
+j:0, 1, dx=1
+n:p=1, d=N
+n:j=0, d="|:p=0.25,r=1"
+n:j=0, d=G
+c:0, 2, 1.23
+c:1, 0
+
+
+==== without defdata (saves all fields) ======
+m:se=1, Vstyle=
+p:0, 0, 0, m=1, s=1, dn=1, fr=0.4, ing=0.25, as=0.25, rx=0, 0, 0, i=, Vstyle=part
+p:1, 0, 0, m=1, s=1, dn=1, fr=0.4, ing=0.25, as=0.25, rx=0, 0, 0, i=, Vstyle=part
+j:0, 1, rx=0, 0, 0, dx=1, 0, 0, stif=1, rotstif=1, stam=0.25, i=, Vstyle=joint
+n:p=1, j=-1, d=N, i=, Vstyle=neuro
+n:p=-1, j=0, d="|:p=0.25,r=1", i=, Vstyle=neuro
+n:p=-1, j=0, d=G, i=, Vstyle=neuro
+c:0, 2, 1.23, i=
+c:1, 0, 1, i=
+
+*************************************************************************/
+
Index: /cpp/frams/_demos/full_props.cpp
===================================================================
--- /cpp/frams/_demos/full_props.cpp	(revision 109)
+++ /cpp/frams/_demos/full_props.cpp	(revision 109)
@@ -0,0 +1,104 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <frams/virtfile/stdiofile.h>
+
+#include <frams/model/model.h>
+#include <frams/genetic/defgenoconv.h>
+#include <frams/errmgr/stdouterr.h>
+
+/**
+ @file
+ Sample code: Adding/removing default properties in f0 genotypes
+ Usage: full_props [-r[everse]] [genotype_or_stdin]
+
+ Calling examples:
+
+ # convert f1 -> f0 + explicitly set all properties to their default values  (the result is still valid f0)
+ ./full_props "X[|]arX[-1:1,fo:0.2]"
+
+   //0
+   p:x=0, y=0, z=0, m=1, s=1, dn=1, fr=0.4, ing=0.25, as=0.25, rx=0, ry=0, rz=0, i=, Vstyle=part, vs=0.2, vr=0.5, vg=0.5, vb=0.5
+   p:x=1, y=0, z=0, m=2, s=1, dn=1, fr=0.4, ing=0.25, as=0.25, rx=0, ry=0, rz=0, i=, Vstyle=part, vs=0.2, vr=0.5, vg=0.5, vb=0.5
+   p:x=2, y=0, z=0, m=1, s=1, dn=1, fr=0.4, ing=0.277778, as=0.166667, rx=0, ry=0, rz=0, i=, Vstyle=part, vs=0.2, vr=0.5, vg=0.5, vb=0.5
+   j:p1=0, p2=1, rx=0, ry=0, rz=0, dx=1, dy=0, dz=0, stif=1, rotstif=1, stam=0.25, i=, Vstyle=joint, vr=0.5, vg=0.5, vb=0.5
+   j:p1=1, p2=2, rx=-0.7853, ry=0, rz=0, dx=1, dy=0, dz=0, stif=1, rotstif=1, stam=0.277778, i=, Vstyle=joint, vr=0.5, vg=0.5, vb=0.5
+   n:p=-1, j=0, d="|:p=0.25, r=1", i=, Vstyle=neuro
+   n:p=2, j=-1, d="N:in=0.8, fo=0.2, si=2, s=0", i=, Vstyle=neuro
+   c:n1=1, n2=0, w=1, i=
+
+ # convert the previous output to the regular f0 format (removing redundant default properties)
+ ./full_props "X[|]arX[-1:1,fo:0.2]" | ./full_props -r
+
+   //0
+   p:
+   p:1, m=2
+   p:2, ing=0.277778, as=0.166667
+   j:0, 1, dx=1
+   j:1, 2, rx=-0.7853, dx=1, stam=0.277778
+   n:j=0, d=|
+   n:p=2, d=N:fo=0.2
+   c:1, 0
+
+*/
+
+//optional:
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+int main(int argc,char*argv[])
+{
+StdioFILE::setStdio();//setup VirtFILE::Vstdin/out/err
+StdoutErrorHandler err(ErrorHandlerBase::DontBlock,VirtFILE::Vstderr); //errors -> stderr, don't interfere with stdout
+
+bool reverse=false;
+char* gen_arg=0;
+for(int i=1;i<argc;i++)
+	{
+	char* ar=argv[i];
+	if (ar[0]=='-')
+		switch(ar[1])
+			{
+			case 'r': reverse=true; break;
+			case 'h': puts("usage: full_props [-r[everse]] [genotype_or_stdin]\n"); break;
+			}
+	else
+		if (!gen_arg)
+			gen_arg=ar;
+	}
+SString gen;
+if (gen_arg)
+	gen=gen_arg;
+else
+	loadSString(VirtFILE::Vstdin,gen);
+Geno g(gen);
+Model m(g);
+
+if (!m.isValid())
+	{
+	FMprintf("","full_props",FMLV_ERROR,"Cannot build Model from the supplied genotype\n");
+	return 2;	
+	}
+
+m.open();
+for(int i=0;i<m.getNeuroCount();i++)
+	{
+	Neuro *n=m.getNeuro(i);
+	SyntParam p=n->classProperties(reverse);
+	p.update();// ...so everyone reading the source can recognize that p was created to update the neuro d field
+	// but actually, calling update() here is not necessary, because ~SyntParam() would do it anyway
+	}
+m.close();
+
+// normal f0 would be retrieved using m.getF0Geno()
+// the following form allows for more control:
+Geno f0_g;
+m.makeGeno(f0_g,NULL,reverse);//third arg is "handle_defaults" == whether f0 should omit default property values
+puts((const char*)f0_g.toString());
+
+return 0;
+}
+
Index: /cpp/frams/_demos/gdk_test.cpp
===================================================================
--- /cpp/frams/_demos/gdk_test.cpp	(revision 109)
+++ /cpp/frams/_demos/gdk_test.cpp	(revision 109)
@@ -0,0 +1,356 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <frams/virtfile/stdiofile.h>
+
+#include <frams/model/model.h>
+#include <frams/genetic/defgenoconv.h>
+#include <frams/errmgr/stdouterr.h>
+
+/**
+ @file
+ Sample code: Accessing model elements
+*/
+
+StdoutErrorHandler err; //redirect model-related errors to stdout
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+void printNiceBanner(const char* title)
+{
+printf("    ########################################\n"
+       "   ##                                      ##\n"
+       "  ##    %-32s    ##\n"
+       "   ##                                      ##\n"
+       "    ########################################\n",title);
+}
+void printProperties(Param &pi)
+{
+printf(" #        id                      type  name        group (%d properties)\n",pi.getPropCount());
+for (int i=0;i<pi.getPropCount();i++)
+	{
+	const char* type=pi.type(i);
+	if (*type=='p') continue;
+	printf("%2d. %8s = %-20s %-3s %-10s  %-10s\n",i,pi.id(i),(const char*)pi.get(i),pi.type(i),pi.name(i),pi.grname(pi.group(i)));
+	}
+}
+
+#define PRINT_PROPERTIES(p) {Param tmp_param(p); printProperties(tmp_param);}
+
+void changeOneProperty(Param &pi)
+{
+if (pi.getPropCount()<=0) return;
+int i=rand() % pi.getPropCount();
+double maxprop=1,minprop=0,def;
+pi.getMinMax(i,minprop,maxprop,def);
+printf("      Change property #%d to random value from range [%g..%g]\n",i,minprop,maxprop);
+printf("      Current value of '%s' (%s) is '%s'\n",pi.id(i),pi.name(i),(const char*)pi.get(i));
+char t[100];
+sprintf(t,"%g",minprop+(rnd01)*(maxprop-minprop));
+printf("      Setting new value... [ using ParamInterface::set() ]\n");
+pi.set(i,t);
+printf("      The value is now '%s'\n",(const char*)pi.get(i));
+}
+
+#define CHANGE_ONE_PROPERTY(p) {Param tmp_param(p); changeOneProperty(tmp_param);}
+
+void moreAboutPart(Part* p)
+{
+printf("Here is the full listing of properties as they are printed in f0\n"
+       " (please compare with f0 genotype).\n"
+       "Some properties have special meaning (eg. geometry and connections groups)\n"
+       "and should be handled with care, because they influence other elements of the model.\n\n"
+       " [this data is provided by Part::properties() ]\n");
+PRINT_PROPERTIES(p->properties());
+printf("\nHowever, there is a subset of properties which may be modified more freely.\n"
+       "Properties on this list are related only to this part and can be changed\n"
+       "without much consideration. They are guaranteed to be always valid; any inconsistencies\n"
+       "will be silently repaired.\n"
+       "\n [this data is provided by Part::extraProperties() ]\n");
+PRINT_PROPERTIES(p->extraProperties());
+printf("\nThis set of properties can vary from release to release,\n"
+       "but can be safely accessed by using extraProperties() call.\n"
+	   "This method accesses the full set of properies (even those\n"
+	   "which appear in future releases).\n"
+       "Now we will try to change some of properties:\n\n");
+p->getModel().open();
+CHANGE_ONE_PROPERTY(p->extraProperties());
+p->getModel().close();
+printf("\nLet's see f0... (check out part #%d !)\n\n%s\n", p->refno, (const char*)p->getModel().getF0Geno().getGene());
+}
+
+void playWithAbsolute(Joint *j)
+{
+printf("\nAbsolute Joints adapt to its Parts' positions.\n"
+       "We can move a Part, and it does not influence the second part, nor the Joint.\n"
+       "Let's move the first Part along y axis by -0.1...\n");
+j->getModel().open();
+j->part1->p.y-=0.1;
+j->getModel().close();
+printf("The Part's position is changed, but everything else stays intact:\n\n%s\n",
+       (const char*)j->getModel().getF0Geno().getGene());
+}
+
+void playWithDelta(Joint *j)
+{
+printf("\nDelta fields (dx,dy,dz) describe relative location of the second part.\n"
+       "This joint will change the second Part's positions to preserve delta distance.\n"
+       "Let's move the first Part (#%d) along y axis (+0.1) and change delta.z (dz) by 0.1.\n",j->part1->refno);
+j->getModel().open();
+j->part1->p.y+=0.1;
+j->d.z+=0.1;
+j->getModel().close();
+printf("Position of the second Part referenced by this joint (part #%d) is now changed:\n\n%s\n",
+       j->part2->refno, (const char*)j->getModel().getF0Geno().getGene());
+printf("If no delta fields are defined, they will be computed automatically.\n"
+       "You can always delete existing delta values by using Joint::resetDelta().\n"
+       "Now we will change the second Part's z position by -0.2 and call resetDelta()...\n");
+j->getModel().open();
+j->part2->p.z-=0.2;
+j->resetDelta();
+j->getModel().close();
+printf("As you can see, Joint's delta fields have altered:\n\n%s\n", (const char*)j->getModel().getF0Geno().getGene());
+}
+
+void switchDelta(Joint *j)
+{
+int option=! j->isDelta();
+printf("How would this joint look like with delta option %s?\n[ by calling Joint::useDelta(%d) ]\n",option?"enabled":"disabled",option);
+j->getModel().open();
+j->useDelta( ! j->isDelta() );
+j->getModel().close();
+printf("f0 is now:\n\n%s\n...so this is %s joint.\n",
+       (const char*)j->getModel().getF0Geno().getGene(), option?"a delta":"an absolute");
+
+}
+
+void moreAboutJoint(Joint* j)
+{
+printf("Similarly as with Part, the full list of properties comes first:\n\n");
+CHANGE_ONE_PROPERTY(j->properties());
+printf("\nActually, there are two kinds of Joints: delta and absolute.\n"
+       "For this object, Joint::isDelta() returns %d, so this is the %s Joint.\n",
+       j->isDelta(),j->isDelta()?"delta":"absolute");
+if (j->isDelta())
+	{
+	playWithDelta(j);
+	switchDelta(j);
+	playWithAbsolute(j);
+	}
+else
+	{
+	playWithAbsolute(j);
+	switchDelta(j);
+	playWithDelta(j);
+	}
+
+printf("Part references and delta fields are the 'core' properties of the Joint.\n"
+       "The other properties are available from Joint::extraProperties()\n"
+       "and at the moment are defined as follows:\n\n");
+PRINT_PROPERTIES(j->extraProperties());
+printf("\nThey can be changed just like Part's extra properties:\n");
+j->getModel().open();
+CHANGE_ONE_PROPERTY(j->extraProperties());
+j->getModel().close();
+printf("And after that we have this genotype:\n\n%s\n", (const char*)j->getModel().getF0Geno().getGene());
+}
+
+
+
+void moreAboutNeuro(Neuro* n)
+{
+printf("Basic features of Neuro object are similar to those of Part and Joint.\n"
+       "We can request a property list:\n\n");
+PRINT_PROPERTIES(n->properties());
+printf("\n...and extra properties (which are designed to be always valid and easy to change):\n\n");
+PRINT_PROPERTIES(n->extraProperties());
+printf("\nAs usual, we will change something:\n");
+n->getModel().open();
+CHANGE_ONE_PROPERTY(n->extraProperties());
+n->getModel().close();
+printf("Each neuron can have any number of inputs = weighted connections\n with other neurons.\n"
+       "According to Neuro::getInputCount(), this one has %d inputs.\n",n->getInputCount());
+printf("Standard API is provided for accessing those inputs (getInput(int)),\n"
+       "adding inputs (addInput(Neuro*)) and removing them (removeInput(int)).\n\n");
+
+printf("\nThe most unusual thing is 'item details' field (d).\n"
+       "It is something like separate object with its own set of properties.\n"
+       "Currently the value of 'd' is '%s'.\n",(const char*)n->getDetails());
+
+{
+NeuroClass* cl=n->getClass();
+if (!cl)
+	printf("It should contain the class name but the meaning of '%s' is unknown\n",(const char*)n->getDetails());
+else
+{
+
+printf("'%s' is the class name (NeuroItem::getClassName() == '%s') and means '%s'.\n",
+       (const char*)cl->getName(),(const char*)cl->getName(),(const char*)cl->getLongName());
+printf("Neuro::getClass() gives you information about basic characteristic\n"
+       "of the class, that can be analyzed automatically.\n");
+printf("For the current object we can learn that it supports ");
+if (cl->getPreferredInputs()<0) printf("any number of inputs");
+  else if (cl->getPreferredInputs()==0) printf("no inputs");
+  else printf("%d inputs",cl->getPreferredInputs());
+printf(" (getPreferredInputs()) ");
+printf(cl->getPreferredOutput()?"and provides meaningful output signal (getPreferredOutput()==1).\n":"and doesn't provide useful output signal (getPreferredOutput()==0).\n");
+
+SyntParam p=n->classProperties();
+if (p.getPropCount()>0)
+	{
+	printf("The class defines its own properties:\n\n [ data provided by Neuro::classProperties() ]\n");
+	printProperties(p);
+	printf("and they can be changed:\n");
+	n->getModel().open();
+	changeOneProperty(p);
+	p.update();
+	n->getModel().close();
+	printf("After that, 'item details' contains the new object: '%s'.\n",(const char*)n->getDetails());
+	}
+else
+	printf("(This class does not have its own properties\n"
+	       " - Neuro::classProperties().getPropCount()==0)\n");
+}
+}
+
+printf("The class of this object can be changed using Neuro::setClassName()\n"
+       "The following classes are available:\n"
+       " [ data provided by Neuro::getClassInfo()->getProperties() ]\n\n");
+printf(" #  class  description       properties\n");
+for (int i=0;i<n->getClassCount();i++)
+	{
+	NeuroClass* cl=n->getClass(i);
+	Param p=cl->getProperties();
+	printf("%2d.%6s  %-20s  %2d\n",i,(const char*)cl->getName(),(const char*)cl->getLongName(),p.getPropCount());
+	}
+int cl=rand() % n->getClassCount();
+printf("\nLet's change the NeuroItem's class to '%s'...\n",(const char*)n->getClassName(cl));
+n->getModel().open();
+n->setClass(n->getClass(cl));
+{
+SyntParam p=n->classProperties();
+if (p.getPropCount()>0)
+	{
+	printProperties(p);
+	changeOneProperty(p);
+	p.update();
+	}
+}
+
+if (n->getInputCount()>0)
+{
+printf("input info 0 = \"%s\"\n",(const char*)n->getInputInfo(0));
+printf("input info 0 abc = \"%s\"\n",(const char*)n->getInputInfo(0,"abc"));
+	n->setInputInfo(0,"test",44);
+	n->setInputInfo(0,"abc","yeah");
+}
+
+n->getModel().close();
+printf("The final object description will be then: '%s'\nAnd the full f0 genotype:\n\n%s\n",
+       (const char*)n->getDetails(), (const char*)n->getModel().getF0Geno().getGene());
+
+
+}
+
+void findingConverters()
+{
+GenoConverter *gc=gcm.findConverters(0,'1');
+if (gc) printf("found converter accepting f1: \"%s\"\n",gc->name);
+SListTempl<GenoConverter*> found;
+gcm.findConverters(&found,-1,'0');
+printf("found %d converter(s) producing f0\n",found.size());
+}
+
+int main(int argc,char*argv[])
+{
+srand(time(0));
+printNiceBanner("Welcome to GDK test application!");
+
+findingConverters();
+
+SString gen(argc>1?argv[1]:"X[|G:1.23]");
+if (!strcmp(gen,"-"))
+	{
+	gen=0;
+	StdioFILEDontClose in(stdin);
+	loadSString(&in,gen);
+	}
+Geno g(gen);
+printf("\nSource genotype: '%s'\n",(const char*)g.getGene());
+printf("                  ( format %c %s)\n",
+       g.getFormat(), (const char*)g.getComment());
+
+Model m(g);//.getConverted('0'));
+
+if (!m.isValid())
+	{
+	printf("Cannot build Model from this genotype!\n");
+	return 2;	
+	}
+printf("Converted to f0:\n%s\n",(const char*)m.getF0Geno().getGene());
+
+printf("Model contains: %d part(s)\n"
+       "                %d joint(s)\n"
+       "                %d neuron(s)\n",m.getPartCount(),m.getJointCount(),m.getNeuroCount());
+
+printf("\nInvestigating details...\n");
+
+if (m.getPartCount()>0)
+	{
+	int p=rand()%m.getPartCount();
+	printNiceBanner("P A R T    O B J E C T");
+	printf("            (part # %d)\n",p);
+	moreAboutPart(m.getPart(p));
+	}
+
+if (m.getJointCount()>0)
+	{
+	int j=rand()%m.getJointCount();
+	printNiceBanner("J O I N T    O B J E C T");
+	printf("            (joint # %d)\n",j);
+	moreAboutJoint(m.getJoint(j));
+	}
+
+if (m.getNeuroCount()>0)
+	{
+	int n=rand()%m.getNeuroCount();
+	printNiceBanner("N E U R O    O B J E C T");
+	printf("            (neuro # %d)\n",n);
+	moreAboutNeuro(m.getNeuro(n));
+	}
+
+#ifdef MODEL_V1_COMPATIBLE
+printNiceBanner("Old Neuro/NeuroItem view");
+int nc=m.old_getNeuroCount();
+printf("Model::old_getNeuroCount() = %d\n",nc);
+for (int i=0;i<nc;i++)
+	{
+	Neuro *n=m.old_getNeuro(i);
+	printf("neuron #%d: p=%d, j=%d, force=%g, inertia=%g, sigmoid=%g\n",
+	       i,n->part_refno,n->joint_refno,
+	       n->force,n->inertia,n->sigmo);
+	int nicount=n->getItemCount();
+	printf("    %d items\n",nicount);
+	for (int j=0;j<nicount;j++)
+		{
+		NeuroItem *ni=n->getNeuroItem(j);
+		printf("        item #%d - '%s', conn=%d, weight=%g\n",
+		       j,(const char*)ni->getDetails(),ni->conn_refno,ni->weight);
+		}
+	}
+printf("end.\n");
+#endif
+
+printf("\n######### THE END ###########\n\n"
+       "Hints:\n"
+       "  1. You can redirect output: gdk_test >filename.txt\n"
+       "  2. Each run can yield different results, because some\n"
+       "     values are randomly generated.\n"
+       "  3. This application will use custom genotype passed as\n"
+       "     a commandline parameter: gdk_test XX\n"
+       "\n");
+return 0;
+}
Index: /cpp/frams/_demos/geno_test.cpp
===================================================================
--- /cpp/frams/_demos/geno_test.cpp	(revision 109)
+++ /cpp/frams/_demos/geno_test.cpp	(revision 109)
@@ -0,0 +1,36 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <frams/genetic/geno.h>
+#include <frams/virtfile/stdiofile.h>
+#include <frams/util/sstringutils.h>
+#include <frams/genetic/defgenoconv.h>
+
+/**
+ @file
+ Sample code: Testing genotype validity
+
+ \include geno_test.cpp
+*/
+
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+int main(int argc,char*argv[])
+{
+if (argc<=1)
+	{
+	puts("no genotype");
+	return 10;
+	}
+SString gen(argv[1]);
+if (!strcmp(gen,"-"))
+	{
+	gen=0;
+	StdioFILEDontClose in(stdin);
+	loadSString(&in,gen);
+	}
+Geno g(gen);
+puts(g.isValid()?"valid":"invalid");
+return !g.isValid();
+}
Index: /cpp/frams/_demos/genotypeloader.cpp
===================================================================
--- /cpp/frams/_demos/genotypeloader.cpp	(revision 109)
+++ /cpp/frams/_demos/genotypeloader.cpp	(revision 109)
@@ -0,0 +1,35 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "genotypeloader.h"
+
+#define FIELDSTRUCT MiniGenotype
+ParamEntry minigenotype_paramtab[]=
+ {
+ {"Genotype",1,3,"org",},
+ {"name",0,0,"Name","s 0 40",FIELD(name),},
+ {"genotype",0,0,"Genotype","s 1",FIELD(genotype),},
+ {"info",0,0,"Info","s 1",FIELD(info),},
+ {0,0,0,},
+};
+#undef FIELDSTRUCT
+
+MiniGenotypeLoader::MiniGenotypeLoader():genotype_param(minigenotype_paramtab,&genotype_object) {init();}
+MiniGenotypeLoader::MiniGenotypeLoader(VirtFILE *f):MultiParamLoader(f),genotype_param(minigenotype_paramtab,&genotype_object) {init();}
+MiniGenotypeLoader::MiniGenotypeLoader(const char* filename):MultiParamLoader(filename),genotype_param(minigenotype_paramtab,&genotype_object) {init();}
+
+void MiniGenotypeLoader::init()
+{
+addObject(&genotype_param);
+breakOn(MultiParamLoader::OnError + MultiParamLoader::AfterObject);
+}
+
+MiniGenotype* MiniGenotypeLoader::loadNextGenotype()
+{
+genotype_object.clear();
+if ((go()==AfterObject)&&(getObject().matchesInterfaceName(&genotype_param)))
+	return &genotype_object;
+else
+	return 0;
+}
Index: /cpp/frams/_demos/genotypeloader.h
===================================================================
--- /cpp/frams/_demos/genotypeloader.h	(revision 109)
+++ /cpp/frams/_demos/genotypeloader.h	(revision 109)
@@ -0,0 +1,70 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _GENOTYPELOADER_H_
+#define _GENOTYPELOADER_H_
+
+#include <frams/util/sstring.h>
+#include <frams/param/multiparamload.h>
+
+/** Helper class, mostly useful with MultiParamLoader
+    or its specialized version: MiniGenotypeLoader.
+    MiniGenotype stores 3 essential fields of the Genotype (name, gene and info)
+ */
+class MiniGenotype
+{
+public:
+SString name,genotype,info;
+void clear() {name=""; genotype=""; info="";}
+};
+
+
+/** Defines the association between "org:" object (found in genotype files)
+    and the MiniGenotype fields. MiniGenotypeLoader uses this definition
+    but you can also use it to make MultiParamLoader load genotype
+ */
+extern ParamEntry minigenotype_paramtab[];
+
+
+/** In most simple cases this is the class you would use to load a series of genotypes from
+    the Framsticks genotype file.
+
+    Usage pattern: (see loadertest.cpp for the working code)
+
+    1.Initialize
+
+    2.while(genotype=loadNextGenotype()) doSomethingWith(genotype);
+
+    3.Done!
+
+    MiniGenotypeLoader is simply the MultiParamLoader configured to load one kind of data: "org:" objects.
+    The instance of this class can also be reconfigured to recognize more objects by using MultiParamLoader
+    methods, or you can use it as a guide for creating your own specialized class.
+ */
+class MiniGenotypeLoader: public MultiParamLoader
+{
+MiniGenotype genotype_object;
+Param genotype_param;
+bool initialized;
+void init();
+public:
+MiniGenotypeLoader();
+MiniGenotypeLoader(VirtFILE *f);
+MiniGenotypeLoader(const char* filename);
+
+/** @returns genotype object if one was loaded or NULL otherwise.
+
+    Returned MiniGenotype pointer always references the the same object (MiniGenotypeLoader::genotype_object)
+    which means you may need to copy the data from it before calling loadNextGenotype() again.
+    In the default configuration (simple MiniGenotypeLoader) NULL is always final and should be used
+    to finish processing.
+
+    If the loader is configured to load other objects or stop on other conditions, NULL will also mean
+    every condition other than "GenotypeLoaded". In such cases you need MultiParamLoader::getStatus(),
+    MultiParamLoader::finished() and other methods to determine the real cause of NULL.
+ */
+MiniGenotype* loadNextGenotype();
+};
+
+#endif
Index: /cpp/frams/_demos/loader_test.cpp
===================================================================
--- /cpp/frams/_demos/loader_test.cpp	(revision 109)
+++ /cpp/frams/_demos/loader_test.cpp	(revision 109)
@@ -0,0 +1,41 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "genotypeloader.h"
+
+/**
+ @file
+ Sample code: Loading genotypes from Framsticks files
+
+ \include loader_test.cpp
+*/
+
+int main(int argc,char*argv[])
+{
+puts("This example shows how to load genotypes from the standard Framsticks genotype file");
+if (argc<2)
+	{
+	puts("(Please give the file name as command line argument)");
+	return 1;
+	}
+
+long count=0,totalsize=0;
+MiniGenotypeLoader loader(argv[1]);
+// using char* constructor (passing the file name to open)
+MiniGenotype *loaded;
+while(loaded=loader.loadNextGenotype())
+	{ // if loaded != NULL then the "org:" object data was
+	 // loaded into MiniGenotype object
+	count++;
+	totalsize+=loaded->genotype.len();
+	printf("%d. %s\t(%d characters)\n",count,(const char*)loaded->name,loaded->genotype.len());
+	}
+// the loop repeats until loaded==NULL, which could be beacause of error
+if (loader.getStatus()==MiniGenotypeLoader::OnError)
+	printf("Error: %s",(const char*)loader.getError());
+// (otherwise it was the end of the file)
+
+printf("\ntotal: %d items, %d characters\n",count,totalsize);
+return 0;
+}
Index: /cpp/frams/_demos/multiline_f0_test.cpp
===================================================================
--- /cpp/frams/_demos/multiline_f0_test.cpp	(revision 109)
+++ /cpp/frams/_demos/multiline_f0_test.cpp	(revision 109)
@@ -0,0 +1,193 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <frams/virtfile/stdiofile.h>
+
+#include <frams/model/model.h>
+#include <frams/genetic/defgenoconv.h>
+#include <frams/errmgr/stdouterr.h>
+#include <frams/virtfile/stringfile.h>
+
+StdoutErrorHandler err; //redirect model-related errors to stdout
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+int main(int argc,char*argv[])
+{
+SString gen(argc>1?argv[1]:"X[|G:1.23]");
+if (!strcmp(gen,"-"))
+	{
+	gen=0;
+	StdioFILEDontClose in(stdin);
+	loadSString(&in,gen);
+	}
+Geno g(gen);
+printf("\nSource genotype: '%s'\n",(const char*)g.getGene());
+printf("                  ( format %c %s)\n",
+       g.getFormat(), (const char*)g.getComment());
+
+Model m(g);//.getConverted('0'));
+
+if (!m.isValid())
+	{
+	printf("Cannot build Model from this genotype!\n");
+	return 2;	
+	}
+printf("Converted to f0:\n%s\n",(const char*)m.getF0Geno().getGene());
+
+printf("\nusing Param::save() to create the \"expanded\" form of the f0 genotype...\n(MultiParamLoader should be able to load this)");
+
+StringFILE2 f;
+
+static Param modelparam(f0_model_paramtab);
+static Param partparam(f0_part_paramtab);
+static Param jointparam(f0_joint_paramtab);
+static Param neuroparam(f0_neuro_paramtab);
+static Param connparam(f0_neuroconn_paramtab);
+
+modelparam.select(&m);
+modelparam.save(&f,&SString("m"));
+
+Part *p;
+Joint *j;
+Neuro *n;
+
+for (int i=0;p=(Part*)m.getPart(i);i++)
+	{
+	partparam.select(p);
+	partparam.save(&f,&SString("p"));
+	}
+for (int i=0;j=(Joint*)m.getJoint(i);i++)
+	{
+	jointparam.select(j);
+	jointparam.setParamTab(j->usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab);
+	jointparam.save(&f,&SString("j"));
+	}
+for (int i=0;n=(Neuro*)m.getNeuro(i);i++)
+	{
+	neuroparam.select(n);
+	neuroparam.save(&f,&SString("n"));
+	}
+for (int a=0;n=(Neuro*)m.getNeuro(a);a++)
+	{ // inputs
+	for (int b=0;b<n->getInputCount();b++)
+		{
+		double w;
+		NeuroConn nc;
+		Neuro* n2=n->getInput(b,w);
+		nc.n1_refno=n->refno; nc.n2_refno=n2->refno;
+		nc.weight=w;
+		nc.info=n->getInputInfo(b);
+		connparam.select(&nc);
+		connparam.save(&f,&SString("c"));
+		}
+	}
+
+printf("\n============================\n%s\n",(const char*)f.getString());
+
+return 0;
+}
+
+/*********************** EXAMPLE OUTPUT *********************************
+
+Source genotype: 'X[|G:1.23]'
+                  ( format 1 )
+Converted to f0:
+p:
+p:1
+j:0, 1, dx=1
+n:p=1
+n:j=0, d="|:p=0.25,r=1"
+n:j=0, d=G
+c:0, 2, 1.23
+c:1, 0
+
+
+using Param::save() to create the "expanded" form of the f0 genotype...
+(MultiParamLoader should be able to load this)
+============================
+m:
+se:1
+Vstyle:
+
+p:
+x:0
+y:0
+z:0
+m:1
+s:1
+dn:1
+fr:0.4
+ing:0.25
+as:0.25
+rx:0
+ry:0
+rz:0
+i:
+Vstyle:part
+
+p:
+x:1
+y:0
+z:0
+m:1
+s:1
+dn:1
+fr:0.4
+ing:0.25
+as:0.25
+rx:0
+ry:0
+rz:0
+i:
+Vstyle:part
+
+j:
+p1:0
+p2:1
+rx:0
+ry:0
+rz:0
+dx:1
+dy:0
+dz:0
+stif:1
+rotstif:1
+stam:0.25
+i:
+Vstyle:joint
+
+n:
+p:1
+j:-1
+d:N
+i:
+Vstyle:neuro
+
+n:
+p:-1
+j:0
+d:|:p=0.25,r=1
+i:
+Vstyle:neuro
+
+n:
+p:-1
+j:0
+d:G
+i:
+Vstyle:neuro
+
+c:
+n1:0
+n2:2
+w:1.23
+i:
+
+c:
+n1:1
+n2:0
+w:1
+i:
+
+
+*************************************************************************/
Index: /cpp/frams/_demos/neuro_test.cpp
===================================================================
--- /cpp/frams/_demos/neuro_test.cpp	(revision 109)
+++ /cpp/frams/_demos/neuro_test.cpp	(revision 109)
@@ -0,0 +1,139 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <frams/genetic/geno.h>
+#include <frams/virtfile/stdiofile.h>
+#include <frams/util/sstringutils.h>
+#include <frams/genetic/defgenoconv.h>
+#include <frams/neuro/neuroimpl.h>
+#include <frams/neuro/neurofactory.h>
+#include <frams/errmgr/stdouterr.h>
+
+/**
+ @file
+ Sample code: Neural network tester (can run your custom neurons)
+*/
+
+StdoutErrorHandler err; //redirect model-related errors to stdout
+DefaultGenoConvManager gcm; //without this object the application would only handle "format 0" genotypes
+
+#ifndef GDK_WITHOUT_FRAMS
+#include <frams/mech/creatmechobj.h>
+int CreatMechObject::modeltags_id=0;
+int CreatMechObject::mechtags_id=0;
+#endif
+
+ParamEntry creature_paramtab[]={0};
+
+#ifdef VEYETEST
+#include <frams/neuro/impl/neuroimpl-vectoreye.h>
+
+#define N_VEye 0
+#define N_VMotor 1
+#define N_Mode 2
+#define N_Fitness 3
+#define LEARNINGSTEPS 50
+
+void veyeStep(Model &m,int step)
+{
+  static float angle=0;
+
+  NeuroNetImpl::getImpl(m.getNeuro(N_Mode))->setState(step>=LEARNINGSTEPS); //0 (learning) or 1 (normal)
+
+	NeuroImpl *ni=NeuroNetImpl::getImpl(m.getNeuro(N_VEye));
+  ((NI_VectorEye*)ni)->relpos.y=0;
+  ((NI_VectorEye*)ni)->relpos.z=0;
+  if (NeuroNetImpl::getImpl(m.getNeuro(N_Mode))->getNewState()<0.5)
+  { //learning
+    ((NI_VectorEye*)ni)->relpos.x=5.0*sin(2*M_PI*step/LEARNINGSTEPS);
+  }
+  else
+  { //VMotor controls location of VEye
+    angle+=NeuroNetImpl::getImpl(m.getNeuro(N_VMotor))->getState();
+    angle=fmod((double)angle,M_PI*2.0);
+    ((NI_VectorEye*)ni)->relpos.x=5*sin(angle);
+  }
+
+  NeuroNetImpl::getImpl(m.getNeuro(N_Fitness))->setState(angle); //wymaga poprawy
+  //oraz trzeba przemyslec kolejnosc get/set'ow neuronow zeby sygnal sie dobrze propagowal.
+}
+#endif
+
+int main(int argc,char*argv[])
+{
+if (argc<=1)
+	{
+		puts("Parameters: <genotype> [number of simulation steps]");
+	  return 10;
+	}
+SString gen(argv[1]);
+if (!strcmp(gen,"-"))
+	{
+	gen=0;
+	StdioFILEDontClose in(stdin);
+	loadSString(&in,gen);
+	}
+Geno g(gen);
+if (!g.isValid()) {puts("invalid genotype");return 5;}
+Model m(g);
+if (!m.getNeuroCount()) {puts("no neural network");return 1;}
+printf("%d neurons,",m.getNeuroCount());
+NeuroFactory neurofac;
+neurofac.setStandardImplementation();
+NeuroNetConfig nn_config(&neurofac);
+NeuroNetImpl *nn=new NeuroNetImpl(m,nn_config);
+int i; Neuro *n;
+if (!nn->getErrorCount()) printf(" no errors\n");
+else
+	{
+	printf(" %d errors:",nn->getErrorCount());
+	int no_impl=0; SString no_impl_names;
+	int init_err=0; SString init_err_names;
+	for(i=0;i<m.getNeuroCount();i++)
+		{
+		n=m.getNeuro(i);
+		NeuroImpl *ni=NeuroNetImpl::getImpl(n);
+		if (!ni)
+			{
+			if (no_impl) no_impl_names+=',';
+			no_impl_names+=SString::sprintf("#%d.%s",i,(const char*)n->getClassName());
+			no_impl++;
+			}
+		else if (ni->status==NeuroImpl::InitError)
+			{
+			if (init_err) init_err_names+=',';
+			init_err_names+=SString::sprintf("#%d.%s",i,(const char*)n->getClassName());
+			init_err++;
+			}
+		}
+	printf("\n");
+	if (no_impl) printf("%d x missing implementation (%s)\n",no_impl,(const char*)no_impl_names);
+	if (init_err) printf("%d x failed initialization (%s)\n",init_err,(const char*)init_err_names);
+	}
+int steps=1;
+if (argc>2) steps=atol(argv[2]);
+int st;
+printf("step");
+for(i=0;i<m.getNeuroCount();i++)
+	{
+	n=m.getNeuro(i);
+	printf("\t#%d.%s",i,(const char*)n->getClassName());
+	}
+printf("\n");
+for(st=0;st<=steps;st++)
+	{
+#ifdef VEYETEST
+  veyeStep(m,st);
+#endif
+	printf("%d",st);
+	for(i=0;i<m.getNeuroCount();i++)
+		{
+		n=m.getNeuro(i);
+		printf("\t%g",n->state);
+		}
+	printf("\n");
+	nn->simulateNeuroNet();
+	}
+neurofac.freeImplementation();
+}
Index: /cpp/frams/_demos/part_shapes.cpp
===================================================================
--- /cpp/frams/_demos/part_shapes.cpp	(revision 109)
+++ /cpp/frams/_demos/part_shapes.cpp	(revision 109)
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <frams/model/model.h>
+#include <frams/model/modelparts.h>
+
+int main()
+{
+Model *m=new Model;
+Part *p1,*p2;
+Joint *j;
+
+m->open();
+
+// chain of ellipsoids - subsequent parts are placed relative to the previous part's orientation and location
+p1=m->addNewPart(Part::SHAPE_ELLIPSOID); //initial part
+p1->scale=Pt3D(1.0,0.7,0.4);
+
+Orient rotation=Orient_1; //must be initialized explicitly because the default Orient constructor does not initialize anything
+rotation.rotate(Pt3D(0.1,0.2,0.3));
+
+for(int N=10;N>0;N--,p1=p2)
+	{
+	p2=m->addNewPart(Part::SHAPE_ELLIPSOID);
+	p2->scale=p1->scale*0.9; //each part is smaller than its predecessor
+	
+	Pt3D advance(p1->scale.x,0,0); //advance by previous part's ellipsoid x radius
+	p2->p = p1->p + p1->o.transform(advance); //advance vector transformed by p1 orientation == in p1 local coordinates
+	p2->setOrient(p1->o.transform(rotation)); //rotation transformed by p1 orientation
+
+	m->addNewJoint(p1,p2,Joint::SHAPE_SOLID); //all parts must be connected
+	}
+
+// chain of cyllinders - line segments between points calculated from the parametric formula P(a)=(2-2*cos(a),2*sin(a)) (circle with r=2)
+Pt3D prev,next;
+p1=m->getPart(0);
+for(float a=0;a<M_PI;a+=M_PI/10)
+	{
+	Pt3D next(2-2*cos(a),0,2*sin(a));
+	if (a>0)
+		{
+		p2=m->addNewPart(Part::SHAPE_CYLINDER);
+		p2->setPositionAndRotationFromAxis(prev,next);
+		p2->scale=Pt3D(prev.distanceTo(next)*0.5,0.05,0.05);// distance*0.5 because scale is "radius", not cylinder length
+
+		m->addNewJoint(p1,p2,Joint::SHAPE_SOLID); //all parts must be connected
+		}
+	p1=p2;
+	prev=next;
+	}
+
+m->close();
+puts((const char*)m->getF0Geno().toString());
+// the genotype can be fed directly to the genotype viewer, like this:
+// part_shapes | theater -g -vpart_shapes -
+}
Index: /cpp/frams/_demos/printconvmap.cpp
===================================================================
--- /cpp/frams/_demos/printconvmap.cpp	(revision 109)
+++ /cpp/frams/_demos/printconvmap.cpp	(revision 109)
@@ -0,0 +1,96 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "printconvmap.h"
+#include <stdio.h>
+#include <frams/util/multimap.h>
+
+#define GEN1MAX 15
+
+void printN(const char* t,int maxlen)
+{
+while(maxlen-- > 0)
+	if (*t) putchar(*(t++));
+	else putchar(' ');
+}
+
+void printN(char t,int maxlen)
+{
+while(maxlen-- > 0) putchar(t);
+}
+
+void printmapping(const char* gen1, int len1,const MultiRange &mr,const char* gen2,int len2)
+{
+printN(' ',GEN1MAX-len1);
+printN(gen1,len1);
+printf(" : ");
+int i;
+for(i=0;i<len2;i++)
+	if (mr.contains(i))
+		putchar(gen2[i]);
+	else
+		putchar('.');
+putchar('\n');
+}
+
+void stripstring(SString &str)
+{
+char *t=str.directWrite();
+for(;*t;t++)
+	if (strchr("\n\r\t",*t)) *t=' ';
+}
+
+void printConvMap(const SString& gen1,const SString& gen2,const MultiMap& map)
+{
+int y,y2,len1;
+int id=0;
+if (map.isEmpty())
+	{
+	printf("{ empty }\n");
+	return;
+	}
+len1=gen1.len();
+SString g1=gen1;
+stripstring(g1);
+SString g2=gen2;
+stripstring(g2);
+const char* g=g1;
+y=0;
+MultiRange *mr;
+MultiRange emptyrange;
+printN(' ',GEN1MAX);
+printf("   %s\n",(const char*)g2);
+int begin=map.getBegin();
+int end=map.getEnd();
+while(y<len1)
+	{
+	if (y<begin)
+		{
+		mr=&emptyrange;
+		y2=begin;
+		}
+	else if (y>end)
+		{
+		mr=&emptyrange;
+		y2=len1;
+		}
+	else	{
+		id=map.findMappingId(y);
+		mr=&map.getMapping(id)->to;
+		y2=map.getMapping(id+1)->begin;
+		}
+	if ((y2-y) > GEN1MAX) y2=y+GEN1MAX;
+	if (y2>(y+len1)) y2=y+len1;
+	printmapping(g+y,y2-y,*mr,g2,g2.len());
+	y=y2;
+	}
+}
+
+void printModelMap(const SString& gen1,const MultiMap& map)
+{
+SString g2("012345678901234567890123456789");
+printN(' ',GEN1MAX);
+printf("   Parts     Joints    Neurons\n");
+printConvMap(gen1,g2,map);
+}
Index: /cpp/frams/_demos/printconvmap.h
===================================================================
--- /cpp/frams/_demos/printconvmap.h	(revision 109)
+++ /cpp/frams/_demos/printconvmap.h	(revision 109)
@@ -0,0 +1,15 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _PRINTCONVMAP_H_
+#define _PRINTCONVMAP_H_
+
+#include <frams/util/sstring.h>
+
+class MultiMap;
+
+void printConvMap(const SString& gen1,const SString& gen2,const MultiMap& map);
+void printModelMap(const SString& gen1,const MultiMap& map);
+
+#endif
Index: /cpp/frams/_demos/serial_test.cpp
===================================================================
--- /cpp/frams/_demos/serial_test.cpp	(revision 109)
+++ /cpp/frams/_demos/serial_test.cpp	(revision 109)
@@ -0,0 +1,93 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <frams/vm/classes/collectionobj.h>
+
+void printIndent(int indent)
+{
+for(int i=0;i<indent;i++)
+	putchar(' ');
+}
+
+struct Trace
+{
+ExtObject object;
+Trace *previous;
+bool isAlreadyVisited(const ExtObject& o)
+	{
+	if (object==o) return true;
+	if (previous) return previous->isAlreadyVisited(o);
+	return false;
+	}
+};
+
+void print(ExtValue* v,int indent=0,Trace *prev_trace=NULL)
+{
+printIndent(indent);
+if (!v)
+	{puts("null"); return;}
+
+switch(v->getType())
+	{
+	case TUnknown: puts("null"); return;
+	case TInt: printf("int:"); break;
+	case TDouble: printf("double:"); break;
+	case TString: printf("string:"); break;
+	case TObj:
+		{
+		Trace next_trace={v->getObject(),prev_trace};
+		printf("%s object:",v->getObject().interfaceName());
+		if (prev_trace && prev_trace->isAlreadyVisited(next_trace.object))
+			{
+			printf(" already visited - breaking recursion\n");
+			return;
+			}
+		VectorObject *vec=VectorObject::fromObject(v->getObject());
+		if (vec)
+			{
+			printf("\n");
+			for(int i=0;i<vec->data.size();i++)
+				print((ExtValue*)vec->data(i),indent+3,&next_trace);
+			return;
+			}
+		DictionaryObject *dict=DictionaryObject::fromObject(v->getObject());
+		if (dict)
+			{
+			printf("\n");
+			for(HashEntryIterator it(dict->hash);it.isValid();it++)
+				{
+				printIndent(indent+3);
+				printf("key \"%s\"\n",(const char*)it->key);
+				print((ExtValue*)it->value,indent+6,&next_trace);
+				}
+			return;
+			}
+		}
+	}
+puts((const char*)v->getString());
+}
+
+int main(int argc,char*argv[])
+{
+const char* in= (argc>1) ? argv[1] : "[1,2,3,null,{\"x\":3.4,\"y\":[7,77,777]},4]";
+
+// note: this is not an example of how to read a param field, but only a special case where the input data was
+// previously identified as "serialized" and the "@Serialized:" prefix was already removed
+
+printf("DESERIALIZATION TEST\n"
+       "input string = %s\n",in);
+ExtValue v;
+const char* ret=v.deserialize(in);
+if (ret==NULL)
+	{
+	printf("failed!\n");
+	return 1;
+	}
+else
+	{
+	printf("OK, consumed %d of %d characters\n\n",ret-in,strlen(in));
+	print(&v);
+	return 0;
+	}
+}
Index: /cpp/frams/config/f0-GDK.def
===================================================================
--- /cpp/frams/config/f0-GDK.def	(revision 109)
+++ /cpp/frams/config/f0-GDK.def	(revision 109)
@@ -0,0 +1,201 @@
+CLASS(Model,f0_model,m)
+GROUP(Properties)
+GROUP(Visual)
+PROP(se,0,1024,startenergy,f,,,,startenergy)
+PROP(Vstyle,1,0,vis_style,s,0,-1,,vis_style)
+ENDCLASS
+
+CLASS(Part,f0_part,p)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(x,0,1024,position.x,f,,,,p.x)
+PROP(y,0,1024,position.y,f,,,,p.y)
+PROP(z,0,1024,position.z,f,,,,p.z)
+XPROP(m,1,0,mass,f,0.1,999.0,1.0,mass)
+PROP(sh,1,0,shape,d,0,3,0,shape)
+PROP(s,1,0,size,f,0.1,10.0,1.0,size)
+PROP(sx,1,0,scale.x,f,0.001,1000.0,1.0,scale.x)
+PROP(sy,1,0,scale.y,f,0.001,1000.0,1.0,scale.y)
+PROP(sz,1,0,scale.z,f,0.001,1000.0,1.0,scale.z)
+XPROP(dn,1,0,density,f,0.2,5.0,1.0,density)
+XPROP(fr,1,0,friction,f,0.0,4.0,0.4,friction)
+XPROP(ing,1,0,ingestion,f,0.0,1.0,0.25,ingest)
+XPROP(as,1,0,assimilation,f,0.0,1.0,0.25,assim)
+PROP(rx,0,0,rot.x,f,,,,rot.x)
+PROP(ry,0,1024,rot.y,f,,,,rot.y)
+PROP(rz,0,1024,rot.z,f,,,,rot.z)
+PROP(i,1,0,`info',s,,,,info)
+PROP(Vstyle,2,0,vis_style,s,0,-1,part,vis_style)
+XPROP(vs,2,0,visual thickness,f,0.05,0.7,0.2,vsize)
+XPROP(vr,2,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,2,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,2,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Joint,f0_joint,j)
+GROUP(Connections)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(p1,0,1024,`part1 ref#',d,-1,999999,-1,p1_refno)
+PROP(p2,0,1024,`part2 ref#',d,-1,999999,-1,p2_refno)
+PROP(rx,1,0,rotation.x,f,,,,rot.x)
+PROP(ry,1,1024,rotation.y,f,,,,rot.y)
+PROP(rz,1,1024,rotation.z,f,,,,rot.z)
+PROP(dx,1,0,delta.x,f,-2,2,0,d.x)
+PROP(dy,1,1024,delta.y,f,-2,2,0,d.y)
+PROP(dz,1,1024,delta.z,f,-2,2,0,d.z)
+PROP(sh,1,0,shape,d,0,1,0,shape)
+XPROP(stif,2,0,stiffness,f,0.0,1.0,1.0,stif)
+XPROP(rotstif,2,0,rotation stiffness,f,0.0,1.0,1.0,rotstif)
+PROP(stam,2,0,stamina,f,0.0,1.0,0.25,stamina)
+PROP(i,2,0,`info',s,,,,info)
+PROP(Vstyle,3,0,vis_style,s,0,-1,joint,vis_style)
+XPROP(vr,3,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,3,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,3,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Joint,f0_nodeltajoint,j,NOXML)
+GROUP(Connections)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(p1,0,1024,`part1 ref#',d,-1,999999,-1,p1_refno)
+PROP(p2,0,1024,`part2 ref#',d,-1,999999,-1,p2_refno)
+PROP(sh,1,0,shape,d,0,1,0,shape)
+XPROP(stif,2,0,stiffness,f,0.0,1.0,1.0,stif)
+XPROP(rotstif,2,0,rotation stiffness,f,0.0,1.0,1.0,rotstif)
+PROP(stam,2,0,stamina,f,0.0,1.0,0.25,stamina)
+PROP(i,2,0,`info',s,,,,info)
+PROP(Vstyle,3,0,vis_style,s,0,-1,joint,vis_style)
+XPROP(vr,3,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,3,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,3,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Neuro,f0_neuro,n)
+GROUP(Connections)
+GROUP(Other)
+GROUP(Visual)
+PROP(p,0,0,`part ref#',d,-1,999999,-1,part_refno)
+PROP(j,0,0,`joint ref#',d,-1,999999,-1,joint_refno)
+PROP(d,1,0,item details,s,,,N,details,GETSET)
+PROP(i,1,0,`info',s,,,,info)
+PROP(Vstyle,2,0,vis_style,s,0,-1,neuro,vis_style)
+PROP(getInputCount,0,1+2,`input count',d,,,,inputCount,GETONLY)
+PROP(getInputNeuroDef,0,1+2,`get input neuron',p oNeuroDef(d),,,,p_getInputNeuroDef,PROCEDURE)
+PROP(getInputNeuroIndex,0,1+2,`get input neuron index',p d(d),,,,p_getInputNeuroIndex,PROCEDURE)
+PROP(getInputWeight,0,1+2,`get input weight',p f(d),,,,p_getInputWeight,PROCEDURE)
+PROP(classObject,0,1+2,`neuron class',o NeuroClass,,,,classObject,GETONLY)
+ENDCLASS
+
+CLASS(NeuroConn,f0_neuroconn,c)
+GROUP(Connection)
+GROUP(Other)
+PROP(n1,0,1024,`this neuro ref#',d,-1,999999,-1,n1_refno)
+PROP(n2,0,1024,`connected neuro ref#',d,-1,999999,-1,n2_refno)
+PROP(w,0,1024,weight,f,-999999,999999,1.0,weight)
+PROP(i,1,0,`info',s,,,,info)
+ENDCLASS
+
+NEUROCLASS(StdNeuron,N,Neuron,`Standard neuron',-1,1,0)
+VISUALHINTS(DontShowClass)
+NEUROPROP(in,1,0,Inertia,f,0.0,1.0,0.8,inertia)
+NEUROPROP(fo,1,0,Force,f,0.0,999.0,0.04,force)
+NEUROPROP(si,1,0,Sigmoid,f,-99999.0,99999.0,2.0,sigmo)
+NEUROPROP(s,2,0,State,f,-1.0,1.0,0.0,newstate)
+ENDNEUROCLASS
+
+NEUROCLASS(StdUNeuron,Nu,`Unipolar neuron [EXPERIMENTAL!]',`Works like standard neuron (N) but the output value is scaled to 0...+1 instead of -1...+1.\nHaving 0 as one of the saturation states should help in \"gate circuits\", where input signal is passed through or blocked depending on the other singal.',-1,1,0)
+NEUROPROP(in,1,0,Inertia,f,0.0,1.0,0.8,inertia)
+NEUROPROP(fo,1,0,Force,f,0.0,999.0,0.04,force)
+NEUROPROP(si,1,0,Sigmoid,f,-99999.0,99999.0,2.0,sigmo)
+NEUROPROP(s,2,0,State,f,-1.0,1.0,0.0,newstate)
+ENDNEUROCLASS
+
+NEUROCLASS(Gyro,G,Gyroscope,`Equilibrium sensor.\n0=the stick is horizontal\n+1/-1=the stick is vertical',0,1,2)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`8,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,12,43,24,48,24,48,19,38,19,38,24,43,24,43,54,48,54,48,64,43,69,38,64,38,54,43,54,5,63,69,58,74,48,79,38,79,28,74,23,69,1,43,79,43,74,1,23,69,26,66,1,63,69,60,66,1,55,76,53,73,1,31,75,33,72')
+ENDNEUROCLASS
+
+NEUROCLASS(Touch,T,Touch,`Touch sensor.\n-1=no contact\n0=just touching\n>0=pressing, value depends on the force applied',0,1,1)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`2,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,11,75,50,65,50,60,55,55,45,50,55,45,45,40,50,35,50,30,45,25,50,30,55,35,50')
+NEUROPROP(r,1,0,Range,f,0.0,1.0,1.0,range)
+ENDNEUROCLASS
+
+NEUROCLASS(Smell,S,Smell,`Smell sensor. Aggregated \"smell of energy\" experienced from all energy objects (creatures and food pieces).\nClose objects have bigger influence than the distant ones: for each energy source, its partial feeling is proportional to its energy/(distance^2)',0,1,1)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`5,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,3,10,40,15,45,15,55,10,60,5,20,30,25,35,30,45,30,55,25,65,20,70,4,15,35,20,40,22,50,20,60,15,65,5,75,50,50,50,45,45,40,50,45,55,50,50')
+ENDNEUROCLASS
+
+NEUROCLASS(Const,*,Constant,Constant value,0,1,0)
+VISUALHINTS(Invisible)
+SYMBOL(`4,4,26,27,26,73,73,73,73,27,26,27,1,73,50,100,50,1,56,68,46,68,2,41,47,51,32,51,68')
+ENDNEUROCLASS
+
+NEUROCLASS(BendMuscle,|,Bend muscle,,1,0,2)
+VISUALHINTS(DontShowClass+EffectorClass+V1BendMuscle+AtFirstPart)
+SYMBOL(`6,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,3,65,56,49,29,57,24,72,50,4,68,53,70,53,70,55,68,55,68,53,5,50,21,60,15,70,14,79,15,87,20,81,10,1,86,20,77,21')
+NEUROPROP(p,0,0,power,f,0.01,1.0,0.25,power)
+NEUROPROP(r,0,0,bending range,f,0.0,1.0,1.0,bendrange)
+ENDNEUROCLASS
+
+NEUROCLASS(RotMuscle,@,Rotation muscle,,1,0,2)
+VISUALHINTS(DontShowClass+EffectorClass+V1RotMuscle+AtFirstPart)
+SYMBOL(`5,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,1,69,10,77,17,10,59,15,57,17,57,22,60,26,69,27,78,26,82,21,82,16,79,12,69,10,80,6,3,65,50,65,20,75,20,75,50')
+NEUROPROP(p,0,0,power,f,0.01,1.0,1.0,power)
+ENDNEUROCLASS
+
+NEUROCLASS(Diff,D,Differentiate,Calculate the difference between the current and previous input value. Multiple inputs are aggregated with respect to their weights,-1,1,0)
+SYMBOL(`3,3,25,0,25,100,75,50,25,0,1,75,50,100,50,3,44,42,51,57,36,57,44,42')
+ENDNEUROCLASS
+
+NEUROCLASS(FuzzyNeuro,Fuzzy,Fuzzy system [EXPERIMENTAL!],Refer to publications to learn more about this neuron.,-1,1,0)
+SYMBOL(`5,2,30,65,37,37,44,65,3,37,65,44,37,51,37,58,65,2,51,65,58,37,65,65,6,100,50,70,50,70,25,25,10,25,90,70,75,70,50,1,70,65,25,65')
+NEUROPROP(ns,0,0,number of fuzzy sets,d,1,,,fuzzySetsNr)
+NEUROPROP(nr,0,0,number of rules,d,1,,,rulesNr)
+NEUROPROP(fs,0,0,fuzzy sets,s,0,-1,0,fuzzySetString)
+NEUROPROP(fr,0,0,fuzzy rules,s,0,-1,0,fuzzyRulesString)
+ENDNEUROCLASS
+
+NEUROCLASS(Sticky,Sti,Sticky [EXPERIMENTAL!],,1,0,1)
+VISUALHINTS(EffectorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(LinearMuscle,LMu,Linear muscle [EXPERIMENTAL!],,1,0,2)
+VISUALHINTS(EffectorClass)
+NEUROPROP(p,0,0,power,f,0.01,1.0,1.0,power)
+ENDNEUROCLASS
+
+NEUROCLASS(WaterDetect,Water,Water detector,`Output signal:\n0=on or above water surface\n1=under water (deeper than 1)\n0..1=in the transient area just below water surface',0,1,1)
+VISUALHINTS(ReceptorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(Energy,Energy,Energy level,`The current energy level divided by the initial energy level.\nUsually falls from initial 1.0 down to 0.0 and then the creature dies. It can rise above 1.0 if enough food is ingested',0,1,0)
+VISUALHINTS(ReceptorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(Channelize,Ch,Channelize,`Combines all input signals into a single multichannel output; Note: ChSel and ChMux are the only neurons which support multiple channels. Other neurons discard everything except the first channel.',-1,1,0)
+SYMBOL(`10,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,1,30,80,55,50,1,30,20,55,50,1,30,35,55,50,1,30,45,55,50,1,30,55,55,50,1,61,53,65,47,1,30,65,55,50')
+ENDNEUROCLASS
+
+NEUROCLASS(ChMux,ChMux,Channel multiplexer,`Outputs the selected channel from the second (multichannel) input. The first input is used as the selector value (-1=select first channel, .., 1=last channel)',2,1,0)
+SYMBOL(`7,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,3,30,67,45,67,45,50,50,50,1,35,70,39,64,2,30,33,53,33,53,48')
+ENDNEUROCLASS
+
+NEUROCLASS(ChSel,ChSel,Channel selector,`Outputs a single channel (selected by the \"ch\" parameter) from multichannel input',1,1,0)
+SYMBOL(`6,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,1,30,50,50,50,1,35,53,39,47')
+NEUROPROP(ch,0,0,channel,d,,,,ch)
+ENDNEUROCLASS
+
+NEUROCLASS(Random,Rnd,Random noise,`Generates random noise (subsequent random values in the range of -1..+1)',0,1,0)
+ENDNEUROCLASS
+
+NEUROCLASS(Sinus,Sin,Sinus generator,`Output frequency = f0+input',1,1,0)
+SYMBOL(`3,12,75,50,71,37,62,28,50,25,37,28,28,37,25,50,28,62,37,71,50,75,62,71,71,62,75,50,1,75,50,100,50,5,35,50,40,35,45,35,55,65,60,65,65,50')
+NEUROPROP(f0,0,0,base frequency,f,-1.0,1.0,0.06283185307,f0)
+NEUROPROP(t,0,0,time,f,0,6.283185307,0,t)
+ENDNEUROCLASS
Index: /cpp/frams/config/f0.def
===================================================================
--- /cpp/frams/config/f0.def	(revision 109)
+++ /cpp/frams/config/f0.def	(revision 109)
@@ -0,0 +1,220 @@
+CLASS(Model,f0_model,m)
+GROUP(Properties)
+GROUP(Visual)
+PROP(se,0,1024,startenergy,f,,,,startenergy)
+PROP(Vstyle,1,0,vis_style,s,0,-1,,vis_style)
+ENDCLASS
+
+CLASS(Part,f0_part,p)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(x,0,1024,position.x,f,,,,p.x)
+PROP(y,0,1024,position.y,f,,,,p.y)
+PROP(z,0,1024,position.z,f,,,,p.z)
+XPROP(m,1,0,mass,f,0.1,999.0,1.0,mass)
+PROP(sh,1,0,shape,d,0,3,0,shape)
+PROP(s,1,0,size,f,0.1,10.0,1.0,size)
+PROP(sx,1,0,scale.x,f,0.001,1000.0,1.0,scale.x)
+PROP(sy,1,0,scale.y,f,0.001,1000.0,1.0,scale.y)
+PROP(sz,1,0,scale.z,f,0.001,1000.0,1.0,scale.z)
+XPROP(dn,1,0,density,f,0.2,5.0,1.0,density)
+XPROP(fr,1,0,friction,f,0.0,4.0,0.4,friction)
+XPROP(ing,1,0,ingestion,f,0.0,1.0,0.25,ingest)
+XPROP(as,1,0,assimilation,f,0.0,1.0,0.25,assim)
+PROP(rx,0,0,rot.x,f,,,,rot.x)
+PROP(ry,0,1024,rot.y,f,,,,rot.y)
+PROP(rz,0,1024,rot.z,f,,,,rot.z)
+PROP(i,1,0,`info',s,,,,info)
+PROP(Vstyle,2,0,vis_style,s,0,-1,part,vis_style)
+XPROP(vs,2,0,visual thickness,f,0.05,0.7,0.2,vsize)
+XPROP(vr,2,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,2,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,2,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Joint,f0_joint,j)
+GROUP(Connections)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(p1,0,1024,`part1 ref#',d,-1,999999,-1,p1_refno)
+PROP(p2,0,1024,`part2 ref#',d,-1,999999,-1,p2_refno)
+PROP(rx,1,0,rotation.x,f,,,,rot.x)
+PROP(ry,1,1024,rotation.y,f,,,,rot.y)
+PROP(rz,1,1024,rotation.z,f,,,,rot.z)
+PROP(dx,1,0,delta.x,f,-2,2,0,d.x)
+PROP(dy,1,1024,delta.y,f,-2,2,0,d.y)
+PROP(dz,1,1024,delta.z,f,-2,2,0,d.z)
+PROP(sh,1,0,shape,d,0,1,0,shape)
+XPROP(stif,2,0,stiffness,f,0.0,1.0,1.0,stif)
+XPROP(rotstif,2,0,rotation stiffness,f,0.0,1.0,1.0,rotstif)
+PROP(stam,2,0,stamina,f,0.0,1.0,0.25,stamina)
+PROP(i,2,0,`info',s,,,,info)
+PROP(Vstyle,3,0,vis_style,s,0,-1,joint,vis_style)
+XPROP(vr,3,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,3,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,3,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Joint,f0_nodeltajoint,j,NOXML)
+GROUP(Connections)
+GROUP(Geometry)
+GROUP(Other properties)
+GROUP(Visual)
+PROP(p1,0,1024,`part1 ref#',d,-1,999999,-1,p1_refno)
+PROP(p2,0,1024,`part2 ref#',d,-1,999999,-1,p2_refno)
+PROP(sh,1,0,shape,d,0,1,0,shape)
+XPROP(stif,2,0,stiffness,f,0.0,1.0,1.0,stif)
+XPROP(rotstif,2,0,rotation stiffness,f,0.0,1.0,1.0,rotstif)
+PROP(stam,2,0,stamina,f,0.0,1.0,0.25,stamina)
+PROP(i,2,0,`info',s,,,,info)
+PROP(Vstyle,3,0,vis_style,s,0,-1,joint,vis_style)
+XPROP(vr,3,0,red component,f,0.0,1.0,0.5,vcolor.x)
+XPROP(vg,3,1024,green component,f,0.0,1.0,0.5,vcolor.y)
+XPROP(vb,3,1024,blue component,f,0.0,1.0,0.5,vcolor.z)
+ENDCLASS
+
+CLASS(Neuro,f0_neuro,n)
+GROUP(Connections)
+GROUP(Other)
+GROUP(Visual)
+PROP(p,0,0,`part ref#',d,-1,999999,-1,part_refno)
+PROP(j,0,0,`joint ref#',d,-1,999999,-1,joint_refno)
+PROP(d,1,0,item details,s,,,N,details,GETSET)
+PROP(i,1,0,`info',s,,,,info)
+PROP(Vstyle,2,0,vis_style,s,0,-1,neuro,vis_style)
+PROP(getInputCount,0,1+2,`input count',d,,,,inputCount,GETONLY)
+PROP(getInputNeuroDef,0,1+2,`get input neuron',p oNeuroDef(d),,,,p_getInputNeuroDef,PROCEDURE)
+PROP(getInputNeuroIndex,0,1+2,`get input neuron index',p d(d),,,,p_getInputNeuroIndex,PROCEDURE)
+PROP(getInputWeight,0,1+2,`get input weight',p f(d),,,,p_getInputWeight,PROCEDURE)
+PROP(classObject,0,1+2,`neuron class',o NeuroClass,,,,classObject,GETONLY)
+ENDCLASS
+
+CLASS(NeuroConn,f0_neuroconn,c)
+GROUP(Connection)
+GROUP(Other)
+PROP(n1,0,1024,`this neuro ref#',d,-1,999999,-1,n1_refno)
+PROP(n2,0,1024,`connected neuro ref#',d,-1,999999,-1,n2_refno)
+PROP(w,0,1024,weight,f,-999999,999999,1.0,weight)
+PROP(i,1,0,`info',s,,,,info)
+ENDCLASS
+
+NEUROCLASS(StdNeuron,N,Neuron,`Standard neuron',-1,1,0)
+VISUALHINTS(DontShowClass)
+NEUROPROP(in,1,0,Inertia,f,0.0,1.0,0.8,inertia)
+NEUROPROP(fo,1,0,Force,f,0.0,999.0,0.04,force)
+NEUROPROP(si,1,0,Sigmoid,f,-99999.0,99999.0,2.0,sigmo)
+NEUROPROP(s,2,0,State,f,-1.0,1.0,0.0,newstate)
+ENDNEUROCLASS
+
+NEUROCLASS(StdUNeuron,Nu,`Unipolar neuron [EXPERIMENTAL!]',`Works like standard neuron (N) but the output value is scaled to 0...+1 instead of -1...+1.\nHaving 0 as one of the saturation states should help in \"gate circuits\", where input signal is passed through or blocked depending on the other singal.',-1,1,0)
+NEUROPROP(in,1,0,Inertia,f,0.0,1.0,0.8,inertia)
+NEUROPROP(fo,1,0,Force,f,0.0,999.0,0.04,force)
+NEUROPROP(si,1,0,Sigmoid,f,-99999.0,99999.0,2.0,sigmo)
+NEUROPROP(s,2,0,State,f,-1.0,1.0,0.0,newstate)
+ENDNEUROCLASS
+
+NEUROCLASS(Gyro,G,Gyroscope,`Equilibrium sensor.\n0=the stick is horizontal\n+1/-1=the stick is vertical',0,1,2)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`8,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,12,43,24,48,24,48,19,38,19,38,24,43,24,43,54,48,54,48,64,43,69,38,64,38,54,43,54,5,63,69,58,74,48,79,38,79,28,74,23,69,1,43,79,43,74,1,23,69,26,66,1,63,69,60,66,1,55,76,53,73,1,31,75,33,72')
+ENDNEUROCLASS
+
+NEUROCLASS(Touch,T,Touch,`Touch sensor.\n-1=no contact\n0=just touching\n>0=pressing, value depends on the force applied',0,1,1)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`2,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,11,75,50,65,50,60,55,55,45,50,55,45,45,40,50,35,50,30,45,25,50,30,55,35,50')
+NEUROPROP(r,1,0,Range,f,0.0,1.0,1.0,range)
+ENDNEUROCLASS
+
+NEUROCLASS(Smell,S,Smell,`Smell sensor. Aggregated \"smell of energy\" experienced from all energy objects (creatures and food pieces).\nClose objects have bigger influence than the distant ones: for each energy source, its partial feeling is proportional to its energy/(distance^2)',0,1,1)
+VISUALHINTS(ReceptorClass)
+SYMBOL(`5,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,3,10,40,15,45,15,55,10,60,5,20,30,25,35,30,45,30,55,25,65,20,70,4,15,35,20,40,22,50,20,60,15,65,5,75,50,50,50,45,45,40,50,45,55,50,50')
+ENDNEUROCLASS
+
+NEUROCLASS(Const,*,Constant,Constant value,0,1,0)
+VISUALHINTS(Invisible)
+SYMBOL(`4,4,26,27,26,73,73,73,73,27,26,27,1,73,50,100,50,1,56,68,46,68,2,41,47,51,32,51,68')
+ENDNEUROCLASS
+
+NEUROCLASS(BendMuscle,|,Bend muscle,,1,0,2)
+VISUALHINTS(DontShowClass+EffectorClass+V1BendMuscle+AtFirstPart)
+SYMBOL(`6,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,3,65,56,49,29,57,24,72,50,4,68,53,70,53,70,55,68,55,68,53,5,50,21,60,15,70,14,79,15,87,20,81,10,1,86,20,77,21')
+NEUROPROP(p,0,0,power,f,0.01,1.0,0.25,power)
+NEUROPROP(r,0,0,bending range,f,0.0,1.0,1.0,bendrange)
+ENDNEUROCLASS
+
+NEUROCLASS(RotMuscle,@,Rotation muscle,,1,0,2)
+VISUALHINTS(DontShowClass+EffectorClass+V1RotMuscle+AtFirstPart)
+SYMBOL(`5,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,1,69,10,77,17,10,59,15,57,17,57,22,60,26,69,27,78,26,82,21,82,16,79,12,69,10,80,6,3,65,50,65,20,75,20,75,50')
+NEUROPROP(p,0,0,power,f,0.01,1.0,1.0,power)
+ENDNEUROCLASS
+
+NEUROCLASS(Diff,D,Differentiate,Calculate the difference between the current and previous input value. Multiple inputs are aggregated with respect to their weights,-1,1,0)
+SYMBOL(`3,3,25,0,25,100,75,50,25,0,1,75,50,100,50,3,44,42,51,57,36,57,44,42')
+ENDNEUROCLASS
+
+NEUROCLASS(FuzzyNeuro,Fuzzy,Fuzzy system [EXPERIMENTAL!],Refer to publications to learn more about this neuron.,-1,1,0)
+SYMBOL(`5,2,30,65,37,37,44,65,3,37,65,44,37,51,37,58,65,2,51,65,58,37,65,65,6,100,50,70,50,70,25,25,10,25,90,70,75,70,50,1,70,65,25,65')
+NEUROPROP(ns,0,0,number of fuzzy sets,d,1,,,fuzzySetsNr)
+NEUROPROP(nr,0,0,number of rules,d,1,,,rulesNr)
+NEUROPROP(fs,0,0,fuzzy sets,s,0,-1,0,fuzzySetString)
+NEUROPROP(fr,0,0,fuzzy rules,s,0,-1,0,fuzzyRulesString)
+ENDNEUROCLASS
+
+NEUROCLASS(VectorEye,VEye,Vector Eye [EXPERIMENTAL!],Refer to publications to learn more about this neuron.,1,1,1)
+SYMBOL(`11,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,14,70,50,65,40,60,35,45,30,30,30,15,35,10,40,5,50,10,60,15,65,30,70,45,70,60,65,65,60,70,50,8,38,67,28,62,23,52,28,42,38,37,48,42,53,52,48,62,38,67,4,33,52,38,47,43,52,38,57,33,52,5,70,50,60,40,45,35,30,35,15,40,5,50,1,53,33,58,22,1,62,36,68,26,1,45,30,47,19,1,35,30,35,19,1,27,31,24,20,1,18,34,12,24')
+NEUROPROP(tx,0,0,target.x,f,,,,target.x)
+NEUROPROP(ty,0,0,target.y,f,,,,target.y)
+NEUROPROP(tz,0,0,target.z,f,,,,target.z)
+NEUROPROP(ts,0,0,target shape,s,0,-1,0,targetShape)
+NEUROPROP(p,0,0,perspective,f,0.1,10.0,1.0,perspz)
+NEUROPROP(s,0,0,scale,f,0.1,100.0,1.0,perspscale)
+NEUROPROP(h,0,0,show hidden lines,d,0,1,0,showhidden)
+NEUROPROP(o,0,0,`output lines count (each line needs four channels)',d,0,99,0,outputcount)
+NEUROPROP(d,0,0,debug,d,0,1,0,showdebug)
+ENDNEUROCLASS
+
+NEUROCLASS(VisualMotorNeuron,VMotor,Visual-Motor Cortex [EXPERIMENTAL!],`Must be connected to the VEye and properly set up. Refer to publications to learn more about this neuron.',-1,1,0)
+NEUROPROP(noIF,0,0,number of basic features,d,,,,noIF)
+NEUROPROP(noDim,0,0,number of degrees of freedom,d,,,,noDim)
+NEUROPROP(params,0,0,parameters,s,,,,params)
+ENDNEUROCLASS 
+
+NEUROCLASS(Sticky,Sti,Sticky [EXPERIMENTAL!],,1,0,1)
+VISUALHINTS(EffectorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(LinearMuscle,LMu,Linear muscle [EXPERIMENTAL!],,1,0,2)
+VISUALHINTS(EffectorClass+LinearMuscleFlag)
+NEUROPROP(p,0,0,power,f,0.01,1.0,1.0,power)
+ENDNEUROCLASS
+
+NEUROCLASS(WaterDetect,Water,Water detector,`Output signal:\n0=on or above water surface\n1=under water (deeper than 1)\n0..1=in the transient area just below water surface',0,1,1)
+VISUALHINTS(ReceptorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(Energy,Energy,Energy level,`The current energy level divided by the initial energy level.\nUsually falls from initial 1.0 down to 0.0 and then the creature dies. It can rise above 1.0 if enough food is ingested',0,1,0)
+VISUALHINTS(ReceptorClass)
+ENDNEUROCLASS
+
+NEUROCLASS(Channelize,Ch,Channelize,`Combines all input signals into a single multichannel output; Note: ChSel and ChMux are the only neurons which support multiple channels. Other neurons discard everything except the first channel.',-1,1,0)
+SYMBOL(`10,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,1,30,80,55,50,1,30,20,55,50,1,30,35,55,50,1,30,45,55,50,1,30,55,55,50,1,61,53,65,47,1,30,65,55,50')
+ENDNEUROCLASS
+
+NEUROCLASS(ChMux,ChMux,Channel multiplexer,`Outputs the selected channel from the second (multichannel) input. The first input is used as the selector value (-1=select first channel, .., 1=last channel)',2,1,0)
+SYMBOL(`7,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,3,30,67,45,67,45,50,50,50,1,35,70,39,64,2,30,33,53,33,53,48')
+ENDNEUROCLASS
+
+NEUROCLASS(ChSel,ChSel,Channel selector,`Outputs a single channel (selected by the \"ch\" parameter) from multichannel input',1,1,0)
+SYMBOL(`6,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,1,30,50,50,50,1,35,53,39,47')
+NEUROPROP(ch,0,0,channel,d,,,,ch)
+ENDNEUROCLASS
+
+NEUROCLASS(Random,Rnd,Random noise,`Generates random noise (subsequent random values in the range of -1..+1)',0,1,0)
+ENDNEUROCLASS
+
+NEUROCLASS(Sinus,Sin,Sinus generator,`Output frequency = f0+input',1,1,0)
+SYMBOL(`3,12,75,50,71,37,62,28,50,25,37,28,28,37,25,50,28,62,37,71,50,75,62,71,71,62,75,50,1,75,50,100,50,5,35,50,40,35,45,35,55,65,60,65,65,50')
+NEUROPROP(f0,0,0,base frequency,f,-1.0,1.0,0.06283185307,f0)
+NEUROPROP(t,0,0,time,f,0,6.283185307,0,t)
+ENDNEUROCLASS
Index: /cpp/frams/config/f0def.xml
===================================================================
--- /cpp/frams/config/f0def.xml	(revision 109)
+++ /cpp/frams/config/f0def.xml	(revision 109)
@@ -0,0 +1,209 @@
+<!DOCTYPE F0CLASSES>
+<F0CLASSES>
+
+ <CLASS ID="m" NAME="Model">
+  <GROUP NAME="Properties" />
+  <GROUP NAME="Visual" />
+  <PROP ID="se" NAME="startenergy" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="Vstyle" NAME="vis_style" GROUP="1" FLAGS="0" TYPE="s" MIN="0" MAX="-1" />
+ </CLASS>
+
+ <CLASS ID="p" NAME="Part">
+  <GROUP NAME="Geometry" />
+  <GROUP NAME="Other properties" />
+  <GROUP NAME="Visual" />
+  <PROP ID="x" NAME="position.x" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="y" NAME="position.y" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="z" NAME="position.z" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="m" XTRA="1" NAME="mass" GROUP="1" FLAGS="0" TYPE="f" MIN="0.1" MAX="999.0" DEF="1.0" />
+  <PROP ID="sh" NAME="shape" GROUP="1" FLAGS="0" TYPE="d" MIN="0" MAX="3" DEF="0" />
+  <PROP ID="s" NAME="size" GROUP="1" FLAGS="0" TYPE="f" MIN="0.1" MAX="10.0" DEF="1.0" />
+  <PROP ID="sx" NAME="scale.x" GROUP="1" FLAGS="0" TYPE="f" MIN="0.001" MAX="1000.0" DEF="1.0" />
+  <PROP ID="sy" NAME="scale.y" GROUP="1" FLAGS="0" TYPE="f" MIN="0.001" MAX="1000.0" DEF="1.0" />
+  <PROP ID="sz" NAME="scale.z" GROUP="1" FLAGS="0" TYPE="f" MIN="0.001" MAX="1000.0" DEF="1.0" />
+  <PROP ID="dn" XTRA="1" NAME="density" GROUP="1" FLAGS="0" TYPE="f" MIN="0.2" MAX="5.0" DEF="1.0" />
+  <PROP ID="fr" XTRA="1" NAME="friction" GROUP="1" FLAGS="0" TYPE="f" MIN="0.0" MAX="4.0" DEF="0.4" />
+  <PROP ID="ing" XTRA="1" NAME="ingestion" GROUP="1" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.25" />
+  <PROP ID="as" XTRA="1" NAME="assimilation" GROUP="1" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.25" />
+  <PROP ID="rx" NAME="rot.x" GROUP="0" FLAGS="0" TYPE="f" />
+  <PROP ID="ry" NAME="rot.y" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="rz" NAME="rot.z" GROUP="0" FLAGS="1024" TYPE="f" />
+  <PROP ID="i" NAME="info" GROUP="1" FLAGS="0" TYPE="s" />
+  <PROP ID="Vstyle" NAME="vis_style" GROUP="2" FLAGS="0" TYPE="s" MIN="0" MAX="-1" DEF="part" />
+  <PROP ID="vs" XTRA="1" NAME="visual thickness" GROUP="2" FLAGS="0" TYPE="f" MIN="0.05" MAX="0.7" DEF="0.2" />
+  <PROP ID="vr" XTRA="1" NAME="red component" GROUP="2" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+  <PROP ID="vg" XTRA="1" NAME="green component" GROUP="2" FLAGS="1024" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+  <PROP ID="vb" XTRA="1" NAME="blue component" GROUP="2" FLAGS="1024" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+ </CLASS>
+
+ <CLASS ID="j" NAME="Joint">
+  <GROUP NAME="Connections" />
+  <GROUP NAME="Geometry" />
+  <GROUP NAME="Other properties" />
+  <GROUP NAME="Visual" />
+  <PROP ID="p1" NAME="part1 ref#" GROUP="0" FLAGS="1024" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="p2" NAME="part2 ref#" GROUP="0" FLAGS="1024" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="rx" NAME="rotation.x" GROUP="1" FLAGS="0" TYPE="f" />
+  <PROP ID="ry" NAME="rotation.y" GROUP="1" FLAGS="1024" TYPE="f" />
+  <PROP ID="rz" NAME="rotation.z" GROUP="1" FLAGS="1024" TYPE="f" />
+  <PROP ID="dx" NAME="delta.x" GROUP="1" FLAGS="0" TYPE="f" MIN="-2" MAX="2" DEF="0" />
+  <PROP ID="dy" NAME="delta.y" GROUP="1" FLAGS="1024" TYPE="f" MIN="-2" MAX="2" DEF="0" />
+  <PROP ID="dz" NAME="delta.z" GROUP="1" FLAGS="1024" TYPE="f" MIN="-2" MAX="2" DEF="0" />
+  <PROP ID="sh" NAME="shape" GROUP="1" FLAGS="0" TYPE="d" MIN="0" MAX="1" DEF="0" />
+  <PROP ID="stif" XTRA="1" NAME="stiffness" GROUP="2" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="1.0" />
+  <PROP ID="rotstif" XTRA="1" NAME="rotation stiffness" GROUP="2" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="1.0" />
+  <PROP ID="stam" NAME="stamina" GROUP="2" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.25" />
+  <PROP ID="i" NAME="info" GROUP="2" FLAGS="0" TYPE="s" />
+  <PROP ID="Vstyle" NAME="vis_style" GROUP="3" FLAGS="0" TYPE="s" MIN="0" MAX="-1" DEF="joint" />
+  <PROP ID="vr" XTRA="1" NAME="red component" GROUP="3" FLAGS="0" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+  <PROP ID="vg" XTRA="1" NAME="green component" GROUP="3" FLAGS="1024" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+  <PROP ID="vb" XTRA="1" NAME="blue component" GROUP="3" FLAGS="1024" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.5" />
+ </CLASS>
+
+
+
+ <CLASS ID="n" NAME="Neuro">
+  <GROUP NAME="Connections" />
+  <GROUP NAME="Other" />
+  <GROUP NAME="Visual" />
+  <PROP ID="p" NAME="part ref#" GROUP="0" FLAGS="0" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="j" NAME="joint ref#" GROUP="0" FLAGS="0" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="d" NAME="item details" GROUP="1" FLAGS="0" TYPE="s" DEF="N" />
+  <PROP ID="i" NAME="info" GROUP="1" FLAGS="0" TYPE="s" />
+  <PROP ID="Vstyle" NAME="vis_style" GROUP="2" FLAGS="0" TYPE="s" MIN="0" MAX="-1" DEF="neuro" />
+  <PROP ID="getInputCount" NAME="input count" GROUP="0" FLAGS="1+2" TYPE="d" />
+  <PROP ID="getInputNeuroDef" NAME="get input neuron" GROUP="0" FLAGS="1+2" TYPE="p oNeuroDef(d)" />
+  <PROP ID="getInputNeuroIndex" NAME="get input neuron index" GROUP="0" FLAGS="1+2" TYPE="p d(d)" />
+  <PROP ID="getInputWeight" NAME="get input weight" GROUP="0" FLAGS="1+2" TYPE="p f(d)" />
+  <PROP ID="classObject" NAME="neuron class" GROUP="0" FLAGS="1+2" TYPE="o NeuroClass" />
+ </CLASS>
+
+ <CLASS ID="c" NAME="NeuroConn">
+  <GROUP NAME="Connection" />
+  <GROUP NAME="Other" />
+  <PROP ID="n1" NAME="this neuro ref#" GROUP="0" FLAGS="1024" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="n2" NAME="connected neuro ref#" GROUP="0" FLAGS="1024" TYPE="d" MIN="-1" MAX="999999" DEF="-1" />
+  <PROP ID="w" NAME="weight" GROUP="0" FLAGS="1024" TYPE="f" MIN="-999999" MAX="999999" DEF="1.0" />
+  <PROP ID="i" NAME="info" GROUP="1" FLAGS="0" TYPE="s" />
+ </CLASS>
+
+ <NEUROCLASS ID="N" NAME="Neuron" DESCRIPTION="Standard neuron" INPUTS="-1" OUTPUT="1" LOCATION="0"  VISUALHINTS="2"  >
+
+  <NEUROPROP ID="in" NAME="Inertia" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.8" />
+  <NEUROPROP ID="fo" NAME="Force" TYPE="f" MIN="0.0" MAX="999.0" DEF="0.04" />
+  <NEUROPROP ID="si" NAME="Sigmoid" TYPE="f" MIN="-99999.0" MAX="99999.0" DEF="2.0" />
+  <NEUROPROP ID="s" NAME="State" TYPE="f" MIN="-1.0" MAX="1.0" DEF="0.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Nu" NAME="Unipolar neuron [EXPERIMENTAL!]" DESCRIPTION="Works like standard neuron (N) but the output value is scaled to 0...+1 instead of -1...+1.\nHaving 0 as one of the saturation states should help in &quot;gate circuits&quot;, where input signal is passed through or blocked depending on the other singal." INPUTS="-1" OUTPUT="1" LOCATION="0"  >
+  <NEUROPROP ID="in" NAME="Inertia" TYPE="f" MIN="0.0" MAX="1.0" DEF="0.8" />
+  <NEUROPROP ID="fo" NAME="Force" TYPE="f" MIN="0.0" MAX="999.0" DEF="0.04" />
+  <NEUROPROP ID="si" NAME="Sigmoid" TYPE="f" MIN="-99999.0" MAX="99999.0" DEF="2.0" />
+  <NEUROPROP ID="s" NAME="State" TYPE="f" MIN="-1.0" MAX="1.0" DEF="0.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="G" NAME="Gyroscope" DESCRIPTION="Equilibrium sensor.\n0=the stick is horizontal\n+1/-1=the stick is vertical" INPUTS="0" OUTPUT="1" LOCATION="2"  VISUALHINTS="32"  SYMBOL="8,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,12,43,24,48,24,48,19,38,19,38,24,43,24,43,54,48,54,48,64,43,69,38,64,38,54,43,54,5,63,69,58,74,48,79,38,79,28,74,23,69,1,43,79,43,74,1,23,69,26,66,1,63,69,60,66,1,55,76,53,73,1,31,75,33,72"  >
+
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="T" NAME="Touch" DESCRIPTION="Touch sensor.\n-1=no contact\n0=just touching\n>0=pressing, value depends on the force applied" INPUTS="0" OUTPUT="1" LOCATION="1"  VISUALHINTS="32"  SYMBOL="2,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,11,75,50,65,50,60,55,55,45,50,55,45,45,40,50,35,50,30,45,25,50,30,55,35,50"  >
+
+
+  <NEUROPROP ID="r" NAME="Range" TYPE="f" MIN="0.0" MAX="1.0" DEF="1.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="S" NAME="Smell" DESCRIPTION="Smell sensor. Aggregated &quot;smell of energy&quot; experienced from all energy objects (creatures and food pieces).\nClose objects have bigger influence than the distant ones: for each energy source, its partial feeling is proportional to its energy/(distance^2)" INPUTS="0" OUTPUT="1" LOCATION="1"  VISUALHINTS="32"  SYMBOL="5,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,3,10,40,15,45,15,55,10,60,5,20,30,25,35,30,45,30,55,25,65,20,70,4,15,35,20,40,22,50,20,60,15,65,5,75,50,50,50,45,45,40,50,45,55,50,50"  >
+
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="*" NAME="Constant" DESCRIPTION="Constant value" INPUTS="0" OUTPUT="1" LOCATION="0"  VISUALHINTS="1"  SYMBOL="4,4,26,27,26,73,73,73,73,27,26,27,1,73,50,100,50,1,56,68,46,68,2,41,47,51,32,51,68"  >
+
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="|" NAME="Bend muscle" DESCRIPTION="" INPUTS="1" OUTPUT="0" LOCATION="2"  VISUALHINTS="86"  SYMBOL="6,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,3,65,56,49,29,57,24,72,50,4,68,53,70,53,70,55,68,55,68,53,5,50,21,60,15,70,14,79,15,87,20,81,10,1,86,20,77,21"  >
+
+
+  <NEUROPROP ID="p" NAME="power" TYPE="f" MIN="0.01" MAX="1.0" DEF="0.25" />
+  <NEUROPROP ID="r" NAME="bending range" TYPE="f" MIN="0.0" MAX="1.0" DEF="1.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="@" NAME="Rotation muscle" DESCRIPTION="" INPUTS="1" OUTPUT="0" LOCATION="2"  VISUALHINTS="150"  SYMBOL="5,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,1,69,10,77,17,10,59,15,57,17,57,22,60,26,69,27,78,26,82,21,82,16,79,12,69,10,80,6,3,65,50,65,20,75,20,75,50"  >
+
+
+  <NEUROPROP ID="p" NAME="power" TYPE="f" MIN="0.01" MAX="1.0" DEF="1.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="D" NAME="Differentiate" DESCRIPTION="Calculate the difference between the current and previous input value. Multiple inputs are aggregated with respect to their weights" INPUTS="-1" OUTPUT="1" LOCATION="0"  SYMBOL="3,3,25,0,25,100,75,50,25,0,1,75,50,100,50,3,44,42,51,57,36,57,44,42"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Fuzzy" NAME="Fuzzy system [EXPERIMENTAL!]" DESCRIPTION="Refer to publications to learn more about this neuron." INPUTS="-1" OUTPUT="1" LOCATION="0"  SYMBOL="5,2,30,65,37,37,44,65,3,37,65,44,37,51,37,58,65,2,51,65,58,37,65,65,6,100,50,70,50,70,25,25,10,25,90,70,75,70,50,1,70,65,25,65"  >
+
+  <NEUROPROP ID="ns" NAME="number of fuzzy sets" TYPE="d" MIN="1" MAX="" DEF="" />
+  <NEUROPROP ID="nr" NAME="number of rules" TYPE="d" MIN="1" MAX="" DEF="" />
+  <NEUROPROP ID="fs" NAME="fuzzy sets" TYPE="s" MIN="0" MAX="-1" DEF="0" />
+  <NEUROPROP ID="fr" NAME="fuzzy rules" TYPE="s" MIN="0" MAX="-1" DEF="0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="VEye" NAME="Vector Eye [EXPERIMENTAL!]" DESCRIPTION="Refer to publications to learn more about this neuron." INPUTS="1" OUTPUT="1" LOCATION="1"  SYMBOL="11,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,14,70,50,65,40,60,35,45,30,30,30,15,35,10,40,5,50,10,60,15,65,30,70,45,70,60,65,65,60,70,50,8,38,67,28,62,23,52,28,42,38,37,48,42,53,52,48,62,38,67,4,33,52,38,47,43,52,38,57,33,52,5,70,50,60,40,45,35,30,35,15,40,5,50,1,53,33,58,22,1,62,36,68,26,1,45,30,47,19,1,35,30,35,19,1,27,31,24,20,1,18,34,12,24"  >
+
+  <NEUROPROP ID="tx" NAME="target.x" TYPE="f" MIN="" MAX="" DEF="" />
+  <NEUROPROP ID="ty" NAME="target.y" TYPE="f" MIN="" MAX="" DEF="" />
+  <NEUROPROP ID="tz" NAME="target.z" TYPE="f" MIN="" MAX="" DEF="" />
+  <NEUROPROP ID="ts" NAME="target shape" TYPE="s" MIN="0" MAX="-1" DEF="0" />
+  <NEUROPROP ID="p" NAME="perspective" TYPE="f" MIN="0.1" MAX="10.0" DEF="1.0" />
+  <NEUROPROP ID="s" NAME="scale" TYPE="f" MIN="0.1" MAX="100.0" DEF="1.0" />
+  <NEUROPROP ID="h" NAME="show hidden lines" TYPE="d" MIN="0" MAX="1" DEF="0" />
+  <NEUROPROP ID="o" NAME="output lines count (each line needs four channels)" TYPE="d" MIN="0" MAX="99" DEF="0" />
+  <NEUROPROP ID="d" NAME="debug" TYPE="d" MIN="0" MAX="1" DEF="0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="VMotor" NAME="Visual-Motor Cortex [EXPERIMENTAL!]" DESCRIPTION="Must be connected to the VEye and properly set up. Refer to publications to learn more about this neuron." INPUTS="-1" OUTPUT="1" LOCATION="0"  >
+  <NEUROPROP ID="noIF" NAME="number of basic features" TYPE="d" MIN="" MAX="" DEF="" />
+  <NEUROPROP ID="noDim" NAME="number of degrees of freedom" TYPE="d" MIN="" MAX="" DEF="" />
+  <NEUROPROP ID="params" NAME="parameters" TYPE="s" MIN="" MAX="" DEF="" />
+ </NEUROCLASS> 
+
+ <NEUROCLASS ID="Sti" NAME="Sticky [EXPERIMENTAL!]" DESCRIPTION="" INPUTS="1" OUTPUT="0" LOCATION="1"  VISUALHINTS="16"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="LMu" NAME="Linear muscle [EXPERIMENTAL!]" DESCRIPTION="" INPUTS="1" OUTPUT="0" LOCATION="2"  VISUALHINTS="272"  >
+
+  <NEUROPROP ID="p" NAME="power" TYPE="f" MIN="0.01" MAX="1.0" DEF="1.0" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Water" NAME="Water detector" DESCRIPTION="Output signal:\n0=on or above water surface\n1=under water (deeper than 1)\n0..1=in the transient area just below water surface" INPUTS="0" OUTPUT="1" LOCATION="1"  VISUALHINTS="32"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Energy" NAME="Energy level" DESCRIPTION="The current energy level divided by the initial energy level.\nUsually falls from initial 1.0 down to 0.0 and then the creature dies. It can rise above 1.0 if enough food is ingested" INPUTS="0" OUTPUT="1" LOCATION="0"  VISUALHINTS="32"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Ch" NAME="Channelize" DESCRIPTION="Combines all input signals into a single multichannel output; Note: ChSel and ChMux are the only neurons which support multiple channels. Other neurons discard everything except the first channel." INPUTS="-1" OUTPUT="1" LOCATION="0"  SYMBOL="10,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,1,30,80,55,50,1,30,20,55,50,1,30,35,55,50,1,30,45,55,50,1,30,55,55,50,1,61,53,65,47,1,30,65,55,50"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="ChMux" NAME="Channel multiplexer" DESCRIPTION="Outputs the selected channel from the second (multichannel) input. The first input is used as the selector value (-1=select first channel, .., 1=last channel)" INPUTS="2" OUTPUT="1" LOCATION="0"  SYMBOL="7,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,3,30,67,45,67,45,50,50,50,1,35,70,39,64,2,30,33,53,33,53,48"  >
+
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="ChSel" NAME="Channel selector" DESCRIPTION="Outputs a single channel (selected by the &quot;ch&quot; parameter) from multichannel input" INPUTS="1" OUTPUT="1" LOCATION="0"  SYMBOL="6,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,1,30,50,50,50,1,35,53,39,47"  >
+
+  <NEUROPROP ID="ch" NAME="channel" TYPE="d" MIN="" MAX="" DEF="" />
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Rnd" NAME="Random noise" DESCRIPTION="Generates random noise (subsequent random values in the range of -1..+1)" INPUTS="0" OUTPUT="1" LOCATION="0"  >
+ </NEUROCLASS>
+
+ <NEUROCLASS ID="Sin" NAME="Sinus generator" DESCRIPTION="Output frequency = f0+input" INPUTS="1" OUTPUT="1" LOCATION="0"  SYMBOL="3,12,75,50,71,37,62,28,50,25,37,28,28,37,25,50,28,62,37,71,50,75,62,71,71,62,75,50,1,75,50,100,50,5,35,50,40,35,45,35,55,65,60,65,65,50"  >
+
+  <NEUROPROP ID="f0" NAME="base frequency" TYPE="f" MIN="-1.0" MAX="1.0" DEF="0.06283185307" />
+  <NEUROPROP ID="t" NAME="time" TYPE="f" MIN="0" MAX="6.283185307" DEF="0" />
+ </NEUROCLASS>
+
+</F0CLASSES>
Index: /cpp/frams/config/gdk_build_config.h
===================================================================
--- /cpp/frams/config/gdk_build_config.h	(revision 109)
+++ /cpp/frams/config/gdk_build_config.h	(revision 109)
@@ -0,0 +1,19 @@
+#ifndef _GDK_BUILD_CONFIG_H_
+#define _GDK_BUILD_CONFIG_H_
+
+#define MAIN_REL_ID "3.1"
+
+#define NOCREATUREOBJECT
+#define EASYMAPDEBUG
+#define GDK_WITHOUT_FRAMS
+#define NO_BARRIER
+#define NO_VMACHINE
+
+#define NEURO_IMPL_FILES "neuroimplfiles-GDK.h"
+#define NEURO_CLS_FACTORY "neurocls-f0-GDK-factory.h"
+#define NEURO_CLS_LIBRARY "neurocls-f0-GDK-library.h"
+#define F0_DEFASSIGN_FILE "defassign-f0-GDK.h"
+#define F0_CLASSES_FILE "f0-GDK-classes.h"
+#define GEN_CONFIG_FILE "gen-config-GDK.h"
+
+#endif
Index: /cpp/frams/errmgr/errmanager.cpp
===================================================================
--- /cpp/frams/errmgr/errmanager.cpp	(revision 109)
+++ /cpp/frams/errmgr/errmanager.cpp	(revision 109)
@@ -0,0 +1,110 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "errmanager.h"
+
+
+void FramMessage(const char *o,const char *m,const char *bl,int w)
+{
+tlsGetRef(errmgr_instance).send(o,m,bl,w);
+}
+
+THREAD_LOCAL_DEF(ErrorManager,errmgr_instance);
+
+void ErrorManager::send(int level,const char *o,const char *m,const char *bl,int w)
+{
+if (level>=handlers.size()) level=handlers.size()-1;
+bool blocked=0;
+for(int i=level;i>=0;i--)
+	{
+	ErrorHandlerBase *r=handlers(i);
+	if ((!blocked)||(r->options & ErrorHandlerBase::CannotBeBlocked))
+		r->handle(o,m,bl,w);
+	if (!(r->options & ErrorHandlerBase::DontBlock)) blocked=1;
+	}
+}
+
+int ErrorManager::add(ErrorHandlerBase *h)
+{
+h->mgr=this;
+handlers+=h;
+return handlers.size()-1;
+}
+
+void ErrorManager::remove(int i)
+{
+ErrorHandlerBase *h=handlers(i);
+h->mgr=0;
+handlers.remove(i);
+}
+
+void ErrorManager::remove(ErrorHandlerBase *h)
+{
+int i;
+if ((i=handlers.find(h))<0) return;
+remove(i);
+}
+
+void ErrorManager::removeAll()
+{
+while(handlers.size()>0)
+	remove(handlers.size()-1);
+}
+
+//////////////////////////////////
+
+void ErrorHandlerBase::send(const char *o,const char *m,const char *bl,int w)
+{
+if (!isEnabled()) return;
+int level=mgr->find(this);
+if (level>=0) mgr->send(level-1,o,m,bl,w);
+}
+
+void ErrorHandlerBase::FMprintf(const char *o,const char *m,int w,const char *bl, ...)
+{
+if (!isEnabled()) return;
+char buf[10000];
+va_list argptr;
+va_start(argptr,bl);
+vsnprintf(buf,10000,bl,argptr);
+va_end(argptr);
+send(o,m,buf,w);
+}
+
+
+void ErrorHandlerBase::enable()
+{
+if (isEnabled()) return;
+tlsGetRef(errmgr_instance).add(this);
+}
+
+void ErrorHandlerBase::disable()
+{
+if (!isEnabled()) return;
+tlsGetRef(errmgr_instance).remove(this);
+}
+
+/////////////////////////////////
+
+void ErrorHandler::handle(const char *o,const char *m,const char *bl,int w)
+{
+if (w>maxlevel) maxlevel=w;
+if (w>=FMLV_INFO) infocount++;
+if (w>=FMLV_WARN) warncount++;
+if (w>=FMLV_ERROR) errcount++;
+
+if (w>=storlevel)
+	{
+	storcount++;
+	if (options & (StoreFirstMessage|StoreAllMessages))
+		{
+		if (!((options&StoreFirstMessage)&&(msgs.len()>0)))
+			{
+			if (msgs.len()>0) msgs+='\n';
+			msgs+=o; msgs+="::"; msgs+=m;
+			msgs+=" - "; msgs+=bl;
+			}
+		}
+	}
+}
Index: /cpp/frams/errmgr/errmanager.h
===================================================================
--- /cpp/frams/errmgr/errmanager.h	(revision 109)
+++ /cpp/frams/errmgr/errmanager.h	(revision 109)
@@ -0,0 +1,104 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _ERRMANAGER_H_
+#define _ERRMANAGER_H_
+
+#include <frams/util/list.h>
+#include <frams/util/sstring.h>
+#include <common/framsg.h>
+#include <common/threads.h>
+
+class ErrorHandlerBase;
+
+class ErrorManager
+{
+friend class ErrorHandlerBase;
+SListTempl<ErrorHandlerBase*> handlers;
+void send(int level,const char *o,const char *m,const char *bl,int w);
+  public:
+int find(ErrorHandlerBase *r) {return handlers.find(r);}
+int add(ErrorHandlerBase *r);
+void remove(int i);
+void remove(ErrorHandlerBase *r);
+void removeAll();
+void send(const char *o,const char *m,const char *bl,int w)
+	{send(handlers.size()-1,o,m,bl,w);}
+~ErrorManager() {removeAll();}
+};
+
+extern THREAD_LOCAL_DECL(ErrorManager,errmgr_instance);
+
+////////////////////////////////////////
+
+class ErrorHandlerBase
+{
+friend class ErrorManager;
+  protected:
+ErrorManager* mgr;
+int options;
+
+  public:
+
+enum HandlerOptions
+ { DontBlock=1, CannotBeBlocked=2, DontEnable=4 };
+
+void FMprintf(const char *o,const char *m,int w,const char *bl, ...);
+void send(const char *o,const char *m,const char *bl,int w);
+
+bool isEnabled() {return mgr?1:0;}
+void enable();
+void disable();
+
+ErrorHandlerBase(int opts=0):mgr(0),options(opts)
+	{if (!(options&DontEnable)) enable();}
+virtual ~ErrorHandlerBase()
+	{disable();}
+
+virtual void handle(const char *o,const char *m,const char *bl,int w) {}
+};
+
+///////////////////////////////////////////
+
+class ErrorHandler: public ErrorHandlerBase
+{
+  protected:
+int maxlevel,errcount,warncount,storlevel,storcount,infocount;
+SString msgs;
+
+  public:
+
+void reset() {maxlevel=FMLV_INFO-1; errcount=warncount=storcount=infocount=0; msgs=0;}
+
+enum Options2
+ { StoreFirstMessage=8, StoreAllMessages=16 };
+
+int getErrorCount()       {return errcount;}
+int getWarningCount()     {return warncount;}
+int getInfoCount()        {return infocount;}
+int getStoredCount()      {return storcount;}
+int getErrorLevel()       {return maxlevel;}
+const SString& getMessages() {return msgs;}
+
+ErrorHandler(int opts=0,int store=FMLV_ERROR):ErrorHandlerBase(opts),storlevel(store)
+	{reset();}
+
+void handle(const char *o,const char *m,const char *bl,int w);
+};
+
+class ErrorRedirector: public ErrorHandlerBase
+{
+	ErrorManager *other_mgr;
+public:
+	ErrorRedirector(ErrorManager *om)
+		:ErrorHandlerBase(),other_mgr(om) {}
+
+	void handle(const char *o,const char *m,const char *bl,int w)
+		{
+		other_mgr->send(o,m,bl,w);
+		}
+};
+
+#endif
+
Index: /cpp/frams/errmgr/stderrors.cpp
===================================================================
--- /cpp/frams/errmgr/stderrors.cpp	(revision 109)
+++ /cpp/frams/errmgr/stderrors.cpp	(revision 109)
@@ -0,0 +1,34 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "stderrors.h"
+#include <frams/util/sstringutils.h>
+#include <common/framsg.h>
+
+bool listIndexCheck(SList* list,int index,const char* msgobj,const char* msgfun)
+{
+	int size=list->size();
+if ((index<0)||(index>=size))
+	{
+	if (size>0) 
+		FMprintf(msgobj,msgfun,FMLV_ERROR,"Invalid index %d (allowed range is 0..%d)",index,size-1);
+	else
+		FMprintf(msgobj,msgfun,FMLV_ERROR,"Invalid index %d (this list is empty)",index);
+	return 0;
+	}
+return 1;
+}
+
+SString stringCheck(SString& in,const char* msgobj,const char* msgfun,const char* msg,SString (*checker)(SString& in))
+{
+if (!checker)
+	checker=trim;
+SString corrected=checker(in);
+if (corrected!=in)
+	{
+	SString msg2=SString(msg)+": \"%s\" (adjusted to \"%s\")";
+	FMprintf(msgobj,msgfun,FMLV_WARN,(const char*)msg2,(const char*)in,(const char*)corrected);
+	}
+return corrected;
+}
Index: /cpp/frams/errmgr/stderrors.h
===================================================================
--- /cpp/frams/errmgr/stderrors.h	(revision 109)
+++ /cpp/frams/errmgr/stderrors.h	(revision 109)
@@ -0,0 +1,14 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _STDERRORS_H_
+#define _STDERRORS_H_
+
+#include <frams/util/list.h>
+#include <frams/util/sstring.h>
+
+bool listIndexCheck(SList* list,int index,const char* msgobj,const char* msgfun);
+SString stringCheck(SString& in,const char* msgobj,const char* msgfun,const char* msg,SString (*checker)(SString& in)=0);
+
+#endif
Index: /cpp/frams/errmgr/stdouterr.cpp
===================================================================
--- /cpp/frams/errmgr/stdouterr.cpp	(revision 109)
+++ /cpp/frams/errmgr/stdouterr.cpp	(revision 109)
@@ -0,0 +1,23 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "stdouterr.h"
+#ifdef SHP
+#include <FBaseSys.h> //AppLog
+#else
+#include <stdio.h>
+#endif
+
+void StdoutErrorHandler::handle(const char *o,const char *m,const char *bl,int w)
+{
+if (w<-1) w=-1; else if (w>3) w=3;
+#ifdef SHP
+AppLog("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
+#else
+if (file)
+	file->printf("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
+else
+	printf("[%s] %s::%s - %s\n",MSG_LEVEL[w+1],o,m,bl);
+#endif
+}
Index: /cpp/frams/errmgr/stdouterr.h
===================================================================
--- /cpp/frams/errmgr/stdouterr.h	(revision 109)
+++ /cpp/frams/errmgr/stdouterr.h	(revision 109)
@@ -0,0 +1,19 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _STDOUTERRORHANDLER_H_
+#define _STDOUTERRORHANDLER_H_
+
+#include "errmanager.h"
+#include <frams/virtfile/virtfile.h>
+
+class StdoutErrorHandler: public ErrorHandlerBase
+{
+VirtFILE *file;
+  public:
+StdoutErrorHandler(int opts=DontBlock,VirtFILE *_file=NULL):ErrorHandlerBase(opts),file(_file) {}
+void handle(const char *o,const char *m,const char *bl,int w);
+};
+
+#endif
Index: /cpp/frams/genetic/conv/conv_f1.cpp
===================================================================
--- /cpp/frams/genetic/conv/conv_f1.cpp	(revision 109)
+++ /cpp/frams/genetic/conv/conv_f1.cpp	(revision 109)
@@ -0,0 +1,489 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "conv_f1.h"
+#include <common/nonstd_stl.h>
+#include <common/framsg.h>
+#include <frams/util/multirange.h>
+#include <frams/util/multimap.h>
+#include <ctype.h>
+
+//#define v1f1COMPATIBLE
+
+F1Props stdprops={1, 0, 1, 0.4, 0.25, 0.25, 0.25, 0.25, 0.0, 1.0, 1.0, 1,
+		 0.2, 0.5,0.5,0.5 };
+
+class Builder
+{
+public:
+Builder(const char*g,int mapping=0):invalid(0),genbegin(g),usemapping(mapping),energ(0),energ_div(0),first_part_mapping(NULL) {}
+~Builder() {SAFEDELETE(first_part_mapping);}
+char tmp[222];
+bool invalid;
+Model model;
+const char *genbegin;
+SList neuro_f1_to_f0; // neuro_f1_to_f0(f1_refno) = actual neuro pointer
+Neuro *last_f1_neuro;
+SyntParam *neuro_cls_param;
+
+struct Connection { int n1,n2; double w;
+Connection(int _n1,int _n2, double _w):n1(_n1),n2(_n2),w(_w) {} };
+
+SListTempl<Connection> connections;
+int usemapping;
+MultiRange range;
+MultiRange *first_part_mapping;
+double lastjoint_muscle_power;
+double energ,energ_div;
+void grow(int part1,const char*g,Pt3D k,F1Props c);
+void setPartMapping(int p,const char* g);
+int growJoint(int part1,int part2,Pt3D &angle,F1Props &c,const char *g);
+int growPart(F1Props &c,const char *g);
+const char *skipNeuro(const char *z);
+const char* growNeuro(const char* t,F1Props &c,int&);
+void growConnection(const char* begin,const char* colon,const char* end,F1Props& props);
+int countBranches(const char*g,SList &out);
+SyntParam* lastNeuroClassParam();
+void addClassParam(const char* name,double value);
+void addClassParam(const char* name,const char* value);
+
+const MultiRange* makeRange(const char*g) {return makeRange(g,g);}
+const MultiRange* makeRange(const char*g,const char*g2);
+Part *getLastPart() {return getLastJoint()->part2;}
+Neuro *getLastNeuro() {return model.getNeuro(model.getNeuroCount()-1);}
+Joint *getLastJoint() {return model.getJoint(model.getJointCount()-1);}
+void addOrRememberInput(int n1,int n2,double w)
+		{
+		//if (!addInput(n1,n2,w,false))
+		connections+=Connection(n1,n2,w);
+		}
+bool addInput(int n1,int n2,double w,bool final)
+		{
+		if ((n1<0) || (n2<0) || (n1>=neuro_f1_to_f0.size()) || (n2>=neuro_f1_to_f0.size()))
+			{
+			if (final) FMprintf("GenoConvF1","addInput",FMLV_WARN,
+					    "illegal neuron connection %d <- %d (ignored)",n1,n2);
+			return 0;
+			}
+		Neuro *neuro=(Neuro*)neuro_f1_to_f0(n1);
+		Neuro *input=(Neuro*)neuro_f1_to_f0(n2);
+		neuro->addInput(input,w);
+		return 1;
+		}
+void addPendingInputs()
+		{
+		for(int i=0;i<connections.size();i++)
+			{
+			Connection *c=&connections(i);
+			addInput(c->n1,c->n2,c->w,true);
+			}
+		}
+};
+
+const MultiRange* Builder::makeRange(const char*g,const char*g2)
+{
+if (!usemapping) return 0;
+range.clear();
+range.add(g-genbegin,g2-genbegin);
+return &range;
+}
+
+void F1Props::wykluczanie()
+{
+double s=ruch+asym+odpor+wchl;
+ruch=ruch/s;
+asym=asym/s;
+odpor=odpor/s;
+wchl=wchl/s;
+}
+
+/** main conversion function - with conversion map support */
+SString GenoConv_F1::convert(SString &i,MultiMap *map)
+{
+const char* g=(const char*)i;
+Builder builder(g,map?1:0);
+builder.model.open();
+builder.grow(-1,g,Pt3D_0,stdprops); // uses Model::singleStepBuild to create model elements
+if (builder.invalid) return SString();
+builder.addPendingInputs();
+builder.model.startenergy=(builder.energ_div>0)?(builder.energ/builder.energ_div):1.0;
+builder.model.close(); // model is ready to use now
+if (map) builder.model.getCurrentToF0Map(*map); // generate f1-to-f0 conversion map
+return builder.model.getF0Geno().getGene();
+}
+
+void Builder::setPartMapping(int p,const char* g)
+{
+if (!usemapping) return;
+const MultiRange *r=makeRange(g);
+if (p<0)
+	{ //special case: mapping the part which is not yet created
+	if (first_part_mapping) first_part_mapping->add(*r);
+	else first_part_mapping=new MultiRange(*r);
+	}
+else
+	model.getPart(p)->addMapping(*r);
+}
+
+void Builder::grow(int part1,const char*g,Pt3D k,F1Props c)
+{
+int hasmuscles=0;
+k+=Pt3D(c.rot,0,c.skr);
+while(1)
+{
+switch(*g)
+	{
+	case 0: case ',': case ')': return;
+	case 'R': k.x+=0.7853; setPartMapping(part1,g); break;
+	case 'r': k.x-=0.7853;	setPartMapping(part1,g); break;
+	case 'Q': c.rot+=(1.58-c.rot)*0.3; setPartMapping(part1,g); break;
+	case 'q': c.rot+=(-1.58-c.rot)*0.3; setPartMapping(part1,g); break;
+#ifdef v1f1COMPATIBLE
+	case 'L': c.dlug+=(3.0-c.dlug)*0.3; setPartMapping(part1,g); break;
+#else
+	case 'L': c.dlug+=(2.0-c.dlug)*0.3; setPartMapping(part1,g); break;
+#endif                                       
+	case 'l': c.dlug+=(0.33-c.dlug)*0.3; setPartMapping(part1,g); break;
+	case 'A': c.asym+=(1-c.asym)*0.8;	c.wykluczanie(); setPartMapping(part1,g);  break;
+	case 'a': c.asym-=c.asym*0.4;	c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'I': c.wchl+=(1-c.wchl)*0.8;	c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'i': c.wchl-=c.wchl*0.4;	c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'S': c.odpor+=(1-c.odpor)*0.8; c.wykluczanie(); setPartMapping(part1,g); break;
+	case 's': c.odpor-=c.odpor*0.4; c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'M': c.ruch+=(1-c.ruch)*0.8;	c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'm': c.ruch-=c.ruch*0.4;	c.wykluczanie(); setPartMapping(part1,g); break;
+	case 'C': c.skr+=(2.0-c.skr)*0.25;	setPartMapping(part1,g); break;
+	case 'c': c.skr+=(-2.0-c.skr)*0.25; setPartMapping(part1,g); break;
+	case 'F': c.tarcie+=(4-c.tarcie)*0.2; setPartMapping(part1,g); break;
+	case 'f': c.tarcie-=c.tarcie*0.2; setPartMapping(part1,g); break;
+	case 'W': c.masa+=(2.0-c.masa)*0.3; setPartMapping(part1,g); break;
+	case 'w': c.masa+=(0.5-c.masa)*0.3; setPartMapping(part1,g); break;
+	case 'E': c.energ+=(10.0-c.energ)*0.1; setPartMapping(part1,g); break;
+	case 'e': c.energ-=c.energ*0.1;	setPartMapping(part1,g); break;
+
+	case 'D': c.cred+=(1.0-c.cred)*0.25; setPartMapping(part1,g); break;
+	case 'd': c.cred+=(0.0-c.cred)*0.25; setPartMapping(part1,g); break;
+	case 'G': c.cgreen+=(1.0-c.cgreen)*0.25; setPartMapping(part1,g); break;
+	case 'g': c.cgreen+=(0.0-c.cgreen)*0.25; setPartMapping(part1,g); break;
+	case 'B': c.cblue+=(1.0-c.cblue)*0.25; setPartMapping(part1,g); break;
+	case 'b': c.cblue+=(0.0-c.cblue)*0.25; setPartMapping(part1,g); break;
+	case 'H': c.grub+=(0.7-c.grub)*0.25; setPartMapping(part1,g); break;
+	case 'h': c.grub+=(0.05-c.grub)*0.25; setPartMapping(part1,g); break;
+
+	case '[': //neuron
+//		setdebug(g-(char*)geny,DEBUGNEURO | !l_neu);
+		if (model.getJointCount())
+			g=growNeuro(g+1,c,hasmuscles);
+		else
+			{
+			FramMessage("GenoConv_F1","grow","Illegal neuron position (ignored)",1);
+			g=skipNeuro(g+1);
+			}
+		break;
+	case 'X':
+		{
+		int freshpart=0;
+//setdebug(g-(char*)geny,DEBUGEST | !l_est);
+		if (part1<0) //initial grow
+			{
+		  if (model.getPartCount()>0)
+			    part1=0;
+		    else
+			    {
+			    part1=growPart(c,g);
+			    freshpart=1;
+			    if (first_part_mapping)
+				    model.getPart(part1)->setMapping(*first_part_mapping);
+			    }
+		  }
+		if (!freshpart)
+			{
+			Part *part=model.getPart(part1);
+			part->density=((part->mass*part->density)+1.0/c.masa)/(part->mass+1.0); // v=m*d
+//			part->volume+=1.0/c.masa;
+			part->mass+=1.0;
+			}
+		energ+=0.9*c.energ+0.1;
+		energ_div+=1.0;
+
+		int part2 = growPart(c,g);
+		growJoint(part1,part2,k,c,g);
+//		est* e = new est(*s,*s2,k,c,zz,this);
+
+		// oslabianie cech wzdluz struktury
+		c.dlug=0.5*c.dlug+0.5*stdprops.dlug;
+		c.grub=0.5*c.grub+0.5*stdprops.grub;
+		c.skr=0.66*c.skr;
+		c.rot=0.66*c.rot;
+		c.tarcie=0.8*c.tarcie+0.2*stdprops.tarcie;
+
+		c.asym=0.8*c.asym+0.2*stdprops.asym;
+		c.odpor=0.8*c.odpor+0.2*stdprops.odpor;
+		c.ruch=0.8*c.ruch+0.2*stdprops.ruch;
+		c.wchl=0.8*c.wchl+0.2*stdprops.wchl;
+		c.masa+=(stdprops.masa-c.masa)*0.5;
+		c.wykluczanie();
+	
+		if (c.resetrange) c.bendrange=1.0; else c.resetrange=1;
+		grow(part2,g+1,Pt3D_0,c);
+		return;
+		}
+	case '(':
+		{
+		setPartMapping(part1,g);
+		SList ga;
+		int i,ile;
+		ile=countBranches(g+1,ga);
+		c.resetrange=0;
+		c.bendrange=1.0/ile;
+		for (i=0;i<ile;i++)
+			grow(part1,(char*)ga(i),k+Pt3D(0,0,-3.141+(i+1)*(6.282/(ile+1))),c);
+		return;
+		}
+	case ' ': case '\t': case '\n': case '\r': break;
+	default: invalid=1; return;
+	}
+	g++;
+	}
+}
+
+SyntParam* Builder::lastNeuroClassParam()
+{
+if (!neuro_cls_param)
+	{
+	NeuroClass *cls=last_f1_neuro->getClass();
+	if (cls)
+		{
+		neuro_cls_param=new SyntParam(last_f1_neuro->classProperties());
+// this is equivalent to:
+//		SyntParam tmp=last_f1_neuro->classProperties();
+//		neuro_cls_param=new SyntParam(tmp);
+// interestingly, some compilers eliminate the call to new SyntParam,
+// realizing that a copy constructor is redundant when the original object is
+// temporary. there are no side effect of such optimization, as long as the
+// copy-constructed object is exact equivalent of the original.
+		}
+	}
+return neuro_cls_param;
+}
+
+void Builder::addClassParam(const char* name,double value)
+{
+lastNeuroClassParam();
+if (neuro_cls_param)
+	neuro_cls_param->setDoubleById(name,value);
+}
+
+void Builder::addClassParam(const char* name,const char* value)
+{
+lastNeuroClassParam();
+if (neuro_cls_param)
+	{
+	ExtValue e(value);
+	const ExtValue &re(e);
+	neuro_cls_param->setById(name,re);
+	}
+}
+
+int Builder::countBranches(const char*g,SList &out)
+{
+int gl=0;
+out+=(void*)g;
+while (gl>=0)
+	{
+	switch(*g)
+		{
+		case 0: gl=-1; break;
+		case '(': case '[': ++gl; break;
+		case ')': case ']': --gl; break;
+		case ',': if (!gl) out+=(void*)(g+1);
+		}
+	g++;
+	}
+return !out;
+}
+
+int Builder::growJoint(int part1,int part2,Pt3D &angle,F1Props &c,const char *g)
+{
+double len=min(2.0,c.dlug);
+sprintf(tmp,"j:p1=%ld,p2=%ld,dx=%lg,rx=%lg,ry=%lg,rz=%lg,stam=%lg,vr=%g,vg=%g,vb=%g",
+	part1,part2,len,angle.x,angle.y,angle.z,c.odpor, c.cred,c.cgreen,c.cblue);
+lastjoint_muscle_power=c.ruch;
+return model.singleStepBuild(tmp,makeRange(g));
+}
+
+int Builder::growPart(F1Props &c,const char *g)
+{
+sprintf(tmp,"p:m=1,dn=%lg,fr=%lg,ing=%lg,as=%lg,vs=%g,vr=%g,vg=%g,vb=%g",
+	1.0/c.masa,c.tarcie,c.wchl,c.asym, c.grub, c.cred,c.cgreen,c.cblue);
+return model.singleStepBuild(tmp,makeRange(g));
+}
+
+const char *Builder::skipNeuro(const char *z)
+{
+for (;*z;z++) if ((*z==']')||(*z==')')) break;
+return z-1;
+}
+
+const char* Builder::growNeuro(const char* t, F1Props& props,int &hasmuscles)
+{
+const char*neuroend=skipNeuro(t);
+last_f1_neuro=model.addNewNeuro();
+neuro_cls_param=NULL;
+last_f1_neuro->attachToPart(getLastPart());
+const MultiRange *mr=makeRange(t-1,neuroend+1);
+if (mr) last_f1_neuro->addMapping(*mr);
+neuro_f1_to_f0+=last_f1_neuro;
+
+SString clsname;
+bool haveclass=0;
+while(*t && *t<=' ') t++;
+const char* next=(*t)?(t+1):t;
+while(*next && *next<=' ') next++;
+if (*t && *next!=',' && *next!=']') // old style muscles [|rest] or [@rest]
+switch(*t)
+	{
+	case '@': if (t[1]==':') break;
+		haveclass=1;
+//		if (!(hasmuscles&1))
+                {
+                  hasmuscles|=1;
+		  Neuro *muscle=model.addNewNeuro();
+                  sprintf(tmp,"@:p=%lg",lastjoint_muscle_power);
+		  muscle->addInput(last_f1_neuro);
+		  muscle->setDetails(tmp);
+		  muscle->attachToJoint(getLastJoint());
+		  if (usemapping) muscle->addMapping(*makeRange(t));
+                }
+		t++;
+		break;
+	case '|': if (t[1]==':') break;
+		haveclass=1;
+//		if (!(hasmuscles&2))
+                {
+                  hasmuscles|=2;
+		  Neuro *muscle=model.addNewNeuro();
+                  sprintf(tmp,"|:p=%lg,r=%lg",lastjoint_muscle_power,props.bendrange);
+		  muscle->addInput(last_f1_neuro);
+		  muscle->setDetails(tmp);
+		  muscle->attachToJoint(getLastJoint());
+		  if (usemapping) muscle->addMapping(*makeRange(t));
+                }
+		t++;
+		break;
+	}
+while(*t && *t<=' ') t++;
+bool finished=0;
+const char *begin=t;
+const char* colon=0;
+SString classparams;
+while(!finished)
+	{
+	switch (*t)
+		{
+		case ':': colon=t; break;
+		case 0: case ']': case ')': finished=1;
+			// NO break!
+		case ',':
+			if ( !haveclass && !colon && t>begin )
+				{
+				haveclass=1;
+				SString clsname(begin,t-begin);
+				clsname=trim(clsname);
+				last_f1_neuro->setClassName(clsname);
+				NeuroClass *cls=last_f1_neuro->getClass();
+				if (cls)
+					{
+					if (cls->getPreferredLocation()==2)
+						last_f1_neuro->attachToJoint(getLastJoint());
+					else if (cls->getPreferredLocation()==1)
+						last_f1_neuro->attachToPart(getLastPart());
+
+					lastNeuroClassParam();
+					//special handling: muscle properties (can be overwritten by subsequent property assignments)
+					if (!strcmp(cls->getName(),"|"))
+						{
+						neuro_cls_param->setDoubleById("p",lastjoint_muscle_power);
+						neuro_cls_param->setDoubleById("r",props.bendrange);
+						}
+					else if (!strcmp(cls->getName(),"@"))
+						{
+						neuro_cls_param->setDoubleById("p",lastjoint_muscle_power);
+						}
+					}
+				}
+			else if (colon && (colon>begin) && (t>colon))
+				growConnection(begin,colon,t,props);
+			if (t[0]!=',') t--;
+			begin=t+1; colon=0;
+			break;
+		}
+	t++;
+	}
+SAFEDELETE(neuro_cls_param);
+return t;
+}
+void Builder::growConnection(const char* begin, const char* colon,const char* end,F1Props& props)
+{
+while(*begin && *begin<=' ') begin++;
+int i;
+if (isdigit(begin[0]) || (begin[0]=='-'))
+	{
+	double weight=atof(colon+1);
+	int relative=atoi(begin);
+	int this_refno=neuro_f1_to_f0.size()-1;
+	addOrRememberInput(this_refno,this_refno+relative,weight);
+	}
+else if ((i=last_f1_neuro->extraProperties().findIdn(begin,colon-begin))>=0)
+	{
+	last_f1_neuro->extraProperties().set(i,colon+1);
+	}
+else if (isupper(begin[0]) || strchr("*|@",begin[0]))
+	{
+	SString clsname(begin,colon-begin);
+	trim(clsname);
+	Neuro *receptor=model.addNewNeuro();
+	receptor->setClassName(clsname);
+	NeuroClass *cls=receptor->getClass();
+	if (cls)
+		{
+		if (cls->getPreferredLocation()==2) receptor->attachToJoint(getLastJoint());
+		else if (cls->getPreferredLocation()==1) receptor->attachToPart(getLastPart());
+		}
+	last_f1_neuro->addInput(receptor,atof(colon+1));
+	if (usemapping) receptor->addMapping(*makeRange(begin,end-1));
+	}
+else if ((begin[0]=='>')&&(begin[1]))
+	{
+	Neuro *out=model.addNewNeuro();
+	out->addInput(last_f1_neuro,atof(colon+1));
+	out->setClassName(SString(begin+1,end-colon-1));
+	if (begin[1]=='@')
+		{
+		sprintf(tmp,"p=%lg",lastjoint_muscle_power);
+		out->setClassParams(tmp);
+		}
+	else if (begin[1]=='|')
+		{
+		sprintf(tmp,"p=%lg,r=%lg",lastjoint_muscle_power,props.bendrange);
+		out->setClassParams(tmp);
+		}
+	NeuroClass *cls=out->getClass();
+	if (cls)
+		{
+		if (cls->getPreferredLocation()==2) out->attachToJoint(getLastJoint());
+		else if (cls->getPreferredLocation()==1) out->attachToPart(getLastPart());
+		}
+	if (usemapping) out->addMapping(*makeRange(begin,end-1));
+	}
+else if (*begin=='!') addClassParam("fo",atof(colon+1));
+else if (*begin=='=') addClassParam("in",atof(colon+1));
+else if (*begin=='/') addClassParam("si",atof(colon+1));
+else if (islower(begin[0]))
+	{
+	SString name(begin,colon-begin);
+	SString value(colon+1,end-(colon+1));
+	addClassParam(name,value);
+	}
+}
Index: /cpp/frams/genetic/conv/conv_f1.h
===================================================================
--- /cpp/frams/genetic/conv/conv_f1.h	(revision 109)
+++ /cpp/frams/genetic/conv/conv_f1.h	(revision 109)
@@ -0,0 +1,81 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _CONV_F1_H
+#define _CONV_F1_H
+
+#include <frams/genetic/genoconv.h>
+#include <frams/model/model.h>
+
+struct F1Props
+{
+	double dlug,skr,masa,tarcie,ruch,asym,odpor,wchl,rot,energ;
+	double bendrange;
+	int resetrange;
+	double grub,cred,cgreen,cblue;
+	void wykluczanie();
+};
+
+/**
+ Official framsticks f1 -> f0 converter.
+ Source code is published as educational example, but currently not
+ well documented.
+ This converter features building creature using Model class
+ and some advanced tricks (changing model data during construction).
+ Final f0 genotype is generated by model class.
+
+ <b>GDK 1.0.5 NN extension: (for Framsticks 2.0)</B>
+
+ Until now, only the "standard" Framsticks v1 neuron classes could be used.
+ Starting with GDK 1.0.5 you can use all neuron types.
+
+ \b Syntax: [classname , input_or_parameter:weight , input_or_parameter:weight , ...]
+
+ The classname is optional, the default class will be used if it is omitted.
+ Note the comma after \b classname. It must be present if you provide the classname.
+ This is to distinguish between old muscle syntax and new classname specification:
+ - "[|,0:1]" is the neuron of class "|" (bend muscle) with one self connection.
+ \image html nn-ex1.gif
+ - "[|0:1]" describes the default neuron class (N) connected with the bend muscle (|).
+ \image html nn-ex2.gif
+
+
+ Neuron parameters can be specified along with inputs, eg: "[myclass,param1:34,param2:56]"
+ The names beginning with UPPER case character are assumed to be neuron classes.
+ "[myclass,g:1.23,G:2.34]" - the first 'g' is the parameter, the second one is the receptor
+ G ("gyroscope").
+
+ Another example:
+
+ "X[G][-1:2.3][-2:3.4]" - The first neuron is the standalone G receptor. Other neuron can use its output 
+ signal by specifying it as regular input ("-1:2.3" and "-2:3.4"). This NN contains 3 neurons.
+ \image html nn-ex3.gif
+
+ "X[G:2.3][G:3.4]" - with the old syntax it is impossible to connect 2 neurons with the same gyroscope,
+ because everything inside brackets is private to the neuron, others can't refer to it.
+ Adding another neuron with "G" input will add another gyroscope object. This NN contains 4 neurons
+ (or 2 neurons if you try it in Framsticks v1).
+  \image html nn-ex4.gif
+ 
+*/
+class GenoConv_F1: public GenoConverter
+{
+public:
+GenoConv_F1()
+	{
+	name="Recursive encoding";
+	in_format='1';
+	mapsupport=1;
+	}
+SString convert(SString &i,MultiMap *map);
+~GenoConv_F1() {}
+};
+
+
+#endif
+
+
+
+
+
Index: /cpp/frams/genetic/defgenoconv.cpp
===================================================================
--- /cpp/frams/genetic/defgenoconv.cpp	(revision 109)
+++ /cpp/frams/genetic/defgenoconv.cpp	(revision 109)
@@ -0,0 +1,70 @@
+#include "defgenoconv.h"
+
+#include GEN_CONFIG_FILE
+
+#ifdef USE_GENCONV_F10
+#include "conv/conv_f1.h"
+#endif
+#ifdef USE_GENCONV_F20
+#include "conv/conv_f2.h"
+#endif
+#ifdef USE_GENCONV_F32
+#include "conv/conv_f3.h"
+#endif
+#ifdef USE_GENCONV_F40
+#include "conv/conv_f4.h"
+#endif
+#ifdef USE_GENCONV_F41_TEST
+#include "conv/conv_f4.h"
+#endif
+#ifdef USE_GENCONV_F50
+#include "conv/conv_f5.h"
+#endif
+#ifdef USE_GENCONV_F60
+#include "conv/geno_f6.h"
+#endif
+#ifdef USE_GENCONV_F70
+#include "conv/conv_f7.h"
+#endif
+#ifdef USE_GENCONV_F81
+#include "conv/conv_f8tof1.h"
+#endif
+#ifdef USE_GENCONV_F90
+#include "conv/conv_f9.h"
+#endif
+
+DefaultGenoConvManager::DefaultGenoConvManager()
+{
+#ifdef USE_GENCONV_F10
+addConverter(new GenoConv_F1());
+#endif
+#ifdef USE_GENCONV_F20
+addConverter(new GenoConv_F20());
+#endif
+#ifdef USE_GENCONV_F32
+addConverter(new GenoConv_F32());
+#endif
+#ifdef USE_GENCONV_F40
+addConverter(new GenoConv_F40());
+#endif
+#ifdef USE_GENCONV_F41_TEST
+addConverter(new GenoConv_F41_TestOnly());
+#endif
+#ifdef USE_GENCONV_F50
+addConverter(new GenoConv_F50);
+#endif
+#ifdef USE_GENCONV_F60
+addConverter(new GenoConv_F60);
+#endif
+#ifdef USE_GENCONV_F70
+addConverter(new GenoConv_F70);
+#endif
+#ifdef USE_GENCONV_F81
+addConverter(new GenoConv_F8ToF1());
+#endif
+#ifdef USE_GENCONV_F90
+addConverter(new GenoConv_F90);
+#endif
+
+param.updatetab();
+}
Index: /cpp/frams/genetic/defgenoconv.h
===================================================================
--- /cpp/frams/genetic/defgenoconv.h	(revision 109)
+++ /cpp/frams/genetic/defgenoconv.h	(revision 109)
@@ -0,0 +1,19 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _DEFGENOCONV_H_
+#define _DEFGENOCONV_H_
+
+#include "genoconv.h"
+
+/// This GenoConvManager automatically registers the "standard" converters available in the GDK.
+/// All you have to do is create a single instance of this class somewhere in your application.
+/// @see genotest.cpp
+class DefaultGenoConvManager: public GenoConvManager
+{
+  public:
+DefaultGenoConvManager();
+};
+
+#endif
Index: /cpp/frams/genetic/gen-config-GDK.h
===================================================================
--- /cpp/frams/genetic/gen-config-GDK.h	(revision 109)
+++ /cpp/frams/genetic/gen-config-GDK.h	(revision 109)
@@ -0,0 +1,24 @@
+// This file is a part of the Framsticks GDK library. See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+/**
+ gen-config.h
+
+ USE_GENCONV_xxx - configure converters
+ USE_GENMAN_xxx - configure genman
+
+ */
+
+#define USE_GENCONV_F10
+//#define USE_GENCONV_F21
+//#define USE_GENCONV_F30
+//#define USE_GENCONV_F40
+//#define USE_GENCONV_F41_TEST
+//#define USE_GENCONV_F50
+
+//#define USE_GENMAN_F0
+//#define USE_GENMAN_F1
+//#define USE_GENMAN_F2
+//#define USE_GENMAN_F3
+//#define USE_GENMAN_F4
+//#define USE_GENMAN_F5
Index: /cpp/frams/genetic/geno.cpp
===================================================================
--- /cpp/frams/genetic/geno.cpp	(revision 109)
+++ /cpp/frams/genetic/geno.cpp	(revision 109)
@@ -0,0 +1,274 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "geno.h"
+#include <frams/model/model.h>
+
+SListTempl<GenoValidator*> Geno::validators;
+
+void Geno::init(const SString& genstring,char genformat,const SString& genname,const SString& comment)
+{
+refcount=1;
+owner=0;
+f0gen=0;
+mapinshift=0;
+mapoutshift=0;
+isvalid=-1;
+SString gencopy(genstring);
+if (genformat==-1)
+	{ // unknown format
+	genformat='1';
+	if (genstring.charAt(0)=='/')
+		{
+		int end;
+		SString newcomment;
+		switch(genstring.charAt(1))
+			{
+			case '/':
+				genformat=genstring.charAt(2);
+				if ((end=genstring.indexOf('\n'))>=0)
+					{
+					newcomment=genstring.substr(2,end-2);
+					gencopy=genstring.substr(end+1);
+					mapinshift=end+1;
+					}
+				else
+					{
+					gencopy=0;
+					mapinshift=genstring.len();
+					}
+				break;
+			case '*':
+				genformat=genstring.charAt(2);
+				if ((end=genstring.indexOf("*/"))>=0)
+					{
+					newcomment=genstring.substr(2,end-2);
+					gencopy=genstring.substr(end+2);
+					mapinshift=end+2;
+					}
+				else
+					{
+					gencopy=0;
+					mapinshift=genstring.len();
+					}
+				break;
+			}
+		if (newcomment.len()>0)
+			{
+			SString token; int pos=0;
+			if (newcomment.getNextToken(pos,token,';'))
+			if (newcomment.getNextToken(pos,token,';'))
+				{
+				if (token.len()) txt=token;
+			if (newcomment.getNextToken(pos,token,';'))
+				if (token.len()) name=token;
+				}
+			}
+		}
+	}
+
+gen=gencopy;
+format=genformat;
+if (!name.len()) name=genname;
+if (!txt.len()) txt=comment;
+multiline=(strchr((const char*)gen,'\n')!=0);
+// mapoutshift...?
+}
+
+void Geno::freeF0()
+{
+if (f0gen) {delete f0gen; f0gen=0;}
+}
+
+Geno::Geno(const char *genstring,char genformat,const char *genname,const char *comment)
+{
+init(SString(genstring),genformat,SString(genname),SString(comment));
+}
+
+Geno::Geno(const SString& genstring,char genformat,const SString& genname,const SString& comment)
+{
+init(genstring,genformat,genname,comment);
+}
+
+Geno::Geno(const Geno& src)
+	:gen(src.gen),name(src.name),format(src.format),txt(src.txt),isvalid(src.isvalid),
+	 f0gen(0),mapinshift(src.mapinshift),mapoutshift(src.mapinshift),
+	 multiline(src.multiline),owner(0)
+{f0gen=src.f0gen?new Geno(*src.f0gen):0; refcount=1;}
+
+void Geno::operator=(const Geno& src)
+{
+freeF0();
+gen=src.gen;
+name=src.name;
+format=src.format;
+txt=src.txt;
+isvalid=src.isvalid;
+mapinshift=src.mapinshift;
+mapoutshift=src.mapinshift;
+multiline=src.multiline;
+f0gen=src.f0gen?new Geno(*src.f0gen):0;
+owner=0;
+}
+
+Geno::Geno(const SString& src)
+{
+init(src,-1,SString::empty(),SString::empty());
+}
+
+void Geno::setGene(const SString& g,char newformat)
+{
+gen=g;
+isvalid=-1;
+freeF0();
+if (newformat>=0) format=newformat;
+}
+
+void Geno::setString(const SString& g)
+{
+freeF0();
+init(g,-1,SString::empty(),SString::empty());
+}
+
+void Geno::setName(const SString& n)
+{
+name=n;
+}
+
+void Geno::setComment(const SString& c)
+{
+txt=c;
+}
+
+SString Geno::toString(void) const
+{
+SString out;
+int comment=0;
+if ((format!='1')||(comment=(txt.len()||name.len())))
+	{
+	if (multiline)
+		out+="//";
+	else
+		out+="/*";
+	out+=format;
+	if (comment)
+		{
+		if (txt.len()) {out+=";";out+=txt;}
+		if (name.len()){out+=";";out+=name;}
+		}
+	if (multiline)
+		out+="\n";
+	else
+		out+="*/";
+	}
+out+=gen;
+return out;
+}
+
+SString Geno::shortString(void) const
+{
+SString out;
+if (format!='1')
+	{
+	if (multiline)
+		out+="//";
+	else
+		out+="/*";
+	if (format==0)
+		out+="invalid";
+	else
+		out+=format;
+	if (multiline)
+		out+="\n";
+	else
+		out+="*/";
+	}
+out+=gen;
+return out;
+}
+
+int Geno::mapGenToString(int genpos) const
+{
+if (genpos>gen.len()) return -2;
+if (genpos<0) return -1;
+return mapinshift+genpos;
+}
+
+int Geno::mapStringToGen(int stringpos) const
+{
+stringpos-=mapinshift;
+if (stringpos>gen.len()) return -2;
+if (stringpos<0) return -1;
+return stringpos;
+}
+
+SString Geno::getGene(void) const {return gen;}
+SString Geno::getName(void) const {return name;}
+char Geno::getFormat(void) const {return format;}
+SString Geno::getComment(void) const {return txt;}
+
+class ModelGenoValidator: public GenoValidator
+{
+public:
+	ModelGenoValidator();
+	int testGenoValidity(Geno& g);
+};
+
+ModelGenoValidator::ModelGenoValidator()
+{
+Geno::validators+=this;
+}
+
+int ModelGenoValidator::testGenoValidity(Geno& g)
+{
+if (g.getFormat()=='0')
+	{
+	Model mod(g);
+	return mod.isValid();
+	}
+else
+	{
+	Geno f0geno=g.getConverted('0');
+	return f0geno.isValid();
+	}
+}
+
+static ModelGenoValidator default_validator;
+
+void Geno::validate()
+{
+if (isvalid>=0) return;
+if (gen.len()==0) { isvalid=0; return; }
+FOREACH(GenoValidator*,v,validators)
+	if ((isvalid=v->testGenoValidity(*this))>=0)
+		break;
+}
+
+bool Geno::isValid(void)
+{
+if (isvalid<0) validate();
+return isvalid>0;
+}
+
+Geno Geno::getConverted(char otherformat,MultiMap *m)
+{
+if (otherformat==getFormat()) return *this;
+#ifndef NO_GENOCONVMANAGER
+if ((otherformat=='0')&&(!m))
+	{
+	if (!f0gen)
+		f0gen=new Geno(GenoConvManager::globalConvert(*this,otherformat));
+	return *f0gen;
+	}
+else
+	return GenoConvManager::globalConvert(*this,otherformat,m);
+#else
+return (otherformat==getFormat())?*this:Geno(0,0,0,"GenConvManager not available");
+#endif
+}
+
+Geno::~Geno()
+{
+if (f0gen) delete f0gen;
+}
Index: /cpp/frams/genetic/geno.h
===================================================================
--- /cpp/frams/genetic/geno.h	(revision 109)
+++ /cpp/frams/genetic/geno.h	(revision 109)
@@ -0,0 +1,112 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _GENO_H_
+#define _GENO_H_
+
+#include <frams/util/sstring.h>
+#include <frams/util/extvalue.h>
+
+class MultiMap;
+class Geno;
+
+class GenoValidator
+{
+  public:
+virtual int testGenoValidity(Geno& g)=0;/// -1=no information  0=invalid  1=valid
+};
+
+/// basic information about a single genotype.
+class Geno: public DestrBase
+{
+SString gen;
+SString name;
+char format;
+SString txt;
+int isvalid; ///< <0 -> unknown   >=0 -> value for "isValid"
+
+Geno *f0gen;
+
+int mapinshift; /// # of characters in the initial comment
+int mapoutshift; /// # of characters in the output comment
+int multiline;
+
+void init(const SString& genstring,char genformat,const SString& genname,const SString& comment);
+void validate(void);
+
+void freeF0();
+
+bool isInvalid() {return isvalid==0;}
+
+friend class Model;
+friend class GenoConvManager;
+	
+public:
+/// create a genotype object from primitives
+/// @param genstring pure genotype, without any comments
+/// @param genformat genotype format
+/// @param comment information about genotype (for genetic operators and "history")
+Geno(const char *genstring=0,char genformat=-1,const char *genname=0,const char *comment=0);
+
+/// create a genotype object from primitives
+/// @param genstring pure genotype, wihtout any comments
+/// @param genformat genotype format
+/// @param name genotype name, new name will generated if needed
+/// @param comment information about genotype (for genetic operators and "history")
+Geno(const SString& genstring,char genformat,const SString& genname,const SString& comment);
+
+/// create object from full string, containing optional format and comment information
+Geno(const SString & fullstring);
+
+/// clone
+Geno(const Geno& src);
+
+void operator=(const Geno& src);
+
+~Geno();
+
+void setValid(int v) {isvalid=v;}
+int getValid() {return isvalid;}
+
+/// return string representation, with format comment at the beginning
+SString toString(void) const;
+SString shortString(void) const;
+
+void setString(const SString& genewithcomments);
+
+/** @param newformat=-1 -> don't change */
+void setGene(const SString& g, char newformat=-1);
+SString getGene(void) const;
+
+SString getName(void) const;
+void setName(const SString&);
+char getFormat(void) const;
+
+SString getComment(void) const;
+void setComment(const SString&);
+
+/// invalid genotype cannot be used to build a creature
+bool isValid(void);
+
+/// make converted version of the genotype.
+Geno getConverted(char otherformat,MultiMap *m=0);
+
+/// @return -1 = before first char in the string
+/// @return -2 = after last char in the string
+int mapGenToString(int genpos) const;
+/// @return -1 = before first char in the genotype
+/// @return -2 = after last char in the genotype
+int mapStringToGen(int stringpos) const;
+
+int operator==(const Geno &g) {return (format==g.format)&&(gen==g.gen);}
+
+void* owner;
+static SListTempl<GenoValidator*> validators;
+};
+
+#ifndef NO_GENOCONVMANAGER
+#include "genoconv.h"
+#endif
+
+#endif
Index: /cpp/frams/genetic/genoconv.cpp
===================================================================
--- /cpp/frams/genetic/genoconv.cpp	(revision 109)
+++ /cpp/frams/genetic/genoconv.cpp	(revision 109)
@@ -0,0 +1,227 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <common/nonstd.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+
+#include "genoconv.h"
+#include <frams/util/multimap.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+GenoConvParam::GenoConvParam(GenoConvManager *g):Param(0),gcm(g)
+{
+updatetab();
+}
+
+void GenoConvParam::freetab()
+{
+if (tab) free(tab);
+tab=0;
+}
+
+const char *GenoConvParam::id(int i)
+{
+if (i>=gcm->converters.size()) return 0;
+sprintf(tmp_id,"genkonw%d",i);
+return tmp_id;
+}
+
+void GenoConvParam::updatetab()
+{
+int i;
+GenoConverter *gk;
+ParamEntry *pe;
+int ile=gcm->converters.size();
+freetab();
+tab=(ParamEntry*)calloc(2+ile,sizeof(ParamEntry));
+tab[0].id="Genetics: Conversions";
+tab[0].group=1;
+tab[0].flags=(short)ile;
+tab[0].name="gkparam:";
+gcnames.clear();
+for (i=0,pe=tab+1;gk=(GenoConverter *)gcm->converters(i);pe++,i++)
+	{
+	pe->id="?";
+	pe->group=0;
+	pe->flags=0;
+	std::string descr="f";
+	descr+=gk->in_format;
+	descr+=" -> f";
+	descr+=gk->out_format;
+	descr+=" : ";
+	descr+=gk->name;
+	gcnames.push_back(descr);
+	pe->name=descr.c_str();
+	pe->type="d 0 1";
+	pe->help=gk->info;
+	}
+pe->id=0;
+}
+
+GenoConvParam::~GenoConvParam()
+{
+freetab();
+}
+
+void *GenoConvParam::getTarget(int i)
+{
+GenoConverter *gk=(GenoConverter *)gcm->converters(i);
+return &gk->enabled;
+}
+
+GenoConvManager::GenoConvManager()
+	:param(this)
+{
+if (!globalobject) globalobject=this;
+}
+
+GenoConvManager::~GenoConvManager()
+{
+GenoConverter *gc;
+for (converters.start();gc=(GenoConverter*)converters();) delete gc;
+if (globalobject==this) globalobject=NULL;
+}
+
+GenoConvManager *GenoConvManager::globalobject=NULL;
+
+void GenoConvManager::addConverter(GenoConverter *gc)
+{
+converters+=gc;
+param.updatetab();
+}
+void GenoConvManager::removeConverter(GenoConverter *gc)
+{
+converters-=gc;
+param.updatetab();
+}
+
+GenoConverter *GenoConvManager::findConverters(SListTempl<GenoConverter*>* result,char in,char out,int enabled,char* name)
+{
+GenoConverter *gk,*retval=0;
+int i=0;
+for (;gk=(GenoConverter*)converters(i);i++)
+	{
+	if ((in!=-1)&&(in!=gk->in_format)) continue;
+	if ((out!=-1)&&(out!=gk->out_format)) continue;
+	if ((enabled!=-1)&&(enabled!=gk->enabled)) continue;
+	if ((name)&&(strcmp(name,gk->name))) continue;
+	if (!retval) {retval=gk; if (!result) break;}
+	if (result) result->append(gk);
+	}
+return retval;
+}
+
+/// write path into 'path'
+/// return the last path element (return >= path)
+/// null -> path not found
+/// @param mapavailable will receive 1 if conversion map is supported by all converters in path
+/// (can be NULL if you don't need this information)
+
+char *GenoConvManager::getPath(char in,char out,char *path,int maxlen,int *mapavailable)
+{
+if (!maxlen) return 0;
+GenoConverter *gk;
+int i=0;
+for (;gk=(GenoConverter*)converters(i);i++)
+	{
+	if ((gk->enabled)&&(gk->in_format == in))
+		{
+		*path=(char)i;
+		if (gk->out_format == out)
+			{
+			if (mapavailable)
+				*mapavailable=gk->mapsupport;
+			return path;
+			}
+		else
+			{
+			int mapavail;
+			char *ret=getPath(gk->out_format,out,path+1,maxlen-1,&mapavail);
+			if (ret)
+				{
+				if (mapavailable)
+					*mapavailable=gk->mapsupport && mapavail;
+				return ret;
+				}
+			}
+		}
+	}
+return 0;
+}
+
+char *GenoConvManager::getFormatPath(char in,char out,char *path,int maxlen,int *mapavailable)
+{
+char *ret=getPath(in,out,path,maxlen,mapavailable);
+if (ret)
+	{
+	for (char*t=path;t<=ret;t++)
+		*t=((GenoConverter*)converters(*t))->out_format;
+	}
+return ret;
+}
+
+Geno GenoConvManager::convert(Geno &in,char format,MultiMap *map)
+{
+if (in.getFormat()==format) return in;
+char path[10];
+int dep;
+char *ret;
+if (in.isInvalid()) { return Geno("",0,"","invalid genotype cannot be converted"); }
+int mapavail;
+for (dep=1;dep<(int)sizeof(path);dep++) // sogenannte iteracyjne poglebianie...
+	if (ret=getPath(in.getFormat(),format,path,dep,&mapavail)) break;
+if (!ret) { return Geno("",0,"","converter not found"); }
+if (!map) mapavail=0;
+char *t=path;
+SString tmp;
+tmp=in.getGene();
+MultiMap lastmap,tmpmap;
+int firstmap=1;
+for (;t<=ret;t++)
+	{
+	GenoConverter *gk=(GenoConverter*)converters(*t);
+	tmp=gk->convert(tmp,mapavail?&tmpmap:0);
+	if (!tmp.len())
+		{
+		char t[100];
+		sprintf(t,"f%c->f%c conversion failed (%s)",gk->in_format,gk->out_format,gk->name);
+		return Geno(0,0,0,t);
+		}
+	if (mapavail)
+		{
+		if (firstmap)
+			{
+			lastmap=tmpmap;
+			firstmap=0;
+			}
+		else
+			{
+			MultiMap m;
+			m.addCombined(lastmap,tmpmap);
+			lastmap=m;
+			}
+		tmpmap.clear();
+		}
+	}
+if (map)
+	*map=lastmap;
+return Geno(tmp, format, in.getName(), in.getComment());
+}
+
+Geno GenoConvManager::globalConvert(Geno &in,char format,MultiMap *map)
+{
+if (globalobject) return globalobject->convert(in,format,map);
+if (format==in.getFormat()) return in;
+return Geno(0,0,0,"GenConvManager not initialized");
+}
+
+/////////////////////////////////
Index: /cpp/frams/genetic/genoconv.h
===================================================================
--- /cpp/frams/genetic/genoconv.h	(revision 109)
+++ /cpp/frams/genetic/genoconv.h	(revision 109)
@@ -0,0 +1,104 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _GENCONV_H_
+#define _GENCONV_H_
+
+#include "geno.h"
+#include <frams/param/param.h>
+#include <frams/util/list.h>
+#include <frams/util/sstring.h>
+
+#include <string>
+#include <vector>
+
+
+class GenoConvManager;
+
+class GenoConvParam: public Param
+{
+GenoConvManager *gcm;
+std::vector<std::string> gcnames;
+char tmp_id[20];
+void freetab();
+public:
+GenoConvParam(GenoConvManager *g);
+~GenoConvParam();
+void *getTarget(int);
+const char* id(int i);
+void updatetab();
+};
+
+class MultiMap;
+
+/// Base class for all Geno Converters.
+/// In constructor you have to set public fields
+/// indicating your identity and supported formats.
+/// Each converter serves one in-out format pair.
+/// Instance of your converter should be registered
+/// in GenoConvManager.
+class GenoConverter
+{
+public:
+const char *name;	//< converter name (short)
+char in_format,		//< input format, eg. '1'
+	out_format;	//< output format, eg. '0'
+const char *info;	//< detailed info about converter, format or copyright
+long enabled;	//< don't touch this! (used by configuration module)
+long mapsupport; //< set to 1 if your converter supports genotype mapping
+
+/// You have to reimplement this method.
+/// If your converter cannot do its job, return empty string
+/// (return SString();), any other return value is assumed
+/// to be output genotype.
+/// @param map if not null, mapping informaton is requested, converter should add conversion map to this object
+virtual SString convert(SString &i,MultiMap *map) {return SString();}
+
+virtual ~GenoConverter() {}
+/// Don't forget to set public fields in your constructor
+GenoConverter():name(""),in_format(-1),out_format('0'),info(""),enabled(1),mapsupport(0) {}
+};
+
+/// This class gathers abilities of all converters and can
+/// convert a genotype to any other one, provided there is
+/// a path of GenoConverters between them.
+/// In most cases you don't use this class directly,
+/// Geno::getConverted(int) provides full converting functionality.
+/// Explicit GenoConvManager object is only needed for registering
+/// your GenoConverter.
+/// Use DefaultGenoConvManager to register the standard genotype converters automatically.
+class GenoConvManager
+{
+friend class GenoConvParam;
+SList converters;
+static GenoConvManager *globalobject;
+public:
+GenoConvManager();
+~GenoConvManager();
+class GenoConvParam param;
+/// select an object for use as global GenoConvManager
+void useManager(GenoConvManager *m) {globalobject=m;}
+/// get global converter
+static GenoConvManager *getGlobalObject() {return globalobject;}
+/// make a genotype in other format. genotype will be invalid
+/// if GenoConvManager cannot convert it.
+Geno convert(Geno &in,char format,MultiMap *map=0);
+/// static conversion function (uses global GenoConvManager)
+static Geno globalConvert(Geno &in,char format,MultiMap *map=0);
+/// register GenoConverter
+void addConverter(GenoConverter *conv);
+/// unregister GenoConverter
+void removeConverter(GenoConverter *conv);
+
+char *getPath(char in,char out,char *path,int maxlen,int *mapavailable=0);
+char *getFormatPath(char in,char out,char *path,int maxlen,int *mapavailable=0);
+/// returns the list of converters meeting the specified criteria
+/// pass result=0 if you only need one result (by return value)
+/// default criteria values mean "don't care", pass anything else to narrow your search
+GenoConverter *findConverters(SListTempl<GenoConverter*>* result=0,char in=-1,char out=-1,int enabled=-1,char* name=0);
+};
+
+#endif
+
+
Index: /cpp/frams/genetic/oper/geno_fx.cpp
===================================================================
--- /cpp/frams/genetic/oper/geno_fx.cpp	(revision 109)
+++ /cpp/frams/genetic/oper/geno_fx.cpp	(revision 109)
@@ -0,0 +1,244 @@
+// This file is a part of the Framsticks GenoFX library.
+// Copyright (C) 2002-2011  Maciej Komosinski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <ctype.h>  //isupper()
+#include "geno_fx.h"
+#include <common/framsg.h>
+#include <common/nonstd_math.h>
+#include <frams/util/rndutil.h>
+
+static double distrib_force[]=   // for '!'
+{
+3,                 // distribution 0 -__/ +1
+0.001, 0.2,        // "slow" neurons
+0.001, 1,
+    1, 1,          // "fast" neurons
+};
+static double distrib_inertia[]=  // for '='
+{
+2,                 // distribution 0 |..- +1
+  0, 0,            // "fast" neurons
+0.7, 0.98,
+};
+static double distrib_sigmo[]=  // for '/'
+{
+5,                 // distribution -999 -..-^-..- +999
+-999, -999,        //"perceptron"
+ 999, 999,
+  -5, -1,          // nonlinear
+   1, 5,
+  -1, 1,           // ~linear
+};
+
+
+int Geno_fx::roulette(const double *probtab,const int count)
+{
+   double sum=0;
+   int i;
+   for (i=0;i<count;i++) sum+=probtab[i];
+   double sel=rnd01*sum;
+   for (sum=0,i=0;i<count;i++) {sum+=probtab[i]; if (sel<sum) return i;}
+   return -1;
+}
+
+bool Geno_fx::getMinMaxDef(ParamInterface *p,int i,double &mn,double &mx,double &def)
+{
+   mn=mx=def=0;
+   int defined=0;
+   if (p->type(i)[0]=='f')
+   {
+      double _mn=0,_mx=1,_def=0.5;
+      defined=p->getMinMax(i,_mn,_mx,_def);
+      if (defined==1) _mx=_mn+1.0;
+      if (_mx<_mn && defined==3) _mn=_mx=_def; //only default was defined, let's assume min=max=default
+      if (defined<3) _def=(_mn+_mx)/2.0;
+      mn=_mn; mx=_mx; def=_def;
+   }
+   if (p->type(i)[0]=='d')
+   {
+      long _mn=0,_mx=1,_def=0;
+      defined=p->getMinMax(i,_mn,_mx,_def);
+      if (defined==1) _mx=_mn+1;
+      if (_mx<_mn && defined==3) _mn=_mx=_def; //only default was defined, let's assume min=max=default
+      if (defined<3) _def=(_mn+_mx)/2;
+      mn=_mn; mx=_mx; def=_def;
+   }
+   return defined==3;
+}
+
+int Geno_fx::selectRandomProperty(Neuro* n)
+{
+   int neuext=n->extraProperties().getPropCount(),
+       neucls=n->getClass()==NULL?0:n->getClass()->getProperties().getPropCount();
+   if (neuext+neucls==0) return -1; //no properties in this neuron
+   int index=randomN(neuext+neucls);
+   if (index>=neuext) index=index-neuext+100;
+   return index;
+}
+
+double Geno_fx::mutateNeuProperty(double current,Neuro *n,int i)
+{
+   if (i==-1) return mutateCreepNoLimit('f',current,-10,10); //i==-1: mutating weight of neural connection
+   Param p;
+   if (i>=100) {i-=100; p=n->getClass()->getProperties();}
+      else p=n->extraProperties();
+   double newval=current;
+   /*bool ok=*/getMutatedProperty(p,i,current,newval);
+   return newval;
+}
+
+bool Geno_fx::mutatePropertyNaive(ParamInterface &p,int i)
+{
+   double mn,mx,df;
+   if (p.type(i)[0]!='f' && p.type(i)[0]!='d') return false; //don't know how to mutate
+   getMinMaxDef(&p,i,mn,mx,df);
+
+   ExtValue ev;
+   p.get(i,ev);
+   ev.setDouble(mutateCreep(p.type(i)[0],ev.getDouble(),mn,mx));
+   p.set(i,ev);
+   return true;
+}
+
+bool Geno_fx::mutateProperty(ParamInterface &p,int i)
+{
+   double newval;
+   ExtValue ev;
+   p.get(i,ev);
+   bool ok=getMutatedProperty(p,i,ev.getDouble(),newval);
+   if (ok) {ev.setDouble(newval); p.set(i,ev);}
+   return ok;
+}
+
+bool Geno_fx::getMutatedProperty(ParamInterface &p,int i,double oldval,double &newval)
+{
+   newval=0;
+   if (p.type(i)[0]!='f' && p.type(i)[0]!='d') return false; //don't know how to mutate
+   const char *n=p.id(i),*na=p.name(i);
+     if (strcmp(n,"si")==0 && strcmp(na,"Sigmoid")==0) newval=CustomRnd(distrib_sigmo); else
+     if (strcmp(n,"in")==0 && strcmp(na,"Inertia")==0) newval=CustomRnd(distrib_inertia); else
+     if (strcmp(n,"fo")==0 && strcmp(na,"Force")==0) newval=CustomRnd(distrib_force); else
+     {
+       double mn,mx,df;
+       getMinMaxDef(&p,i,mn,mx,df);
+       newval=mutateCreep(p.type(i)[0],oldval,mn,mx);
+     }
+   return true;
+}
+
+double Geno_fx::mutateCreepNoLimit(char type,double current,double mn,double mx)
+{
+   double result=RndGen.Gauss(current,(mx-mn)/2/5); // /halfinterval, 5 times narrower
+   if (type=='d') {result=int(result+0.5); if (result==current) result+=randomN(2)*2-1;}
+    else result=floor(result*1000+0.5)/1000.0; //round
+   return result;
+}
+
+double Geno_fx::mutateCreep(char type,double current,double mn,double mx)
+{
+   double result=mutateCreepNoLimit(type,current,mn,mx); //TODO consider that when boundary is touched (reflect/absorb below), the default precision (3 digits) may change. Is it good or bad?
+   //reflect:
+   if (result>mx) result=mx-(result-mx); else
+    if (result<mn) result=mn+(mn-result);
+   //absorb (just in case 'result' exceeded the allowed range so much):
+   if (result>mx) result=mx; else
+    if (result<mn) result=mn;
+   return result;
+}
+
+NeuroClass* Geno_fx::getRandomNeuroClass()
+{
+	SListTempl<NeuroClass*> active;
+   for(int i=0;i<Neuro::getClassCount();i++)
+      if (Neuro::getClass(i)->genactive) active+=Neuro::getClass(i);
+   if (!active==0) return NULL; else return active(randomN(!active));
+}
+
+NeuroClass* Geno_fx::parseNeuroClass(char*& s)
+{
+   int len=strlen(s);
+   int Len=0;
+   NeuroClass *I=NULL;
+   for(int i=0;i<Neuro::getClassCount();i++)
+   {
+      const char *n=Neuro::getClass(i)->name;
+      int l=strlen(n);
+      if (len>=l && l>Len && (strncmp(s,n,l)==0)) {I=Neuro::getClass(i); Len=l;}
+   }
+   s+=Len;
+   return I;
+}
+
+Neuro* Geno_fx::findNeuro(const Model *m,const NeuroClass *nc)
+{
+  if (!m) return NULL;
+  for(int i=0;i<m->getNeuroCount();i++)
+     if (m->getNeuro(i)->getClass()==nc) return m->getNeuro(i);
+  return NULL; //neuron of class 'nc' was not found
+}
+
+int Geno_fx::neuroClassProp(char*& s,NeuroClass *nc,bool also_v1_N_props)
+{
+   int len=strlen(s);
+   int Len=0,I=-1;
+   if (nc)
+   {
+      Param p=nc->getProperties();
+      for(int i=0;i<p.getPropCount();i++)
+      {
+         const char *n=p.id(i);
+         int l=strlen(n);
+         if (len>=l && l>Len && (strncmp(s,n,l)==0)) {I=100+i; Len=l;}
+         if (also_v1_N_props) //recognize old properties symbols /=!
+         {
+            if (strcmp(n,"si")==0) n="/"; else
+            if (strcmp(n,"in")==0) n="="; else
+            if (strcmp(n,"fo")==0) n="!";
+            l=strlen(n);
+            if (len>=l && l>Len && (strncmp(s,n,l)==0)) {I=100+i; Len=l;}
+         }
+      }
+   }
+   Neuro n;
+   Param p=n.extraProperties();
+   for(int i=0;i<p.getPropCount();i++)
+   {
+      const char *n=p.id(i);
+      int l=strlen(n);
+      if (len>=l && l>Len && (strncmp(s,n,l)==0)) {I=i; Len=l;}
+   }
+   s+=Len;
+   return I;
+}
+
+bool Geno_fx::isWS(const char c)
+{return c==' ' || c=='\n' || c=='\t' || c=='\r';}
+
+void Geno_fx::skipWS(char *&s)
+{ if (!s) FramMessage("Geno_fx","skipWS","NULL reference!",1); else
+     while (isWS(*s)) s++;
+}
+
+bool Geno_fx::areAlike(char *g1,char *g2)
+{
+	while (*g1 || *g2)
+	{
+		skipWS(g1);
+		skipWS(g2);
+		if (*g1 != *g2) return false; //when difference
+      if (!*g1 && !*g2) break; //both end
+      g1++;
+      g2++;
+	}
+	return true; //equal
+}
+
+char* Geno_fx::strchrn0(const char *str,char ch)
+{ return ch==0?NULL:strchr((char*)str,ch); }
+
+bool Geno_fx::isNeuroClassName(const char firstchar)
+{
+   return isupper(firstchar) || firstchar=='|' || firstchar=='@' || firstchar=='*';
+}
+
Index: /cpp/frams/genetic/oper/geno_fx.h
===================================================================
--- /cpp/frams/genetic/oper/geno_fx.h	(revision 109)
+++ /cpp/frams/genetic/oper/geno_fx.h	(revision 109)
@@ -0,0 +1,218 @@
+// This file is a part of the Framsticks GenoFX library.
+// Copyright (C) 2002-2011  Maciej Komosinski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _GENO_FX_H_
+#define _GENO_FX_H_
+
+#include <common/nonstd.h>
+#include <frams/model/model.h>
+
+/** @file */
+
+/** \name Return codes for genetic operators */
+//@{
+#define GENOPER_OK          0 ///<operation successful
+#define GENOPER_OPFAIL     -1 ///<operation failed or could not be completed
+#define GENOPER_REPAIR     -2 ///<do not use in Geno_fx. GenMan uses it in checkValidity()... but will not. only f4 uses it
+#define GENOPER_NOOPER     -3 ///<do not use in Geno_fx. GenMan uses it for "no suitable operator for this genotype format"
+//@}
+
+/** \name gene/character predefined styles (for style() method) */
+//@{
+#define GENSTYLE_NONE       0 ///<no style specified (=normal font)
+#define GENSTYLE_INVALID    1 ///<this char cannot be accepted
+#define GENSTYLE_BOLD       2 ///<bold
+#define GENSTYLE_ITALIC     4 ///<italic
+#define GENSTYLE_STRIKEOUT  8 ///<strikeout (not recommended)
+//UNDERLINE used to mark errors
+//@}
+
+/** \name other useful style/color macros */
+//@{
+#define GENRGB(r,g,b) ((unsigned long)(((unsigned char)(r)|((unsigned short)((unsigned char)(g))<<8))|(((unsigned long)(unsigned char)(b))<<16)))
+#define GENSTYLE_RGBS(r,g,b,s) ((unsigned long)((unsigned char)s)<<24 | GENRGB(r,g,b))
+#define GENSTYLE_CS(rgb,s) ((unsigned long)((unsigned char)s)<<24 | rgb)
+
+#define GENGETSTYLE(style) ((style)>>24)
+#define GENGETCOLOR(style) ((style)&0x00ffffff)
+#define GENGET_R(style) ((style)&0xff)
+#define GENGET_G(style) ((style>>8)&0xff)
+#define GENGET_B(style) ((style>>16)&0xff)
+
+#define GENCOLOR_TEXT    GENRGB(0,0,0) ///<recommended color to use for text genes
+#define GENCOLOR_NUMBER  GENRGB(200,0,0) ///<recommended color to use for number genes
+//@}
+
+///Base class for genetic operations on genotypes of some genetic format
+/**\author Maciej Komosinski
+
+When designing genetic operations on some representation, inherit your class
+(for example Geno_fMy) from Geno_fx. Define some methods,
+like mutate(), in your class, to allow for evolution.
+Ensure they have the same names and arguments as the corresponding
+virtual methods in Geno_fx. Set the 'supported_format' variable to the
+appropriate genetic representation ID.
+Whenever arguments are genotypes, they are without
+trailing characters which describe genetic format
+(for example, "p:", not "//0\np:").
+When allocating/reallocating char* parameters, use malloc, free, realloc, strdup, etc.
+Do not use new and delete.
+
+All the methods you might define are:
+- checkValidity()
+- validate()
+- mutate()
+- crossOver()
+- getSimplest()
+- style()
+
+Your code must not cause errors (like invalid memory access, memory
+leaks) on any arguments, even 'random' ones. GENOPER_OPFAIL should
+be returned when an operator cannot cope with its argument genotype.
+
+To compile your code, you may also need some GDK files.
+A simple example is Geno_ftest class (see \ref geno_ftest_example "C++ code" for details).
+A more realistic example is Geno_f4 derived from Geno_fx: refer to
+the available source on developmental encoding and f4 genotype format.*/
+
+class Geno_fx
+{
+ public:
+   Param par;
+   char supported_format; ///<genotype format which is supported by this class ('6' for Geno_f6, etc.). Must be initialized in constructor
+   SString name; ///<name of this set of genetic operators
+   char **mutation_method_names; ///<array of names for mutation methods. If initialized (by new char*[]), must have entries for each method index returned by mutate(geno,chg,METHOD).  If initialized, it is automatically freed by this destructor.
+   Geno_fx() : par(empty_paramtab) {supported_format='x'; name="Default"; mutation_method_names=NULL; setDefaults();}
+
+/**Used to perform initializations of Param parameters that are not handled by the Param itself
+(i.e. string parameters need to be initialized here)*/
+   virtual void setDefaults() {}
+
+/**Checks a genotype for minor mistakes and major errors.
+\param geno genotype to be checked
+\retval error_position 1-based (or 1 if no exact error position known)
+\retval GENOPER_OK when the genotype is fully valid, and can be translated by the converter with \b no modifications nor tweaks*/
+   virtual int checkValidity(const char *geno) {return GENOPER_NOOPER;}
+
+/**Validates a genotype. The purpose of this function is to validate
+obvious/minor errors (range overruns, invalid links, etc.). Do not try
+to introduce entirely new genes in place of an error.
+\param geno input/output: genotype to be validated
+\retval GENOPER_OK must be returned in any case ("did my best to validate")*/
+   virtual int validate(char *&geno) {return GENOPER_NOOPER;}
+
+/**Mutates a genotype. Mutation should always change something.
+
+Avoid unnecessary calls in your code. Every genotype argument passed to this
+function is first checked, and validated if checkValidity() reported an error (or
+if there is no checkValidity() implemented). Every resulting genotype is subject
+to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
+to call these functions on input and output genotypes, because they are validated
+if needed.
+\param geno input/output: genotype to be mutated
+\param chg output: initialize with a value (in most cases 0..1) corresponding
+to the amount of genotype mutated. For example, it could be the number of changed
+genes divided by the total number of genes before mutation.
+\param chg method: initialize with the ID (number) of mutation method used.
+\retval GENOPER_OK
+\retval GENOPER_OPFAIL
+\sa
+Mutation example to illustrate the exchange of pointers for \e geno.
+The mutation adds random letter at the beginning or removes last letter from \e geno.
+\code
+{
+ int len=strlen(geno);
+ if (len==0 || random(2)==0) //add
+ {
+    method=0;
+    char* mutated=(char*)malloc(mutated,len+2); //allocate for mutated genotype
+    mutated[0]='A'+random(10); //first char random
+    strcpy(mutated+1,geno); //the rest is original
+    free(geno); //must take care of the original allocation
+    geno=mutated;
+ } else 
+ {
+    method=1;
+    geno[len-1]=0; //simply shorten the string - remove last char
+ }
+ chg=1.0/max(len,1); //estimation of mutation strength, divby0-safe
+} \endcode
+*/
+   virtual int mutate(char *&geno,float& chg,int &method) {method=-1; chg=-1; return GENOPER_NOOPER;}
+
+/**Crosses over two genotypes. It is sufficient to return only one child (in \e g1) and set \e chg1 only, then \e g2 must equal "".
+
+Avoid unnecessary calls in your code. Every genotype argument passed to this
+function is first checked, and validated if checkValidity() reported an error (or
+if there is no checkValidity() implemented). Every resulting genotype is subject
+to the same procedure, unless GENOPER_OPFAIL was returned. Thus you do not have
+to call these functions on input and output genotypes, because they are validated
+if needed.
+\param g1 input/output: parent1 genotype, initialize with child1
+\param g2 input/output: parent2 genotype, initialize with child2 if both children are available
+\param chg1 output: initialize with the fraction of parent1 genes in child1 (parent2 has the rest)
+\param chg2 output: initialize with the fraction of parent2 genes in child2 (parent1 has the rest)
+\retval GENOPER_OK
+\retval GENOPER_OPFAIL
+\sa mutate() for an example*/
+   virtual int crossOver(char *&g1,char *&g2,float& chg1,float& chg2) {chg1=chg2=-1; return GENOPER_NOOPER;}
+
+/**\return a pointer to the simplest genotype string*/
+   virtual char* getSimplest() {return NULL;}
+
+/**You may want to have your genotype colored. This method provides desired character styles for genes.
+\param geno genotype
+\param pos 0-based char offset
+\retval number-encoded visual style (and validity) of the genotype char at \e geno[pos].
+Assume white background.
+\sa GENSTYLE_* macros, like GENSTYLE_BOLD*/
+   virtual unsigned long style(const char *geno,int pos) {return GENSTYLE_RGBS(0,0,0,GENSTYLE_NONE);}
+
+///currently not used (similarity of two genotypes)
+   virtual float similarity(const char*,const char*) {return GENOPER_NOOPER;}
+   virtual ~Geno_fx() {if (mutation_method_names) {delete []mutation_method_names; mutation_method_names=NULL;}}
+//   virtual char getFormat() {return 255;} //returns supported genotype format, for ex. '1'
+//   virtual int enabled() {return 1;} // should be enabled by default
+
+/** \name Some helpful methods for you */
+//@{
+	 static int roulette(const double *probtab,const int count); 	///<returns random index according to probabilities in the \e probtab table or -1 if all probs are zero. \e count is the number of elements in \e probtab.
+   static bool getMinMaxDef(ParamInterface *p,int propindex,double &mn,double &mx,double &def); ///<perhaps a more useful (higher-level) way to obtain min/max/def info for integer and double properties. Returns true if min/max/def was really available (otherwise it is just invented).
+   static int selectRandomProperty(Neuro* n); ///<selects random property (either 0-based extraproperty of Neuro or 100-based property of its NeuroClass). -1 if Neuro has no properties.
+   static double mutateNeuProperty(double current,Neuro *n,int propindex); ///<returns value \e current mutated for the property \e propindex of NeuroClass \e nc or for extraproperty (\e propindex - 100) of Neuro. Neuro is used as read-only. Give \e propindex == -1 to mutate connection weight (\e nc is then ignored).
+   static bool mutatePropertyNaive(ParamInterface &p,int propindex); ///<creep-mutate selected property. Returns true when success. mutateProperty() should be used instead of this function.
+   static bool mutateProperty(ParamInterface &p,int propindex); ///<like mutatePropertyNaive(), but uses special probability distributions for some neuron properties.
+   static bool getMutatedProperty(ParamInterface &p,int i,double oldval,double &newval); ///<like mutateProperty(), but just returns \e newval, does not get nor set it using \e p.
+   static double mutateCreepNoLimit(char type,double current,double mn,double mx); ///<returns \e current value creep-mutated with Gaussian distribution within [ \e mn , \e mx ] interval. Forced precision: 3 digits after comma. \e type must be either 'd' (integer) or 'f' (float/double).
+   static double mutateCreep(char type,double current,double mn,double mx); ///<just as mutateCreepNoLimit(), but forces mutated value into the [mn,mx] range using the 'reflect' approach.
+   static NeuroClass* getRandomNeuroClass(); ///<returns random neuroclass or NULL when no active classes.
+   static NeuroClass* parseNeuroClass(char *&s); ///<returns neuroclass or NULL if the string does not begin with a valid name. Advance \e s pointer.
+   static Neuro* findNeuro(const Model *m,const NeuroClass *nc); ///<returns pointer to first Neuro of class \e nc, or NULL if there is no such Neuro.
+   static int neuroClassProp(char *&s,NeuroClass *nc,bool also_v1_N_props=false); ///<returns 0-based property number for \e neuroclass, 100-based extraproperty number for Neuro, or -1 if the string does not begin with a valid property name. Advance \e s pointer if success.
+   static bool isWS(const char c); ///<is \e c a whitespace char?
+   static void skipWS(char *&s); ///<advances pointer \e s skipping whitespaces.
+   static bool areAlike(char*,char*); ///<compares two text strings skipping whitespaces. Returns 1 when equal, 0 when different.
+ 	 static char* strchrn0(const char *str,char ch); ///<like strchr, but does not find zero char in \e str.
+   static bool isNeuroClassName(const char firstchar); ///<determines if \e firstchar may start NeuroClass name. If not, it may start NeuroClass' (or Neuro's) property name.
+//@}
+};
+
+
+//
+// custom distributions for mutations of various parameters
+//
+/*
+static double distrib_weight[]=
+{
+5,                 // distribution -999 _-^_^-_ +999
+-999, 999,         // each weight value may be useful, especially...
+  -5, -0.3,        // ...little non-zero values
+  -3, -0.6,
+ 0.6, 3,
+ 0.3, 5,
+};
+*/
+
+#endif
+
Index: /cpp/frams/model/defassign-f0-GDK.h
===================================================================
--- /cpp/frams/model/defassign-f0-GDK.h	(revision 109)
+++ /cpp/frams/model/defassign-f0-GDK.h	(revision 109)
@@ -0,0 +1,171 @@
+
+
+
+
+void Part::defassign()
+{
+mass=1.0;
+shape=0;
+size=1.0;
+scale.x=1.0;
+scale.y=1.0;
+scale.z=1.0;
+density=1.0;
+friction=0.4;
+ingest=0.25;
+assim=0.25;
+vis_style="part";
+vsize=0.2;
+vcolor.x=0.5;
+vcolor.y=0.5;
+vcolor.z=0.5;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void Joint::defassign()
+{
+p1_refno=-1;
+p2_refno=-1;
+d.x=0;
+d.y=0;
+d.z=0;
+shape=0;
+stif=1.0;
+rotstif=1.0;
+stamina=0.25;
+vis_style="joint";
+vcolor.x=0.5;
+vcolor.y=0.5;
+vcolor.z=0.5;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void Neuro::defassign()
+{
+part_refno=-1;
+joint_refno=-1;
+
+vis_style="neuro";
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void NeuroConn::defassign()
+{
+n1_refno=-1;
+n2_refno=-1;
+weight=1.0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /cpp/frams/model/f0-GDK-classes.h
===================================================================
--- /cpp/frams/model/f0-GDK-classes.h	(revision 109)
+++ /cpp/frams/model/f0-GDK-classes.h	(revision 109)
@@ -0,0 +1,200 @@
+
+#define FIELDSTRUCT Model
+ParamEntry f0_model_paramtab[]=
+{
+ {"Properties",2,2,"m" },
+ {"Visual",},
+ {"se",0,1024,"startenergy","f",FIELD(startenergy),},
+ {"Vstyle",1,0,"vis_style","s 0 -1 ",FIELD(vis_style),},
+ {0,0,0,}
+};
+ParamEntry f0_model_xtra_paramtab[]=
+{
+ {"Extra properties",1,0,"m"},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+#define FIELDSTRUCT Part
+ParamEntry f0_part_paramtab[]=
+{
+ {"Geometry",3,22,"p" },
+ {"Other properties",},
+ {"Visual",},
+ {"x",0,1024,"position.x","f",FIELD(p.x),},
+ {"y",0,1024,"position.y","f",FIELD(p.y),},
+ {"z",0,1024,"position.z","f",FIELD(p.z),},
+ {"m",1,0,"mass","f 0.1 999.0 1.0",FIELD(mass),},
+ {"sh",1,0,"shape","d 0 3 0",FIELD(shape),},
+ {"s",1,0,"size","f 0.1 10.0 1.0",FIELD(size),},
+ {"sx",1,0,"scale.x","f 0.001 1000.0 1.0",FIELD(scale.x),},
+ {"sy",1,0,"scale.y","f 0.001 1000.0 1.0",FIELD(scale.y),},
+ {"sz",1,0,"scale.z","f 0.001 1000.0 1.0",FIELD(scale.z),},
+ {"dn",1,0,"density","f 0.2 5.0 1.0",FIELD(density),},
+ {"fr",1,0,"friction","f 0.0 4.0 0.4",FIELD(friction),},
+ {"ing",1,0,"ingestion","f 0.0 1.0 0.25",FIELD(ingest),},
+ {"as",1,0,"assimilation","f 0.0 1.0 0.25",FIELD(assim),},
+ {"rx",0,0,"rot.x","f",FIELD(rot.x),},
+ {"ry",0,1024,"rot.y","f",FIELD(rot.y),},
+ {"rz",0,1024,"rot.z","f",FIELD(rot.z),},
+ {"i",1,0,"info","s",FIELD(info),},
+ {"Vstyle",2,0,"vis_style","s 0 -1 part",FIELD(vis_style),},
+ {"vs",2,0,"visual thickness","f 0.05 0.7 0.2",FIELD(vsize),},
+ {"vr",2,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",2,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",2,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+ParamEntry f0_part_xtra_paramtab[]=
+{
+ {"Extra properties",1,9,"p"},
+ {"m",0,0,"mass","f 0.1 999.0 1.0",FIELD(mass),},
+ {"dn",0,0,"density","f 0.2 5.0 1.0",FIELD(density),},
+ {"fr",0,0,"friction","f 0.0 4.0 0.4",FIELD(friction),},
+ {"ing",0,0,"ingestion","f 0.0 1.0 0.25",FIELD(ingest),},
+ {"as",0,0,"assimilation","f 0.0 1.0 0.25",FIELD(assim),},
+ {"vs",0,0,"visual thickness","f 0.05 0.7 0.2",FIELD(vsize),},
+ {"vr",0,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",0,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",0,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+#define FIELDSTRUCT Joint
+ParamEntry f0_joint_paramtab[]=
+{
+ {"Connections",4,17,"j" },
+ {"Geometry",},
+ {"Other properties",},
+ {"Visual",},
+ {"p1",0,1024,"part1 ref#","d -1 999999 -1",FIELD(p1_refno),},
+ {"p2",0,1024,"part2 ref#","d -1 999999 -1",FIELD(p2_refno),},
+ {"rx",1,0,"rotation.x","f",FIELD(rot.x),},
+ {"ry",1,1024,"rotation.y","f",FIELD(rot.y),},
+ {"rz",1,1024,"rotation.z","f",FIELD(rot.z),},
+ {"dx",1,0,"delta.x","f -2 2 0",FIELD(d.x),},
+ {"dy",1,1024,"delta.y","f -2 2 0",FIELD(d.y),},
+ {"dz",1,1024,"delta.z","f -2 2 0",FIELD(d.z),},
+ {"sh",1,0,"shape","d 0 1 0",FIELD(shape),},
+ {"stif",2,0,"stiffness","f 0.0 1.0 1.0",FIELD(stif),},
+ {"rotstif",2,0,"rotation stiffness","f 0.0 1.0 1.0",FIELD(rotstif),},
+ {"stam",2,0,"stamina","f 0.0 1.0 0.25",FIELD(stamina),},
+ {"i",2,0,"info","s",FIELD(info),},
+ {"Vstyle",3,0,"vis_style","s 0 -1 joint",FIELD(vis_style),},
+ {"vr",3,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",3,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",3,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+ParamEntry f0_joint_xtra_paramtab[]=
+{
+ {"Extra properties",1,5,"j"},
+ {"stif",0,0,"stiffness","f 0.0 1.0 1.0",FIELD(stif),},
+ {"rotstif",0,0,"rotation stiffness","f 0.0 1.0 1.0",FIELD(rotstif),},
+ {"vr",0,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",0,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",0,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+#define FIELDSTRUCT Joint
+ParamEntry f0_nodeltajoint_paramtab[]=
+{
+ {"Connections",4,11,"j" },
+ {"Geometry",},
+ {"Other properties",},
+ {"Visual",},
+ {"p1",0,1024,"part1 ref#","d -1 999999 -1",FIELD(p1_refno),},
+ {"p2",0,1024,"part2 ref#","d -1 999999 -1",FIELD(p2_refno),},
+ {"sh",1,0,"shape","d 0 1 0",FIELD(shape),},
+ {"stif",2,0,"stiffness","f 0.0 1.0 1.0",FIELD(stif),},
+ {"rotstif",2,0,"rotation stiffness","f 0.0 1.0 1.0",FIELD(rotstif),},
+ {"stam",2,0,"stamina","f 0.0 1.0 0.25",FIELD(stamina),},
+ {"i",2,0,"info","s",FIELD(info),},
+ {"Vstyle",3,0,"vis_style","s 0 -1 joint",FIELD(vis_style),},
+ {"vr",3,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",3,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",3,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+ParamEntry f0_nodeltajoint_xtra_paramtab[]=
+{
+ {"Extra properties",1,5,"j"},
+ {"stif",0,0,"stiffness","f 0.0 1.0 1.0",FIELD(stif),},
+ {"rotstif",0,0,"rotation stiffness","f 0.0 1.0 1.0",FIELD(rotstif),},
+ {"vr",0,0,"red component","f 0.0 1.0 0.5",FIELD(vcolor.x),},
+ {"vg",0,1024,"green component","f 0.0 1.0 0.5",FIELD(vcolor.y),},
+ {"vb",0,1024,"blue component","f 0.0 1.0 0.5",FIELD(vcolor.z),},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+#define FIELDSTRUCT Neuro
+ParamEntry f0_neuro_paramtab[]=
+{
+ {"Connections",3,10,"n" },
+ {"Other",},
+ {"Visual",},
+ {"p",0,0,"part ref#","d -1 999999 -1",FIELD(part_refno),},
+ {"j",0,0,"joint ref#","d -1 999999 -1",FIELD(joint_refno),},
+ {"d",1,0,"item details","s",GETSET(details),},
+ {"i",1,0,"info","s",FIELD(info),},
+ {"Vstyle",2,0,"vis_style","s 0 -1 neuro",FIELD(vis_style),},
+ {"getInputCount",0,1+2,"input count","d",GETONLY(inputCount),},
+ {"getInputNeuroDef",0,1+2,"get input neuron","p oNeuroDef(d)",PROCEDURE(p_getInputNeuroDef),},
+ {"getInputNeuroIndex",0,1+2,"get input neuron index","p d(d)",PROCEDURE(p_getInputNeuroIndex),},
+ {"getInputWeight",0,1+2,"get input weight","p f(d)",PROCEDURE(p_getInputWeight),},
+ {"classObject",0,1+2,"neuron class","o NeuroClass",GETONLY(classObject),},
+ {0,0,0,}
+};
+ParamEntry f0_neuro_xtra_paramtab[]=
+{
+ {"Extra properties",1,0,"n"},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+#define FIELDSTRUCT NeuroConn
+ParamEntry f0_neuroconn_paramtab[]=
+{
+ {"Connection",2,4,"c" },
+ {"Other",},
+ {"n1",0,1024,"this neuro ref#","d -1 999999 -1",FIELD(n1_refno),},
+ {"n2",0,1024,"connected neuro ref#","d -1 999999 -1",FIELD(n2_refno),},
+ {"w",0,1024,"weight","f -999999 999999 1.0",FIELD(weight),},
+ {"i",1,0,"info","s",FIELD(info),},
+ {0,0,0,}
+};
+ParamEntry f0_neuroconn_xtra_paramtab[]=
+{
+ {"Extra properties",1,0,"c"},
+ {0,0,0,}
+};
+#undef FIELDSTRUCT
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /cpp/frams/model/model.cpp
===================================================================
--- /cpp/frams/model/model.cpp	(revision 109)
+++ /cpp/frams/model/model.cpp	(revision 109)
@@ -0,0 +1,1268 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <common/nonstd_math.h>
+#include "model.h"
+#include <common/framsg.h>
+#include <frams/util/multimap.h>
+#include <frams/errmgr/errmanager.h>
+
+Model::Model()
+{
+autobuildmaps=false;
+init();
+}
+
+void Model::init()
+{
+partmappingchanged=0;
+buildstatus=empty;
+modelfromgenotype=0;
+startenergy=1.0;
+checklevel=1;
+#ifdef MODEL_V1_COMPATIBLE
+oldneurocount=-1; // == unknown
+oldconnections=0;
+#endif
+map=0;
+f0map=0;
+f0genoknown=1;
+}
+
+void Model::moveElementsFrom(Model &source)
+{
+int i;
+open();
+for (i=0;i<source.getPartCount();i++)
+	addPart(source.getPart(i));
+for (i=0;i<source.getJointCount();i++)
+	addJoint(source.getJoint(i));
+for (i=0;i<source.getNeuroCount();i++)
+	addNeuro(source.getNeuro(i));
+source.parts.clear(); source.joints.clear(); source.neurons.clear();
+source.clear();
+}
+
+void Model::internalCopy(const Model &mod)
+{
+geno=mod.geno;
+f0genoknown=0;
+startenergy=mod.startenergy;
+if (mod.getStatus()==valid)
+	{
+	modelfromgenotype=mod.modelfromgenotype;
+	{for (int i=0;i<mod.getPartCount();i++)
+		addPart(new Part(*mod.getPart(i)));}
+	{for (int i=0;i<mod.getJointCount();i++)
+		{
+		Joint *oldj=mod.getJoint(i);
+		Joint *j=new Joint(*oldj);
+		addJoint(j);
+		j->attachToParts(oldj->part1->refno,oldj->part2->refno);
+		}}
+	{for (int i=0;i<mod.getNeuroCount();i++)
+		{
+		Neuro *oldn=mod.getNeuro(i);
+		Neuro *n=new Neuro(*oldn);
+		addNeuro(n);
+		if (oldn->part_refno>=0) n->attachToPart(oldn->part_refno);
+		else n->attachToJoint(oldn->joint_refno);
+		}}
+	for (int i=0;i<mod.getNeuroCount();i++)
+		{
+		Neuro *oldn=mod.getNeuro(i);
+		Neuro *n=getNeuro(i);
+		for (int ni=0;ni < oldn->getInputCount();ni++)
+			{
+			double w;
+			Neuro *oldinput=oldn->getInput(ni,w);
+			SString info=n->getInputInfo(ni);
+			n->addInput(getNeuro(oldinput->refno),w,&info);
+			}
+		}
+	}
+}
+
+
+Model::Model(const Geno &src,bool buildmaps)
+	:autobuildmaps(buildmaps)
+{init(src);}
+
+void Model::operator=(const Model &mod)
+{
+clear();
+open();
+internalCopy(mod);
+buildstatus=mod.buildstatus;
+}
+
+Model::Model(const Model &mod,bool buildmaps)
+	:autobuildmaps(buildmaps)
+{
+init();
+open();
+internalCopy(mod);
+buildstatus=mod.buildstatus;
+}
+
+void Model::init(const Geno &src)
+{
+init();
+modelfromgenotype=1;
+geno=src;
+build();
+}
+
+void Model::resetAllDelta()
+{
+for (int i=0;i<getJointCount();i++)
+	getJoint(i)->resetDelta();
+}
+
+void Model::useAllDelta(bool yesno)
+{
+for (int i=0;i<getJointCount();i++)
+	getJoint(i)->useDelta(yesno);
+}
+
+Model::~Model()
+{
+delmodel_list.action((long)this);
+clear();
+}
+
+void Model::clear()
+{
+Part *p;
+for (parts.start();p=(Part*)parts();) delete p;
+Joint* j;
+for (joints.start();j=(Joint*)joints();) delete j;
+Neuro *n;
+for (neurons.start();n=(Neuro*)neurons();) delete n;
+parts.clear(); joints.clear(); neurons.clear();
+delMap();
+delF0Map();
+init();
+geno=Geno();
+f0geno=Geno();
+}
+
+Part *Model::addPart(Part *p)
+{
+p->owner=this;
+p->refno=parts.size();
+parts+=p;
+return p;
+}
+
+Joint *Model::addJoint(Joint *j)
+{
+j->owner=this;
+j->refno=joints.size();
+joints+=j;
+return j;
+}
+
+Neuro *Model::addNeuro(Neuro *n)
+{
+n->owner=this; 
+n->refno=neurons.size();
+neurons+=n;
+return n;
+}
+
+void Model::removeNeuros(SList &nlist)
+{
+FOREACH(Neuro*,nu,nlist)
+	{
+	int i=findNeuro(nu);
+	if (i>=0) removeNeuro(i);
+	}
+}
+
+void Model::removePart(int partindex,int removeattachedjoints,int removeattachedneurons)
+{
+Part *p=getPart(partindex);
+if (removeattachedjoints)
+	{
+	SList jlist;
+	findJoints(jlist,p);
+	FOREACH(Joint*,j,jlist)
+		{
+		int i=findJoint(j);
+		if (i>=0) removeJoint(i,removeattachedneurons);
+		}
+	}
+if (removeattachedneurons)
+	{
+	SList nlist;
+	findNeuros(nlist,0,p);
+	removeNeuros(nlist);
+	}
+parts-=partindex;
+delete p;
+}
+
+void Model::removeJoint(int jointindex,int removeattachedneurons)
+{
+Joint *j=getJoint(jointindex);
+if (removeattachedneurons)
+	{
+	SList nlist;
+	findNeuros(nlist,0,0,j);
+	removeNeuros(nlist);
+	}
+joints-=jointindex;
+delete j;
+}
+
+void Model::removeNeuro(int neuroindex,bool removereferences)
+{
+Neuro* thisN=getNeuro(neuroindex);
+
+if (removereferences)
+	{
+	Neuro* n;
+// remove all references to thisN
+	for (int i=0;n=(Neuro*)neurons(i);i++)
+		{
+		Neuro *inp;
+		for (int j=0;inp=n->getInput(j);j++)
+			if (inp==thisN)
+				{
+				n->removeInput(j);
+				j--;
+				}
+		}
+	}
+
+neurons-=neuroindex;
+delete thisN;
+}
+
+MultiMap& Model::getMap()
+{
+if (!map) map=new MultiMap();
+return *map;
+}
+
+void Model::delMap()
+{
+if (map) {delete map; map=0;}
+}
+void Model::delF0Map()
+{
+if (f0map) {delete f0map; f0map=0;}
+}
+
+void Model::makeGenToGenMap(MultiMap& result,const MultiMap& gen1tomodel,const MultiMap& gen2tomodel)
+{
+result.clear();
+MultiMap m;
+m.addReversed(gen2tomodel);
+result.addCombined(gen1tomodel,m);
+}
+
+void Model::getCurrentToF0Map(MultiMap& result)
+{
+result.clear();
+if (!map) return;
+const MultiMap& f0m=getF0Map();
+makeGenToGenMap(result,*map,f0m);
+}
+
+void Model::rebuild(bool buildm)
+{
+autobuildmaps=buildm;
+clear();
+build();
+}
+
+void Model::initMap()
+{
+if (!map) map=new MultiMap();
+else map->clear();
+}
+
+void Model::initF0Map()
+{
+if (!f0map) f0map=new MultiMap();
+else f0map->clear();
+}
+
+void Model::build()
+{
+f0errorposition=-1;
+f0warnposition=-1;
+MultiMap *convmap=autobuildmaps ? new MultiMap() : NULL;
+f0geno=(geno.getFormat()=='0')? geno : geno.getConverted('0',convmap);
+f0genoknown=1;
+if (f0geno.isInvalid())
+	{
+	buildstatus=invalid;
+	if (convmap) delete convmap;
+	return;
+	}
+SString f0txt=f0geno.getGene();
+buildstatus=building; // was: open();
+if (autobuildmaps)
+	{
+	partmappingchanged=0;
+	initMap();
+	initF0Map();
+	}
+int pos=0,lnum=1,lastpos=0;
+SString line;
+MultiRange frommap;
+ErrorHandler err(ErrorHandler::DontBlock);
+for (;f0txt.getNextToken(pos,line,'\n');lnum++)
+	{
+	if (autobuildmaps)
+		{
+		frommap.clear();
+		frommap.add(lastpos,pos-1);
+		}
+	err.reset();
+	if (singleStepBuild(line,autobuildmaps ? (&frommap) : 0)==-1)
+		{
+		buildstatus=invalid;
+		FMprintf("Model","build",FMLV_ERROR,
+			 geno.getName().len()?"illegal f0 code at line %d (%s)":"illegal f0 code at line %d",
+			 lnum,(const char*)geno.getName());
+		f0errorposition=lastpos;
+		if (convmap) delete convmap;
+		return;
+		}
+	if (err.getWarningCount())
+		{if (f0warnposition<0) f0warnposition=lastpos;}
+	lastpos=pos;
+	}
+err.disable();
+close();
+if (convmap)
+	{
+	*f0map=*map;
+	if (geno.getFormat()!='0')
+		{
+		MultiMap tmp;
+		tmp.addCombined(*convmap,getMap());
+		*map=tmp;
+		}
+	delete convmap;
+	}
+}
+
+const MultiMap& Model::getF0Map()
+{
+if (!f0map)
+	{
+	f0map=new MultiMap();
+	makeGeno(f0geno,f0map);
+	f0genoknown=1;
+	}
+return *f0map;
+}
+
+Geno Model::rawGeno()
+{
+Geno tmpgen;
+makeGeno(tmpgen);
+return tmpgen;
+}
+
+void Model::makeGeno(Geno &g,MultiMap *map,bool handle_defaults)
+{
+if ((buildstatus!=valid)&&(buildstatus!=building))
+	{
+	g=Geno(0,0,0,"invalid model");
+	return;
+	}
+
+SString gen;
+
+Param modelparam(f0_model_paramtab);
+Param partparam(f0_part_paramtab);
+Param jointparam(f0_joint_paramtab);
+Param neuroparam(f0_neuro_paramtab);
+Param connparam(f0_neuroconn_paramtab);
+
+static Part defaultpart;
+static Joint defaultjoint;
+static Neuro defaultneuro;
+static Model defaultmodel;
+static NeuroConn defaultconn;
+//static NeuroItem defaultneuroitem;
+
+Part *p;
+Joint *j;
+Neuro *n;
+int i;
+int len;
+int a,b;
+//NeuroItem *ni;
+
+if (startenergy!=defaultmodel.startenergy)
+	{
+	modelparam.select(this);
+	gen+="m:";
+	modelparam.save2(gen,handle_defaults ? &defaultmodel : NULL,true,!handle_defaults);
+	}
+
+for (i=0;p=(Part*)parts(i);i++)
+	{
+	partparam.select(p);
+	len=gen.len();
+	gen+="p:";
+	partparam.save2(gen,handle_defaults ? &defaultpart : NULL,true,!handle_defaults);
+	if (map)
+		map->add(len,gen.len()-1,partToMap(i));
+	}
+for (i=0;j=(Joint*)joints(i);i++)
+	{
+	jointparam.select(j);
+	len=gen.len();
+	jointparam.setParamTab(j->usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab);
+	gen+="j:";
+	jointparam.save2(gen,handle_defaults ? &defaultjoint : NULL,true,!handle_defaults);
+	if (map)
+		map->add(len,gen.len()-1,jointToMap(i));
+	}
+for (i=0;n=(Neuro*)neurons(i);i++)
+	{
+	neuroparam.select(n);
+	len=gen.len();
+	gen+="n:";
+	neuroparam.save2(gen,handle_defaults ? &defaultneuro : NULL,true,!handle_defaults);
+	if (map)
+		map->add(len,gen.len()-1,neuroToMap(i));
+	}
+for (a=0;a<neurons.size();a++)
+	{ // inputs
+	n=(Neuro*)neurons(a);
+//	if ((n->getInputCount()==1)&&(n->getInput(0).refno <= n->refno))
+//		continue; // already done with Neuro::conn_refno
+
+	for (b=0;b<n->getInputCount();b++)
+		{
+		double w;
+		NeuroConn nc;
+		Neuro* n2=n->getInput(b,w);
+//		if (((n2.parentcount==1)&&(n2.parent)&&(n2.parent->refno < n2.refno)) ^ 
+//		    (n2.neuro_refno>=0))
+//			printf("!!!! bad Neuro::neuro_refno ?!\n");
+
+//		if ((n2.parentcount==1)&&(n2.parent)&&(n2.parent->refno < n2.refno))
+//		if (n2.neuro_refno>=0)
+//			continue; // already done with Neuro::neuro_refno
+
+		nc.n1_refno=n->refno; nc.n2_refno=n2->refno;
+		nc.weight=w;
+		SString **s=n->inputInfo(b);
+		if ((s)&&(*s))
+		nc.info=**s;
+		connparam.select(&nc);
+		len=gen.len();
+		gen+="c:";
+		connparam.save2(gen,handle_defaults ? &defaultconn : NULL,true,!handle_defaults);
+		if (map)
+			map->add(len,gen.len()-1,neuroToMap(n->refno));
+		}
+	}
+g=Geno(gen,'0');
+}
+
+//////////////
+
+void Model::open()
+{
+if (buildstatus==building) return;
+buildstatus=building;
+modelfromgenotype=0;
+partmappingchanged=0;
+f0genoknown=0;
+delMap();
+}
+
+void Model::checkpoint()
+{}
+
+void Model::setGeno(const Geno& newgeno)
+{
+geno=newgeno;
+}
+
+void Model::clearMap()
+{
+Part *p; Joint *j; Neuro *n;
+int i;
+delMap();
+delF0Map();
+for (i=0;p=(Part*)parts(i);i++)
+	p->clearMapping();
+for (i=0;j=(Joint*)joints(i);i++)
+	j->clearMapping();
+for (i=0;n=(Neuro*)neurons(i);i++)
+	n->clearMapping();
+}
+
+int Model::close()
+{
+if (buildstatus!=building)
+	FMprintf("Model","close",FMLV_WARN,"unexpected close() - no open()");
+if (internalcheck(1)>0)
+	{
+	buildstatus=valid;
+
+	if (partmappingchanged)
+		{
+		getMap();
+		Part *p; Joint *j; Neuro *n;
+		int i;
+		for (i=0;p=(Part*)parts(i);i++)
+			if (p->getMapping())
+				map->add(*p->getMapping(),partToMap(i));
+		for (i=0;j=(Joint*)joints(i);i++)
+			if (j->getMapping())
+				map->add(*j->getMapping(),jointToMap(i));
+		for (i=0;n=(Neuro*)neurons(i);i++)
+			if (n->getMapping())
+				map->add(*n->getMapping(),neuroToMap(i));
+		}
+	}
+else 
+	buildstatus=invalid;
+
+return (buildstatus==valid);
+}
+
+int Model::validate()
+{
+return internalcheck(0);
+}
+
+Pt3D Model::whereDelta(const Part& start,const Pt3D& rot, const Pt3D& delta)
+{
+Orient roto;
+roto=rot;
+Orient o;
+roto.transform(o,start.o);
+//o.x=start.o/roto.x;
+//o.y=start.o/roto.y;
+//o.z=start.o/roto.z;
+return o.transform(delta)+start.p;
+}
+
+int Model::singleStepBuild(const SString &line,const MultiRange* srcrange)
+{
+int pos=0; const char*t=(const char*)line;
+for (;*t;t++,pos++)
+	if (!strchr(" \r\t",*t)) break;
+if (*t=='#') return 0;
+if (!*t) return 0;
+if (!strncmp(t,"p:",2))
+	{
+	Param partparam(f0_part_paramtab);
+	Part *p=new Part();
+	partparam.select(p);
+	pos+=2;
+	partparam.load2(line,pos);
+	p->o.rotate(p->rot);
+	parts+=p;
+	p->owner=this;
+	if (srcrange) p->setMapping(*srcrange);
+	return getPartCount()-1;
+	}
+if (!strncmp(t,"m:",2))
+	{
+	Param modelparam(f0_model_paramtab);
+	modelparam.select(this);
+	pos+=2;
+	modelparam.load2(line,pos);
+	return 0;
+	}
+else if (!strncmp(t,"j:",2))
+	{
+	Param jointparam(f0_joint_paramtab);
+	Joint *j=new Joint();
+	jointparam.select(j);
+	pos+=2;
+	j->owner=this;
+	jointparam.load2(line,pos);
+	if ((j->p1_refno>=0)&&(j->p1_refno<getPartCount())&&
+	   (j->p2_refno>=0)&&(j->p2_refno<getPartCount()))
+		{
+		addJoint(j);
+		if (j->d.x < (JOINT_DELTA_MARKER-1.0))
+			j->useDelta(1);
+		j->attachToParts(j->p1_refno,j->p2_refno);
+		if (srcrange) j->setMapping(*srcrange);
+		return j->refno;
+		}
+	else
+		{
+		delete j;
+		FMprintf("Model","build",FMLV_ERROR,
+			 "invalid part reference for joint #%d",getJointCount()-1);
+		return -1;
+		}
+	}
+else if (!strncmp(t,"n:",2)) // neuro (or the old neuro object as the special case)
+	{
+	Param neuroparam(f0_neuro_paramtab);
+	Neuro *nu=new Neuro();
+	neuroparam.select(nu);
+	pos+=2;
+	neuroparam.load2(line,pos);
+#ifdef MODEL_V1_COMPATIBLE
+	if (nu->neuro_refno>=0) // parent specified...
+		{
+		if (nu->neuro_refno >= getNeuroCount()) // and it's illegal
+			{
+			delete nu;
+			return -1;
+			}
+		Neuro *parentNU=getNeuro(nu->neuro_refno);
+		parentNU->addInput(nu,nu->weight);
+		// default class for parented units: n-n link
+		//if (nu->moredata.len()==0) nu->moredata="-";
+		}
+	else
+#endif
+		{
+		// default class for unparented units: standard neuron
+		if (nu->getClassName().len()==0) nu->setClassName("N");
+		}
+/*
+	if (nu->conn_refno>=0) // input specified...
+		{
+		if (nu->conn_refno >= getNeuroCount()) // and it's illegal
+			{
+			delete nu;
+			return -1;
+			}
+		Neuro *inputNU=getNeuro(nu->conn_refno);
+		nu->addInput(inputNU,nu->weight);
+		}
+*/
+#ifdef MODEL_V1_COMPATIBLE
+	nu->weight=1.0;
+#endif
+	nu->owner=this;
+	// attach to part/joint
+	if (nu->part_refno>=0)
+		nu->attachToPart(nu->part_refno);
+	if (nu->joint_refno>=0)
+		nu->attachToJoint(nu->joint_refno);
+	if (srcrange) nu->setMapping(*srcrange);
+	// todo: check part/joint ref# 
+#ifdef MODEL_V1_COMPATIBLE
+	if (hasOldNeuroLayout())
+		{
+		int count=old_getNeuroCount();
+		neurons.insert(count,nu);
+		oldneurocount=count+1;
+		return oldneurocount-1;
+		}
+	else
+#endif
+		{
+		neurons+=nu;
+		return neurons.size()-1;
+		}
+	}
+else if (!strncmp(t,"c:",2)) // add input
+	{
+	Param ncparam(f0_neuroconn_paramtab);
+	NeuroConn c;
+	ncparam.select(&c);
+	pos+=2;
+	ncparam.load2(line,pos);
+	if ((c.n1_refno>=0)&&(c.n1_refno<getNeuroCount())&&(c.n2_refno>=0)&&(c.n2_refno<getNeuroCount()))
+		{
+		Neuro *na=getNeuro(c.n1_refno);
+		Neuro *nb=getNeuro(c.n2_refno);
+		na->addInput(nb,c.weight,&c.info);
+		if (srcrange)
+			na->addMapping(*srcrange);
+		return 0;
+		}
+	FMprintf("Model","build",FMLV_ERROR,
+		 "invalid neuron connection #%d <- #%d",c.n1_refno,c.n2_refno);
+	return -1;
+	}
+#ifdef MODEL_V1_COMPATIBLE
+else if (!strncmp(t,"ni:",3)) // old neuroitem
+	{
+	// we always use old layout for "ni:"
+	Param neuroitemparam(f0_neuroitem_paramtab);
+	Neuro *nu=new Neuro();
+	neuroitemparam.select(nu);
+	pos+=3;
+	neuroitemparam.load2(line,pos);
+	// illegal parent?
+	if ((nu->neuro_refno<0)||(nu->neuro_refno>=old_getNeuroCount()))
+		{
+		delete nu;
+		return -1;
+		}
+	Neuro *parentN=getNeuro(nu->neuro_refno);
+	// copy part/joint refno from parent, if possible
+	if ((nu->part_refno<0)&&(parentN->part_refno>=0))
+		nu->part_refno=parentN->part_refno;
+	if ((nu->joint_refno<0)&&(parentN->joint_refno>=0))
+		nu->joint_refno=parentN->joint_refno;
+	nu->owner=this;
+	// attach to part/joint
+	if (nu->part_refno>=0)
+		nu->attachToPart(nu->part_refno);
+	if (nu->joint_refno>=0)
+		nu->attachToJoint(nu->joint_refno);
+	if (srcrange)
+		nu->setMapping(*srcrange);
+	// special case: old muscles
+	// PARENT neuron will be set up to be the CHILD of the current one (!)
+	if (nu->isOldEffector())
+		{
+		nu->neuro_refno=parentN->refno;
+		neurons+=nu;
+		nu->owner=this;
+		nu->addInput(parentN,nu->weight); // (!)
+		nu->weight=1.0;
+		parentN->invalidateOldItems();
+		return 0; // !!! -> ...getItemCount()-1;
+		}
+	parentN->addInput(nu,nu->weight);
+	neurons+=nu;
+	parentN->invalidateOldItems();
+	if (nu->getClassName().len()==0)
+		{
+		nu->setClassName("-");
+		// for compatibility, "ni" can define a n-n connection
+		// referring to non-existent neuron (which will be hopefully defined later)
+		// internal check will add the proper input to this unit
+		// if conn_refno >=0 and input count==0
+		oldconnections=1;
+		if (srcrange)
+			parentN->addMapping(*srcrange);
+		}
+	else
+		nu->weight=1.0;
+	return 0; // !!! -> ...getItemCount()-1;
+	}
+#endif
+else return -1;
+}
+
+#ifdef MODEL_V1_COMPATIBLE
+int Model::addOldConnectionsInputs()
+{
+if (!oldconnections) return 1;
+Neuro* n;
+for (int i=0;i<neurons.size();i++)
+	{
+	n=(Neuro*)neurons(i);
+	if (n->conn_refno>=0)
+		if (n->isNNConnection())
+			if (n->conn_refno < old_getNeuroCount())
+				{ // good reference
+				Neuro* parent=n->parent; // nn connection has exactly one parent
+				int inp=parent->findInput(n);
+				Neuro* target=getNeuro(n->conn_refno);
+				parent->setInput(inp,target,n->weight);
+				removeNeuro(i,0); // no need to remove references
+				i--;
+				}
+			else
+				{
+				FMprintf("Model","internalCheck",FMLV_ERROR,
+					 "illegal N-N connection #%d (reference to #%d) (%s)",
+					 i,n->conn_refno,(const char*)geno.getName());
+				return 0;
+				}
+	}
+oldconnections=0;
+return 1;
+}
+#endif
+
+/////////////
+
+/** change the sequence of neuro units 
+    and fix references in "-" objects (n-n connections)  */
+void Model::moveNeuro(int oldpos,int newpos)
+{
+if (oldpos==newpos) return; // nop!
+Neuro *n=getNeuro(oldpos);
+neurons-=oldpos;
+neurons.insert(newpos,n);
+ // conn_refno could be broken -> fix it
+#ifdef MODEL_V1_COMPATIBLE
+for (int i=0;i<neurons.size();i++)
+	{
+	Neuro *n2=getNeuro(i);
+	if (n2->isNNConnection())
+		if (n2->conn_refno == oldpos) n2->conn_refno=newpos;
+		else
+		{ if (n2->conn_refno > oldpos) n2->conn_refno--;
+		  if (n2->conn_refno >= newpos) n2->conn_refno++; }
+	}
+invalidateOldNeuroCount();
+#endif
+}
+
+#ifdef MODEL_V1_COMPATIBLE
+/** move all old neurons (class "N") to the front of the neurons list.
+    @return number of old neurons
+  */
+int Model::reorderToOldLayout()
+{
+Neuro *n;
+int neurocount=0;
+for (int i=0;i<neurons.size();i++)
+	{
+	n=(Neuro*)neurons(i);
+	if (n->isOldNeuron())
+		{
+		moveNeuro(i,neurocount);
+		neurocount++;
+		i=neurocount-1;
+		}
+	}
+return neurocount;
+}
+#endif
+////////////
+
+void Model::updateNeuroRefno()
+{
+for (int i=0;i<neurons.size();i++)
+	{
+	Neuro* n=(Neuro*)neurons(i);
+	n->refno=i;
+	}
+}
+
+#define VALIDMINMAX(var,template,field) \
+if (var -> field < getMin ## template () . field) \
+	{ var->field= getMin ## template () . field; \
+	FMprintf("Model","internalCheck",FMLV_WARN,# field " too small in " # template "#%d (adjusted)",i);} \
+else if (var -> field > getMax ## template () . field) \
+	{ var->field= getMax ## template ()  . field; \
+	FMprintf("Model","internalCheck",FMLV_WARN,# field " too big in " # template "#%d (adjusted)",i);}
+
+#define LINKFLAG 0x8000000
+
+void Model::updateRefno()
+{
+int i;
+for (i=0;i<getPartCount();i++) getPart(i)->refno=i;
+for (i=0;i<getJointCount();i++) getJoint(i)->refno=i;
+for (i=0;i<getNeuroCount();i++) getNeuro(i)->refno=i;
+}
+
+int Model::internalcheck(int final)
+{
+Part *p;
+Joint *j;
+Neuro *n;
+int i,k;
+int ret=1;
+if ((parts.size()==0)&&(neurons.size()==0)) return 0;
+if (parts.size()==0)
+	size=Pt3D_0;
+else
+	{
+Pt3D bbmin=((Part*)parts(0))->p, bbmax=bbmin;
+for (i=0;i<parts.size();i++)
+	{
+	p=(Part*)parts(i);
+	p->owner=this;
+	p->refno=i;
+	if (checklevel>0)
+		p->mass=0.0;
+//	VALIDMINMAX(p,part,mass);
+	VALIDMINMAX(p,Part,size);
+	VALIDMINMAX(p,Part,density);
+	VALIDMINMAX(p,Part,friction);
+	VALIDMINMAX(p,Part,ingest);
+	VALIDMINMAX(p,Part,assim);
+	p->flags&=~LINKFLAG; // for delta joint cycle detection
+	if (p->p.x-p->size < bbmin.x) bbmin.x=p->p.x-p->size;
+	if (p->p.y-p->size < bbmin.y) bbmin.y=p->p.y-p->size;
+	if (p->p.z-p->size < bbmin.z) bbmin.z=p->p.z-p->size;
+	if (p->p.x+p->size > bbmax.x) bbmax.x=p->p.x+p->size;
+	if (p->p.y+p->size > bbmax.y) bbmax.y=p->p.y+p->size;
+	if (p->p.z+p->size > bbmax.z) bbmax.z=p->p.z+p->size;
+	}
+size=bbmax-bbmin;
+for (i=0;i<joints.size();i++)
+	{
+	j=(Joint*)joints(i);
+	VALIDMINMAX(j,Joint,stamina);
+	VALIDMINMAX(j,Joint,stif);
+	VALIDMINMAX(j,Joint,rotstif);
+	j->refno=i;
+	j->owner=this;
+	if (j->part1 && j->part2 && (j->part1 != j->part2))
+		{
+		j->p1_refno=j->part1->refno;
+		j->p2_refno=j->part2->refno;
+		if (checklevel>0)
+			{
+			j->part1->mass+=1.0;
+			j->part2->mass+=1.0;
+			}
+		if ((j->usedelta)&&(j->d.x < (JOINT_DELTA_MARKER-1.0)))
+			{ // delta positioning -> calc. orient.
+			if (j->part2->flags & LINKFLAG)
+				{
+				ret=0;
+				FMprintf("Model","internalCheck",FMLV_ERROR,
+					 "delta joint cycle detected at joint#%d (%s)",
+					 i,(const char*)geno.getName());
+				}
+			j->o=j->rot;
+			j->part1->o.transform(j->part2->o,j->o);
+//			j->part2->o.x=j->part1->o/j->o.x;
+//			j->part2->o.y=j->part1->o/j->o.y;
+//			j->part2->o.z=j->part1->o/j->o.z;
+			j->part2->p=j->part2->o.transform(j->d)+j->part1->p;
+			j->part2->flags|=LINKFLAG; j->part1->flags|=LINKFLAG; // for delta joint cycle detection
+			}
+		else
+			{ // abs.positioning -> calc. delta
+			if (final)
+			{
+			// calc orient delta
+//			Orient tmpo(j->part2->o);
+//			tmpo*=j->part1->o;
+			Orient tmpo;
+			j->part1->o.revTransform(tmpo,j->part2->o);
+			tmpo.getAngles(j->rot);
+			j->o=j->rot;
+			// calc position delta
+			Pt3D tmpp(j->part2->p);
+			tmpp-=j->part1->p;
+			j->d=j->part2->o.revTransform(tmpp);
+			}
+			}
+		if (final)
+			{
+			if (j->shape!=Joint::SHAPE_SOLID)
+				{
+			if (j->d()>getMaxJoint().d.x)
+			{
+			ret=0;
+			FMprintf("Model","internalCheck",FMLV_ERROR,"delta too big in joint #%d (%s)",
+				 i,(const char*)geno.getName());
+			}
+			else if (j->d()<getMinJoint().d.x)
+			{
+			ret=0;
+			FMprintf("Model","internalCheck",FMLV_ERROR,"delta too small in joint #%d (%s)",
+				 i,(const char*)geno.getName());
+			}
+				}
+			}
+		}
+	else
+		{
+		FMprintf("Model","internalCheck",FMLV_ERROR,"illegal part references in joint #%d (%s)",
+			 i,(const char*)geno.getName());
+		ret=0;
+		}
+	}
+	}
+#ifdef MODEL_V1_COMPATIBLE
+if (!addOldConnectionsInputs())
+	return 0;
+#endif
+
+updateNeuroRefno(); // valid refno is important for n-n connections check (later)
+
+for (i=0;i<neurons.size();i++)
+	{
+	n=(Neuro*)neurons(i);
+	VALIDMINMAX(n,Neuro,state);
+#ifdef MODEL_V1_COMPATIBLE
+	VALIDMINMAX(n,Neuro,inertia);
+	VALIDMINMAX(n,Neuro,force);
+	VALIDMINMAX(n,Neuro,sigmo);
+	n->conn_refno=-1;
+	n->weight=1.0;
+	n->neuro_refno=-1;
+#endif
+	n->part_refno=(n->part)?n->part->refno:-1;
+	n->joint_refno=(n->joint)?n->joint->refno:-1;
+	}
+
+if (parts.size()&&(checklevel>0))
+	{
+	for (i=0;i<parts.size();i++)
+		{
+		p=(Part*)parts(i);
+		if (p->mass<=0.001)
+			p->mass=1.0;
+		p->flags&=~LINKFLAG;
+		}
+	getPart(0)->flags|=LINKFLAG;
+	int change=1;
+	while(change)
+		{
+		change=0;
+		for (i=0;i<joints.size();i++)
+			{
+			j=(Joint*)joints(i);
+			if (j->part1->flags&LINKFLAG)
+				{
+				if (!(j->part2->flags&LINKFLAG))
+					{
+					change=1;
+					j->part2->flags|=LINKFLAG;
+					}
+				}
+			else
+			if (j->part2->flags&LINKFLAG)
+				{
+				if (!(j->part1->flags&LINKFLAG))
+					{
+					change=1;
+					j->part1->flags|=LINKFLAG;
+					}
+				}
+			}
+		}
+	for (i=0;i<parts.size();i++)
+		{
+		p=(Part*)parts(i);
+		if (!(p->flags&LINKFLAG))
+			{
+			FMprintf("Model","internalCheck",FMLV_ERROR,"not all parts connected (eg.#0-#%d) (%s)",
+				 i,(const char*)geno.getName());
+			ret=0;
+			break;
+			}
+		}
+	}
+
+for (i=0;i<joints.size();i++)
+	{
+	j=(Joint*)joints(i);
+	if (j->p1_refno==j->p2_refno)
+		{
+		FMprintf("Model","internalCheck",FMLV_ERROR,"illegal self connection, joint #%d (%s)",
+			 i,(const char*)geno.getName());
+		ret=0;
+		break;
+		}
+	for (k=i+1;k<joints.size();k++)
+		{
+		Joint* j2=(Joint*)joints(k);
+		if (((j->p1_refno==j2->p1_refno)&&(j->p2_refno==j2->p2_refno))
+		    || ((j->p1_refno==j2->p2_refno)&&(j->p2_refno==j2->p1_refno)))
+			{
+			FMprintf("Model","internalCheck",FMLV_ERROR,"illegal duplicate joints #%d and #%d (%s)",
+				 i,k,(const char*)geno.getName());
+			ret=0;
+			break;
+			}
+		}
+	}
+return ret;
+}
+
+/////////////
+
+int Model::getErrorPosition(bool includingwarnings)
+{
+return includingwarnings?
+	((f0errorposition>=0) ? f0errorposition : f0warnposition)
+	:
+	f0errorposition;
+}
+
+const Geno& Model::getGeno() const
+{
+return geno;
+}
+
+const Geno Model::getF0Geno()
+{
+if (buildstatus==building)
+	FMprintf("Model","getGeno",FMLV_WARN,"model was not completed - missing close()");
+if (buildstatus!=valid)
+	return Geno("",'0',"","invalid");
+if (!f0genoknown)
+	{
+	if (autobuildmaps)
+		{
+		initF0Map();
+		makeGeno(f0geno,f0map);
+		}
+	else
+		{
+		delF0Map();
+		makeGeno(f0geno);
+		}
+	f0genoknown=1;
+	}
+return f0geno;
+}
+
+int Model::getPartCount() const
+{
+return parts.size();
+}
+
+Part* Model::getPart(int i) const
+{
+return ((Part*)parts(i));
+}
+
+int Model::getJointCount() const
+{
+return joints.size();
+}
+
+Joint* Model::getJoint(int i) const
+{
+return ((Joint*)joints(i));
+}
+
+int Model::findJoints(SList& result,const Part* part)
+{
+Joint *j;
+int n0=result.size();
+if (part)
+   for(int i=0;j=(Joint*)joints(i);i++)
+     if ((j->part1 == part) || (j->part2 == part)) result+=(void*)j;
+return result.size()-n0;
+}
+
+int Model::findNeuro(Neuro* n)
+{return neurons.find(n);}
+
+int Model::findPart(Part* p)
+{return parts.find(p);}
+
+int Model::findJoint(Joint* j)
+{return joints.find(j);}
+
+int Model::findJoint(Part *p1, Part *p2)
+{
+Joint* j;
+for(int i=0;j=getJoint(i);i++)
+	if ((j->part1==p1)&&(j->part2==p2)) return i;
+return -1;
+}
+
+
+////////////////////
+
+#ifdef MODEL_V1_COMPATIBLE
+void Model::calcOldNeuroCount()
+{
+if (oldneurocount>=0) return;
+oldneurocount=reorderToOldLayout();
+}
+
+int Model::old_getNeuroCount()
+{ calcOldNeuroCount();
+ return oldneurocount;}
+
+Neuro* Model::old_getNeuro(int i) 
+{calcOldNeuroCount();
+return (i<oldneurocount)? (Neuro*)getNeuro(i) : (Neuro*)0;
+}
+
+int Model::old_findNeuro(Neuro* n)
+{calcOldNeuroCount();
+return findNeuro(n);}
+
+Neuro *Model::old_addNewNeuro()
+{
+int count=old_getNeuroCount();
+Neuro *nu=addNewNeuro();
+nu->setClassName("N");
+moveNeuro(nu->refno,oldneurocount);
+oldneurocount=count+1;
+return (Neuro*)nu;
+}
+#endif
+
+///////////////////////
+
+int Model::getNeuroCount() const
+{return neurons.size();}
+
+Neuro* Model::getNeuro(int i) const
+{return (Neuro*)neurons(i);}
+
+int Model::getConnectionCount() const
+{
+int n=0;
+for(int i=0;i<getNeuroCount();i++)
+	n+=getNeuro(i)->getInputCount();
+return n;
+}
+
+int Model::findNeuros(SList& result,
+			  const char* classname,const Part* part,const Joint* joint)
+{
+Neuro *nu;
+SString cn(classname);
+int n0=result.size();
+for(int i=0;nu=(Neuro*)neurons(i);i++)
+	{
+	if (part)
+		if (nu->part != part) continue;
+	if (joint)
+		if (nu->joint != joint) continue;
+	if (classname)
+		if (nu->getClassName() != cn) continue;
+	result+=(void*)nu;
+	}
+return result.size()-n0;
+}
+
+///////////////////
+
+void Model::disturb(double amount)
+{
+int i;
+if (amount<=0) return;
+for(i=0;i<parts.size();i++)
+	{
+	Part *p=getPart(i);
+	p->p.x+=(rnd01-0.5)*amount;
+	p->p.y+=(rnd01-0.5)*amount;
+	p->p.z+=(rnd01-0.5)*amount;
+	}
+for(i=0;i<joints.size();i++)
+	{
+	Joint *j=getJoint(i);
+	Pt3D tmpp(j->part2->p);
+	tmpp-=j->part1->p;
+	j->d=j->part2->o.revTransform(tmpp);
+	}
+}
+
+//////////////////////
+
+class MinPart: public Part {public: MinPart() {Param par(f0_part_paramtab,this);par.setMin();}};
+class MaxPart: public Part {public: MaxPart() {Param par(f0_part_paramtab,this);par.setMax();}};
+class MinJoint: public Joint {public: MinJoint() {Param par(f0_joint_paramtab,this);par.setMin();}};
+class MaxJoint: public Joint {public: MaxJoint() {Param par(f0_joint_paramtab,this);par.setMax();}};
+class MinNeuro: public Neuro {public: MinNeuro() {Param par(f0_neuro_paramtab,this);par.setMin();}};
+class MaxNeuro: public Neuro {public: MaxNeuro() {Param par(f0_neuro_paramtab,this);par.setMax();}};
+
+Part& Model::getMinPart() {static MinPart part; return part;}
+Part& Model::getMaxPart() {static MaxPart part; return part;}
+Part& Model::getDefPart() {static Part part; return part;}
+Joint& Model::getMinJoint() {static MinJoint joint; return joint;}
+Joint& Model::getMaxJoint() {static MaxJoint joint; return joint;}
+Joint& Model::getDefJoint() {static Joint joint; return joint;}
+Neuro& Model::getMinNeuro() {static MinNeuro neuro; return neuro;}
+Neuro& Model::getMaxNeuro() {static MaxNeuro neuro; return neuro;}
+Neuro& Model::getDefNeuro() {static Neuro neuro; return neuro;}
Index: /cpp/frams/model/model.h
===================================================================
--- /cpp/frams/model/model.h	(revision 109)
+++ /cpp/frams/model/model.h	(revision 109)
@@ -0,0 +1,421 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _MODEL_H_
+#define _MODEL_H_
+
+#include <common/nonstd_math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "modelparts.h"
+#include <frams/util/advlist.h>
+#include <frams/util/usertags.h>
+
+extern ParamEntry f0_model_paramtab[];
+
+//#define EASYMAPDEBUG
+
+enum ModelBuildStatus {empty,building,invalid,valid};
+
+class MultiMap;
+
+class VisualModel;
+
+/**
+    "Model" is the skeleton of the framsticks creature.
+    This object can be used for 2 purposes:
+     - you can build a creature from any supported framsticks genotype
+    format
+     - or generate low level f0 genotype from existing construct.
+
+    In both cases you have access to geometry and neuron net data.
+    Using this standard class assures compatibility and good
+    integration with core framsticks engine.
+
+    Model contains 3 kinds of objects:
+    - Parts (class Part).
+    - Joints (class Joint). Each Joint is connected with 2 Parts. (@see Joint::attachToParts()).
+    - Neurons (class Neuro). Neuron can have 0 or more inputs - other neurons. (@see Neuro::addInput()).
+      Each Neuron can be located on the physical structure, i.e. it can ba attached to Part or Joint
+      (@see Neuro::attachToPart(), Neuro::attachToJoint()).
+
+\f[(dot)
+digraph Model
+ {
+  Joint1; Joint2;
+  node [ shape=box ]
+  Part1; Part2; Part3;
+  Joint1 -> Part1; Joint1 -> Part2; Joint2 -> Part2; Joint2 -> Part3
+  node [ shape=diamond ]
+  Neuro1 -> Neuro2; Neuro1 -> Neuro3; Neuro2 -> Neuro2; Neuro3 -> Neuro2;
+  Neuro1 -> Part1; Neuro2 -> Joint2;
+ }
+\f]
+*/
+
+class Model: public DestrBase
+{
+  protected:
+Geno geno,f0geno;
+char modelfromgenotype;
+char f0genoknown;
+/// make model map in build()
+bool autobuildmaps;
+/// valid if build from f0 genotype
+int f0errorposition;
+/// valid if build from f0 genotype
+int f0warnposition;
+
+ModelBuildStatus buildstatus;
+/// NULL if the map is not (yet) created
+MultiMap *map,*f0map;
+
+SList parts,joints,neurons;
+char partmappingchanged;
+
+#ifdef MODEL_V1_COMPATIBLE
+/** NeuroCount value.
+    compatibility_neurocount = -1 if its value is unknown and the layout is not compatible
+    @see reorderToOldLayout()
+*/
+int oldneurocount;
+char oldconnections;
+
+/** calculate oldNeuroCount */
+void calcOldNeuroCount();
+/** some new calls can invalidate old compatibility data */
+void invalidateOldNeuroCount() {oldneurocount=-1;}
+/**
+   for compatibility with old NeuroClass the layout of Neurons
+   is changed when old 'Neuro' accessing methods are in use.
+   Neurons:
+   0 .. compatibility_neurocount-1                : old Neurons (class "N")
+   compatibility_neurocount .. neurons.size()-1   : other units
+*/
+int reorderToOldLayout();
+
+/** check if compatibility should be preserved */
+int hasOldNeuroLayout() {return oldneurocount>=0;}
+
+/** add inputs to the old "-" units.
+    @return 1=ok, 0=invalid input detected  */
+int addOldConnectionsInputs();
+#endif
+
+void internalCopy(const Model &mod);
+
+/// make the model from current genotype
+void build();
+
+friend class NeuroNetFactory;
+friend class VisualModel;
+friend class GLVisualModel;
+friend class Creature;
+friend class PartBase;
+
+int checklevel;
+
+void updateNeuroRefno(); // set Neuro::refno for all neurons
+int internalcheck(int final);
+
+void moveNeuro(int oldpos,int newpos);
+
+void init(const Geno &srcgen);
+void init();
+
+void delMap();
+void delF0Map();
+void initMap();
+void initF0Map();
+
+public:
+/** get current model state.
+\f[(dot)
+digraph M
+ {
+ node [fontsize=12]
+ edge [fontsize=10]
+ building [label="building = can be modified"]
+ valid -> building [label="open()"]
+ building -> valid [label="close()"]
+ invalid -> building [label="open()"]
+ building -> invalid [label="close() [failed]"]
+ empty -> building [label="open()"]
+ }
+\f]
+*/
+ModelBuildStatus getStatus() const {return buildstatus;}
+int isValid() const {return buildstatus==valid;}
+int getErrorPosition(bool includingwarnings=false);
+
+void updateRefno(); // set ::refno for all elements
+
+/// The bounding box size. Valid if the model is valid. Read only.
+Pt3D size;
+
+SString vis_style;
+double startenergy;
+Callback delmodel_list;
+ModelUserTags userdata;
+
+/// Create empty model with invalid empty genotype
+Model();
+
+/** Create a model based on provided genotype
+   @param buildmaps if not 0, generate mapping information for the model.
+   default is 0, because mapping uses additional time and memory.
+   @see getMap()
+ */
+Model(const Geno &src,bool buildmaps=false);
+Model(const Model &mod,bool buildmaps=false);
+/** duplicate the model.
+    the resulting object's status is 'building' (opened).
+    @see getStatus()
+ */
+void operator=(const Model &source);
+
+/** move all elements from 'source' into our model object.
+    'source' becomes empty after this operation.
+    the model will be opened if it is not already open.
+    @see addElementsFrom(const Model &source);
+ */
+void moveElementsFrom(Model &source);
+
+/** copy all elements from 'source' into our model object
+    without affecting the 'source'.
+    the model will be opened if it is not already open.
+    @see moveElementsFrom(Model &source);
+ */
+void addElementsFrom(const Model &source)
+{Model m(source); moveElementsFrom(m);}
+
+void operator+=(const Model &source)
+{addElementsFrom(source);}
+
+~Model();
+
+/** @return source genotype.
+    @warn source genotype will not automatically change 
+    when the model is modified. this behaviour is inconsistent
+    with the previous release. use getF0Geno() if you need
+    the updated genotype.
+    @see getF0Geno(), setGeno()
+*/
+const Geno &getGeno() const;
+
+/// change source genotype
+void setGeno(const Geno& newgeno);
+
+/** @return f0 genotype - generated from current model state
+    don't use between open()-close()
+*/
+const Geno getF0Geno();
+
+/// make f0 genotype from current construction (low level version of getF0Geno)
+void makeGeno(Geno &,MultiMap *map=0,bool handle_defaults=true);
+
+/** @return mapping from source genotype (0-based position in text) to model elements reference numbers.
+    the map can be empty if the mapping hasn't been requested earlier (in constructor)
+    or the converters don't support mapping.
+    if you create or modify the model using singleStepBuild() or direct manipulation
+    the map will be not changed or created automatically - it is your responsibility.
+    @see Model(const Geno &src,int buildmaps=0), singleStepBuild(), PartBase::addMapping()
+    @see clearMap()
+    @see convmap
+
+*/
+MultiMap &getMap();
+
+/** @return mapping from f0 genotype (0-based position in text) to model elements reference numbers
+ */
+const MultiMap &getF0Map();
+
+/** discard all mapping information for this model.
+    getMap().clear() also works, but it doesn't remove mappings from model elements.
+    if there are any, the will be incorporated into model map during close().
+    @see close(), getMap(), PartBase::clearMapping()
+ */
+void clearMap();
+
+/** generate mapping from the current genotype to the f0 genotype.
+    this works only if both current and f0 maps are already known.
+    @see convmap
+*/
+void getCurrentToF0Map(MultiMap& m);
+
+void setValidationLevel(int level)
+	{checklevel=level;}
+
+/// calculate location of the new part connected to the existing one
+/// using delta option
+Pt3D whereDelta(const Part& start,const Pt3D& rot, const Pt3D& delta);
+
+/// create the whole model from scratch, using current genotype
+void rebuild(bool buildmaps);
+
+/// setGeno(newgeno); rebuild();
+void rebuild(const Geno& newgeno,bool buildmaps) {setGeno(newgeno); rebuild(buildmaps);}
+
+/// reuse current model object but discard all model data
+void clear();
+
+/** execute single line of <B>f0</B> genotype.
+    return value is non-negative reference number of the created item,
+    or negative value. reference number can be used to access
+    the item using getPart(int), getJoint(int) and getNeuroItem(int) methods.
+    @param srcrange source genotype range which will be mapped to this element
+*/
+int singleStepBuild(const SString &singleline,const MultiRange* srcrange=0);
+
+/// separate build stages (for future use)
+void checkpoint();
+
+/// call resetDelta() on all joints
+void resetAllDelta();
+
+/// call useDelta() on all joints
+void useAllDelta(bool yesno);
+
+/// final validity check of the model, all model data has to be available at this point.
+/// if the model was modified, the genotype will be also updated.
+/// it also calls "validate" with all side effects.
+/// @return > 0 means "valid"
+int close();
+
+/// enable model building.
+/// you should use it if you need to create new model, modify the model after close
+/// or modify the model created from the genotype.
+/// between open() and close() the model is not fully usable, 
+void open();
+
+/// current model written as f0 genotype while building
+/// (not cached, not validated, probably unusable and bad if used before close(). but good for debugging.)
+Geno rawGeno();
+
+/// partial validity check - you can use this call
+/// anytime between open - close.
+/// this function will check (and repair)
+/// - part-joint-neuro connections
+/// - model geometry (if "delta option" was used)
+/// - physical/biological limits
+/// @return 1 = valid
+/// @return 0 = invalid
+/// validate doesn't make the model fully usable (for simulation)
+/// you still need to use close if you have changed anything
+int validate();
+
+int getPartCount() const;
+/// you can access parts 0 .. getPartCount()-1.
+Part *getPart(int i) const;
+
+int getJointCount() const;
+/// you can access joints 0 .. getJointCount()-1.
+Joint *getJoint(int i) const;
+
+int getNeuroCount() const;
+int getConnectionCount() const;
+/// you can access neurons 0 .. getNeuroCount()-1.
+Neuro *getNeuro(int i) const;
+
+#ifdef MODEL_V1_COMPATIBLE
+/* compatibility calls for accessing old Neuro objects */
+
+/// @deprecated Neuro class will be removed soon
+/// @see compat
+int old_getNeuroCount();
+/// you can access neurons 0 .. getNeuroCount()-1.
+/// @deprecated Neuro class will be removed soon
+Neuro *old_getNeuro(int i);
+/// @see addNewNeuro, addNeuro
+/// @deprecated Neuro class will be removed soon
+Neuro *old_addNewNeuro();
+/// @return neuro index or -1 if not found
+/// @deprecated Neuro class will be removed soon
+int old_findNeuro(Neuro* n);
+#endif
+
+/** create new Part and add it to the model. @see addPart()  */
+Part *addNewPart(Part::Shape shape=Part::SHAPE_DEFAULT) {return addPart(new Part(shape));}
+/** create new Joint and add it to the model. @see addJoint() */
+Joint *addNewJoint(Part *p1=NULL,Part *p2=NULL,Joint::Shape shape=Joint::SHAPE_DEFAULT) { Joint *j=addJoint(new Joint()); j->shape=shape; if ((p1!=NULL)&&(p2!=NULL)) j->attachToParts(p1,p2); return j;}
+/** create new Neuro and add it to the model. @see addNeuro() */
+Neuro *addNewNeuro() {return addNeuro(new Neuro());}
+
+/** add p to the model. p->refno is adjusted. @return the Part just added (==p). */
+Part *addPart(Part *p);
+/** add j to the model. j->refno is adjusted. @return the Joint just added (==j). */
+Joint *addJoint(Joint *j);
+/** add n to the model. n->refno is adjusted. @return the Neuro just added (==n). */
+Neuro *addNeuro(Neuro *n);
+
+/** remove the part from model.
+    @param removeattachedjoints if not 0 -> remove all joints connected with this part
+    @param removeattachedneurons if not 0 -> remove neurons attached to this part */
+void removePart(int partindex,int removeattachedjoints=1,int removeattachedneurons=1);
+
+/** remove the joint from model.
+    @param removeattachedneurons if not 0 -> remove neurons attached to this joint */
+void removeJoint(int jointindex,int removeattachedneurons=1);
+
+/** remove the neuron from model.
+    @param removereferences if true -> look for references to this neuron
+    (i.e. connections from other neurons) and remove them as well */
+void removeNeuro(int neuroindex,bool removereferences=true);
+
+void removeNeuros(SList &nlist);
+
+/// @return part index or -1 if not found in the model
+int findPart(Part* p);
+/// @return joint index or -1 if not found in the model
+int findJoint(Joint* j);
+/// @return neuro index or -1 if not found in the model
+int findNeuro(Neuro* nu);
+/// @return joint index or -1 if not found in the model
+int findJoint(Part *p1, Part *p2);
+
+/** make the list of neuros satisfying given search criteria: classname,part,joint
+    @param result objects will be appended here
+    @return number of objects found  */
+int findNeuros(SList& result,const char* classname=0,const Part* part=0,const Joint* joint=0);
+
+/** search for joints connected to the part
+    @param result objects will be appended here
+    @return number of objects found  */ 
+int findJoints(SList& result,const Part* part=0);
+
+void disturb(double amount);
+
+#ifdef EASYMAPDEBUG
+static int partToMap(int i) {return 0+i;}
+static int jointToMap(int i) {return 10+i;}
+static int neuroToMap(int i) {return 20+i;}
+static int mapToPart(int i) {return i-0;}
+static int mapToJoint(int i) {return i-10;}
+static int mapToNeuro(int i) {return i-20;}
+#else
+static int partToMap(int i) {return 0x10000000+i;}
+static int jointToMap(int i) {return 0x20000000+i;}
+static int neuroToMap(int i) {return 0x30000000+i;}
+static int mapToPart(int i) {return i-0x10000000;}
+static int mapToJoint(int i) {return i-0x20000000;}
+static int mapToNeuro(int i) {return i-0x30000000;}
+#endif
+
+static void makeGenToGenMap(MultiMap& result,const MultiMap& gen1tomodel,const MultiMap& gen2tomodel);
+
+///////////////////////////
+
+static Part& getMinPart();
+static Part& getMaxPart();
+static Part& getDefPart();
+static Joint& getMinJoint();
+static Joint& getMaxJoint();
+static Joint& getDefJoint();
+static Neuro& getMinNeuro();
+static Neuro& getMaxNeuro();
+static Neuro& getDefNeuro();
+};
+
+#endif
Index: /cpp/frams/model/modelparts.cpp
===================================================================
--- /cpp/frams/model/modelparts.cpp	(revision 109)
+++ /cpp/frams/model/modelparts.cpp	(revision 109)
@@ -0,0 +1,885 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+
+///////////////////////////////     MODELPARTS.CPP     ///////////////
+
+#include "modelparts.h"
+#include "model.h"
+
+#include <common/nonstd.h>
+#include <frams/param/param.h>
+#include <frams/neuro/neurolibrary.h>
+#include <frams/util/multirange.h>
+#include <frams/util/extvalue.h>
+#include <frams/param/paramobj.h>
+
+#include F0_DEFASSIGN_FILE
+
+#ifndef GDK_WITHOUT_FRAMS
+#include <frams/neuro/neuroclsobject.h>
+#endif
+
+template<>
+char ModelUserTags::reg[5]={0};
+
+/////////////////////////
+
+PartBase::~PartBase()
+{if (mapped) delete mapped;}
+
+void PartBase::notifyMappingChange()
+{
+if (owner) owner->partmappingchanged=1;
+}
+
+void PartBase::setMapping(const IRange &r)
+{
+if (mapped) (*mapped)=r;
+else mapped=new MultiRange(r);
+notifyMappingChange();
+}
+
+void PartBase::clearMapping()
+{
+if (mapped) {delete mapped; mapped=0;}
+}
+
+void PartBase::addMapping(const IRange &r)
+{
+if (mapped) mapped->add(r);
+else mapped=new MultiRange(r);
+notifyMappingChange();
+}
+
+void PartBase::setMapping(const MultiRange &mr)
+{
+if (mapped) (*mapped)=mr;
+else mapped=new MultiRange(mr);
+notifyMappingChange();
+}
+
+void PartBase::addMapping(const MultiRange &mr)
+{
+if (mapped) mapped->add(mr);
+else mapped=new MultiRange(mr);
+notifyMappingChange();
+}
+
+void PartBase::setInfo(const SString& name,const SString& value)
+{
+strSetField(info,name,value);
+}
+
+void PartBase::setInfo(const SString& name,int value)
+{
+setInfo(name,SString::valueOf(value));
+}
+
+void PartBase::setInfo(const SString& name,double value)
+{
+setInfo(name,SString::valueOf(value));
+}
+
+SString PartBase::getInfo(const SString& name)
+{
+return strGetField(info,name);
+}
+
+/////////////////////////
+
+NeuroClass::NeuroClass(ParamEntry *_props,SString _description,
+		       int _prefinputs,int _prefoutput,int _preflocation,
+		       int *_vectordata,bool own_vd,int vhints)
+	:ownedvectordata(own_vd),
+	 name(_props->name),longname(_props->id),description(_description),
+	 props(_props),ownedprops(false),
+	 prefinputs(_prefinputs),
+	 prefoutput(_prefoutput),
+	 preflocation(_preflocation),
+	 vectordata(_vectordata),
+	 visualhints(vhints),impl_count(0),/*impl(0),*/active(1),genactive(0)
+{}
+
+NeuroClass::~NeuroClass()
+{
+setSymbolGlyph(0,0);
+if (props && ownedprops)
+	ParamObject::freeParamTab(props);
+}
+
+NeuroClass::NeuroClass()
+	:ownedvectordata(0),
+	 name("Invalid"),
+	 props(empty_paramtab),ownedprops(false),
+	 prefinputs(0), prefoutput(0),
+	 preflocation(0), vectordata(0), 
+	 visualhints(0),impl_count(0), /*impl(0),*/ active(1), genactive(0)
+{}
+
+SString NeuroClass::getSummary()
+{
+SString t;
+t=getDescription();
+if (t.len()) t+="\n\n";
+t+="Characteristics:\n";
+if(getPreferredInputs())
+	{
+	if (getPreferredInputs()<0) t+="   supports any number of inputs\n";
+	else if (getPreferredInputs()==1) t+="   uses single input\n";
+	else t+=SString::sprintf("   uses %d inputs\n",getPreferredInputs());
+	}
+else t+="   does not use inputs\n";
+if(getPreferredOutput())
+	t+="   provides output value\n";
+else
+	t+="   does not provide output value\n";
+switch(getPreferredLocation())
+	{
+	case 0: t+="   does not require location in body\n"; break;
+	case 1: t+="   should be located on a Part\n"; break;
+	case 2: t+="   should be located on a Joint\n"; break;
+	}
+Param p=getProperties();
+if (p.getPropCount())
+	{
+	if (t.len()) t+="\n\n";
+	t+="Properties:\n";
+	const char *h;
+	int i;
+	for(i=0;i<p.getPropCount();i++)
+		{
+		if (i) t+="\n";
+		t+="   "; t+=p.name(i); t+=" ("; t+=p.id(i); t+=")";
+		switch(*p.type(i))
+			{
+			case 'd': t+=" integer";
+			{long a,b,c; if (p.getMinMax(i,a,b,c)>=2) t+=SString::sprintf(" %d..%d",a,b);}
+				break;
+			case 'f': t+=" float";
+			{double a,b,c; if (p.getMinMax(i,a,b,c)>=2) t+=SString::sprintf(" %g..%g",a,b);}
+				break;
+			case 's': t+=" string"; break;
+			case 'x': t+=" anything"; break;
+			}
+		if (h=p.help(i)) if (*h) {t+=" - "; t+=h;}
+		}
+	}
+return t;
+}
+
+/////////////////////////
+
+/////////////////////////////////////
+
+Neuro::Neuro(double _state,double _inertia,double _force,double _sigmo)
+	:PartBase(getDefaultStyle()),state(_state)
+#ifdef MODEL_V1_COMPATIBLE
+,inertia(_inertia),force(_force),sigmo(_sigmo)
+#endif
+{
+#ifdef MODEL_V1_COMPATIBLE
+olditems=0;
+#endif
+flags=0;
+myclass=0;
+knownclass=1;
+part_refno=-1; joint_refno=-1;
+}
+
+Neuro::Neuro(void):PartBase(getDefaultStyle())
+{
+defassign();
+state=0.0;
+myclass=NULL;
+myclassname="N";//default d="N" but f0.def is unable to set this (d is GETSET, not a regular FIELD)
+knownclass=0;
+refno=0;
+pos=Pt3D_0; rot=Pt3D_0;
+parent=0; part=0; joint=0;
+parentcount=0;
+#ifdef MODEL_V1_COMPATIBLE
+olditems=0;
+#endif
+flags=0;
+part_refno=-1; joint_refno=-1;
+}
+
+
+Neuro::~Neuro()
+{
+#ifdef MODEL_V1_COMPATIBLE
+if (olditems) delete olditems;
+#endif
+int i;
+for(i=0;i<inputs.size();i++)
+	{
+	NInput &ni=inputs(i);
+	if (ni.info) delete ni.info;
+	}
+}
+
+SString** Neuro::inputInfo(int i)
+{
+if (i>=getInputCount()) return 0;
+return &inputs(i).info;
+}
+
+void Neuro::setInputInfo(int i,const SString& name,const SString &value)
+{
+SString **s=inputInfo(i);
+if (!s) return;
+if (!*s) *s=new SString();
+strSetField(**s,name,value);
+}
+
+void Neuro::setInputInfo(int i,const SString& name,int value)
+{
+setInputInfo(i,name,SString::valueOf(value));
+}
+
+void Neuro::setInputInfo(int i,const SString& name,double value)
+{
+setInputInfo(i,name,SString::valueOf(value));
+}
+
+SString Neuro::getInputInfo(int i)
+{
+SString **s=inputInfo(i);
+if (!s) return SString();
+if (!*s) return SString();
+return **s;
+}
+
+SString Neuro::getInputInfo(int i,const SString& name)
+{
+SString **s=inputInfo(i);
+if (!s) return SString();
+if (!*s) return SString();
+return strGetField(**s,name);
+}
+
+void Neuro::operator=(const Neuro& src)
+{
+refno=src.refno;
+#ifdef MODEL_V1_COMPATIBLE
+neuro_refno=-1;
+conn_refno=-1;
+force=src.force;
+sigmo=src.sigmo;
+inertia=src.inertia;
+weight=src.weight;
+olditems=0;
+#endif
+state=src.state;
+part_refno=-1;
+joint_refno=-1;
+pos=src.pos; rot=src.rot;
+parent=0; part=0; joint=0;
+parentcount=0;
+flags=0;
+myclass=src.myclass;
+knownclass=src.knownclass;
+myclassname=src.myclassname;
+myclassparams=src.myclassparams;
+}
+
+void Neuro::attachToPart(int i)
+{attachToPart((i>=0)?owner->getPart(i):0);}
+
+void Neuro::attachToJoint(int i)
+{attachToJoint((i>=0)?owner->getJoint(i):0);}
+
+int Neuro::getClassCount()
+{return NeuroLibrary::staticlibrary.getClassCount();}
+
+NeuroClass* Neuro::getClass(int classindex)
+{return NeuroLibrary::staticlibrary.getClass(classindex);}
+
+NeuroClass* Neuro::getClass(const SString& classname)
+{return NeuroLibrary::staticlibrary.findClass(classname);}
+
+int Neuro::getClassIndex(const NeuroClass*nc)
+{return NeuroLibrary::staticlibrary.classes.find((void*)nc);}
+
+NeuroClass* Neuro::getClass()
+{
+checkClass();
+return myclass;
+}
+
+void Neuro::setClass(NeuroClass* cl)
+{
+myclass=cl;
+myclassname=cl->getName();
+knownclass=1;
+}
+
+SString Neuro::getClassName(int classindex)
+{
+NeuroClass *cl=NeuroLibrary::staticlibrary.getClass(classindex);
+return cl? cl->getName() : SString();
+}
+
+void Neuro::setDetails(const SString& details)
+{
+int colon=details.indexOf(':');
+if (colon>=0) {myclassname=details.substr(0,colon); myclassparams=details.substr(colon+1);}
+else {myclassname=details; myclassparams=0;}
+knownclass=0;
+}
+
+SString Neuro::getDetails()
+{
+SString ret=getClassName();
+if (myclassparams.len()) {if (!ret.len()) ret="N"; ret+=":"; ret+=myclassparams;}
+return ret;
+}
+
+void Neuro::checkClass()
+{
+if (knownclass) return;
+myclass=getClass(myclassname);
+knownclass=1;
+}
+
+SyntParam Neuro::classProperties(bool handle_defaults_when_saving)
+{
+NeuroClass *cl=getClass();
+ParamEntry *pe = cl ? cl->getParamTab() : emptyParamTab;
+return SyntParam(pe,&myclassparams,handle_defaults_when_saving);
+}
+
+SString Neuro::getClassName()
+{
+return myclassname;
+}
+
+void Neuro::setClassName(const SString& clazz)
+{
+myclassname=clazz;
+knownclass=0;
+}
+
+int Neuro::addInput(Neuro* child,double weight,const SString *info)
+{
+inputs+=NInput(child,weight,(info&&(info->len()))?new SString(*info):0);
+child->parentcount++;
+if (child->parentcount==1) {child->parent=this;}
+return inputs.size()-1;
+}
+
+int Neuro::findInput(Neuro* child) const
+{
+for(int i=0;i<inputs.size();i++)
+	if (inputs(i).n==child) return i;
+return -1;
+}
+
+Neuro* Neuro::getInput(int i,double &weight) const
+{
+if (i>=getInputCount()) return 0;
+NInput &inp=inputs(i);
+weight=inp.weight;
+return inp.n;
+}
+
+double Neuro::getInputWeight(int i) const
+{
+return inputs(i).weight;
+}
+
+void Neuro::setInputWeight(int i,double w)
+{
+inputs(i).weight=w;
+}
+
+void Neuro::setInput(int i,Neuro* n)
+{
+NInput &inp=inputs(i);
+inp.n=n;
+}
+
+void Neuro::setInput(int i,Neuro* n,double w)
+{
+NInput &inp=inputs(i);
+inp.n=n;
+inp.weight=w;
+}
+
+void Neuro::removeInput(int refno)
+{
+Neuro *child=getInput(refno);
+child->parentcount--;
+if (child->parent==this) child->parent=0;
+SString *s=inputs(refno).info;
+if (s) delete s;
+inputs.remove(refno);
+}
+
+int Neuro::removeInput(Neuro* child)
+{
+int i=findInput(child);
+if (i>=0) removeInput(i);
+return i;
+}
+
+int Neuro::getOutputsCount() const
+{
+   int c=0;
+   for(int i=0;i<owner->getNeuroCount();i++)
+     for(int j=0;j<owner->getNeuro(i)->getInputCount();j++) c+=owner->getNeuro(i)->getInput(j)==this;
+   return c;
+}
+
+int Neuro::isOldEffector()
+{
+static SString bend("|"),rot("@");
+return ((getClassName()==bend)||(getClassName()==rot));
+}
+
+int Neuro::isOldReceptor()
+{
+static SString g("G"),t("T"),s("S");
+return ((getClassName()==g)||(getClassName()==t)||(getClassName()==s));
+}
+
+int Neuro::isOldNeuron()
+{
+static SString n("N");
+return (getClassName()==n);
+}
+
+int Neuro::isNNConnection()
+{
+static SString conn("-");
+return (getClassName()==conn);
+}
+
+int Neuro::findInputs(SList& result,const char* classname,const Part* part,const Joint* joint) const
+{
+Neuro *nu;
+SString cn(classname);
+int n0=result.size();
+for(int i=0;nu=getInput(i);i++)
+	{
+	if (part)
+		if (nu->part != part) continue;
+	if (joint)
+		if (nu->joint != joint) continue;
+	if (classname)
+		if (nu->getClassName() != cn) continue;
+	result+=(void*)nu;
+	}
+return result.size()-n0;
+}
+
+int Neuro::findOutputs(SList& result,const char* classname,const Part* part,const Joint* joint) const
+{ // not very efficient...
+Neuro *nu,*inp;
+SString cn(classname);
+SList found;
+int n0=result.size();
+for(int i=0;nu=getModel().getNeuro(i);i++)
+	{
+	if (part)
+		if (nu->part != part) continue;
+	if (joint)
+		if (nu->joint != joint) continue;
+	if (classname)
+		if (inp->getClassName() != cn) continue;
+	for (int j=0;inp=nu->getInput(j);j++)
+		if (inp==this)
+			{
+			result+=(void*)nu;
+			break;
+			}
+	}
+return result.size()-n0;
+}
+
+void Neuro::get_inputCount(PARAMGETARGS)
+{ret->setInt(inputs.size());}
+
+void Neuro::p_getInputNeuroDef(ExtValue *args,ExtValue *ret)
+{
+int i=args->getInt();
+if ((i<0)||(i>=inputs.size()))
+	ret->setEmpty();
+else
+	ret->setObject(ExtObject(&Neuro::getStaticParam(),inputs(i).n));
+}
+
+void Neuro::p_getInputWeight(ExtValue *args,ExtValue *ret)
+{
+int i=args->getInt();
+if ((i<0)||(i>=inputs.size()))
+	ret->setEmpty();
+else
+	ret->setDouble(inputs(i).weight);
+}
+
+void Neuro::p_getInputNeuroIndex(ExtValue *args,ExtValue *ret)
+{
+int i=args->getInt();
+if ((i<0)||(i>=inputs.size()))
+	ret->setInt(-1);
+else
+	ret->setInt(inputs(i).n->refno);
+}
+
+void Neuro::get_classObject(PARAMGETARGS)
+{
+#ifndef GDK_WITHOUT_FRAMS
+NeuroClassExt::makeStaticObject(ret,getClass());
+#endif
+}
+
+/////// old items
+#ifdef MODEL_V1_COMPATIBLE
+void OldItems::buildlist()
+{ // guaranteed to work only for old NN layouts
+ // (neurons,neuro connections, old receptors and effectors)
+if (listok) return;
+ // inputs can contain both neuroitem connections (details="") and direct neuron references (details="N")
+ // in OldItems we create neuroitems freom direct references
+for(int i=0;i<neuro.getInputCount();i++)
+	{
+	float w;
+	Neuro *n=neuro.getInput(i,w);
+	if (n->isOldNeuron())
+		{
+		Neuro *ni=new Neuro();
+		ni->setClassName("-");
+		ni->weight=w;
+		ni->neuro_refno=neuro.refno;
+		ni->conn_refno=n->refno;
+		items+=ni;
+		syntitems+=ni;
+		}
+	else
+		{
+		items+=n;
+		n->weight=w;
+		}
+	}
+SList outputs;
+neuro.findOutputs(outputs);
+FOREACH(Neuro*,n,outputs)
+	{
+	if (n->isNNConnection() || n->isOldNeuron())
+		outputs-=n;
+	}
+items+=outputs;
+listok=1;
+}
+
+void OldItems::freelist()
+{
+FOREACH(Neuro*,n,syntitems)
+	delete n;
+syntitems.clear();
+items.clear();
+listok=0;
+}
+
+int OldItems::getItemCount()
+{
+buildlist();
+return items.size();
+}
+
+NeuroItem *OldItems::getNeuroItem(int i)
+{
+buildlist();
+return (NeuroItem*)items(i);
+}
+
+NeuroItem *OldItems::addNewNeuroItem()
+{
+Neuro *nu=neuro.getModel().addNewNeuro();
+nu->setClassName("-");
+if (listok) items+=nu;
+neuro.addInput(nu);
+return (NeuroItem*)nu;
+}
+
+int OldItems::findNeuroItem(NeuroItem *ni)
+{
+buildlist();
+return items.find((void*)ni);
+}
+#endif
+
+///////////////////////////////////////
+
+SString Part::getDefaultStyle()
+{return SString("part");}
+SString Joint::getDefaultStyle()
+{return SString("joint");}
+/*
+const SString& Neuro::getDefaultStyle()
+{static SString s("neuro"); return s;}
+const SString& NeuroItem::getDefaultStyle()
+{static SString s("neuroitem"); return s;}
+*/
+SString Neuro::getDefaultStyle()
+{return SString("neuro");}
+
+Part::Part(enum Shape s):PartBase(getDefaultStyle())
+{
+o=Orient_1;
+p=Pt3D_0;
+rot=Pt3D_0;
+flags=0;
+defassign();
+shape=s;
+}
+
+void Part::operator=(const Part& src)
+{
+p=src.p; o=src.o;
+flags=src.flags;
+mass=src.mass; density=src.density;
+friction=src.friction;
+ingest=src.ingest;
+assim=src.assim;
+size=src.size;
+rot=src.rot;
+refno=src.refno;
+vcolor=src.vcolor;
+vsize=src.vsize;
+shape=src.shape;
+}
+
+void Part::setOrient(const Orient &_o)
+{
+o=_o;
+rot.getAngles(o.x,o.z);
+}
+
+void Part::setRot(const Pt3D &r)
+{
+rot=r;
+o=Orient_1;
+o.rotate(rot);
+}
+
+void Part::setPositionAndRotationFromAxis(const Pt3D &p1,const Pt3D &p2)
+{
+Pt3D x=p2-p1;
+Pt3D dir(x.y,x.z,x.x);
+p=p1+x*0.5;
+rot.getAngles(x,dir);
+}
+
+Param& Part::getStaticParam()
+{
+static Param p(f0_part_paramtab,0,"Part");
+return p;
+}
+
+
+///////////////////////////
+
+Joint::Joint():PartBase(getDefaultStyle())
+{
+rot=Pt3D_0;
+defassign();
+d.x=JOINT_DELTA_MARKER;
+part1=0; part2=0;
+flags=0;
+usedelta=0;
+}
+
+void Joint::operator=(const Joint& src)
+{
+rot=src.rot;
+d=src.d;
+stamina=src.stamina;
+stif=src.stif; rotstif=src.rotstif;
+part1=0; part2=0;
+flags=src.flags;
+usedelta=src.usedelta;
+refno=src.refno;
+}
+
+void Joint::attachToParts(Part* p1,Part* p2)
+{
+part1=p1;
+part2=p2;
+if (p1 && p2)
+	{
+	o=rot;
+	if (usedelta)
+		{
+		p1->o.transform(p2->o,o);
+//		p2->o.x=p1->o/o.x; p2->o.y=p1->o/o.y; p2->o.z=p1->o/o.z;
+		p2->p=p2->o.transform(d)+p1->p;
+		}
+	}
+}
+
+void Joint::attachToParts(int p1,int p2)
+{
+attachToParts((p1>=0)?owner->getPart(p1):0,(p2>=0)?owner->getPart(p2):0);
+}
+
+void Joint::resetDelta()
+{
+d=Pt3D(JOINT_DELTA_MARKER,0,0);
+}
+
+void Joint::useDelta(int false_or_true)
+{
+usedelta=false_or_true;
+}
+
+int Joint::isDelta()
+{
+return usedelta;
+}
+
+Param& Joint::getStaticParam()
+{
+static Param p(f0_joint_paramtab,0,"Joint");
+return p;
+}
+
+
+/////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+
+#include F0_CLASSES_FILE
+
+#ifdef MODEL_V1_COMPATIBLE
+
+#define FIELDSTRUCT Neuro
+ParamEntry f0_old_neuro_tab[]=
+{
+{"Connections",2,6,"n",},
+{"Other properties",},
+{"p",0,0,"part ref#","d",FIELD(part_refno),},
+{"j",0,0,"joint ref#","d",FIELD(joint_refno),},
+{"s",1,0,"state","f",FIELD(state),},
+{"in",1,0,"Inertia","f",FIELD(inertia),},
+{"fo",1,0,"Force","f",FIELD(force),},
+{"si",1,0,"Sigmoid","f",FIELD(sigmo),},
+{0,0,0,},
+}; 
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NeuroItem
+ParamEntry f0_neuroitem_paramtab[]=
+{
+{"Connections",3,12,"ni",},
+{"Geometry",},
+{"Other",},
+{"n",0,0,"neuron ref#","d",FIELD(neuro_refno),},
+{"c",2,0,"connection ref#","d",FIELD(conn_refno),},
+{"w",2,0,"connection weight","f",FIELD(weight),},
+{"p",0,0,"part ref#","d",FIELD(part_refno),},
+{"j",0,0,"joint ref#","d",FIELD(joint_refno),},
+{"px",1,0,"position.x","f",FIELD(pos.x),},
+{"py",1,0,"position.y","f",FIELD(pos.y),},
+{"pz",1,0,"position.z","f",FIELD(pos.z),},
+{"rx",1,0,"rotation.x","f",FIELD(rot.x),},
+{"ry",1,0,"rotation.y","f",FIELD(rot.y),},
+{"rz",1,0,"rotation.z","f",FIELD(rot.z),},
+{"d",2,0,"item details","s",GETSET(details),},
+{0,0,0,},
+}; 
+#undef FIELDSTRUCT
+#endif
+
+////////////////////////////////////////
+
+ParamEntry Neuro::emptyParamTab[]=
+{
+{"Undefined Neuro",1,0,"?",},
+{0,0,0,},
+};
+
+Param Part::extraProperties()
+{
+return Param(f0_part_xtra_paramtab,this);
+}
+
+Param Joint::extraProperties()
+{
+return Param(f0_joint_xtra_paramtab,this);
+}
+
+Param Neuro::extraProperties()
+{
+return Param(f0_neuro_xtra_paramtab,this);
+}
+
+Param Part::properties()
+{
+return Param(f0_part_paramtab,this);
+}
+
+Param Joint::properties()
+{
+return Param(usedelta?f0_joint_paramtab:f0_nodeltajoint_paramtab,this);
+}
+
+Param Neuro::properties()
+{
+return Param(f0_neuro_paramtab,this);
+}
+
+class NeuroExtParamTab: public ParamTab
+{
+  public:
+NeuroExtParamTab():ParamTab(f0_neuro_paramtab)
+	{
+#define FIELDSTRUCT NeuroExt
+	ParamEntry entry={"class",2,0,"neuro class","s",GETSET(neuroclass)};
+#undef FIELDSTRUCT
+	add(&entry);
+
+#define FIELDSTRUCT Neuro
+	ParamEntry entry2={"state",2,0,"state","f",FIELD(state)};
+#undef FIELDSTRUCT
+	add(&entry2);
+	}
+};
+
+Param& Neuro::getStaticParam()
+{
+static Param p(f0_neuro_paramtab,0,"NeuroDef");
+return p;
+}
+
+////////////////////////
+
+NeuroConn::NeuroConn()
+{
+defassign();
+}
+
+//////////////////////////////////////
+
+ParamEntry *NeuroExt::getParamTab()
+{
+static NeuroExtParamTab tab;
+return tab.getParamTab();
+}
+
+void NeuroExt::get_neuroclass(PARAMGETARGS)
+{ret->setString(getClassName());}
+
+int NeuroExt::set_neuroclass(PARAMSETARGS)
+{setClassName(arg->getString());return PSET_CHANGED;}
+
+
Index: /cpp/frams/model/modelparts.h
===================================================================
--- /cpp/frams/model/modelparts.h	(revision 109)
+++ /cpp/frams/model/modelparts.h	(revision 109)
@@ -0,0 +1,542 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _MODELPARTS_H_
+#define _MODELPARTS_H_
+
+#include <frams/util/3d.h>
+#include <frams/genetic/genoconv.h>
+
+#include <frams/util/extvalue.h>
+#include <frams/util/list.h>
+#include <frams/util/sstring.h>
+#include <frams/util/sstringutils.h>
+#include <frams/param/param.h>
+#include <frams/param/syntparam.h>
+#include <frams/util/usertags.h>
+#include <frams/param/paramtabobj.h>
+
+#include <stdio.h>
+
+//#define MODEL_V1_COMPATIBLE
+
+class Model;
+class IRange;
+class MultiRange;
+
+typedef UserTags<Model,void*,5> ModelUserTags;
+
+/** Common base for model elements. */
+class PartBase
+{
+public:
+SString vis_style;
+PartBase(const SString& s):vis_style(s),mapped(0) {}
+~PartBase();
+static SString getDefaultStyle(){return SString("none");}
+MultiRange *mapped;
+enum PartBaseFlags { Selected=1 };
+long flags;
+Model *owner;	///< backlink to the model
+
+SString info;
+
+Model &getModel() const {return *owner;}
+
+ModelUserTags userdata;
+
+void notifyMappingChange();
+
+void clearMapping();
+MultiRange* getMapping() {return mapped;}
+void setMapping(const IRange &mr);
+void addMapping(const IRange &mr);
+void setMapping(const MultiRange &mr);
+void addMapping(const MultiRange &mr);
+
+void setInfo(const SString& name,const SString& value);
+void setInfo(const SString& name,int value);
+void setInfo(const SString& name,double value);
+SString getInfo(const SString& name);
+};
+
+/// Part is the only real physical object in the framsticks creature.
+/// You can use this class for querying and adjusting constructed
+/// model properties
+class Part: public PartBase
+{
+friend class Model;
+static SString getDefaultStyle();
+Part(double _mass,double _size,double _density,double _friction,double _ingest,double _assim)
+	:PartBase(getDefaultStyle()),mass(_mass),size(_size),density(_density),friction(_friction),ingest(_ingest),assim(_assim)
+	{}
+void defassign();
+public:
+// base properties - have special meaning and therefore are often accessed directly for convenience
+Pt3D p;    ///< 3d coordinates of the part
+Orient o;  ///< orientation in 3d space (rotation matrix)
+/// ParamInterface object is preferred way to get/set other properties.
+Param extraProperties();
+Param properties();
+long refno;
+Pt3D rot;///< rotation angles
+
+///
+long shape;///default=old framsticks compatible, do not mix with shapes>0
+enum Shape {SHAPE_DEFAULT=0, SHAPE_ELLIPSOID=1, SHAPE_CUBOID=2, SHAPE_CYLINDER=3};
+double mass,size,density,friction,ingest,assim;
+Pt3D scale;
+Pt3D food;
+//SList points; // collistion points
+//Slist neurons; // "select * from owner->neurons where part=this" ;-)
+
+Pt3D vcolor;
+double vsize;
+
+Part(enum Shape s=SHAPE_DEFAULT);
+Part(const Part& src):PartBase(getDefaultStyle()) {operator=(src);}
+void operator=(const Part& src);
+
+void setPositionAndRotationFromAxis(const Pt3D &p1,const Pt3D &p2);
+void setOrient(const Orient &o);///< set part.o and calculates part.rot (rotation angles)
+void setRot(const Pt3D &r);///< set part.rot (rotation angles) and calculate part.o
+
+static Param& getStaticParam();
+};
+
+/// Imaginary connection between two parts.
+/// Joint has no mass nor intertia but can transfer forces.
+class Joint: public PartBase
+{
+friend class Model;
+SString getDefaultStyle();
+Joint(double _stamina,double _stif,double _rotstif,double _d)
+	:PartBase(getDefaultStyle()),stamina(_stamina),stif(_stif),rotstif(_rotstif)
+	{d=Pt3D(_d,0,0);}
+void defassign();
+public:
+// base properties:
+long p1_refno,p2_refno; ///< parts' reference numbers
+
+Part *part1,*part2;	///< references to parts
+class Pt3D d;		///< position delta between parts
+class Pt3D rot;	///< orientation delta between parts expressed as 3 angles
+enum Shape {SHAPE_DEFAULT=0, SHAPE_SOLID=1};
+long shape;///< default=old framsticks compatible, creates a physical rod between parts (cylinder or cuboid), do not mix with shape>0,  solid=merge parts into one physical entity
+
+Joint();
+Joint(const Joint& src):PartBase(getDefaultStyle()) {operator=(src);}
+void operator=(const Joint& src);
+
+/** connect two parts with this joint.
+    p2 position will be adjusted if delta option is in effect.
+    @see isDelta()
+  */
+void attachToParts(Part* p1,Part* p2);
+/// @see attachToParts(Part*,Part*)
+void attachToParts(int p1,int p2);
+
+/** discard delta information but don't disable delta flag.
+    delta will be calculated from parts positions during final consistency check.
+ */
+void resetDelta();
+
+/** enable or disable delta option.
+    delta value is not changed.
+ */
+void useDelta(int false_or_true);
+
+/** @return 1 if delta option is in effect.
+    @see useDelta(), resetDelta(), useDelta()
+*/
+int isDelta(); 
+
+/// ParamInterface object is preferred way to get/set other properties.
+Param extraProperties();
+Param properties();
+
+// do not touch these:
+long refno; ///< this joint's reference number
+double stamina;
+double stif,rotstif;	///< stiffness for moving and bending forces
+class Orient o;	///< orientation delta between parts as rotation matrix
+/** flag: generated f0 should include delta data.
+    set by 'singlestep' if j: attributes use delta option */
+int usedelta;
+Pt3D vcolor;
+
+static Param& getStaticParam();
+};
+
+#define JOINT_DELTA_MARKER 99999.0
+
+////////////////// NN /////////////////
+
+class NeuroClass;
+
+typedef UserTags<NeuroClass,void*,5> NeuroClassUserTags;
+
+/** Information about neuron class.
+ */
+class NeuroClass
+{
+bool ownedvectordata;
+void operator=(const NeuroClass& nosuchthich){}
+  public:
+SString name,longname,description;
+ParamEntry *props;
+bool ownedprops;//< destructor will free props using ParamObject::freeParamTab
+long prefinputs,prefoutput;
+long preflocation;
+int *vectordata;
+long visualhints;
+
+//void *impl;
+int impl_count;
+bool active;
+int genactive;
+NeuroClassUserTags userdata;
+
+//////////////////////
+~NeuroClass();
+NeuroClass();
+NeuroClass(ParamEntry *_props,SString _description,
+	   int _prefinputs,int _prefoutput,int _preflocation,int *_vectordata,bool own_vd=1,int vhints=0);
+/** class name for use in Neuro::setClassName(), Neuro::setDetails() (former 'moredata' field),
+    eg. "N","-",G" */
+const SString& getName() {return name;}
+/** human friendly name, eg. "Neuron","Link","Gyroscope"  */
+const SString& getLongName() {return longname;}
+/** long description */
+const SString& getDescription() {return description;}
+ParamEntry* getParamTab() {return props;}
+
+/** NeuroClass specific properties, recognized by all neurons of this class */
+Param getProperties() {return Param(props);}
+
+/** preferred number of inputs, -1 = no preference (any number will go).
+    extra inputs may be ignored by the object (depends on the class).
+ */
+int getPreferredInputs() {return prefinputs;}
+
+/** @return 0 if this object doesn't provide useful output signal. */
+int getPreferredOutput() {return prefoutput;}
+
+/** @return 0 if the object doesn't need any assignment to the body element.
+    @return 1 = it likes to be attached to the Part ( @see Neuro::attachToPart() )
+    @return 2 = the object prefers to have the Joint ( @see Neuro::attachToJoint() )
+ */
+int getPreferredLocation() {return preflocation;}
+/** vector drawing to be used in neuro net diagram.
+    interpretation:
+       {   
+           LEN = datalength (excluding this number) 
+           NL = number_of_lines
+line#1 ->  NS = number_of_segments, x1,y1, x2,y2, ... xNS-1,yNS-1,
+         ...
+line#NL -> NS = number_of_segments, x1,y1, x2,y2, ... xNS-1,yNS-1,
+       }
+ */
+int* getSymbolGlyph()
+ {return vectordata;}
+void setSymbolGlyph(int *data,bool owned=1)
+ {if (vectordata&&ownedvectordata) delete []vectordata;
+ vectordata=data; ownedvectordata=owned;}
+/** additional information about how the neuron should be drawn
+    used by structure view (and maybe some other components).
+    return value is defined by the enum Hint
+    @see enum Hint
+ */
+int getVisualHints()
+ {return visualhints;}
+
+enum Hint
+	/** don't draw neurons of this class */
+ { Invisible=1,
+	/** don't draw classname label below the neuron */
+   DontShowClass=2,
+	/** draw the neuron at the first part when attached to joint (default is in the middle) */
+   AtFirstPart=4,
+	/** draw the neuron at the second part when attached to joint (default is in the middle) */
+   AtSecondPart=8,
+	/** use effector colour for this neuro unit */
+   EffectorClass=16,
+	/** use receptor colour for this neuro unit */
+   ReceptorClass=32,
+   V1BendMuscle=64,
+   V1RotMuscle=128,
+   LinearMuscle=256
+ };
+
+/** textual summary, automatically generated from other properties (like the neuro class tooltip) */
+SString getSummary();
+};
+
+class Neuro;
+
+#ifdef MODEL_V1_COMPATIBLE
+class NeuroItem;
+
+/** for compatibility with old Neuro/NeuroItem  */
+class OldItems
+{
+Neuro &neuro;
+SList syntitems; ///< to be deleted
+SList items;
+int listok;
+  public:
+OldItems(Neuro &n):neuro(n),listok(0) {}
+~OldItems() {freelist();}
+void buildlist();
+void freelist();
+
+int getItemCount();
+NeuroItem *getNeuroItem(int i);
+NeuroItem *addNewNeuroItem();
+int findNeuroItem(NeuroItem *n);
+};
+#endif
+
+/** Single processing unit in framsticks NN.  */
+class Neuro: public PartBase
+{
+friend class Model;
+static SString getDefaultStyle();
+
+struct NInput { Neuro *n; double weight; SString *info;
+	NInput(Neuro *_n,double w,SString *i=0):n(_n),weight(w),info(i) {} };
+
+SListTempl<NInput> inputs;
+
+NeuroClass *myclass;
+bool knownclass;
+SString myclassname, myclassparams;
+/** set myclass and make knownclass=true */
+void checkClass();
+SString** inputInfo(int i);
+void defassign();
+
+public:
+enum NeuroFlags { HoldState=2 };
+Param properties();
+Param extraProperties();
+
+void setInputInfo(int i,const SString& name,const SString &value);
+void setInputInfo(int i,const SString& name,int value);
+void setInputInfo(int i,const SString& name,double value);
+SString getInputInfo(int i);
+SString getInputInfo(int i,const SString& name);
+
+NeuroClass* getClass();
+void setClass(NeuroClass*);
+
+SString getClassParams() {return myclassparams;}
+void setClassParams(const SString& cp) {myclassparams=cp;}
+
+SString getClassName();
+void setClassName(const SString& clazz);
+
+/** return neuro unit details encoded as <CLASS> ":" <PROPERTIES>
+    
+    new Neuro can be created as root object (without parent) or can be 
+    the child of existing Neuro. Children of the Neuro are its inputs.
+    Standard framsticks neuron calculates the sum of all input units - other processing
+    units don't have to treat them equally and can even ignore some of them.
+    There are hints about expected inputs in the class database, @see getClass
+
+    Application should not assume anything about classes and its properties
+    except for two standard classes: (information about all current classes
+    can be retrieved with getClass/getClassProperties methods)
+    - getClassName()="N" is the standard framsticks neuron, accepts any number of inputs,
+      compatible with old Neuro object
+    - getClassName()="-" is the neuron link, compatible with old Neuro-Neuro link
+      (NeuroItem with empty details)
+    Empty details defaults to "-" if the parent unit is specified,
+    and "N" if the unit has no parent.
+ */
+SString getDetails();
+
+/** details = classname + ":" + classparams
+    @see getDetails()
+ */
+void setDetails(const SString&);
+
+#define STATRICKCLASS Neuro
+PARAMGETDEF(details) {arg1->setString(getDetails());}
+PARAMSETDEF(details) {setDetails(arg1->getString());return PSET_CHANGED;}
+PARAMGETDEF(inputCount);
+PARAMPROCDEF(p_getInputNeuroDef);
+PARAMPROCDEF(p_getInputNeuroIndex);
+PARAMPROCDEF(p_getInputWeight);
+PARAMGETDEF(classObject);
+#undef STATRICKCLASS
+
+///@param handle_defaults_when_saving see SyntParam
+SyntParam classProperties(bool handle_defaults_when_saving=true);
+// base properties:
+long refno; ///< unique reference number (former 'neuro' refno)
+
+long part_refno; ///< can be used by some items as the part ref#
+long joint_refno; ///< can be used by some items as the joint ref#
+
+Pt3D pos,rot;	///< default = zero
+
+ModelUserTags userdata;
+
+Neuro();
+Neuro(double _state,double _inertia,double _force,double _sigmo);
+Neuro(const Neuro& src):PartBase(getDefaultStyle()) {operator=(src);}
+
+~Neuro();
+
+void operator=(const Neuro& src);
+
+/** Attach this Neuro to the specified Part or detach it from the body if p==NULL.
+    Neuro can be attached to either Part or Joint, but not both.
+    @see getPart()
+ */
+void attachToPart(Part* p) {part=p; joint=0;}
+
+/** Attach this Neuro to the specified Joint or detach it from the body if p==NULL.
+    Neuro can be attached to either Part or Joint, but not both.
+    @see getJoint()
+ */
+void attachToJoint(Joint* j) {joint=j; part=0;}
+
+void attachToPart(int i);
+void attachToJoint(int i);
+
+/** @return Part the Neuro is attached to, or NULL if it has no defined location on the body.
+    @see attachToPart()
+ */
+Part *getPart() {return part;}
+
+/** @return Joint the Neuro is attached to, or NULL if it has no defined location on the body.
+    @see attachToJoint()
+ */
+Joint *getJoint() {return joint;}
+
+int isOldEffector();
+int isOldReceptor();
+int isOldNeuron();
+int isNNConnection();
+
+/** @return the number of inputs connected to this Neuro.
+    Functions like getInput(), getInputWeight() will accept connection number [0..InputCount-1]
+ */
+int getInputCount() const {return inputs.size();}
+
+/// @return the number of output connections (including possible self-connections)
+int getOutputsCount() const;
+
+/** @return the Neuro connected as i-th input */
+Neuro* getInput(int i) const {return (i>=inputs.size())?0:inputs(i).n;}
+/** @return the Neuro connected as i-th input.
+    @param weight
+ */
+Neuro* getInput(int i,double &weight) const;
+/** @return connectin weight for i-th input */
+double getInputWeight(int i) const;
+/** change connection weight for i-th input */
+void setInputWeight(int i,double weight);
+/** connect i-th input with another neuron */
+void setInput(int i,Neuro*n);
+/** connect i-th input with another neuron */
+void setInput(int i,Neuro*n,double weight);
+/** add new input. @return its reference number */
+int addInput(Neuro* child,double weight=1.0,const SString* info=0);
+/** @return reference number [0..InputCount-1] of the input
+   or -1 if 'child' is not connected with this Neuro.*/
+int findInput(Neuro* child) const;
+void removeInput(int refno);
+/**    @return reference number of the child connection, like findInput() */
+int removeInput(Neuro* child);
+
+int findInputs(SList& result,const char* classname=0,const Part* part=0,const Joint* joint=0) const;
+int findOutputs(SList& result,const char* classname=0,const Part* part=0,const Joint* joint=0) const;
+
+/* class database retrieval */
+static int getClassCount();
+/** @return Neuro class name.
+    @param classindex 0 .. getClassCount()
+ */
+static SString getClassName(int classindex);
+static NeuroClass* getClass(int classindex);
+static NeuroClass* getClass(const SString& classname);
+static int getClassIndex(const NeuroClass*nc);
+
+#ifdef MODEL_V1_COMPATIBLE
+friend class OldItems;
+long neuro_refno; ///< parent ref# (called neuro_refno for compatibility with old Neuro class), @see moredata
+long conn_refno; ///< the other neuron ref# in N-N connections, can be used by some other items
+double weight; ///< weight of the N-N connection and (all?) receptors
+double inertia,force,sigmo; //!!!
+
+/** @deprecated provided only for compatibility with old Neuro/NeuroItem classes.
+    use getInputCount() instead. @sa getInputCount() */
+int getItemCount() {return oldItems().getItemCount();}
+
+/** @deprecated provided only for compatibility with old Neuro/NeuroItem classes.
+    use getInput() instead. @sa getInput() */
+NeuroItem* getNeuroItem(int i) {return oldItems().getNeuroItem(i);}
+#endif
+
+  protected:
+#ifdef MODEL_V1_COMPATIBLE
+/** old Neuro compatibility */
+OldItems* olditems;
+OldItems& oldItems() {if (!olditems) olditems=new OldItems(*this); return *olditems;}
+void invalidateOldItems() {if (olditems) olditems->freelist();}
+#endif
+
+  public:
+
+// not really private, but you should not access those directly
+double state;
+
+/** may reference parent neuron if parentcount is exacty 1. parent is invalid otherwise. @sa parentcount */
+Neuro *parent;
+int parentcount; ///< @sa parent
+
+Part *part;	///< link to the Part
+Joint *joint;	///< link to the Joint - required by some objects (eg.muscles)
+Orient o;	///< rotation matrix calculated from "rot"
+static ParamEntry emptyParamTab[];
+static Param& getStaticParam();
+};
+
+#ifdef MODEL_V1_COMPATIBLE
+class NeuroItem;
+
+/// for compatibility with old NeuroItem class.
+class NeuroItem: public Neuro
+{
+public:
+NeuroItem() {}
+};
+#endif
+
+class NeuroExt: public Neuro
+{
+  public:
+#define STATRICKCLASS NeuroExt
+PARAMGETDEF(neuroclass);
+PARAMSETDEF(neuroclass);
+#undef STATRICKCLASS
+static ParamEntry *getParamTab();
+};
+
+class NeuroConn
+{
+void defassign();
+  public:
+int n1_refno,n2_refno;
+double weight;
+SString info;
+NeuroConn();
+};
+
+extern ParamEntry f0_part_paramtab[],f0_joint_paramtab[],f0_nodeltajoint_paramtab[],f0_neuro_paramtab[],f0_neuroconn_paramtab[],f0_neuroitem_paramtab[];
+
+#endif
Index: /cpp/frams/neuro/impl/neuroimpl-body-gdk.h
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-body-gdk.h	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-body-gdk.h	(revision 109)
@@ -0,0 +1,95 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROIMPLBODY_H_
+#define _NEUROIMPLBODY_H_
+
+#include <frams/neuro/neuroimpl.h>
+
+class NI_Gyro: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_Gyro();} // for NeuroFactory
+int lateinit() {if (!neuro->joint) return 0; simorder=0; return 1;}
+void go() {setState(0);}
+};
+
+extern ParamEntry NI_Touch_tab[];
+class NI_Touch: public NeuroImpl
+{
+public:
+double range;
+NeuroImpl* makeNew(){return new NI_Touch();} // for NeuroFactory
+int lateinit() {if (!neuro->part) return 0; simorder=0; return 1;}
+void go() {setState(0);}
+NI_Touch():range(1) {paramentries=NI_Touch_tab;}
+};
+
+class NI_Smell: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_Smell();} // for NeuroFactory
+void go() {setState(0);}
+int lateinit() {if (!neuro->part) return 0; simorder=0; return 1;}
+};
+
+extern ParamEntry NI_BendMuscle_tab[];
+class NI_BendMuscle: public NeuroImpl
+{
+public:
+double power,bendrange;
+NeuroImpl* makeNew(){return new NI_BendMuscle();} // for NeuroFactory
+NI_BendMuscle() {paramentries=NI_BendMuscle_tab;}
+int lateinit() {if (!neuro->joint) return 0; simorder=2; return 1;}
+void go() {}
+};
+
+extern ParamEntry NI_RotMuscle_tab[];
+class NI_RotMuscle: public NeuroImpl
+{
+public:
+double power;
+NeuroImpl* makeNew(){return new NI_RotMuscle();} // for NeuroFactory
+NI_RotMuscle() {paramentries=NI_RotMuscle_tab;}
+int lateinit() {if (!neuro->joint) return 0; simorder=2; return 1;}
+void go() {}
+};
+
+extern ParamEntry NI_LinearMuscle_tab[];
+class NI_LinearMuscle: public NeuroImpl
+{
+public:
+double power;
+NeuroImpl* makeNew(){return new NI_RotMuscle();} // for NeuroFactory
+NI_LinearMuscle() {paramentries=NI_RotMuscle_tab;}
+int lateinit() {if (!neuro->joint) return 0; simorder=2; return 1;}
+void go() {}
+};
+
+class NI_Sticky: public NeuroImpl
+{
+public:
+double power;
+NeuroImpl* makeNew(){return new NI_Sticky();} // for NeuroFactory
+int lateinit() {if (!neuro->part) return 0; simorder=0; return 1;}
+void go() {}
+};
+
+class NI_WaterDetect: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_WaterDetect();} // for NeuroFactory
+int lateinit() {if (!neuro->part) return 0; simorder=0; return 1;}
+void go() {setState(0);}
+};
+
+class NI_Energy: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_Energy();} // for NeuroFactory
+void go() {setState(0);}
+};
+
+#endif
+
Index: /cpp/frams/neuro/impl/neuroimpl-channels.cpp
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-channels.cpp	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-channels.cpp	(revision 109)
@@ -0,0 +1,35 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neuroimpl-channels.h"
+#include <common/nonstd_stl.h>
+
+void NI_Channelize::go()
+{
+setChannelCount(getInputCount());
+for(int i=0;i<getInputCount();i++)
+	setState(getWeightedInputState(i),i);
+}
+
+void NI_ChMux::go()
+{
+int c=getInputChannelCount(1);
+if (c<2) {setState(getWeightedInputState(1)); return;}
+double s=getWeightedInputState(0);
+s=(max(-1.0,min(1.0,s))+1.0)/2.0; // 0..1
+int i1;
+i1=(int)(s*(c-1)); i1=max(0,min(i1,c-2));
+double sw=1.0/(c-1);
+double s1=sw*i1;
+double w1=fabs((s-s1)/sw);
+double w2=1.0-w1;
+double is1=getWeightedInputState(1,i1);
+double is2=getWeightedInputState(1,i1+1);
+setState(is1*w2 + is2*w1);
+}
+
+void NI_ChSel::go()
+{
+setState(getWeightedInputState(0,ch));
+}
Index: /cpp/frams/neuro/impl/neuroimpl-channels.h
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-channels.h	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-channels.h	(revision 109)
@@ -0,0 +1,39 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROIMPLCHANNELS_H
+#define _NEUROIMPLCHANNELS_H
+
+#include <frams/neuro/neuroimpl.h>
+
+class NI_Channelize: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_Channelize();} // for NeuroFactory
+void go();
+};
+
+class NI_ChMux: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_ChMux();} // for NeuroFactory
+void go();
+};
+
+extern ParamEntry NI_ChSel_tab[];
+
+class NI_ChSel: public NeuroImpl
+{
+public:
+int ch; // channel
+NI_ChSel():ch(0) {paramentries=NI_ChSel_tab;}
+NeuroImpl* makeNew(){return new NI_ChSel();} // for NeuroFactory
+void go();
+};
+
+
+#endif
+
+
+
Index: /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.cpp
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.cpp	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.cpp	(revision 109)
@@ -0,0 +1,135 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+/// this is a module included into neuroimpl-fuzzy
+/// it converts string parameters - fuzzy sets and fuzzy rules - into appropriate variables
+
+#include "neuroimpl-fuzzy-f0.h"
+#include <common/nonstd_stl.h> //min,max
+
+//this part concerns fuzzy sets transformation
+
+/** This function converts given string into numbers representing fuzzy sets */
+int FuzzyF0String::convertStrToSets(const SString& str, double numbers[], int nrOfSets)
+{
+  int pos=0;
+  SString t;
+  int have=0;
+  int maxnumbers=4*nrOfSets; //number of semicolons should be equal 4*nrOfSets
+
+  while (str.getNextToken(pos,t,';'))
+    if (have>=maxnumbers)
+      break;
+    else
+      numbers[have++]=atof(t);
+
+  //check if number of read numbers (separated with semicolon) is equal to declared
+  if (have != 4*nrOfSets)
+    return -1; //number of sets found is lower than declared!
+
+  //check corectness of sets - must not be decreasing
+  for(int i=0;i<nrOfSets;i++)
+    if((numbers[4*i]>numbers[4*i+1])||(numbers[4*i+1]>numbers[4*i+2])||(numbers[4*i+2]>numbers[4*i+3]))
+      return -2; //error
+
+  return 0;
+}
+
+//this part concerns fuzzy rules transformation
+
+/** This function counts number of inputs and outputs given in string consisting fuzzy rules.
+It counts number of separators:
+; between inputs/outputs and fuzzy sets number
+: between conditional and decision part of a rule
+/ end of rule
+*/
+int FuzzyF0String::countInputsOutputs(const char* str, int ruldef[], int rulesNr)
+{ //ruledef will remember counted number of inputs and outputs for every rule
+  const char* t;
+  int separators=0, inouts=0;
+
+  for(t=str;*t;t++)
+  {
+    while(isdigit(*t))
+      t++; //only count, does not care about numbers now
+    if (!*t)
+      break; //end of the string - get out of 'for' loop
+    if ( (*t==';')||(*t==':')||(*t=='/') ) //found sth different than digit - it must be a separator
+    {
+      separators++; //one of separators
+      if (*t!=';') // end of [conditional part of] rule
+      {
+	if (inouts >= 2*rulesNr) //more rules declared in string than declared in rulesNr
+	  return -2;
+        ruldef[inouts]=(separators+1)/2; //cause fuzzy sets - for 1 in/out there are 2 semicolons
+	separators=0;  //begin counting number of in/out from zero
+	inouts++; //next part of rule / or next rule
+      }
+    }
+    else // illegal character
+      return -1;
+  }
+
+  //check, if nr of found rules is equal to declared
+  if (inouts == 2*rulesNr) //each rule has a conditional part (inputs) and decisional part (outputs)
+    return 0;
+  else
+    return -5; // ShowMessage("Inconsistent number of rules!");
+}
+
+/** This function converts given string into variable 'rules' that remembers all inputs/outputs and fuzzy set.
+The procedure is conduct according to ruledef, calculated earlier.*/
+int FuzzyF0String::convertStrToRules(const SString& str, const int ruledef[], int **rules, int setsNr, int rulesNr, int &maxOutputNr)
+{
+  int pos=0, j, k, len=str.len();
+  int dNr=0, sNr=0;
+  int inNr, outNr; //number of inputs/outputs and corresponding fuzzy sets
+  SString t;
+  bool conditional=true; //which part of rule: conditional or decisional
+
+  maxOutputNr=0; //sets maximum output nr found in rules string
+
+  //check corectness of the string: number semicolon ... separated with colon or slash
+  while(pos<len)
+  {
+    while((pos<len)&&(isdigit(str.charAt(pos)))) pos++;
+    if(!(pos<len))
+      break; //end of the string
+    if(str.charAt(pos)!=';')
+      if((str.charAt(pos)==':')&&(conditional))
+        {sNr++; conditional=false;}
+      else if((str.charAt(pos)=='/')&&(!conditional))
+        {dNr++; conditional=true;}
+      else
+        return -4; //error - illegal character
+    pos++;
+  }
+  if( (dNr!=sNr) || (dNr!=rulesNr) )
+    return -5; //error - wrong number of rules
+
+  pos=0;
+
+  for(j=0;j<rulesNr;j++)
+  { //sum of inputs and outputs
+    inNr = 2*ruledef[2*j];
+    outNr = 2*ruledef[2*j+1];
+    for(k=0;k<inNr+outNr;k++)
+    {
+      t = ""; //clear previous value
+      //cuts next integer values
+      while ( (pos<len)&&(isdigit(str.charAt(pos))) )
+        t += str.charAt(pos++);
+      pos++;
+      rules[j][k]=atol(t); //convert cut out string into number
+      //fuzzy sets - odd index table - are counted from 0,
+      //so if 5 fuzzy sets declared, values acceptable are 0,1,2,3,4
+      if ( ((k%2)!=0) && (rules[j][k] >= setsNr) )
+      	return -1;
+      if((k>=inNr)&&((k%2)==0))
+        maxOutputNr=max(maxOutputNr,rules[j][k]);
+    }
+  }
+  return 0;
+};
+
Index: /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.h
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.h	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-fuzzy-f0.h	(revision 109)
@@ -0,0 +1,23 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+/// this is a modul included into neuroimpl-fuzzy
+/// it converts string parameters - fuzzy sets and fuzzy rules - into appropriate variables
+
+#ifndef _neuroimpl_fuzzy_f0_h
+#define _neuroimpl_fuzzy_f0_h
+
+#include <ctype.h>
+#include <frams/util/sstring.h>
+
+class FuzzyF0String
+{
+  public:
+  static int convertStrToSets(const SString& str, double numbers[], int nrOfSets);
+  static int countInputsOutputs(const char* str, int ruldef[], int rulesNr);
+  static int convertStrToRules(const SString& str, const int ruledef[], int **rules, int setsNr, int rulesNr, int &maxOutputNr);
+};
+
+#endif
+
Index: /cpp/frams/neuro/impl/neuroimpl-fuzzy.cpp
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-fuzzy.cpp	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-fuzzy.cpp	(revision 109)
@@ -0,0 +1,209 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neuroimpl-fuzzy.h"
+#include "neuroimpl-fuzzy-f0.h"
+#include <common/nonstd_stl.h> //min,max
+
+int NI_FuzzyNeuro::countOuts(const Model *m, const Neuro *fuzzy)
+{
+  int outputs=0;
+  for(int i=0;i<m->getNeuroCount();i++)
+     for(int in=0;in<m->getNeuro(i)->getInputCount();in++)
+        if (m->getNeuro(i)->getInput(in)==fuzzy) outputs++;
+  return outputs;
+}
+
+int NI_FuzzyNeuro::lateinit()
+{
+  int i, maxOutputNr;
+
+  //check correctness of given parameters: string must not be null, sets&rules number > 0
+  if((fuzzySetsNr<1)||(rulesNr<1)||(fuzzySetString.len()==0)||(fuzzyRulesString.len()==0))
+    return 0; //error
+
+  // this part contains transformation of fuzzy sets
+  fuzzySets = new double[4*fuzzySetsNr]; //because every fuzzy set consist of 4 numbers
+  // converts fuzzy string from f0 to table of fuzzy numbers type 'double'
+  // (fill created space with numbers taken from string)
+  // also checks whether number of fuzzy sets in the string equals declared in the definition
+  if (FuzzyF0String::convertStrToSets(fuzzySetString, fuzzySets, fuzzySetsNr) != 0)
+    return 0; //error
+
+  // this part contains transformation of fuzzy rules and defuzzyfication parameters
+  rulesDef = new int[2*rulesNr];    //for each rule remembers number of inputs and outputs
+  //check correctness of string and fill in the rulesDef
+  if (FuzzyF0String::countInputsOutputs(fuzzyRulesString, rulesDef, rulesNr) == 0)
+  {
+    defuzzParam = new double[rulesNr]; // parameters used in defuzyfication process
+    // create space for rules according to rulesDef
+    rules = new int*[rulesNr];   //list of rules...
+    for (i=0; i<rulesNr; i++)    //...that contains rules body
+    {
+      rules[i] = new int[2*(rulesDef[2*i]+rulesDef[2*i+1])];  //each rule can have different number of inputs and outputs
+      defuzzParam[i] = 0; //should be done a little bit earlier, but why do not use this loop?
+    }
+    // fill created space with numbers taken from string
+    if (FuzzyF0String::convertStrToRules(fuzzyRulesString, rulesDef, rules, fuzzySetsNr, rulesNr, maxOutputNr) != 0)
+      return 0; //error
+  }
+  else
+    return 0; //error
+
+  setChannelCount(countOuts(neuro->owner, neuro));
+  return 1; //success
+}
+
+NI_FuzzyNeuro::~NI_FuzzyNeuro()
+{
+  if(rules) //delete rows and columns of **rules
+  {
+    for (int i=0; i<rulesNr; i++) SAFEDELETEARRAY(rules[i])
+    SAFEDELETEARRAY(rules)
+  }
+  SAFEDELETEARRAY(defuzzParam)
+  SAFEDELETEARRAY(rulesDef)
+  SAFEDELETEARRAY(fuzzySets)
+}
+
+int NI_FuzzyNeuro::GetFuzzySetParam(int set_nr, double &left, double &midleft, double &midright, double &right)
+{
+  if ( (set_nr>=0) && (set_nr<fuzzySetsNr) )
+  {
+    left = fuzzySets[4*set_nr];
+    midleft = fuzzySets[4*set_nr+1];
+    midright = fuzzySets[4*set_nr+2];
+    right = fuzzySets[4*set_nr+3];
+    return 0;
+  }
+  else
+    return 1;
+};
+
+/** Function conduct fuzzyfication of inputs and calculates - according to rules - crisp multi-channel output */
+void NI_FuzzyNeuro::go()
+{
+  if (Fuzzyfication()!=0)
+    return;
+  if (Defuzzyfication()!=0)
+    return;
+};
+
+/**
+* Function conduct fuzzyfication process - calculates minimum membership function (of every input) for each rule,
+* and writes results into defuzzParam - variable that contains data necessary for defuzzyfication
+*/
+int NI_FuzzyNeuro::Fuzzyfication()
+{
+  int i, j, nrIn, inputNr, nrFuzzySet;
+  double minimumCut; // actual minimal level of cut (= min. membership function)
+
+  // sets defuzzyfication parameters for each rule:
+  for (i=0; i<rulesNr; i++)
+  {
+    nrIn = rulesDef[2*i]; // nr of inputs in rule #i
+    minimumCut = 2; // the highest value of membership function is 1.0, so this value will definitely change
+    for (j=0; (j<nrIn)&&(minimumCut>0); j++) //minimumCut can not be <0, so if =0 then stop calculations
+    {
+      nrFuzzySet = rules[i][j*2 + 1]; // j*2 moves pointer through each output, +1 moves to nr of fuzzy set
+      inputNr = rules[i][j*2]; // as above but gives input number
+      minimumCut = min( minimumCut, TrapeziumFuzz(nrFuzzySet, getWeightedInputState(inputNr))); // value of membership function for this input and given fuzzy set
+    }
+    if ( (minimumCut>1) || (minimumCut<0) )
+      return 1;
+    defuzzParam[i] = minimumCut;
+  }
+  return 0;
+};
+
+/**
+* Function calculates value of the membership function of the set given by wchich_fuzzy_set for given crisp value input_val
+* In other words, this function fuzzyficates given crisp value with given fuzzy set, returning it's membership function
+* @param which_fuzzy_set - 0 < number of set < fuzzySetsNr
+* @param input_val - crisp value of input in range <-1; 1>
+* @return value of membership function (of given input for given set) in range <0;1> or, if error occur, negative value
+*/
+double NI_FuzzyNeuro::TrapeziumFuzz(int which_fuzzy_set, double input_val)
+{
+  double range=0, left=0, midleft=0, midright=0, right=0;
+
+  if ( (which_fuzzy_set < 0) || (which_fuzzy_set > fuzzySetsNr) )
+    return -2;
+  if ( (input_val < -1) || (input_val > 1) )
+    return -3;
+
+  if (GetFuzzySetParam(which_fuzzy_set, left, midleft, midright, right) != 0)
+    return -4;
+
+  if ( (input_val < left) || (input_val > right) ) // greather than right value
+    return 0;
+  else if ( (input_val >= midleft) && (input_val <= midright) ) // in the core of fuzzy set
+    return 1;
+  else if ( (input_val >= left) && (input_val < midleft) ) // at the left side of trapezium
+  {
+    range = fabs(midleft - left);
+    return fabs(input_val-left)/((range>0)?range:1); // quotient of distance between input and extreme left point of trapezium and range of rising side, or 1
+  }
+  else if ( (input_val > midright) && (input_val <= right) ) // at the right side of trapezium
+  {
+    range = fabs(right - midright);
+    return fabs(right-input_val)/((range>0)?range:1); // quotient of distance between input and extreme right point of trapezium and range of falling side, or 1
+  };
+
+  // should not occur
+  return 0;
+
+};
+
+/**
+* Function conducts defuzzyfication process: multi-channel output values are calculates with singleton method (method of high).
+* For each rules, all outputs fuzzy sets are taken and cut at 'cut-level', that is at minumum membership function (of current rule).
+* For all neuro pseudo-outputs, answer is calculated according to prior computations.
+* In fact, there is one output with multi-channel answer and appropriate values are given to right channels.
+*/
+int NI_FuzzyNeuro::Defuzzyfication()
+{
+  int i, j, nrIn, nrOut, out, set, outputsNr;
+  double *numerators, *denominators, midleft, midright, unimp;
+
+  outputsNr = getChannelCount();
+
+  numerators = new double[outputsNr];
+  denominators = new double[outputsNr];
+
+  for(i=0;i<outputsNr;i++) numerators[i] = denominators[i] = 0;
+
+  // for each rule...
+  for (i=0; i<rulesNr; i++)
+  {
+    nrIn = rulesDef[2*i]; // number of inputs in rule #i
+    nrOut = rulesDef[2*i + 1]; // number of outputs in rule #i
+    // ...calculate each output's product of middle fuzzy set value and minimum membership function (numerator) and sum of minimum membership function (denominator)
+    for (j=0; j<nrOut; j++)
+    {
+      out = rules[i][2*nrIn + 2*j]; //number of j-output
+      set = rules[i][2*nrIn + 2*j + 1]; //number of fuzzy set attributed to j-output
+      if (GetFuzzySetParam(set, unimp, midleft, midright, unimp) != 0) // gets range of core of given fuzzy set
+        { SAFEDELETEARRAY(denominators) SAFEDELETEARRAY(numerators) return 1; }
+      //defuzzParam[i] = minimum membership function for rule #i - calculated in fuzzyfication block
+      // defuzzyfication method of singletons (high): (fuzzy set modal value) * (minimum membership value)
+      numerators[out] += ((midleft + midright)/2.0) * defuzzParam[i];
+      denominators[out] += defuzzParam[i];
+    }
+  }
+
+  for (i=0; i<outputsNr; i++)
+  {
+    if (denominators[i] == 0)
+      setState(0, i);
+    else
+      setState(numerators[i]/denominators[i], i);
+  }
+
+  SAFEDELETEARRAY(denominators)
+  SAFEDELETEARRAY(numerators)
+
+  return 0;
+};
+
Index: /cpp/frams/neuro/impl/neuroimpl-fuzzy.h
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-fuzzy.h	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-fuzzy.h	(revision 109)
@@ -0,0 +1,71 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROIMPLFUZZY_H_
+#define _NEUROIMPLFUZZY_H_
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <frams/neuro/neuroimpl.h>
+#include <frams/util/sstring.h>
+
+extern ParamEntry NI_FuzzyNeuro_tab[];
+
+/** Does the fuzzyfication process (of inputs) and defuzzyfication proces (of outpurs) - represents fuzzy rules
+*/
+class NI_FuzzyNeuro : public NeuroImpl
+{
+private:
+
+  double *fuzzySets;    /// list of four digits which represents fuzzy sets: [0]-l, [1]-m, [2]-n, [3]-r, ...  fuzzySet[4*i] = left, fuzzySet[4*i + 1] = midleft, fuzzySet[4*i + 2] = midright, fuzzySet[4*i + 3] = right
+
+  /** Determines, which fuzzy set is connected with each input of neuron. For instance third rule:
+  *   'IF input3 = fuzzy set #3 AND input5 = fuzzy set #1 then output2 = fuzzy set #6 AND output7 = fuzzy set #5'
+  *   the variables shoul have values as shown below:
+  *   RulesDef[4]=2; RulesDef[5]=2; //rule 3: 2 inputs, 2 outputs
+  *   Rules[2][0]=3, Rules[2][1]=3, Rules[2][2]=5, Rules[2][3]=1, Rules[2][4]=2, Rules[2][5]=6, Rules[2][6]=7, Rules[2][3]=5
+  */
+  int *rulesDef;    ///list of rules definitions: nr of inputs in rule 1, nr of outputs in rule 1, ... and so on for each rule
+  int **rules;      ///list of rules body: input nr, fuzzy set nr, ... , output nr, fuzzy set nr, ... and so on for each rule
+
+  /**
+  *  Sets defuzzyfication parameters: determines - for each rule - cut level <0;1> (minimum membership function of current rule).
+  *  In fact, defuzzParam remembers the values from 'first layer' - fuzzyfication layer (see neuron at documentation)
+  *  i.e. rule 1: defuzzParam[0] = 0.3522
+  */
+  double *defuzzParam; /// i.e.: defuzParam[5] = 0.455 means that rule #6 has got a minimum membership function (of given inputs set for this rule) at value 0.455 (it's cut level)
+
+protected:
+
+  ///Fuzzy functions
+  double TrapeziumFuzz(int which_fuzzy_set, double input_val);
+  int Fuzzyfication();
+  int Defuzzyfication();
+  int GetFuzzySetParam(int set_nr, double &left, double &midleft, double &midright, double &right);
+
+public:
+
+  int fuzzySetsNr;      /// number of fuzzy sets
+  int rulesNr;      ///number of rules
+  SString fuzzySetString; /// strings containing all fuzzy sets given in f0
+  SString fuzzyRulesString; /// strings containing all fuzzy rules given in f0
+
+  NI_FuzzyNeuro() {paramentries=NI_FuzzyNeuro_tab; fuzzySets=defuzzParam=NULL; rulesDef=NULL; rules=NULL;}
+  ~NI_FuzzyNeuro();
+  NeuroImpl* makeNew() { return new NI_FuzzyNeuro(); };
+  void go();
+  int lateinit();
+  /** Function build model based on given genotype and conts number of neurons connected with fuzzy neuro,
+    also checks number of fuzzy neuron inputs.
+    \param genotype genotype to be scanned
+    \param inputs number of fuzzy neuron inputs
+    \param output number of fuzzy neuron outputs (= number of neurons connected to fuzzy neuron)
+    @return success or failure
+  **/
+  static int countOuts(const Model *m, const Neuro *fuzzy);
+  
+};
+
+#endif
Index: /cpp/frams/neuro/impl/neuroimpl-simple.cpp
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-simple.cpp	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-simple.cpp	(revision 109)
@@ -0,0 +1,52 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neuroimpl-simple.h"
+
+#define NEURO_MAX 10.0
+#define OVERLOAD 0.1
+#define MUSCLESPEED   0.08
+#define MUSCLEACC   0.01
+
+int NI_StdNeuron::lateinit()
+{
+#ifdef MODEL_V1_COMPATIBLE
+if (inertia<0) inertia=neuro->inertia;
+if (force<0) force=neuro->force;
+if (sigmo>1e9) sigmo=neuro->sigmo;
+#endif
+istate=newstate+neuro->state; // neuro->state -> random initialization
+calcOutput();
+neuro->state=newstate;
+return 1;
+}
+
+void NI_StdNeuron::calcInternalState()
+{
+double sum=getWeightedInputSum();
+velocity=force*(sum-istate)+inertia*velocity;
+istate+=velocity;
+if (istate>NEURO_MAX) istate=NEURO_MAX;
+else if (istate<-NEURO_MAX) istate=-NEURO_MAX;
+}
+
+void NI_StdNeuron::go()
+{
+calcInternalState();
+calcOutput();
+}
+
+void NI_StdNeuron::calcOutput()
+{
+double s=istate * sigmo;
+if (s<-30.0) setState(-1);
+else setState(2.0/(1.0+exp(-s))-1.0); // -1...1
+}
+
+void NI_StdUNeuron::calcOutput()
+{
+double s=istate * sigmo;
+if (s<-30.0) setState(0);
+else setState(1.0/(1.0+exp(-s))); // 0...1
+}
Index: /cpp/frams/neuro/impl/neuroimpl-simple.h
===================================================================
--- /cpp/frams/neuro/impl/neuroimpl-simple.h	(revision 109)
+++ /cpp/frams/neuro/impl/neuroimpl-simple.h	(revision 109)
@@ -0,0 +1,100 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROIMPLSIMPLE_H_
+#define _NEUROIMPLSIMPLE_H_
+
+#include <frams/neuro/neuroimpl.h>
+#include <common/nonstd_math.h>
+
+extern ParamEntry NI_StdNeuron_tab[];
+
+class NI_StdNeuron: public NeuroImpl
+{
+  protected:
+double istate, velocity;
+void calcInternalState();
+virtual void calcOutput();
+  public:
+double inertia,force,sigmo;
+NI_StdNeuron():velocity(0)
+#ifdef MODEL_V1_COMPATIBLE
+	,inertia(-1),force(-1),sigmo(1e10)// illegal values, will be adjusted in lateinit()
+#else
+	,inertia(0),force(0),sigmo(0)
+#endif
+	{paramentries=NI_StdNeuron_tab;}
+NeuroImpl* makeNew(){return new NI_StdNeuron();} // for NeuroFactory
+int lateinit();
+void go();
+};
+
+extern ParamEntry NI_StdUNeuron_tab[];
+
+class NI_StdUNeuron: public NI_StdNeuron
+{
+  public:
+NI_StdUNeuron()
+	{paramentries=NI_StdUNeuron_tab;}
+NeuroImpl* makeNew(){return new NI_StdUNeuron();} // for NeuroFactory
+void calcOutput();
+};
+
+class NI_Const: public NeuroImpl
+{
+public:
+NeuroImpl* makeNew(){return new NI_Const();} // for NeuroFactory
+int lateinit()
+	{
+	neuro->state=newstate=1.0;
+	simorder=0;
+	return 1;
+	}
+};
+
+class NI_Diff : public NeuroImpl
+{
+double previous;      
+  public:
+NeuroImpl* makeNew() { return new NI_Diff(); };
+
+void go()
+	{
+	double s=getWeightedInputSum();
+	setState(s-previous);
+	previous=s;
+	}
+int lateinit()
+	{
+	NeuroImpl::lateinit();
+	previous=neuro->state;
+	return 1;
+	}
+};
+
+class NI_Random : public NeuroImpl
+{
+  public:
+NeuroImpl* makeNew() { return new NI_Random(); };
+void go() {setState(rnd01*2.0-1.0);}
+};
+
+extern ParamEntry NI_Sinus_tab[];
+
+class NI_Sinus : public NeuroImpl
+{
+  public:
+double f0,t;
+NeuroImpl* makeNew() { return new NI_Sinus(); };
+NI_Sinus():f0(0),t(0)
+	{paramentries=NI_Sinus_tab;}
+void go()
+	{
+	t+=f0+getWeightedInputSum();
+	setState(sin(t));
+	}
+};
+
+#endif
+
Index: /cpp/frams/neuro/neurocls-f0-GDK-factory.h
===================================================================
--- /cpp/frams/neuro/neurocls-f0-GDK-factory.h	(revision 109)
+++ /cpp/frams/neuro/neurocls-f0-GDK-factory.h	(revision 109)
@@ -0,0 +1,164 @@
+
+// do not edit - generated automatically from "f0.def"
+// (to be included in "neurofactory.cpp")
+
+
+
+
+
+
+#define FIELDSTRUCT NI_StdNeuron 
+ParamEntry NI_StdNeuron_tab []={
+{"Neuron",1, 4 ,"N",},
+{"in",1,0,"Inertia","f 0.0 1.0 0.8",FIELD(inertia),},
+{"fo",1,0,"Force","f 0.0 999.0 0.04",FIELD(force),},
+{"si",1,0,"Sigmoid","f -99999.0 99999.0 2.0",FIELD(sigmo),},
+{"s",2,0,"State","f -1.0 1.0 0.0",FIELD(newstate),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_StdUNeuron 
+ParamEntry NI_StdUNeuron_tab []={
+{"Unipolar neuron [EXPERIMENTAL!]",1, 4 ,"Nu",},
+{"in",1,0,"Inertia","f 0.0 1.0 0.8",FIELD(inertia),},
+{"fo",1,0,"Force","f 0.0 999.0 0.04",FIELD(force),},
+{"si",1,0,"Sigmoid","f -99999.0 99999.0 2.0",FIELD(sigmo),},
+{"s",2,0,"State","f -1.0 1.0 0.0",FIELD(newstate),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Gyro 
+ParamEntry NI_Gyro_tab []={
+{"Gyroscope",1, 0 ,"G",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Touch 
+ParamEntry NI_Touch_tab []={
+{"Touch",1, 1 ,"T",},
+{"r",1,0,"Range","f 0.0 1.0 1.0",FIELD(range),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Smell 
+ParamEntry NI_Smell_tab []={
+{"Smell",1, 0 ,"S",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Const 
+ParamEntry NI_Const_tab []={
+{"Constant",1, 0 ,"*",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_BendMuscle 
+ParamEntry NI_BendMuscle_tab []={
+{"Bend muscle",1, 2 ,"|",},
+{"p",0,0,"power","f 0.01 1.0 0.25",FIELD(power),},
+{"r",0,0,"bending range","f 0.0 1.0 1.0",FIELD(bendrange),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_RotMuscle 
+ParamEntry NI_RotMuscle_tab []={
+{"Rotation muscle",1, 1 ,"@",},
+{"p",0,0,"power","f 0.01 1.0 1.0",FIELD(power),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Diff 
+ParamEntry NI_Diff_tab []={
+{"Differentiate",1, 0 ,"D",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_FuzzyNeuro 
+ParamEntry NI_FuzzyNeuro_tab []={
+{"Fuzzy system [EXPERIMENTAL!]",1, 4 ,"Fuzzy",},
+{"ns",0,0,"number of fuzzy sets","d 1  ",FIELD(fuzzySetsNr),},
+{"nr",0,0,"number of rules","d 1  ",FIELD(rulesNr),},
+{"fs",0,0,"fuzzy sets","s 0 -1 0",FIELD(fuzzySetString),},
+{"fr",0,0,"fuzzy rules","s 0 -1 0",FIELD(fuzzyRulesString),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Sticky 
+ParamEntry NI_Sticky_tab []={
+{"Sticky [EXPERIMENTAL!]",1, 0 ,"Sti",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_LinearMuscle 
+ParamEntry NI_LinearMuscle_tab []={
+{"Linear muscle [EXPERIMENTAL!]",1, 1 ,"LMu",},
+{"p",0,0,"power","f 0.01 1.0 1.0",FIELD(power),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_WaterDetect 
+ParamEntry NI_WaterDetect_tab []={
+{"Water detector",1, 0 ,"Water",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Energy 
+ParamEntry NI_Energy_tab []={
+{"Energy level",1, 0 ,"Energy",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Channelize 
+ParamEntry NI_Channelize_tab []={
+{"Channelize",1, 0 ,"Ch",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_ChMux 
+ParamEntry NI_ChMux_tab []={
+{"Channel multiplexer",1, 0 ,"ChMux",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_ChSel 
+ParamEntry NI_ChSel_tab []={
+{"Channel selector",1, 1 ,"ChSel",},
+{"ch",0,0,"channel","d   ",FIELD(ch),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Random 
+ParamEntry NI_Random_tab []={
+{"Random noise",1, 0 ,"Rnd",},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT NI_Sinus 
+ParamEntry NI_Sinus_tab []={
+{"Sinus generator",1, 2 ,"Sin",},
+{"f0",0,0,"base frequency","f -1.0 1.0 0.06283185307",FIELD(f0),},
+{"t",0,0,"time","f 0 6.283185307 0",FIELD(t),},
+ {0,0,0,},};
+#undef FIELDSTRUCT
+
+#define SETIMPLEMENTATION \
+setImplementation("N",new NI_StdNeuron); \
+setImplementation("Nu",new NI_StdUNeuron); \
+setImplementation("G",new NI_Gyro); \
+setImplementation("T",new NI_Touch); \
+setImplementation("S",new NI_Smell); \
+setImplementation("*",new NI_Const); \
+setImplementation("|",new NI_BendMuscle); \
+setImplementation("@",new NI_RotMuscle); \
+setImplementation("D",new NI_Diff); \
+setImplementation("Fuzzy",new NI_FuzzyNeuro); \
+setImplementation("Sti",new NI_Sticky); \
+setImplementation("LMu",new NI_LinearMuscle); \
+setImplementation("Water",new NI_WaterDetect); \
+setImplementation("Energy",new NI_Energy); \
+setImplementation("Ch",new NI_Channelize); \
+setImplementation("ChMux",new NI_ChMux); \
+setImplementation("ChSel",new NI_ChSel); \
+setImplementation("Rnd",new NI_Random); \
+setImplementation("Sin",new NI_Sinus); \
+
Index: /cpp/frams/neuro/neurocls-f0-GDK-library.h
===================================================================
--- /cpp/frams/neuro/neurocls-f0-GDK-library.h	(revision 109)
+++ /cpp/frams/neuro/neurocls-f0-GDK-library.h	(revision 109)
@@ -0,0 +1,184 @@
+
+// do not edit - generated automatically from "f0.def"
+// (to be included in "neurolibrary.cpp")
+
+
+
+
+
+
+     
+static ParamEntry NI_StdNeuron_tab[]={
+{"Neuron",1, 4 ,"N",},
+
+{"in",1,0,"Inertia","f 0.0 1.0 0.8",},
+{"fo",1,0,"Force","f 0.0 999.0 0.04",},
+{"si",1,0,"Sigmoid","f -99999.0 99999.0 2.0",},
+{"s",2,0,"State","f -1.0 1.0 0.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_StdNeuron_tab,"Standard neuron",-1,1,0, NULL,false, 2));
+
+     
+static ParamEntry NI_StdUNeuron_tab[]={
+{"Unipolar neuron [EXPERIMENTAL!]",1, 4 ,"Nu",},
+{"in",1,0,"Inertia","f 0.0 1.0 0.8",},
+{"fo",1,0,"Force","f 0.0 999.0 0.04",},
+{"si",1,0,"Sigmoid","f -99999.0 99999.0 2.0",},
+{"s",2,0,"State","f -1.0 1.0 0.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_StdUNeuron_tab,"Works like standard neuron (N) but the output value is scaled to 0...+1 instead of -1...+1.\nHaving 0 as one of the saturation states should help in \"gate circuits\", where input signal is passed through or blocked depending on the other singal.",-1,1,0, NULL,false, 0));
+
+     static int Gyro_xy[]={83,8,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,12,43,24,48,24,48,19,38,19,38,24,43,24,43,54,48,54,48,64,43,69,38,64,38,54,43,54,5,63,69,58,74,48,79,38,79,28,74,23,69,1,43,79,43,74,1,23,69,26,66,1,63,69,60,66,1,55,76,53,73,1,31,75,33,72};   
+static ParamEntry NI_Gyro_tab[]={
+{"Gyroscope",1, 0 ,"G",},
+
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Gyro_tab,"Equilibrium sensor.\n0=the stick is horizontal\n+1/-1=the stick is vertical",0,1,2, Gyro_xy,false, 32));
+
+     static int Touch_xy[]={43,2,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,11,75,50,65,50,60,55,55,45,50,55,45,45,40,50,35,50,30,45,25,50,30,55,35,50};   
+static ParamEntry NI_Touch_tab[]={
+{"Touch",1, 1 ,"T",},
+
+
+{"r",1,0,"Range","f 0.0 1.0 1.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Touch_tab,"Touch sensor.\n-1=no contact\n0=just touching\n>0=pressing, value depends on the force applied",0,1,1, Touch_xy,false, 32));
+
+     static int Smell_xy[]={64,5,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,3,10,40,15,45,15,55,10,60,5,20,30,25,35,30,45,30,55,25,65,20,70,4,15,35,20,40,22,50,20,60,15,65,5,75,50,50,50,45,45,40,50,45,55,50,50};   
+static ParamEntry NI_Smell_tab[]={
+{"Smell",1, 0 ,"S",},
+
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Smell_tab,"Smell sensor. Aggregated \"smell of energy\" experienced from all energy objects (creatures and food pieces).\nClose objects have bigger influence than the distant ones: for each energy source, its partial feeling is proportional to its energy/(distance^2)",0,1,1, Smell_xy,false, 32));
+
+     static int Const_xy[]={29,4,4,26,27,26,73,73,73,73,27,26,27,1,73,50,100,50,1,56,68,46,68,2,41,47,51,32,51,68};   
+static ParamEntry NI_Const_tab[]={
+{"Constant",1, 0 ,"*",},
+
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Const_tab,"Constant value",0,1,0, Const_xy,false, 1));
+
+     static int BendMuscle_xy[]={63,6,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,3,65,56,49,29,57,24,72,50,4,68,53,70,53,70,55,68,55,68,53,5,50,21,60,15,70,14,79,15,87,20,81,10,1,86,20,77,21};   
+static ParamEntry NI_BendMuscle_tab[]={
+{"Bend muscle",1, 2 ,"|",},
+
+
+{"p",0,0,"power","f 0.01 1.0 0.25",},
+{"r",0,0,"bending range","f 0.0 1.0 1.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_BendMuscle_tab,"",1,0,2, BendMuscle_xy,false, 2+16+64+4));
+
+     static int RotMuscle_xy[]={62,5,5,25,40,35,40,45,50,35,60,25,60,25,40,4,65,85,65,50,75,50,75,85,65,85,1,69,10,77,17,10,59,15,57,17,57,22,60,26,69,27,78,26,82,21,82,16,79,12,69,10,80,6,3,65,50,65,20,75,20,75,50};   
+static ParamEntry NI_RotMuscle_tab[]={
+{"Rotation muscle",1, 1 ,"@",},
+
+
+{"p",0,0,"power","f 0.01 1.0 1.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_RotMuscle_tab,"",1,0,2, RotMuscle_xy,false, 2+16+128+4));
+
+     static int Diff_xy[]={24,3,3,25,0,25,100,75,50,25,0,1,75,50,100,50,3,44,42,51,57,36,57,44,42};   
+static ParamEntry NI_Diff_tab[]={
+{"Differentiate",1, 0 ,"D",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Diff_tab,"Calculate the difference between the current and previous input value. Multiple inputs are aggregated with respect to their weights",-1,1,0, Diff_xy,false, 0));
+
+     static int FuzzyNeuro_xy[]={44,5,2,30,65,37,37,44,65,3,37,65,44,37,51,37,58,65,2,51,65,58,37,65,65,6,100,50,70,50,70,25,25,10,25,90,70,75,70,50,1,70,65,25,65};   
+static ParamEntry NI_FuzzyNeuro_tab[]={
+{"Fuzzy system [EXPERIMENTAL!]",1, 4 ,"Fuzzy",},
+
+{"ns",0,0,"number of fuzzy sets","d 1  ",},
+{"nr",0,0,"number of rules","d 1  ",},
+{"fs",0,0,"fuzzy sets","s 0 -1 0",},
+{"fr",0,0,"fuzzy rules","s 0 -1 0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_FuzzyNeuro_tab,"Refer to publications to learn more about this neuron.",-1,1,0, FuzzyNeuro_xy,false, 0));
+
+     
+static ParamEntry NI_Sticky_tab[]={
+{"Sticky [EXPERIMENTAL!]",1, 0 ,"Sti",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Sticky_tab,"",1,0,1, NULL,false, 16));
+
+     
+static ParamEntry NI_LinearMuscle_tab[]={
+{"Linear muscle [EXPERIMENTAL!]",1, 1 ,"LMu",},
+
+{"p",0,0,"power","f 0.01 1.0 1.0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_LinearMuscle_tab,"",1,0,2, NULL,false, 16));
+
+     
+static ParamEntry NI_WaterDetect_tab[]={
+{"Water detector",1, 0 ,"Water",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_WaterDetect_tab,"Output signal:\n0=on or above water surface\n1=under water (deeper than 1)\n0..1=in the transient area just below water surface",0,1,1, NULL,false, 32));
+
+     
+static ParamEntry NI_Energy_tab[]={
+{"Energy level",1, 0 ,"Energy",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Energy_tab,"The current energy level divided by the initial energy level.\nUsually falls from initial 1.0 down to 0.0 and then the creature dies. It can rise above 1.0 if enough food is ingested",0,1,0, NULL,false, 32));
+
+     static int Channelize_xy[]={57,10,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,1,30,80,55,50,1,30,20,55,50,1,30,35,55,50,1,30,45,55,50,1,30,55,55,50,1,61,53,65,47,1,30,65,55,50};   
+static ParamEntry NI_Channelize_tab[]={
+{"Channelize",1, 0 ,"Ch",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Channelize_tab,"Combines all input signals into a single multichannel output; Note: ChSel and ChMux are the only neurons which support multiple channels. Other neurons discard everything except the first channel.",-1,1,0, Channelize_xy,false, 0));
+
+     static int ChMux_xy[]={52,7,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,3,30,67,45,67,45,50,50,50,1,35,70,39,64,2,30,33,53,33,53,48};   
+static ParamEntry NI_ChMux_tab[]={
+{"Channel multiplexer",1, 0 ,"ChMux",},
+
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_ChMux_tab,"Outputs the selected channel from the second (multichannel) input. The first input is used as the selector value (-1=select first channel, .., 1=last channel)",2,1,0, ChMux_xy,false, 0));
+
+     static int ChSel_xy[]={41,6,4,25,0,25,100,75,70,75,30,25,0,1,75,50,100,50,1,70,50,55,50,3,50,55,55,50,50,45,50,55,1,30,50,50,50,1,35,53,39,47};   
+static ParamEntry NI_ChSel_tab[]={
+{"Channel selector",1, 1 ,"ChSel",},
+
+{"ch",0,0,"channel","d   ",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_ChSel_tab,"Outputs a single channel (selected by the \"ch\" parameter) from multichannel input",1,1,0, ChSel_xy,false, 0));
+
+     
+static ParamEntry NI_Random_tab[]={
+{"Random noise",1, 0 ,"Rnd",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Random_tab,"Generates random noise (subsequent random values in the range of -1..+1)",0,1,0, NULL,false, 0));
+
+     static int Sinus_xy[]={46,3,12,75,50,71,37,62,28,50,25,37,28,28,37,25,50,28,62,37,71,50,75,62,71,71,62,75,50,1,75,50,100,50,5,35,50,40,35,45,35,55,65,60,65,65,50};   
+static ParamEntry NI_Sinus_tab[]={
+{"Sinus generator",1, 2 ,"Sin",},
+
+{"f0",0,0,"base frequency","f -1.0 1.0 0.06283185307",},
+{"t",0,0,"time","f 0 6.283185307 0",},
+ 
+{0,0,0,},};
+addClass(new NeuroClass(NI_Sinus_tab,"Output frequency = f0+input",1,1,0, Sinus_xy,false, 0));
Index: /cpp/frams/neuro/neurofactory.cpp
===================================================================
--- /cpp/frams/neuro/neurofactory.cpp	(revision 109)
+++ /cpp/frams/neuro/neurofactory.cpp	(revision 109)
@@ -0,0 +1,103 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neurofactory.h"
+#include <frams/util/sstring.h>
+#include <frams/param/param.h>
+#include "neuroimpl.h"
+#include "neurolibrary.h"
+
+#include NEURO_IMPL_FILES
+
+NeuroImpl* NeuroFactory::getImplementation(NeuroClass *nc)
+{
+if (nc!=NULL)
+	{
+	std::map<NeuroClass*,NeuroImpl*>::iterator it=impl.find(nc);
+	if (it!=impl.end())
+		return it->second;
+	}
+return NULL;
+}
+
+NeuroImpl* NeuroFactory::createNeuroImpl(NeuroClass *nc)
+{
+if (!nc) return 0;
+if (!nc->active) return 0;
+NeuroImpl* ni=getImplementation(nc);
+if (!ni) return 0;
+ni=ni->makeNew();
+if (ni) ni->neuroclass=nc;
+return ni;
+}
+
+NeuroImpl* NeuroFactory::setImplementation(const SString& classname,NeuroImpl *ni,bool deleteold)
+{
+NeuroClass *nc=Neuro::getClass(classname);
+if (!nc) return ni;
+return setImplementation(nc,ni,deleteold);
+}
+
+NeuroImpl* NeuroFactory::setImplementation(NeuroClass *nc,NeuroImpl *ni,bool deleteold)
+{
+if (nc==NULL) return NULL;
+std::map<NeuroClass*,NeuroImpl*>::iterator it=impl.find(nc);
+NeuroImpl* old_ni=NULL;
+if (it==impl.end())
+	{
+	if (ni!=NULL)
+		{
+		impl[nc]=ni;
+		nc->impl_count++;
+		}
+	return NULL;
+	}
+else
+	{
+	old_ni=it->second;
+	if (ni)
+		it->second=ni;
+	else
+		{
+		impl.erase(it);
+		nc->impl_count--;
+		}
+	}
+if (deleteold && old_ni) delete old_ni;
+return old_ni;
+}
+
+#include NEURO_CLS_FACTORY
+
+void NeuroFactory::setStandardImplementation()
+{
+SETIMPLEMENTATION
+}
+
+void NeuroFactory::freeImplementation()
+{
+for(int i=0;i<Neuro::getClassCount();i++)
+	setImplementation(Neuro::getClass(i),0);
+}
+
+void NeuroFactory::removeUnimplemented()
+{
+SString removed;
+for(int i=0;i<Neuro::getClassCount();i++)
+	{
+	NeuroClass *nc=Neuro::getClass(i);
+	if (nc->impl_count==0)
+		{
+		removed+=nc->getName();
+		removed+=" ";
+		NeuroLibrary::staticlibrary.classes-=i;	
+		i--;
+		delete nc;
+		}
+	}
+if (removed.len())
+	FMprintf("NeuroFactory","removeUninmplemented",FMLV_INFO,
+	 "Removed Neuro classes: %s",(const char*)removed);
+}
+
Index: /cpp/frams/neuro/neurofactory.h
===================================================================
--- /cpp/frams/neuro/neurofactory.h	(revision 109)
+++ /cpp/frams/neuro/neurofactory.h	(revision 109)
@@ -0,0 +1,29 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROFACTORY_H_
+#define _NEUROFACTORY_H_
+
+#include <frams/model/modelparts.h>
+#include <map>
+
+class NeuroImpl;
+
+class NeuroFactory
+{
+std::map<NeuroClass*,NeuroImpl*> impl;
+public:
+NeuroImpl* setImplementation(NeuroClass *nc,NeuroImpl *ni,bool deleteold=1);
+NeuroImpl* setImplementation(const SString& classname,NeuroImpl *ni,bool deleteold=1);
+NeuroImpl* getImplementation(NeuroClass *nc);
+class NeuroImpl* createNeuroImpl(Neuro *n)
+	{NeuroClass *nc=n->getClass(); if (!nc) return 0; return createNeuroImpl(nc);}
+class NeuroImpl* createNeuroImpl(NeuroClass *nc);
+
+void setStandardImplementation();
+void freeImplementation();
+void removeUnimplemented();
+};
+
+#endif
Index: /cpp/frams/neuro/neuroimpl.cpp
===================================================================
--- /cpp/frams/neuro/neuroimpl.cpp	(revision 109)
+++ /cpp/frams/neuro/neuroimpl.cpp	(revision 109)
@@ -0,0 +1,562 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neuroimpl.h"
+#include "neurofactory.h"
+#include <frams/util/rndutil.h>
+#include <common/nonstd_math.h>
+#ifndef GDK_WITHOUT_FRAMS
+#include <frams/simul/creature.h>
+#include <frams/mech/creatmechobj.h>
+#include <frams/simul/livegroups.h>
+#include <frams/simul/simul.h>
+#endif
+
+const int NeuroImpl::ENDDRAWING=-9999;
+const int NeuroImpl::MAXDRAWINGXY=0xffff;
+
+int NeuroNetImpl::mytags_id=0;
+
+/////////////////////////////////////////////////////////
+
+#define FIELDSTRUCT NeuroNetConfig
+static ParamEntry nncfg_paramtab[]=
+{
+{"Creature: Neurons",1,3,"nnsim",},
+{"randinit",1,0,"Random initialization","f 0 10 0.01",FIELD(randominit),"Allowed range for initializing all neuron states with uniform distribution random numbers and zero mean. Set to 0 for deterministic initialization."},
+{"nnoise",1,0,"Noise","f 0 1 0",FIELD(nnoise),"Gaussian neural noise: a random value is added to each neural output in each simulation step. Set standard deviation here to add random noise, or 0 for deterministic simulation."},
+{"touchrange",1,0,"T receptor range","f 0 100 1",FIELD(touchrange),},
+{0,0,0,},
+}; 
+#undef FIELDSTRUCT
+
+NeuroNetConfig::NeuroNetConfig(NeuroFactory *fac)
+	:par(nncfg_paramtab,this),
+	 randominit(0.01),
+	 nnoise(0),
+	 touchrange(1),
+	 factory(fac)
+{}
+
+/////////////////////////////////////////////////////////////////
+
+NeuroNetImpl::NeuroNetImpl(Model& model, NeuroNetConfig& conf
+#ifdef NEURO_SIGNALS
+, ChannelSpace *ch
+#endif
+)
+	:mod(model),config(conf),
+	 isbuilt(1),errorcount(0)
+#ifdef NEURO_SIGNALS
+,channels(ch)
+#endif
+{
+if (!mytags_id) mytags_id=mod.userdata.newID();
+
+Neuro *n;
+NeuroImpl *ni;
+Joint *j;
+int i;
+DB(printf("makeNeuroNet(%p)\n",&mod));
+
+minorder=3; maxorder=0;
+errorcount=0;
+
+for (i=0;j=mod.getJoint(i);i++)
+	j->flags&=~(4+8); // todo: !!!neuroitems shouldn't use model fields!!!
+
+for (i=0;n=mod.getNeuro(i);i++)
+	{
+	ni=conf.factory->createNeuroImpl(n);
+	n->userdata[mytags_id]=ni;
+	if (!ni) { errorcount++;
+		FMprintf("NeuroNetImpl","create",FMLV_WARN,"neuron #%d (%s) implementation not available",
+			 i,(const char*)n->getClassName());
+		continue; } // implementation not available?!
+	ni->owner=this;
+	ni->neuro=n;
+	ni->readParam();
+	}
+
+for (i=0;n=mod.getNeuro(i);i++)
+	{
+	n->state+=(rnd01-0.5)*config.randominit;
+	ni=(NeuroImpl*)n->userdata[mytags_id];
+	if (!ni) continue;
+	if (!ni->lateinit())
+		{ ni->status=NeuroImpl::InitError; errorcount++;
+		FMprintf("NeuroNetImpl","create",FMLV_WARN,"neuron #%d (%s) initialization failed",
+			 i,(const char*)n->getClassName());
+		continue; }
+	ni->status=NeuroImpl::InitOk;
+	int order=ni->getSimOrder();
+	if (order<0) order=0; else if (order>2) order=2;
+	if (order<minorder) minorder=order;
+	if (order>maxorder) maxorder=order;
+	neurons[order]+=ni;
+	if (ni->getNeedPhysics())
+		neurons[3]+=ni;
+	}
+cnode=mod.delmodel_list.add(STATRICKCALLBACK(this,&NeuroNetImpl::destroyNN,0));
+}
+
+void NeuroNetImpl::destroyNN(CALLBACKARGS)
+{
+if (!isbuilt) return;
+DB(printf("destroyNeuroNet(%p)\n",&mod));
+NeuroImpl *ni;
+Neuro *n;
+for (int i=0;n=mod.getNeuro(i);i++)
+	{
+	ni=(NeuroImpl*)n->userdata[mytags_id];
+	delete ni;
+	n->userdata[mytags_id]=0;
+	}
+mod.delmodel_list.remove(cnode);
+isbuilt=0; errorcount=0;
+delete this;
+}
+
+NeuroNetImpl::~NeuroNetImpl()
+{
+destroyNN(0,0);
+}
+
+void NeuroNetImpl::simulateNeuroNet()
+{
+NeuroImpl *ni;
+for (int order=minorder;order<=maxorder;order++)
+	{
+	int i;
+	SList &nlist=neurons[order];
+	for (i=0;ni=(NeuroImpl*)nlist(i);i++)
+		ni->go();
+	for (i=0;ni=(NeuroImpl*)nlist(i);i++)
+		ni->commit();
+	}
+}
+
+void NeuroNetImpl::simulateNeuroPhysics()
+{
+NeuroImpl *ni;
+int i;
+SList &nlist=neurons[3];
+for (i=0;ni=(NeuroImpl*)nlist(i);i++)
+	ni->goPhysics();
+}
+
+///////////////////////////////////////////////
+
+void NeuroImpl::setChannelCount(int c)
+{
+if (c<1) c=1;
+if (c==channels) return;
+if (c<channels) {channels=c; chstate.trim(c-1); chnewstate.trim(c-1); return;}
+double s=getState(channels-1);
+chnewstate.setSize(c-1);
+chstate.setSize(c-1);
+for(int i=channels;i<c;i++)
+	{chstate(i-1)=s; chnewstate(i-1)=s;}
+channels=c;
+}
+
+void NeuroImpl::setState(double st,int channel)
+{
+validateNeuroState(st);
+if (channel>=channels) channel=channels-1;
+if (channel<=0) {newstate=st;return;}
+chnewstate(channel-1)=st;
+}
+
+void NeuroImpl::setCurrentState(double st,int channel)
+{
+validateNeuroState(st);
+if (channel>=channels) channel=channels-1;
+if (channel<=0) {neuro->state=st; return;}
+chstate(channel-1)=st;
+}
+
+double NeuroImpl::getNewState(int channel)
+{
+if (neuro->flags&Neuro::HoldState) return getState(channel);
+if (channel>=channels) channel=channels-1;
+if (channel<=0) {return newstate;}
+return chnewstate(channel-1);
+}
+
+double NeuroImpl::getState(int channel)
+{
+if (channel>=channels) channel=channels-1;
+if (channel<=0) return neuro->state;
+return chstate(channel-1);
+}
+
+void NeuroImpl::commit()
+{
+if (!(neuro->flags&Neuro::HoldState))
+	{
+	if (channels>1)
+		chstate=chnewstate;
+	neuro->state=newstate;
+	if (owner->getConfig().nnoise>0.0)
+		{
+		neuro->state+=RndGen.GaussStd()*owner->getConfig().nnoise;
+		if (channels>1)
+			for(int i=0;i<chstate.size();i++)
+				chstate(0)+=RndGen.GaussStd()*owner->getConfig().nnoise;
+		}
+	}
+}
+
+int NeuroImpl::getInputChannelCount(int i)
+{
+if ((i<0)||(i >= neuro->getInputCount())) return 1;
+Neuro *nu=neuro->getInput(i);
+NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
+if (!ni) return 1;
+return ni->channels;
+}
+
+double NeuroImpl::getInputState(int i,int channel)
+{
+if ((i<0)||(i >= neuro->getInputCount())) return 0;
+Neuro *nu=neuro->getInput(i);
+if (channel<=0) return nu->state;
+NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
+if (!ni) return nu->state;
+if (channel>=ni->channels) channel=ni->channels-1;
+if (!channel) return nu->state;
+return ni->chstate(channel-1);
+}
+
+double NeuroImpl::getWeightedInputState(int i, int channel)
+{
+if ((i<0)||(i >= neuro->getInputCount())) return 0;
+double w;
+Neuro *nu=neuro->getInput(i,w);
+if (channel<=0) return nu->state * w;
+NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
+if (!ni) return nu->state * w;
+if (channel>=ni->channels) channel=ni->channels-1;
+if (!channel) return w * nu->state;
+return w * ni->chstate(channel-1);
+}
+
+double NeuroImpl::getInputSum(int startwith)
+{
+if (startwith<0) return 0;
+Neuro *inp;
+double sum=0.0;
+while(inp=neuro->getInput(startwith++))
+	sum+=inp->state;
+return sum;
+}
+
+double NeuroImpl::getWeightedInputSum(int startwith)
+{
+if (startwith<0) return 0;
+Neuro *inp;
+double sum=0.0;
+double w;
+while(inp=neuro->getInput(startwith++,w))
+	sum+=inp->state*w;
+return sum;
+}
+
+void NeuroImpl::readParam()
+{
+Param par;
+if (!paramentries) return;
+par.setParamTab(paramentries);
+par.select(this);
+par.setDefault();
+int zero=0;
+par.load2(neuro->getClassParams(),zero);
+}
+
+Param& NeuroImpl::getStaticParam()
+{
+static Param p(neuroimpl_tab,0,"Neuro");
+return p;
+}
+
+/////////////////////////////
+
+#ifdef NEURO_SIGNALS
+#define NEUROIMPL_SIGNAL_PROPS 1
+#else
+#define NEUROIMPL_SIGNAL_PROPS 0
+#endif
+
+#define FIELDSTRUCT NeuroImpl
+ParamEntry neuroimpl_tab[]=
+{
+{"Neuro",1,27+NEUROIMPL_SIGNAL_PROPS,"Neuro","Live Neuron object."},
+
+{"getInputState",0,0,"Get input signal","p f(d input)",PROCEDURE(p_get),},
+{"getInputWeight",0,0,"Get input weight","p f(d input)",PROCEDURE(p_getweight),},
+{"getWeightedInputState",0,0,"Get weighted input signal","p f(d input)",PROCEDURE(p_getw),},
+{"getInputSum",0,0,"Get signal sum","p f(d input)",PROCEDURE(p_getsum),},
+{"getWeightedInputSum",0,0,"Get weighted signal sum","p f(d input)",PROCEDURE(p_getwsum),"Uses any number of inputs starting with the specified input. getWeightedInputSum(0)=weightedInputSum"},
+{"getInputCount",0,0,"Get input count","d",GETONLY(count),},
+{"inputSum",0,0,"Full signal sum","f",GETONLY(sum),},
+{"weightedInputSum",0,0,"Full weighted signal sum","f",GETONLY(wsum),},
+{"getInputChannelCount",0,0,"Get channel count for input","p d(d input)",PROCEDURE(p_getchancount),},
+{"getInputStateChannel",0,0,"Get input signal from channel","p f(d input,d channel)",PROCEDURE(p_getchan),},
+{"getWeightedInputStateChannel",0,0,"Get weighted input signal from channel","p f(d input,d channel)",PROCEDURE(p_getwchan),},
+{"state",0,0,"Neuron state (channel 0)","f",GETSET(state),"When read, returns the current neuron state.\nWhen written, sets the 'internal' neuron state that will become current in the next step.\nTypically you should use this field, and not currState."},
+{"channelCount",0,0,"Number of output channels","d",GETSET(channels),},
+{"getStateChannel",0,0,"Get state for channel","p f(d channel)",PROCEDURE(p_getstate),},
+{"setStateChannel",0,0,"Set state for channel","p(d channel,f value)",PROCEDURE(p_setstate),},
+{"hold",0,0,"Hold state","d 0 1",GETSET(hold),"\"Holding\" means keeping the neuron state as is, blocking the regular neuron operation. This is useful when your script needs to inject some control signals into the NN. Without \"holding\", live neurons would be constantly overwriting your changes, and the rest of the NN could see inconsistent states, depending on the connections. Setting hold=1 ensures the neuron state will be only set by you, and not by the neuron. The enforced signal value can be set using Neuro.currState before or after setting hold=1. Set hold=0 to resume normal operation.",},
+{"currState",0,0,"Current neuron state (channel 0)","f",GETSET(cstate),"When read, it behaves just like the 'state' field.\nWhen written, changes the current neuron state immediately, which disturbs the regular synchronous NN operation.\nThis feature should only be used while controlling the neuron 'from outside' (like a neuro probe) and not in the neuron definition. See also: Neuro.hold",},
+{"setCurrStateChannel",0,0,"Set current neuron state for channel","p(d channel,f value)",PROCEDURE(p_setcstate),"Analogous to \"currState\"."},
+{"position_x",0,0,"Position x","f",GETONLY(position_x),},
+{"position_y",0,0,"Position y","f",GETONLY(position_y),},
+{"position_z",0,0,"Position z","f",GETONLY(position_z),},
+{"creature",0,0,"Gets owner creature","o Creature",GETONLY(creature),},
+{"part",0,0,"The Part object where this neuron is located","o MechPart",GETONLY(part),},
+{"joint",0,0,"The Joint object where this neuron is located","o MechJoint",GETONLY(joint),},
+{"fields",0,0,"Custom neuron fields","o Fields",GETONLY(fields),
+"Neurons can have different fields depending on their class. Script neurons have their fields defined using the \"prop:\" syntax. If you develop a custom neuron script you should use the Fields object for accessing your own neuron fields. The Neuro.fields property is meant for accessing the neuron fields from the outside script.\n"
+"Examples:\n"
+"var c=Populations.createFromString(\"X[N]\");\n"
+"Simulator.print(\"standard neuron inertia=\"+c.getNeuro(0).fields.in);\n"
+"c=Populations.createFromString(\"X[Nn,e:0.1]\");\n"
+"Simulator.print(\"noisy neuron error rate=\"+c.getNeuro(0).fields.e);\n"
+"\n"
+"The Interface object can be used to discover which fields are available for a certain neuron object:\n"
+"c=Populations.createFromString(\"X[N]\");\n"
+"var iobj=Interface.makeFrom(c.getNeuro(0).fields);\n"
+"var i;\n"
+"for(i=0;i<iobj.properties;i++)\n"
+" Simulator.print(iobj.getId(i)+\" (\"+iobj.getName(i)+\")\");",},
+{"def",0,0,"Neuron definition from which this live neuron was built","o NeuroDef",GETONLY(neurodef),},
+{"classObject",0,0,"Neuron class for this neuron","o NeuroClass",GETONLY(classObject),},
+#ifdef NEURO_SIGNALS
+{"signals",0,PARAM_READONLY,"Signals","o NeuroSignals",FIELD(sigs_obj),},
+#endif
+
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+#ifdef NEURO_SIGNALS
+ParamEntry neurosignals_paramtab[]=
+ {
+{"NeuroSignals",1,8,"NeuroSignals","Signals attached to a neuron.\nSee also: Signal, WorldSignals, CreatureSignals.\nscripts/light.neuro and scripts/seelight.neuro are simple custom neuron examples demonstrating how to send/receive signals between creatures.",},
+
+#define FIELDSTRUCT NeuroSignals
+SIGNPAR_ADD(""),
+SIGNPAR_RECEIVE(""),
+SIGNPAR_RECEIVESET(""),
+SIGNPAR_RECEIVEFILTER(""),
+SIGNPAR_RECEIVESINGLE(""),
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT SignalSet
+SIGNSETPAR_GET,
+SIGNSETPAR_SIZE,
+SIGNSETPAR_CLEAR,
+#undef FIELDSTRUCT
+{0,0,0,},
+ }; 
+
+Param& NeuroSignals::getStaticParam()
+{
+static Param p(neurosignals_paramtab,0);
+return p;
+}
+#endif
+
+#ifdef NEURO_SIGNALS
+class NeuroSigSource: public SigSource
+{
+  protected:
+NeuroImpl* owner;
+  public:
+NeuroSigSource(NeuroImpl *n,Creature *c):SigSource(0,c),owner(n) {}
+bool update();
+};
+
+bool NeuroSigSource::update()
+{
+Pt3D p;
+if (owner->getPosition(p))
+	{
+	setLocation(p);
+	return true;
+	}
+return false;
+}
+
+Creature *NeuroSignals::getCreature()
+{
+if (!cr)
+	{
+	cr=owner->getCreature();
+	}
+return cr;
+}
+
+void NeuroSignals::p_add(PARAMPROCARGS)
+{
+SigSource *s=new NeuroSigSource(owner,getCreature());
+if (owner->owner->channels)
+	{
+	SigChannel *ch=owner->owner->channels->getChannel(args->getString(),true);
+	ch->addSource(s);
+	}
+else
+	SigChannel::dummy_channel.addSource(s);
+sigs+=s;
+s->setupObject(ret);
+}
+
+void NeuroSignals::p_receive(PARAMPROCARGS)
+{
+SigChannel *ch; Pt3D p;
+if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args->getString(),false)) && owner->getPosition(p))
+	ret->setDouble(ch->receive(&p,getCreature()));
+else
+	ret->setDouble(0);
+}
+
+void NeuroSignals::p_receiveFilter(PARAMPROCARGS)
+{
+SigChannel *ch; Pt3D p;
+if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[3].getString(),false)) && owner->getPosition(p))
+	ret->setDouble(ch->receive(&p,getCreature(),args[2].getDouble(),args[1].getDouble(),args[0].getDouble()));
+else
+	ret->setDouble(0);
+}
+
+void NeuroSignals::p_receiveSet(PARAMPROCARGS)
+{
+SigChannel *ch; Pt3D p;
+SigVector *vec=new SigVector();
+if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[1].getString(),false)) && owner->getPosition(p))
+	ch->receiveSet(vec,&p,getCreature(),args[0].getDouble());
+ret->setObject(vec->makeObject());
+}
+
+void NeuroSignals::p_receiveSingle(PARAMPROCARGS)
+{
+SigChannel *ch; Pt3D p;
+if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[1].getString(),false)) && owner->getPosition(p))
+	{
+	SigSource *src=ch->receiveSingle(&p,getCreature(),args[0].getDouble(),0,1e99);
+	if (src)
+		{
+		src->setupObject(ret);
+		return;
+		}
+	}
+ret->setEmpty();
+}
+#endif
+
+extern ParamEntry creature_paramtab[];
+static Param creature_param(creature_paramtab,0);
+
+Creature* NeuroImpl::getCreature()
+{
+#ifndef GDK_WITHOUT_FRAMS
+CreatMechObject *cmo=(CreatMechObject *)neuro->owner->userdata[CreatMechObject::modeltags_id];
+return cmo->creature;
+#else
+return 0;
+#endif
+}
+
+void NeuroImpl::get_creature(ExtValue *ret)
+{
+#ifndef GDK_WITHOUT_FRAMS
+ret->setObject(ExtObject(&creature_param,getCreature()));
+#endif
+}
+
+void NeuroImpl::get_part(ExtValue *ret)
+{
+#ifndef GDK_WITHOUT_FRAMS
+Part *pa;
+if (pa=neuro->getPart())
+	ret->setObject(ExtObject(&MechPart::getStaticParam(),((MechPart *)pa->userdata[CreatMechObject::modeltags_id])));
+else
+	ret->setEmpty();
+#endif
+}
+
+void NeuroImpl::get_joint(ExtValue *ret)
+{
+#ifndef GDK_WITHOUT_FRAMS
+Joint *jo;
+if (jo=neuro->getJoint())
+	ret->setObject(ExtObject(&MechJoint::getStaticParam(),((MechJoint*)jo->userdata[CreatMechObject::modeltags_id])));
+else
+	ret->setEmpty();
+#endif
+}
+
+bool NeuroImpl::getPosition(Pt3D &pos)
+{
+#ifndef GDK_WITHOUT_FRAMS
+Part *pa; Joint *jo;
+if (pa=neuro->getPart())
+	{pos=((MechPart *)pa->userdata[CreatMechObject::modeltags_id])->p; return true;}
+if (jo=neuro->getJoint())
+	{
+	if (neuro->getClass()->getVisualHints() & NeuroClass::AtFirstPart)
+		pos=((MechPart*)jo->part1->userdata[CreatMechObject::modeltags_id])->p;
+	else if (neuro->getClass()->getVisualHints() & NeuroClass::AtSecondPart)
+		pos=((MechPart*)jo->part2->userdata[CreatMechObject::modeltags_id])->p;
+	else pos=(((MechPart*)jo->part1->userdata[CreatMechObject::modeltags_id])->p
+		   +((MechPart*)jo->part2->userdata[CreatMechObject::modeltags_id])->p)/2;
+	return true;
+	}
+#endif
+return false;
+}
+
+void NeuroImpl::get_position_x(ExtValue *ret)
+{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.x); else ret->setEmpty();}
+void NeuroImpl::get_position_y(ExtValue *ret)
+{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.y); else ret->setEmpty();}
+void NeuroImpl::get_position_z(ExtValue *ret)
+{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.z); else ret->setEmpty();}
+
+
+void NeuroImpl::createFieldsObject()
+{
+fields_param=new Param(paramentries?paramentries:(ParamEntry*)&empty_paramtab,this,"Fields");
+fields_object=new ExtObject(fields_param);
+}
+
+void NeuroImpl::get_fields(ExtValue *ret)
+{
+if (!fields_object)
+	createFieldsObject();
+ret->setObject(*fields_object);
+}
+
+void NeuroImpl::get_neurodef(ExtValue *ret)
+{
+ret->setObject(ExtObject(&Neuro::getStaticParam(),neuro));
+}
+
+void NeuroImpl::get_classObject(ExtValue *ret)
+{
+#ifndef GDK_WITHOUT_FRAMS
+NeuroClassExt::makeStaticObject(ret,neuroclass);
+#endif
+}
+
+NeuroImpl::~NeuroImpl()
+{
+if (fields_param)
+	{
+	delete fields_param;
+	delete fields_object;
+	}
+}
Index: /cpp/frams/neuro/neuroimpl.h
===================================================================
--- /cpp/frams/neuro/neuroimpl.h	(revision 109)
+++ /cpp/frams/neuro/neuroimpl.h	(revision 109)
@@ -0,0 +1,307 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NEUROIMPL_H_
+#define _NEUROIMPL_H_
+
+#include <frams/model/model.h>
+#include <frams/param/param.h>
+#include <common/framsg.h>
+#ifdef NEURO_SIGNALS
+#include <frams/simul/signals.h>
+#endif
+
+class NeuroImpl;
+extern ParamEntry neuroimpl_tab[];
+
+class Creature;
+class NeuroLibrary;
+class NeuroFactory;
+
+class NeuroNetConfig
+{
+public:
+NeuroNetConfig(NeuroFactory *fac);
+
+Param par;
+double randominit;
+double nnoise;
+double touchrange;
+
+NeuroFactory *factory;
+//static NeuroNetConfig& getGlobalConfig();
+};
+
+#ifdef NEURO_SIGNALS
+class NeuroSignals: public SignalSet
+{
+  protected:
+Creature *cr;
+NeuroImpl *owner;
+Creature *getCreature();
+  public:
+
+NeuroSignals(NeuroImpl *n):cr(0),owner(n) {}
+
+#define STATRICKCLASS NeuroSignals
+PARAMPROCDEF(p_add);
+PARAMPROCDEF(p_get);
+PARAMGETDEF(size);
+PARAMPROCDEF(p_receive);
+PARAMPROCDEF(p_receiveSet);
+PARAMPROCDEF(p_receiveFilter);
+PARAMPROCDEF(p_receiveSingle);
+#undef STATRICKCLASS
+
+static Param& getStaticParam();
+};
+#endif
+
+/// Neuro net implementation
+class NeuroNetImpl
+{
+CallbackNode *cnode;
+Model &mod;
+SList neurons[4];
+NeuroNetConfig& config;
+int isbuilt,errorcount;
+STCALLBACKDEFC(NeuroNetImpl,destroyNN);
+int minorder,maxorder;
+
+  public:
+#ifdef NEURO_SIGNALS
+ChannelSpace *channels;
+#endif
+static int mytags_id;
+static double getStateFromNeuro(Neuro *n);
+int getErrorCount() {return errorcount;}
+NeuroNetConfig &getConfig() {return config;}
+NeuroNetImpl(Model& model, NeuroNetConfig& conf
+#ifdef NEURO_SIGNALS
+, ChannelSpace *ch=0
+#endif
+);
+~NeuroNetImpl();
+void simulateNeuroNet();
+void simulateNeuroPhysics();
+
+static NeuroImpl *getImpl(Neuro* n) {return (NeuroImpl*)n->userdata[mytags_id];}
+};
+
+
+/**
+   Neuro implementation - this object calculates the Neuron's state
+   (Neuro::state) in each simulation step.
+
+SUBCLASSING TUTORIAL
+====================
+
+1.Derive your custom neuron from NeuroImpl class. The name must be prefixed with NI_
+
+class NI_MyNeuron: public NeuroImpl
+{ ... };
+
+2.Public parameters
+Create any number of public fields, they will be adjustable from the genotype level.
+3 datatypes are supported: long, double and SString
+
+public:
+  long intParameter;
+  double fpParameter;
+  SString txtParameter;
+
+
+3.Required method: "instantiator".
+It is always the same, just create a new instance of your neuron.
+public:
+  NeuroImpl* makeNew() { return new NI_MyNeuron(); };
+
+
+4.Required method: default constructor
+Set the "paramentries" variable if you need public parameters in your neuron.
+NI_..._tab is created automatically and should be declared as: extern ParamEntry NI_..._tab[];
+At this stage the parameter values are not yet available.
+
+public:
+ NI_MyNeuron() // no parameters!
+ {
+ paramentries=NI_MyNeuron_tab;
+ // you add here: some general initialization
+ }
+
+
+5.Optional method: initialization
+This method is called once before the neuron is actually used in the simulation.
+The parameter values are already initialized (according to the genotype) and the neuron is bound to the creature (i.e. this->neuro is valid).
+Return 0 if the neuron cannot be initialized.
+
+int lateinit()
+ {
+ // you add here: initialization using full neuron context
+ // example: if (!neuro->joint) return 0; //this neuron must be attached to joint
+ return 1;//OK
+ }
+
+
+6.Required method: simulation step
+If it has output: calculate the next neuron state and call setState()
+If it is an effector: do anything else
+
+void go()
+ {
+ // you add here: things called every simulation step
+ }
+
+Note: You can make your neuron fire before or after "regular" neurons by changing its "simorder" property (during initialization). The default value is 1, whereas receptors have simorder=0 and effectors have simorder=2.
+
+
+7.Neuron definition
+In order to incorporate the new neuron into Framsticks you need to provide some additional information (to be added to "f0.def" file).
+
+NEUROCLASS(MyNeuron,MN,This is the name,`Neuron description',-1,1,0)
+NEUROPROP(int,0,0,name of the int,d,,,,intParameter)
+NEUROPROP(fp,0,0,name of the floating point,f,,,,fpParameter)
+NEUROPROP(txt,0,0,name of the text,s,,,,txtParameter)
+ENDNEUROCLASS 
+
+NEUROCLASS:
+- MyNeuron: neuron class name (without the NI_ prefix)
+- MN: neuron symbol (used in genotypes)
+- full name and description
+- -1: preferred number of inputs (special case: -1=any)
+- 1: provides output: 1=yes/0=no
+- 0: preferred location: 0=none, 1=part, 2=joint
+
+NEUROPROP:
+- int/fp/txt: parameter names as visible in genotypes and scripting
+- "name of the ...": descriptive name
+- d/f/s: type (int/floating point/string)
+- intParameter/fpParameter/txtParameter: C++ field names
+
+   
+ */
+class NeuroImpl
+{
+protected:
+int simorder;
+int channels;
+SListTempl<double> chstate;
+SListTempl<double> chnewstate;
+Param *fields_param;
+ExtObject *fields_object;
+public:
+static const int ENDDRAWING;
+static const int MAXDRAWINGXY;
+
+enum NeuroImplStats { BeforeInit=0, InitError=1, InitOk=2 };
+NeuroImplStats status;
+/** originating neuron object (from the model) */
+Neuro *neuro;
+NeuroClass *neuroclass;
+/** don't access directly */
+double newstate;
+NeuroNetImpl *owner;
+/** will be used by readParam() method, if not null  */
+ParamEntry *paramentries; // no extra properties if ==0
+
+#ifdef NEURO_SIGNALS
+NeuroSignals sigs;
+ExtObject sigs_obj;
+#endif
+
+/** "virtual constructor" - NeuroFactory uses this method to create the proper implementation object.
+    subclasses must return new object here. */
+virtual NeuroImpl* makeNew() {return 0;} // 
+/** read additional properties from "moredata" field of the originating Neuro */
+void readParam();
+/** called when all other neuro objects were already created and "moredata" transferred to
+    object fields.
+    useful for initialization that cannot be performed in the constructor.
+    @return 1=ok  0=failure
+*/
+virtual int lateinit() {return 1;}
+/** calculate 'newstate - implementation dependent */
+virtual void go(){}
+/** for neurons doing some physical actions (called each simulation step when nnspeed!=1.0) */
+virtual void goPhysics(){}
+
+int getSimOrder() {return simorder;}
+virtual int getNeedPhysics() {return 0;}
+
+void setChannelCount(int c);
+int getChannelCount() {return channels;}
+
+int getInputCount() {return neuro->getInputCount();}
+int getInputChannelCount(int i);
+double getInputState(int i,int channel=0);
+double getWeightedInputState(int i,int channel=0);
+double getInputSum(int startwith=0);
+double getWeightedInputSum(int startwith=0);
+double getInputWeight(int i) {return neuro->getInputWeight(i);}
+void setState(double st,int channel);
+void setState(double st) {validateNeuroState(st); newstate=st;}
+double getState(int channel);
+double getState() {return neuro->state;}
+
+virtual int getDrawingCount() {return 0;}
+virtual int* getDrawing(int i) {return 0;}
+
+void commit();
+void validateNeuroState(double& st) {if (st<=-1e10) st=-1e10; else if (st>1e10) st=1e10;}
+
+NeuroImpl():simorder(1),channels(1),fields_param(0),fields_object(0),status(BeforeInit),neuro(0),newstate(0),owner(0),paramentries(0)
+#ifdef NEURO_SIGNALS
+,sigs(this),sigs_obj(&NeuroSignals::getStaticParam(),&sigs)
+#endif
+ {}
+virtual ~NeuroImpl();
+virtual void createFieldsObject();
+
+/** usually == "newstate" but will obey the "hold state" */
+double getNewState(int channel=0);
+
+/** don't use! */
+void setCurrentState(double st,int channel=0);
+
+bool getPosition(Pt3D &pos);
+Creature* getCreature();
+
+#define STATRICKCLASS NeuroImpl
+PARAMGETDEF(count) {arg1->setInt(getInputCount());}
+PARAMPROCDEF(p_get) {arg2->setDouble(getInputState(arg1->getInt()));}
+PARAMPROCDEF(p_getweight) {arg2->setDouble(getInputWeight(arg1->getInt()));}
+PARAMPROCDEF(p_getw) {arg2->setDouble(getWeightedInputState(arg1->getInt()));}
+PARAMPROCDEF(p_getsum) {arg2->setDouble(getInputSum(arg1->getInt()));}
+PARAMPROCDEF(p_getwsum) {arg2->setDouble(getWeightedInputSum(arg1->getInt()));}
+PARAMGETDEF(sum) {arg1->setDouble(getInputSum(0));}
+PARAMGETDEF(wsum) {arg1->setDouble(getWeightedInputSum(0));}
+PARAMPROCDEF(p_getchancount) {arg2->setInt(getInputChannelCount(arg1->getInt()));}
+PARAMPROCDEF(p_getchan) {arg2->setDouble(getInputState(arg1[1].getInt(),arg1[0].getInt()));}
+PARAMPROCDEF(p_getwchan) {arg2->setDouble(getWeightedInputState(arg1[1].getInt(),arg1[0].getInt()));}
+PARAMGETDEF(state) {arg1->setDouble(getState());}
+PARAMSETDEF(state) {setState(arg1->getDouble()); return 0;}
+PARAMGETDEF(cstate) {arg1->setDouble(neuro->state);}
+PARAMSETDEF(cstate) {setCurrentState(arg1->getDouble()); return 0;}
+PARAMGETDEF(hold) {arg1->setInt((neuro->flags&(Neuro::HoldState))?1:0);}
+PARAMSETDEF(hold) {neuro->flags=(neuro->flags&~Neuro::HoldState)|(arg1->getInt()?Neuro::HoldState:0); return 0;}
+PARAMGETDEF(channels) {arg1->setInt(getChannelCount());}
+PARAMSETDEF(channels) {setChannelCount(arg1->getInt()); return 0;}
+PARAMPROCDEF(p_getstate) {arg2->setDouble(getState(arg1->getInt()));}
+PARAMPROCDEF(p_setstate) {setState(arg1[0].getDouble(),arg1[1].getInt());}
+PARAMPROCDEF(p_setcstate) {setCurrentState(arg1[0].getDouble(),arg1[1].getInt());}
+PARAMGETDEF(creature);
+PARAMGETDEF(part);
+PARAMGETDEF(joint);
+PARAMGETDEF(position_x);
+PARAMGETDEF(position_y);
+PARAMGETDEF(position_z);
+PARAMGETDEF(fields);
+PARAMGETDEF(neurodef);
+PARAMGETDEF(classObject);
+#undef STATRICKCLASS
+
+  static Param& getStaticParam();
+};
+
+#endif
Index: /cpp/frams/neuro/neuroimplfiles-GDK.h
===================================================================
--- /cpp/frams/neuro/neuroimplfiles-GDK.h	(revision 109)
+++ /cpp/frams/neuro/neuroimplfiles-GDK.h	(revision 109)
@@ -0,0 +1,16 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "impl/neuroimpl-simple.h"
+#include "impl/neuroimpl-channels.h"
+#include "impl/neuroimpl-fuzzy.h"
+#ifdef GDK_WITHOUT_FRAMS
+#include "impl/neuroimpl-body-gdk.h"
+#else
+#include "impl/neuroimpl-test.h"
+#include "impl/neuroimpl-body.h"
+#include "impl/neuroimpl-vectoreye.h"
+#include "impl/neuroimpl-vmot.hpp"
+#endif
+
Index: /cpp/frams/neuro/neurolibrary.cpp
===================================================================
--- /cpp/frams/neuro/neurolibrary.cpp	(revision 109)
+++ /cpp/frams/neuro/neurolibrary.cpp	(revision 109)
@@ -0,0 +1,77 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "neurolibrary.h"
+#include <frams/param/param.h>
+#include <frams/model/modelparts.h>
+
+NeuroLibrary NeuroLibrary::staticlibrary;
+
+int NeuroLibrary::findClassIndex(const SString & classname, bool activeonly)
+{
+NeuroClass* cl;
+for(int i=0;cl=(NeuroClass*)classes(i);i++)
+	{
+	if (activeonly && !cl->active) continue;
+	if (classname == cl->getName()) return i;
+	}
+return -1;
+}
+
+NeuroClass* NeuroLibrary::findClass(const SString & classname, bool activeonly)
+{
+int i=findClassIndex(classname,activeonly);
+if (i<0) return 0;
+return getClass(i);
+}
+
+SString NeuroLibrary::getClassName(int classindex)
+{
+NeuroClass *cl=getClass(classindex);
+return cl? cl->getName() : SString();
+}
+
+NeuroClass *NeuroLibrary::addClass(NeuroClass *cls,bool replace)
+{
+NeuroClass *old=findClass(cls->getName());
+if (old)
+	{
+	if (replace)
+		{
+		classes-=old;
+		classes+=cls;
+		}
+	}
+else
+	classes+=cls;
+return old;
+}
+
+void NeuroLibrary::addStandardClasses()
+{
+#include NEURO_CLS_LIBRARY
+}
+
+NeuroLibrary::NeuroLibrary()
+{
+addStandardClasses();
+}
+
+NeuroLibrary::~NeuroLibrary()
+{
+FOREACH(NeuroClass*,cl,classes)
+	delete cl;
+}
+
+void NeuroLibrary::removeClass(int i)
+{
+classes-=i;
+}
+
+void NeuroLibrary::clear()
+{
+while(getClassCount()>0)
+	removeClass(getClassCount()-1);
+}
+
Index: /cpp/frams/neuro/neurolibrary.h
===================================================================
--- /cpp/frams/neuro/neurolibrary.h	(revision 109)
+++ /cpp/frams/neuro/neurolibrary.h	(revision 109)
@@ -0,0 +1,31 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _NILIBRARY_H_
+#define _NILIBRARY_H_
+
+#include <frams/util/advlist.h>
+#include <frams/param/param.h>
+
+class NeuroClass;
+
+class NeuroLibrary
+{
+  public:
+static NeuroLibrary staticlibrary;
+NeuroLibrary();
+~NeuroLibrary();
+AdvList classes;
+NeuroClass* findClass(const SString& classname, bool activeonly=1);
+void addStandardClasses();
+int findClassIndex(const SString& classname, bool activeonly=1);
+NeuroClass* getClass(int classindex) {return (NeuroClass*)classes(classindex);}
+SString getClassName(int classindex);
+int getClassCount() {return classes.size();}
+void removeClass(int i);
+void clear();
+NeuroClass *addClass(NeuroClass *cls,bool replace=1);
+};
+
+#endif
Index: /cpp/frams/param/multiparamload.cpp
===================================================================
--- /cpp/frams/param/multiparamload.cpp	(revision 109)
+++ /cpp/frams/param/multiparamload.cpp	(revision 109)
@@ -0,0 +1,258 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "multiparamload.h"
+#include <frams/util/sstringutils.h>
+#include "common/framsg.h"
+#include <ctype.h>
+
+void MultiParamLoader::init()
+{
+file=0; ownfile=0;
+status=0;
+reset();
+}
+
+void MultiParamLoader::reset()
+{
+status=0;
+breakcond=OnError;
+emptyparam.setParamTab(empty_paramtab);
+}
+
+int MultiParamLoader::findObject(const ExtObject &o)
+{
+for(int i=0;i<objects.size();i++)
+	if ((*objects(i))==o)
+		return i;
+return -1;
+}
+
+void MultiParamLoader::removeObject(const ExtObject &o)
+{
+int i=findObject(o);
+if (i>=0)
+	{
+	delete objects(i);
+	objects-=i;
+	}
+}
+
+void MultiParamLoader::clearObjects()
+{
+FOREACH(ExtObject*,o,objects)
+	delete o;
+objects.clear();
+}
+
+void MultiParamLoader::load()
+{
+clearstack();
+if (!file)
+	{
+	lasterror="can't open file";
+	status=OnError;
+	return;
+	}
+status=Loading;
+}
+
+void MultiParamLoader::abort()
+{
+if (file && ownfile)
+	{
+	fclose(file);
+	file=0;
+	}
+clearstack();
+status=Finished;
+}
+
+void MultiParamLoader::load(VirtFILE *f)
+{
+abort();
+ownfile=0;
+file=f;
+load();
+}
+
+void MultiParamLoader::load(const char* filename)
+{
+abort();
+ownfile=1;
+file=Vfopen(filename,FOPEN_READ_BINARY);
+load();
+}
+
+int MultiParamLoader::go()
+{
+SString buf;
+if (status==OnError) return status;
+while (!finished())
+	{
+	if ((status==BeforeObject) || ((status==BeforeUnknown) && !lastobject.isEmpty()))
+		{
+		Param tmp_param;
+		ParamInterface *pi=lastobject.getParamInterface(tmp_param);
+		pi->load(file);
+		if ((status!=Finished) && maybeBreak(AfterObject))
+			break;
+		continue;
+		}
+	else if (status==BeforeUnknown)
+		{
+		loadObjectNow(&emptyparam);
+		continue;
+		}
+	if (!loadSStringLine(file,buf))
+		{
+		if (!returnFromIncluded())
+			{
+			abort();
+			break;
+			}
+		else
+			continue;
+		}
+	if (buf[0]=='#')
+		{
+		if (buf.startsWith("#include"))
+			{
+			const char* t=strchr((const char*)buf,'\"'),*t2=0;
+			if (t)
+				t2=strchr(t+1,'\"');
+			if (t2)
+				{
+				SString filename(t+1,t2-t-1);
+				include(filename);
+				}
+			else
+				{
+				const char* thisfilename=file->VgetPath();
+				FMprintf("MultiParamLoader","go",FMLV_WARN,"invalid \"%s\"%s%s",(const char*)buf,
+					 (thisfilename?" in ":""),(thisfilename?thisfilename:""));
+				}
+			continue;
+			}
+		else if ((status!=Finished) && maybeBreak(OnComment))
+			{
+			lastcomment=buf.substr(1);
+			break;
+			}
+		}
+	buf=trim(buf);
+	if ((buf.len()>1)&&(buf[buf.len()-1]==':'))
+		{
+		lastunknown=0;
+		lastunknown=buf.substr(0,buf.len()-1);
+		lastobject.setEmpty();
+		FOREACH(ExtObject*,o,objects)
+			{
+			if (!strcmp(o->interfaceName(),lastunknown)) {lastobject=*o; break;}
+			}
+			if (!lastobject.isEmpty())
+				{
+				if (maybeBreak(BeforeObject))
+					break;
+				}
+			else
+				{
+				if (maybeBreak(BeforeUnknown))
+					break;
+				}
+		
+		}
+	}
+return status;
+}
+
+bool MultiParamLoader::alreadyIncluded(const char* filename)
+{
+int i;
+const char* t;
+for(i=0;i<filestack.size();i++)
+	{
+	t=filestack(i)->VgetPath();
+	if (!t) continue;
+	if (!strcmp(filename,t)) return true;
+	}
+return false;
+}
+
+void MultiParamLoader::include(SString& filename)
+{
+const char* thisfilename=file->VgetPath();
+SString newfilename;
+const char* t=thisfilename?strrchr(thisfilename,PATH_SEPARATOR_CHAR):0;
+
+if (thisfilename && t)
+	{
+	newfilename.append(thisfilename,t-thisfilename+1);
+	newfilename+=filename;
+	}
+else
+	newfilename=filename;
+
+if (alreadyIncluded(newfilename))
+	{
+	FMprintf("MultiParamLoader","include",FMLV_WARN,"circular reference ignored (\"%s\")",
+		    (const char*)filename);
+	return;
+	}
+
+VirtFILE *f=Vfopen(newfilename,FOPEN_READ_BINARY);
+if (!f)
+	{
+	FMprintf("MultiParamLoader","include",FMLV_WARN,"\"%s\" not found",(const char*)newfilename);
+	}
+else
+	{
+	filestack+=file;
+	file=f;
+	}
+}
+
+VirtFILE* MultiParamLoader::popstack()
+{
+if (!filestack.size()) return 0;
+VirtFILE* f=filestack(filestack.size()-1);
+filestack.remove(filestack.size()-1);
+return f;
+}
+
+void MultiParamLoader::clearstack()
+{
+VirtFILE *f;
+while(f=popstack()) fclose(f);
+}
+
+bool MultiParamLoader::returnFromIncluded()
+{
+if (!filestack.size()) return false;
+if (file) fclose(file);
+file=popstack();
+return true;
+}
+
+int MultiParamLoader::loadObjectNow(const ExtObject& o)
+{
+Param tmp_param;
+ParamInterface *pi=o.getParamInterface(tmp_param);
+pi->load(file);
+status=AfterObject;
+return 0;
+}
+
+int MultiParamLoader::run()
+{
+int stat;
+breakOn(OnError);
+while(stat=go())
+	if (stat==OnError)
+		{
+		abort();
+		return 0;
+		}
+return 1;
+}
Index: /cpp/frams/param/multiparamload.h
===================================================================
--- /cpp/frams/param/multiparamload.h	(revision 109)
+++ /cpp/frams/param/multiparamload.h	(revision 109)
@@ -0,0 +1,142 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _MULTIPARAMLOAD_H_
+#define _MULTIPARAMLOAD_H_
+
+#include <stdio.h>
+#include "param.h"
+#include <frams/virtfile/virtfile.h>
+#include <frams/util/extvalue.h>
+
+/** This is the general "framsticks-format" file parser for loading multiple objects.
+    The loader can be configured to recognize multiple object types from object headers
+    and automatically call ParamInterface::load for the matching class.
+    
+    Your code should repeatedly call MultiParamLoader::go() method and check the status after each call, until the end of file.
+    The loader pauses before and/or after each object giving you a chance to perform your application-specific actions (see MultiParamLoader::breakOn()).
+    If your application does not require any special actions, then the simple MultiParamLoader:run() can be used.
+    The word "record" (and "record type") used in this description refer to the textual form of a serialized object - this is to avoid confusion with 'live' objects passed to the loader methods. "Record type" can be the same as the class name, but it does not have to be the same. For example, the most common record type for storing the Genotype object is called "org" (think: organism) instead of "Genotype".
+
+    Typical usage scenarios:
+    1. Loading a file that contains at most one object of any given class:
+    - declare the object class(es) - MultiParamLoader::addClass()
+    - call MultiParamLoader::run()
+    - and that's all, the records from the file will be loaded into the corresponding objects
+
+    2. Loading multiple objects and adding them to a list (see loadtest.cpp for a sample code that demonstrates this scenario)
+    - declare the object class giving the empty "template" object - MultiParamLoader::addClass()
+    - set breakOn(AfterObject)
+    - call MultiParamLoader::go() in a loop
+    - the returned status will be equal to AfterObject each time an object is loaded. One can detect this condition and create the real object from our template object
+    (alternatively, one could breakOn(BeforeObject) and use MultiParamLoader::loadObjectNow(ParamInterface*) to load the incoming object into a newly created object).    
+ */
+class MultiParamLoader
+{
+VirtFILE *file;
+SListTempl<VirtFILE*> filestack;
+char ownfile;
+PtrListTempl<ExtObject*> objects;
+int status;
+SString lasterror, lastcomment, lastunknown;
+ExtObject lastobject;
+int breakcond;
+Param emptyparam;
+
+void init();
+
+int maybeBreak(int cond)
+{ status=cond; return breakcond & cond; }
+
+VirtFILE* popstack();
+void clearstack();
+
+  public:
+MultiParamLoader() {init();}
+MultiParamLoader(VirtFILE *f) {init(); load(f);}
+MultiParamLoader(const char* filename) {init(); load(filename);}
+
+~MultiParamLoader() {abort();clearObjects();}
+
+void reset();
+
+void load(); //< use previously opened file
+void load(VirtFILE *f);
+void load(const char* filename);
+
+/** Register the object class. Class names will be matched with object headers ("xxx:" in the input file).
+    Used with breakOn(BeforeObject) and/or breakOn(AfterObject).
+    Note that registered classes will only work when the record name matches the class name, otherwise breakOn(BeforeUnknown) must be used and then getClassName() to check for the expected record.
+ */
+void addObject(ParamInterface *pi) {objects+=new ExtObject(pi);}
+void removeObject(ParamInterface *pi) {removeObject(ExtObject(pi));}
+void addObject(const ExtObject &o) {objects+=new ExtObject(o);}
+void removeObject(const ExtObject &o);
+int findObject(const ExtObject &o);
+void clearObjects();
+
+/** To be used in the main loop: while(event=loader.go()) { ... }
+    loader.go() will return on specified events (@see breakOn(), noBreakOn()),
+    then you can handle the event and resume loading.
+ */
+virtual int go();
+/** same value as 'go()' */
+int getStatus() {return status;}
+int finished() {return (status==Finished);}
+
+VirtFILE *getFile() {return file;}
+
+/** Abort immediately and close the file if needed */
+void abort();
+/** @param conditions can be combined bitwise, eg. MultiParamLoader::BeforeObject |  MultiParamLoader::OnComment
+    @see BreakConfitions
+*/
+void breakOn(int conditions) {breakcond|=conditions;}
+void noBreakOn(int conditions) {breakcond&=~conditions;}
+/**
+   These constants are used as arguments in breakOn(), and as status values from go() and getStatus().
+   The user code can define some classes for automatic recognition (using addClass()); such records can be read without performing any additional actions.
+   
+   - BeforeObject: found an object with recognized classname (addClass()). Application code can choose to skip the incoming record (skipObject()), redirect the incoming data into a different object (loadObjectNow(ParamInterface*)), or do nothing for default behavior (loading into previously registered object).
+
+   - AfterObject: the object was loaded into the registered class interface (addClass()). This is to allow for additional actions after loading the object (e.g. data validation).
+
+   - BeforeUnknown: unknown (not registered) object header detected. Like in BeforeObject, the application can skipObject() or loadObjectNow().
+
+@see getClass(), GetClassName()
+ */
+enum BreakConditions { Finished=0, BeforeObject=1, AfterObject=2, 
+		       BeforeUnknown=4, OnComment=8, OnError=16, Loading=32 };
+
+/** Can be used BeforeObject and AfterObject */
+ExtObject &getObject() {return lastobject;}
+/** Can be used BeforeUnknown, BeforeObject, AfterObject */
+const SString& getObjectName() {return lastunknown;}
+void setObjectName(SString n) {lastunknown=n;}
+/** Unknown object will be loaded if you set its class BeforeUnknown */
+void setObject(ParamInterface *pi) {lastobject=ExtObject(pi);}
+void setObject(const ExtObject& o) {lastobject=o;}
+/** Can be used OnComment */
+const SString& getComment() {return lastcomment;}
+/** Can be used OnError */
+const SString& getError() {return lasterror;}
+/** Can be used BeforeObject and BeforeUnknown */
+int loadObjectNow(ParamInterface *pi) {return loadObjectNow(ExtObject(pi));}
+int loadObjectNow(const ExtObject &o);
+/** Can be used BeforeObject */
+int loadObjectNow() {return loadObjectNow(getObject());}
+/** Can be used BeforeObject and BeforeUnknown.
+    Object data will not be loaded. */
+int skipObject() {return loadObjectNow(&emptyparam);}
+/** @return 1 if no errors */
+int run();
+
+void include(SString& filename);
+bool returnFromIncluded();
+bool alreadyIncluded(const char* filename);
+
+};
+
+#endif
+
Index: /cpp/frams/param/param.cpp
===================================================================
--- /cpp/frams/param/param.cpp	(revision 109)
+++ /cpp/frams/param/param.cpp	(revision 109)
@@ -0,0 +1,940 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2013  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "param.h"
+#include <frams/util/extvalue.h>
+#include "common/framsg.h"
+#include <frams/util/sstringutils.h>
+
+//#define SAVE_ALL_NAMES
+#define SAVE_SELECTED_NAMES
+#define WARN_MISSING_NAME
+
+char MakeCodeGuardHappy;
+
+ParamEntry empty_paramtab[]=
+{ {"Empty",1,0,"Empty",}, {0,0,0,}, }; 
+
+static void czytdotyldy(VirtFILE *f,SString &s)
+{
+SString temp;
+int z;
+char last_char=0;
+while((z=fgetc(f))!=EOF)
+	{
+	if (z=='~')
+		if (last_char!='\\') break;
+	last_char=(char)z;
+	temp+=last_char;
+	}
+s=temp;
+}
+
+static const char *strchrlimit(const char *t,int ch,const char *limit)
+{
+int n=limit-t;
+for (;(n>0)&&*t;t++,n--)
+	if (*t==ch) return t;
+return 0;
+}
+
+void ParamInterface::copyFrom(ParamInterface *src)
+{
+int n=getPropCount();
+ExtValue v;
+int j;
+for(int i=0;i<n;i++)
+  if ((!(flags(i)&PARAM_READONLY))
+	  && (*type(i)!='p'))
+	{
+	j=src->findId(id(i));
+	if (j<0) continue;
+	src->get(j,v);
+	set(i,v);
+	}
+}
+
+void ParamInterface::quickCopyFrom(ParamInterface *src)
+{
+int n=getPropCount();
+ExtValue v;
+for(int i=0;i<n;i++)
+  if ((!(flags(i)&PARAM_READONLY))
+	  && (*type(i)!='p'))
+	  {
+	  src->get(i,v);
+	  set(i,v);
+	  }
+}
+
+int ParamInterface::getMinMax(int prop,long& minumum,long& maximum,long &def)
+{
+const char* t=type(prop)+1;
+while(*t) if (*t==' ') break; else t++;
+return sscanf(t,"%ld %ld %ld",&minumum,&maximum,&def);
+}
+
+int ParamInterface::getMinMax(int prop,double& minumum,double& maximum,double& def)
+{
+const char* t=type(prop)+1;
+while(*t) if (*t==' ') break; else t++;
+return sscanf(t,"%lg %lg %lg",&minumum,&maximum,&def);
+}
+
+void ParamInterface::setDefault(bool numericonly)
+{
+int n=getPropCount();
+for(int i=0;i<n;i++)
+	setDefault(i,numericonly);
+}
+
+void ParamInterface::setMin()
+{
+int n=getPropCount();
+for(int i=0;i<n;i++)
+	setMin(i);
+}
+
+void ParamInterface::setMax()
+{
+int n=getPropCount();
+for(int i=0;i<n;i++)
+	setMax(i);
+}
+
+void ParamInterface::setDefault(int i,bool numericonly)
+{
+const char *t=type(i);
+switch(*t)
+	{
+	case 'f':
+	{
+	double a=0,b=0,c=0;
+	if (getMinMax(i,a,b,c)<3) c=a;
+	setDouble(i,c);
+	}
+	break;
+	case 'd':
+	{
+	long a=0,b=0,c=0;
+	if (getMinMax(i,a,b,c)<3) c=a;
+	setInt(i,c);
+	}
+	break;
+	default: if (!numericonly) set(i,"");
+	}
+}
+
+void ParamInterface::setMin(int i)
+{
+const char *t=type(i);
+switch(*t)
+	{
+	case 'f':
+	{
+	double a=0,b=0,c=0;
+	getMinMax(i,a,b,c);
+	setDouble(i,a);
+	}
+	break;
+	case 'd':
+	{
+	long a=0,b=0,c=0;
+	getMinMax(i,a,b,c);
+	setInt(i,a);
+	}
+	break;
+	default: set(i,"");
+	}
+}
+
+void ParamInterface::setMax(int i)
+{
+const char *t=type(i);
+switch(*t)
+	{
+	case 'f':
+	{
+	double a=0,b=0,c=0;
+	getMinMax(i,a,b,c);
+	setDouble(i,b);
+	}
+	break;
+	case 'd':
+	{
+	long a=0,b=0,c=0;
+	getMinMax(i,a,b,c);
+	setInt(i,b);
+	}
+	break;
+	default: set(i,"");
+	}
+}
+
+SString ParamInterface::getStringById(const char*prop)
+{int i=findId(prop); if (i>=0) return getString(i); else return SString();}
+long ParamInterface::getIntById(const char*prop)
+{int i=findId(prop); if (i>=0) return getInt(i); else return 0;}
+double ParamInterface::getDoubleById(const char*prop)
+{int i=findId(prop); if (i>=0) return getDouble(i); else return 0;}
+ExtObject ParamInterface::getObjectById(const char*prop)
+{int i=findId(prop); if (i>=0) return getObject(i); else return ExtObject();}
+ExtValue ParamInterface::getExtValueById(const char*prop)
+{int i=findId(prop); if (i>=0) return getExtValue(i); else return ExtValue();}
+
+int ParamInterface::setIntById(const char* prop,long v)
+{int i=findId(prop); if (i>=0) return setInt(i,v); else return PSET_NOPROPERTY;}
+int ParamInterface::setDoubleById(const char* prop,double v)
+{int i=findId(prop); if (i>=0) return setDouble(i,v); else return PSET_NOPROPERTY;}
+int ParamInterface::setStringById(const char* prop,const SString &v)
+{int i=findId(prop); if (i>=0) return setString(i,v); else return PSET_NOPROPERTY;}
+int ParamInterface::setObjectById(const char* prop,const ExtObject &v)
+{int i=findId(prop); if (i>=0) return setObject(i,v); else return PSET_NOPROPERTY;}
+int ParamInterface::setExtValueById(const char* prop,const ExtValue &v)
+{int i=findId(prop); if (i>=0) return setExtValue(i,v); else return PSET_NOPROPERTY;}
+int ParamInterface::setById(const char* prop,const ExtValue &v)
+{int i=findId(prop); if (i>=0) return set(i,v); else return PSET_NOPROPERTY;}
+
+int ParamInterface::save(VirtFILE* f,const SString* altname,bool force)
+{
+const char *p;
+SString ws;
+int err=0,i;
+bool withname=false;
+if ((!altname)||(altname->len()))
+	{
+	err|=(fputs(altname?((const char*)(*altname)):getName(),f)==EOF);
+	err|=(fputs(":\n",f)==EOF);
+	withname=true;
+	}
+for (i=0;p=id(i);i++)
+	err|=saveprop(f,i,p,force);
+if (withname)
+	err|=(fputs("\n",f)==EOF);
+return err;
+}
+
+const char* ParamInterface::SERIALIZATION_PREFIX="@Serialized:";
+
+int ParamInterface::saveprop(VirtFILE* f,int i,const char* p,bool force)
+{
+if ((flags(i)&PARAM_DONTSAVE)&&(!force)) return 0;
+const char *typ=type(i);
+if ((*typ=='p')||(*typ=='o')) return 0;
+
+const char *t,*w;
+SString ws;
+int err=0,cr;
+
+err|=(fputs(p,f)==EOF); fputc(':',f);
+cr=0;
+if (*typ=='x')
+	{
+	ExtValue ex;
+	get(i,ex);
+	ws=SString(SERIALIZATION_PREFIX)+ex.serialize();
+	}
+else
+	ws=get(i);
+quoteTilde(ws);
+w=ws;
+if (ws.len()>50) cr=1;
+else for (t=w;*t;t++) if ((*t==10)||(*t==13)) {cr=1; break;}
+if (cr) fputs("~\n",f);
+err|=(fputs(w,f)==EOF);
+err|=(fputs(cr ? "~\n" : "\n",f)==EOF);
+return err;
+}
+
+
+int SimpleAbstractParam::isequal(int i,void* defdata)
+{ // defdata->member == object->member ?
+void *backup=object;
+switch(type(i)[0])
+	{
+	case 'd':
+		{
+		select(defdata);
+		long x=getInt(i);
+		select(backup);
+		return x==getInt(i);
+		}
+	case 'f':
+		{
+		select(defdata);
+		double x=getDouble(i);
+		select(backup);
+		return x==getDouble(i);
+		}
+	case 's':
+		{
+		select(defdata);
+		SString x=getString(i);
+		select(backup);
+		return x==getString(i);
+		}
+	}
+return 1;
+}
+
+void SimpleAbstractParam::save2(SString& f,void *defdata,bool addcr,bool all_names)
+{ // defdata!=NULL -> nie zapisuje wartosci domyslnych
+const char *p;
+int i;
+int needlabel=0;
+int first=1;
+SString val;
+SString t;
+int fl;
+// t+=SString(getName()); t+=':';
+for (i=0;p=id(i);i++)
+	if (!((fl=flags(i))&PARAM_DONTSAVE))
+	{
+	if (defdata && isequal(i,defdata))
+		needlabel=1;
+	else
+		{
+		if (!first) t+=", ";
+#ifndef SAVE_ALL_NAMES
+#ifdef SAVE_SELECTED_NAMES
+		if (needlabel || all_names || !(fl & PARAM_CANOMITNAME))
+#else
+		if (needlabel)
+#endif
+#endif
+			{ t+=p; t+="="; needlabel=0; }
+		if (type(i)[0]=='s')
+			{ // string - special case
+			SString str=getString(i);
+			if (strContainsOneOf(str,", \\\n\r\t\""))
+				{
+				t+="\"";
+				sstringQuote(str);
+				t+=str;
+				t+="\"";
+				}
+			else
+				t+=str;
+			}
+		else
+	   		t+=get(i);
+		first=0;
+		}
+	}
+if (addcr)
+	t+="\n";
+f+=t;
+}
+
+void ParamInterface::load(VirtFILE* f)
+{
+SString buf;
+int i;
+const char *p,*p0;
+int p_len;
+bool loaded;
+while(loadSStringLine(f,buf))
+	{
+	const char* t=(const char*)buf;
+	p0=t; while ((*p0==' ')||(*p0=='\t')) p0++;
+	if (!*p0) break;
+	p=strchr(p0,':'); if (!p) continue;
+	p_len=p-p0;
+	loaded=false;
+	if (p_len&&((i=findIdn(p0,p_len))>=0)&&(!(flags(i)&PARAM_DONTLOAD)))
+		{
+		if (p0[p_len+1]=='~')
+			{
+			SString s;
+			czytdotyldy(f,s);
+			removeCR(s);
+			int ch; while((ch=fgetc(f))!=EOF) if (ch=='\n') break;
+			unquoteTilde(s);
+			set(i,(const char*)s);
+			}
+		else
+			{
+			set(i,p0+p_len+1);
+			}
+		loaded=true;
+		}
+	if ((!loaded) && (p0[p_len+1]=='~'))
+		{ // eat unrecognized multiline field
+		SString s;
+		czytdotyldy(f,s);
+		int ch; while((ch=fgetc(f))!=EOF) if (ch=='\n') break;
+		}
+	}
+}
+
+
+/*
+SString SimpleAbstractParam::getString(int i)
+{
+char *t;
+switch (*(t=type(i)))
+	{
+	case 'd':
+	{
+	for (i=atol(get(i));i>=0;i--) if (t) t=strchr(t+1,'~');
+	if (t)
+		{
+		t++;
+		char *t2=strchr(t,'~');
+		if (!t2) t2=t+strlen(t);
+		SString str;
+		strncpy(str.directWrite(t2-t),t,t2-t);
+		str.endWrite(t2-t);
+		return str;
+		}
+	}
+	}
+return get(i);
+}
+*/
+
+int ParamInterface::findId(const char* n)
+{
+int i; const char *p;
+	for (i=0;p=id(i);i++) if (!strcmp(n,p)) return i;
+return -1;
+}
+
+int ParamInterface::findIdn(const char* naz,int n)
+{
+int i; const char *p;
+	for (i=0;p=id(i);i++) if ((!strncmp(naz,p,n))&&(!p[n])) return i;
+return -1;
+}
+
+void ParamInterface::get(int i,ExtValue &ret)
+{
+switch(type(i)[0])
+	{
+	case 'd':	ret.setInt(getInt(i)); break;
+	case 'f':	ret.setDouble(getDouble(i)); break;
+	case 's':	ret.setString(getString(i)); break;
+	case 'o':	ret.setObject(getObject(i)); break;
+	case 'x':	ret=getExtValue(i); break;
+	default: FMprintf("ParamInterface","get",FMLV_ERROR,"'%s.%s' is not a field",getName(),id(i));
+	}
+}
+
+static bool stringIsNumeric(const char* str)
+{//   /-?.?[0-9]+/
+if (!str) return false;
+if (*str=='-') str++;
+if (*str=='.') str++;
+return isdigit(*str)!=0;
+}
+
+int ParamInterface::setInt(int i,const char* str)
+{
+if (!stringIsNumeric(str))
+	{
+	long a,b,c;
+	if (getMinMax(i,a,b,c)>=3)
+		return setInt(i,c);
+	else
+		return setInt(i,(long)0);
+	}
+else
+	return setInt(i,atol(str));
+}
+
+int ParamInterface::setDouble(int i,const char* str)
+{
+if (!stringIsNumeric(str))
+	{
+	double a,b,c;
+	if (getMinMax(i,a,b,c)>=3)
+		return setDouble(i,c);
+	else
+		return setDouble(i,(double)0);
+	}
+else
+	return setDouble(i,atof(str));
+}
+
+int ParamInterface::set(int i,const ExtValue &v)
+{
+switch(type(i)[0])
+	{
+	case 'd': if ((v.type==TInt)||(v.type==TDouble)) return setInt(i,v.getInt()); else return setInt(i,(const char*)v.getString());
+	case 'f': if ((v.type==TInt)||(v.type==TDouble)) return setDouble(i,v.getDouble()); else return setDouble(i,(const char*)v.getString());
+	case 's': { SString t=v.getString(); return setString(i,t); }
+	case 'o': return setObject(i,v.getObject());
+	case 'x': return setExtValue(i,v);
+	default: FMprintf("ParamInterface","get",FMLV_ERROR,"'%s.%s' is not a field",getName(),id(i));
+	}
+return 0;
+}
+
+int ParamInterface::set(int i,const char *v)
+{
+switch(type(i)[0])
+	{
+	case 'd': return setInt(i,v);
+	case 'f': return setDouble(i,v);
+	case 's': { SString t(v); return setString(i,t); }
+	case 'x':
+	{
+	ExtValue e;
+	const char* after;
+	if (!strncmp(v,SERIALIZATION_PREFIX,strlen(SERIALIZATION_PREFIX)))
+		{
+		after=e.deserialize(v+strlen(SERIALIZATION_PREFIX));
+		if ((after==NULL)||(*after))
+			FMprintf("ParamInterface","set",FMLV_WARN,"serialization format mismatch in %s.%s",(getName()?getName():"<Unknown>"),id(i));
+		}
+	else if ((after=e.parseNumber(v))&&(*after==0)) //consumed the whole string
+		{
+		//OK!
+		}
+	else
+		{
+		e.setString(SString(v));
+		}
+	return setExtValue(i,e);
+	}
+	}
+return 0;
+}
+
+SString ParamInterface::getText(int i)
+{
+const char *t;
+if ((*(t=type(i)))=='d')
+	{
+	for (int j=getInt(i);j>=0;j--) if (t) t=strchr(t+1,'~');
+	if (t)
+		{
+		t++;
+		const char *t2=strchr(t,'~');
+		if (!t2) t2=t+strlen(t);
+		return SString(t,t2-t);
+		}
+	}
+return get(i);
+}
+
+SString ParamInterface::get(int i)
+{
+switch(type(i)[0])
+	{
+	case 'd': return SString::valueOf(getInt(i));
+	case 'f': return SString::valueOf(getDouble(i));
+	case 's': return getString(i);
+	}
+ExtValue v;
+get(i,v);
+return v.getString();
+}
+
+
+//////////////////////////////// PARAM ////////////////////////////////////
+
+void *SimpleAbstractParam::getTarget(int i)
+{
+return (void*)(((char*)object)+entry(i)->offset);
+//return &(object->*(entry(i)->fldptr));
+}
+
+///////// get
+
+long SimpleAbstractParam::getInt(int i)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->fun1)
+	{
+	(*(void(*)(void*,ExtValue*))pe->fun1)(object,&v);
+	return v.getInt();
+	}
+else
+	{
+	void *target=getTarget(i);
+	return *((long*)target);
+	}
+}
+
+double SimpleAbstractParam::getDouble(int i)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->fun1)
+	{
+	(*(void(*)(void*,ExtValue*))pe->fun1)(object,&v);
+	return v.getDouble();
+	}
+else
+	{
+	void *target=getTarget(i);
+	return *((double*)target);
+	}
+}
+
+SString SimpleAbstractParam::getString(int i)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->fun1)
+	{
+	(*(void(*)(void*,ExtValue*))pe->fun1)(object,&v);
+	return v.getString();
+	}
+else
+	{
+	void *target=getTarget(i);
+	return *((SString*)target);
+	}
+}
+
+ExtObject SimpleAbstractParam::getObject(int i)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->fun1)
+	{
+	(*(void(*)(void*,ExtValue*))pe->fun1)(object,&v);
+	return v.getObject();
+	}
+else
+	{
+	void *target=getTarget(i);
+	return *((ExtObject*)target);
+	}
+}
+
+ExtValue SimpleAbstractParam::getExtValue(int i)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->fun1)
+	{
+	(*(void(*)(void*,ExtValue*))pe->fun1)(object,&v);
+	return v;
+	}
+else
+	{
+	void *target=getTarget(i);
+	return *((ExtValue*)target);
+	}
+}
+
+
+//////// set
+
+int SimpleAbstractParam::setInt(int i,long x)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+long xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
+long a=0,b=0;
+int result=0;
+const char* t=pe->type+1;
+while(*t) if (*t==' ') break; else t++;
+if (sscanf(t,"%ld %ld",&a,&b)==2)
+	if (a<=b) // jezeli max<min to znaczy ze min/max nie obowiazuje
+		{
+		if (x<a) {x=a; result=PSET_HITMIN;}
+		else if (x>b) {x=b; result=PSET_HITMAX;}
+		}
+
+if (pe->fun2)
+	{
+	v.setInt(x);
+	result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v);
+	}
+else
+	{
+	void *target=getTarget(i);
+	if (dontcheckchanges || (*((long*)target)!=x))
+			{
+			result |= PSET_CHANGED;
+			*((long*)target)=x;
+			}
+	}
+    messageOnExceedRange(i,result,xcopy);
+	return result;
+}
+
+int SimpleAbstractParam::setDouble(int i,double x)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+double xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
+double a=0,b=0;
+int result=0;
+const char* t=pe->type+1;
+while(*t) if (*t==' ') break; else t++;
+if (sscanf(t,"%lg %lg",&a,&b)==2)
+	if (a<=b) // jezeli max<min to znaczy ze min/max nie obowiazuje
+		{
+		if (x<a) {x=a; result=PSET_HITMIN;}
+		else if (x>b) {x=b; result=PSET_HITMAX;}
+		}
+
+if (pe->fun2)
+	{
+	v.setDouble(x);
+	result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v);
+	}
+else
+	{
+	void *target=getTarget(i);
+	if (dontcheckchanges || (*((double*)target)!=x))
+		{
+		result|=PSET_CHANGED;
+		*((double*)target)=x;
+		}
+	}
+    messageOnExceedRange(i,result,xcopy);
+	return result;
+}
+
+int SimpleAbstractParam::setString(int i,const SString& x)
+{
+ExtValue v;
+SString vs;
+const SString *xx=&x;
+ParamEntry *pe=entry(i);
+if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+SString xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
+const char* t=pe->type+1;
+while(*t) if (*t==' ') break; else t++;
+long a=0,b=0;
+int result=0;
+if (sscanf(t,"%ld %ld",&a,&b)==2)
+	{
+	if ((x.len()>b)&&(b>0))
+		{
+		vs=x.substr(0,b);
+		xx=&vs;
+		result|=PSET_HITMAX;
+		}
+	}
+
+if (pe->fun2)
+	{
+	v.setString(*xx);
+	result |= (*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v);
+	}
+else
+	{
+	void *target=getTarget(i);
+	if (dontcheckchanges || (!(*((SString*)target) == *xx)))
+		{
+		result|=PSET_CHANGED;
+		*((SString*)target)=x;
+		}
+	}
+    messageOnExceedRange(i,result,xcopy);
+	return result;
+}
+
+int SimpleAbstractParam::setObject(int i,const ExtObject& x)
+{
+ExtValue v;
+ParamEntry *pe=entry(i);
+if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+ExtObject xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
+if (pe->fun2)
+	{
+	v.setObject(x);
+	int result=(*(int(*)(void*,const ExtValue*))pe->fun2)(object,&v);
+    messageOnExceedRange(i,result,xcopy);
+    return result;
+	}
+else
+	{
+	void *target=getTarget(i);
+	*((ExtObject*)target)=x;
+	return PSET_CHANGED;
+	}
+}
+
+int SimpleAbstractParam::setExtValue(int i,const ExtValue& x)
+{
+ParamEntry *pe=entry(i);
+if (pe->flags&PARAM_READONLY) return PSET_RONLY;
+ExtValue xcopy=x; //only needed for messageOnExceedRange(): retain original, requested value of x because it may be changed below
+if (pe->fun2)
+	{
+	int result=(*(int(*)(void*,const ExtValue*))pe->fun2)(object,&x);
+    messageOnExceedRange(i,result,xcopy);
+    return result;
+	}
+else
+	{
+	void *target=getTarget(i);
+	*((ExtValue*)target)=x;
+	return PSET_CHANGED;
+	}
+}
+
+void SimpleAbstractParam::call(int i,ExtValue *args,ExtValue *ret)
+{
+ParamEntry *pe=entry(i);
+if (!pe) return;
+if (pe->fun1 && (pe->type[0]=='p'))
+	(*(void(*)(void*,ExtValue*,ExtValue*))pe->fun1)(object,args,ret);
+else
+	{
+	FMprintf("SimpleAbstractParam","call",FMLV_ERROR,
+		 (*pe->type!='p')?"'%s.%s' is not a function":"Internal error - undefined function pointer for '%s.%s'",getName(),pe->id);
+	}
+}
+
+void SimpleAbstractParam::setDefault(bool numericonly)
+{
+bool save=dontcheckchanges;
+dontcheckchanges=1;
+ParamInterface::setDefault(numericonly);
+dontcheckchanges=save;
+}
+
+void SimpleAbstractParam::setDefault(int i,bool numericonly)
+{
+bool save=dontcheckchanges;
+dontcheckchanges=1;
+ParamInterface::setDefault(i,numericonly);
+dontcheckchanges=save;
+}
+
+// zwraca adres poczatku linii
+// len = dlugosc linii (bez \n)
+// 0 moze oznaczac linie dlugosci 0 lub koniec SStringa
+// poz jest przesuwane na poczatek nastepnej linii
+// typowa petla: for(poz=0;poz<s.d;) {line=getline(s,poz,len);...
+static const char *getline(const SString &s,int &poz,int &len)
+{
+const char *beg=(const char*)s+poz;
+if (poz>=s.len()) {poz=s.len(); len=0; return (const char*)s+s.len();}
+const char *lf=strchr(beg,'\n');
+if (!lf) { lf=(const char*)s+s.len()-1; poz=s.len(); }
+else {poz=(lf-(const char*)s)+1; if (poz>s.len()) poz=s.len();}
+while (lf>=beg) if ((*lf=='\n')||(*lf=='\r')) lf--; else break;
+len=lf-beg+1;
+return beg;
+}
+
+void ParamInterface::load2(const SString &s,int &poz)
+{
+int i; // numer akt. parametru
+int tmpi;
+int len;
+int ret;
+const char *t,*lin,*end;
+const char *rownasie,*przecinek;
+char remember;
+const char *quote,*quote2;
+const char *value,*valstop;
+SString tmpvalue;
+if (poz>=s.len()) return;
+t=(const char*)s+poz;
+
+// na razie wszystko musi byc w jednej linii...
+lin=getline(s,poz,len);
+if (!len) return; // pusta linia = koniec
+i=0;
+end=lin+len;
+while(t<end)
+{
+// przetwarzanie jednego par
+while (strchr(" \n\r\t",*t)) if (t<end) t++; else return;
+
+przecinek=strchrlimit(t,',',end); if (!przecinek) przecinek=end;
+quote=strchrlimit(t,'\"',przecinek);
+if (quote)
+	{
+	quote2=skipQuoteString(quote+1,end);
+	if (quote2>przecinek)
+		{
+		przecinek=strchrlimit(quote2+1,',',end);
+		if (!przecinek) przecinek=end;
+		}
+	rownasie=strchrlimit(t,'=',quote);
+	}
+else
+	{
+	rownasie=strchrlimit(t,'=',przecinek);
+	quote2=0;
+	}
+if (rownasie==t) { t++; rownasie=0; }
+if (przecinek==t)	// skip empty value
+	{
+	t++; i++;
+	continue;
+	}
+if (rownasie) // have parameter name
+	{
+	tmpi=findIdn(t,rownasie-t);
+	i=tmpi;
+	if (tmpi<0)
+		FMprintf("Param","load2",FMLV_WARN,"Unknown property name for '%s' (ignored)",getName());
+	t=rownasie+1; // t=value
+	}
+#ifdef WARN_MISSING_NAME
+else
+#ifdef SAVE_SELECTED_NAMES
+if (!(flags(i)&PARAM_CANOMITNAME))
+#endif
+	{
+	FMprintf("Param","load2",FMLV_WARN,"Missing property name in '%s' (assuming '%s')",
+		 getName(),id(i)?id(i):"unknown property?");
+	}
+#endif
+if ((i>=0)&&id(i))
+	{
+	value=t;
+	if (quote)
+		{
+		tmpvalue.copyFrom(quote+1,quote2-quote-1);
+		sstringUnquote(tmpvalue);
+		value=tmpvalue;
+		valstop=quote2;
+		}
+	else
+		if (przecinek<end) valstop=przecinek; else valstop=end;
+
+	remember=*valstop;
+	*(char*)valstop=0;
+	ret=set(i,value);
+	if (ret&(PSET_HITMAX|PSET_HITMIN))
+		FMprintf("Param","load2",FMLV_WARN,"Adjusted '%s' in '%s' (was too %s)",
+			id(i),getName(),(ret&PSET_HITMAX)?"big":"small");
+	*(char*)valstop=remember;
+	}
+
+if (i>=0) i++;
+#ifdef __CODEGUARD__ 
+if (przecinek<end-1) t=przecinek+1; else return;
+#else
+t=przecinek+1;
+#endif
+}
+return;
+}
+
+int Param::grmember(int g,int a)
+{
+if ((getGroupCount()<2)&&(!g))
+	return (a<getPropCount())?a:-9999;
+
+ParamEntry *e=entry(0);
+int x=0,i=0;
+for (;e->id;i++,e++)
+	{
+	if (e->group==g) 
+		if (a==x) return i; else x++;
+	}
+return -9999;
+}
+
Index: /cpp/frams/param/param.h
===================================================================
--- /cpp/frams/param/param.h	(revision 109)
+++ /cpp/frams/param/param.h	(revision 109)
@@ -0,0 +1,304 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _PARAM_H_
+#define _PARAM_H_
+
+#include <stdio.h>
+#include <frams/util/sstring.h>
+#include <frams/util/list.h>
+#include <frams/util/statrick.h>
+#include <frams/virtfile/virtfile.h>
+#include <common/framsg.h>
+
+class ExtValue;
+class ExtObject;
+
+// ParamInterface flags:
+#define PARAM_READONLY	1
+#define PARAM_DONTSAVE	2
+#define PARAM_SETLEVEL(x) (((x)&3)<<2)
+#define PARAM_LEVEL(x) (((x)>>2)&3)
+#define PARAM_USERREADONLY 16
+#define PARAM_USERHIDDEN 32
+// for mutableparam (private!)
+#define MUTPARAM_ALLOCENTRY 64
+#define MUTPARAM_ALLOCDATA 128
+#define PARAM_NOSTATIC 256
+#define PARAM_CONST 512
+#define PARAM_CANOMITNAME 1024
+#define PARAM_DONTLOAD	2048
+#define PARAM_NOISOLATION	4096
+#define PARAM_DEPRECATED	8192
+
+// wynik z param::set() to kombinacja bitow:
+
+#define PSET_RONLY	1
+// oznacza,ze nie mozna zmienic
+
+#define PSET_CHANGED	2
+// zaawansowane: wartosc zostala zmieniona
+
+#define PSET_HITMIN	4
+#define PSET_HITMAX	8
+// wartosc zostala dopasowana do min lub max
+
+#define PSET_WARN (PSET_RONLY | PSET_HITMIN | PSET_HITMAX)
+// pozyteczna kombinacja - oznacza, ze nalezy pobrac i wyswietlic
+// wartosc, zeby uzytkownik wiedzial, ze jego propozycja jest odrzucona
+
+#define PSET_NOPROPERTY	16
+
+struct ParamEntry;
+
+/** Property get/set interface - runtime access to named properties */
+class ParamInterface
+{
+public:
+virtual int getGroupCount()=0; ///< @return number of property groups
+virtual int getPropCount()=0; ///< @return number of properties
+
+virtual const char* getName()=0;
+virtual const char* getDescription() {return 0;}
+virtual ParamEntry *getParamTab() const {return NULL;}
+
+int findId(const char *n);	///< find id number for internal name
+int findIdn(const char *naz,int n);
+
+virtual const char *id(int i)=0;	///< get internal name
+virtual const char *name(int i)=0;	///< get human readable name
+
+/** get type description.
+    first character defines basic datatype:
+    - d = integer
+    - f = floating point
+    - s = string
+    - o = ExtObject
+    - x = ExtValue (universal datatype)
+ */
+virtual const char *type(int i)=0;	
+
+virtual const char *help(int i)=0;	///< get long description (tooltip)
+
+virtual int flags(int i)=0;		///< get flags
+
+virtual int group(int i)=0;		///< get group id for a property
+virtual const char *grname(int gi)=0;	///< get group name
+virtual int grmember(int gi,int n)=0;	///< get property id for n'th member of group "gi"
+
+virtual void call(int i,ExtValue* args,ExtValue *ret)=0;
+
+void get(int,ExtValue &retval);	///< most universal get, can be used for every datatype
+
+virtual SString getString(int)=0;	///< get string value, you can only use this for "s" type property
+virtual long getInt(int)=0;	///< get long value, you can only use this for "d" type property
+virtual double getDouble(int)=0;	///< get double value, you can only use this for "f" type property
+virtual ExtObject getObject(int)=0;	///< get object reference, you can only use this for "o" type property
+virtual ExtValue getExtValue(int)=0;	///< get extvalue object, you can only use this for "x" type property
+
+SString get(int);		///< old style get, can convert long or double to string
+SString getText(int);		///< like getString, returns enumeration label for subtype "d 0 n ~enum1~enum2...
+
+SString getStringById(const char*prop);  ///< get string value, you can only use this for "s" type property
+long getIntById(const char* prop);    ///< get long value, you can only use this for "d" type property
+double getDoubleById(const char* prop);///< get double value, you can only use this for "f" type property
+ExtObject getObjectById(const char* prop);///< get object reference, you can only use this for "o" type property
+ExtValue getExtValueById(const char* prop);///< get extvalue object, you can only use this for "x" type property
+ExtValue getById(const char* prop);
+
+int setInt(int i,const char* str);
+int setDouble(int i,const char* str);
+virtual int setInt(int,long)=0;		///< set long value, you can only use this for "d" type prop
+virtual int setDouble(int,double)=0;	///< set double value, you can only use this for "f" type prop
+virtual int setString(int,const SString &)=0; 	///< set string value, you can only use this for "s" type prop
+virtual int setObject(int,const ExtObject &)=0; 	///< set object reference, you can only use this for "o" type prop
+virtual int setExtValue(int,const ExtValue &)=0;	///< 4 in 1
+
+int set(int,const ExtValue &);///< most universal set, can be used for every datatype
+
+int set(int,const char*);		///< oldstyle set, can convert string to long or double
+
+int setIntById(const char* prop,long);///< set long value, you can only use this for "d" type prop
+int setDoubleById(const char* prop,double);///< set double value, you can only use this for "f" type prop
+int setStringById(const char* prop,const SString &);///< set string value, you can only use this for "s" type prop
+int setObjectById(const char* prop,const ExtObject &);///< set object reference, you can only use this for "o" type prop
+int setExtValueById(const char* prop,const ExtValue &);	///< for ExtValue types only
+int setById(const char* prop,const ExtValue &);///< can be used for all property types
+
+/** get valid minimum, maximum and default value for property 'prop'
+    @return 0 if min/max/def information is not available */
+int getMinMax(int prop,long& minumum,long& maximum,long& def);
+/** get valid minimum, maximum and default value for property 'prop'
+    @return 0 if min/max/def information is not available */
+int getMinMax(int prop,double& minumum,double& maximum,double& def);
+
+virtual void setDefault(bool numericonly=false);
+virtual void setDefault(int i,bool numericonly=false);
+void setMin();
+void setMax();
+void setMin(int i);
+void setMax(int i);
+
+/** copy all property values from other ParamInterface object */
+void copyFrom(ParamInterface *src);
+
+/** copy all property values from compatible ParamInterface object.
+    this method is more efficient than copyFrom,
+    but can be used only if the other object has the same properties sequence, eg:
+    - any two Param objects having common paramtab
+    - any ParamInterface object and the Param with paramtab constructed by ParamObject::makeParamTab
+ */
+void quickCopyFrom(ParamInterface *src);
+
+int save(VirtFILE*,const SString* altname=0,bool force=0);
+int saveprop(VirtFILE*,int i,const char* p,bool force=0);
+void load(VirtFILE*);
+void load2(const SString &,int &);
+
+static const char* SERIALIZATION_PREFIX;
+};
+
+// implementations:
+
+extern char MakeCodeGuardHappy;
+
+#define PROCOFFSET(_proc_) ( (void (*)(void*,ExtValue*,ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
+#define STATICPROCOFFSET(_proc_) ( (void (*)(void*,ExtValue*,ExtValue*)) &(FIELDSTRUCT :: _proc_))
+#define GETOFFSET(_proc_) ( (void (*)(void*,ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
+#define SETOFFSET(_proc_) ( (int (*)(void*,const ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
+
+#define FIELDOFFSET(_fld_) ((long)((char*)(&((FIELDSTRUCT*)&MakeCodeGuardHappy)->_fld_)-((char*)((FIELDSTRUCT*)&MakeCodeGuardHappy))))
+
+#define FIELD(_fld_) FIELDOFFSET(_fld_),0,0
+#define LONGOFFSET(_o_) (_o_),0,0
+#define PROCEDURE(_proc_) 0,(void*)PROCOFFSET(_proc_),0
+#define STATICPROCEDURE(_proc_) 0,(void*)STATICPROCOFFSET(_proc_),0
+#define GETSET(_proc_) 0,(void*)GETOFFSET(get_ ## _proc_),(void*)SETOFFSET(set_ ## _proc_)
+#define GETFIELD(_proc_) FIELDOFFSET(_proc_),(void*)GETOFFSET(get_ ## _proc_),0
+#define SETFIELD(_proc_) FIELDOFFSET(_proc_),0,(void*)SETOFFSET(set_ ## _proc_)
+#define GETONLY(_proc_) 0,(void*)GETOFFSET(get_ ## _proc_),0
+#define SETONLY(_proc_) 0,0,(void*)SETOFFSET(set_ ## _proc_)
+
+#define PARAMPROCARGS ExtValue* args,ExtValue* ret
+#define PARAMSETARGS const ExtValue* arg
+#define PARAMGETARGS ExtValue* ret
+
+#define PARAMPROCDEF(name) STATRICKDEF2(name,ExtValue*,ExtValue*)
+#define PARAMGETDEF(name) STATRICKDEF1(get_ ## name,ExtValue*)
+#define PARAMSETDEF(name) STATRICKRDEF1(int,set_ ## name,const ExtValue*)
+
+///////////////////////////////
+
+struct ParamEntry
+{
+const char *id;
+short group,flags;
+const char *name,*type;
+long offset;
+void *fun1; ///< procedure or get
+void *fun2; ///< set
+const char *help;
+};
+
+struct ParamEntryConstructor: public ParamEntry
+{
+public:
+ParamEntryConstructor(const char *_id,short _group=0,short _flags=0,const char *_name=0,const char *_type=0,long _offset=0,void *_fun1=0, void *_fun2=0, const char *_help=0)
+{id=_id;group=_group;flags=_flags;name=_name;type=_type;offset=_offset;fun1=_fun1;fun2=_fun2;help=_help;}
+};
+
+class SimpleAbstractParam: public virtual ParamInterface
+{
+protected:
+virtual void *getTarget(int i);
+virtual ParamEntry *entry(int i)=0;
+const char* myname;
+bool dontcheckchanges;
+
+public:
+void *object;
+
+const char* getName() {return myname;}
+void setName(const char* n) {myname=n;}
+
+/**
+	@param t ParamEntry table
+	@param o controlled object
+	@param n Param's name
+*/
+SimpleAbstractParam(void* o=0,const char*n=0):myname(n),dontcheckchanges(0),object(o) {}
+void setDontCheckChanges(bool x) {dontcheckchanges=x;}
+
+void select(void *o) {object=o;}
+void* getSelected() {return object;}
+
+const char *id(int i) {return (i>=getPropCount())?0:entry(i)->id;}
+const char *name(int i) {return entry(i)->name;}
+const char *type(int i) {return entry(i)->type;}
+const char *help(int i) {return entry(i)->help;}
+int flags(int i) {return entry(i)->flags;}
+int group(int i) {return entry(i)->group;}
+void call(int i,ExtValue* args,ExtValue *ret);
+
+SString getString(int);
+long getInt(int);
+double getDouble(int);
+ExtObject getObject(int);
+ExtValue getExtValue(int);
+
+template<typename T> void messageOnExceedRange(int i,int setflags, T valuetoset) ///< prints a warning when setflags indicates that allowed param range has been exceeded during set
+{
+	if (setflags & (PSET_HITMIN | PSET_HITMAX))
+    {
+        SString svaluetoset=SString::valueOf(valuetoset); //converts any type to SString
+    	SString actual=get(i);
+		FMprintf("Param","set",FMLV_WARN,"Setting '%s.%s = %s' exceeded allowed range (too %s). Adjusted to %s.",
+				 getName(),id(i),(const char*)svaluetoset,(setflags&PSET_HITMAX)?"big":"small",(const char*)actual);
+    }
+}                          
+
+int setInt(int,long);
+int setDouble(int,double);
+int setString(int,const SString &);
+int setObject(int,const ExtObject &);
+int setExtValue(int,const ExtValue &);
+
+int isequal(int i,void* defdata);
+void save2(SString&,void *defdata,bool addcr=true,bool all_names=true);
+
+virtual void setDefault(bool numericonly=false);
+virtual void setDefault(int i,bool numericonly=false);
+};
+
+class Param: public SimpleAbstractParam
+{
+protected:
+ParamEntry *entry(int i) {return tab+tab[0].group+i;}
+public:
+ParamEntry *tab;
+/**
+	@param t ParamEntry table
+	@param o controlled object
+	@param n Param's name
+*/
+
+Param(ParamEntry *t=0,void* o=0,const char*n=0):SimpleAbstractParam(o,n),tab(t)
+{if (!n&&tab) myname=tab[0].name;}
+
+Param(const Param& p):SimpleAbstractParam(p.object,p.myname),tab(p.tab) {}
+void operator=(const Param&p) {object=p.object; myname=p.myname; tab=p.tab;}
+
+const char* getDescription() {return tab[0].type;}
+
+int getGroupCount() {return tab[0].group;}
+int getPropCount() {return tab[0].flags;}
+const char *grname(int i) {return (i<getGroupCount())?tab[i].id:0;}
+int grmember(int,int);
+void setParamTab(ParamEntry *t,int dontupdatename=0) {tab=t; if ((!dontupdatename)&&tab) myname=tab[0].name; }
+ParamEntry *getParamTab() const {return tab;}
+};
+
+extern ParamEntry empty_paramtab[];
+
+#endif
Index: /cpp/frams/param/paramobj.cpp
===================================================================
--- /cpp/frams/param/paramobj.cpp	(revision 109)
+++ /cpp/frams/param/paramobj.cpp	(revision 109)
@@ -0,0 +1,167 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <frams/param/paramobj.h>
+#include <frams/util/extvalue.h>
+#include <common/nonstd_stl.h>
+
+static const char* maybedup(bool dup,const char* src)
+{ return dup ? (src?strdup(src):0) : src; }
+static void maybefree(void* mem)
+{ if (mem) free(mem); }
+
+ParamEntry* ParamObject::makeParamTab(ParamInterface *pi,bool stripgroups,bool stripproc,
+				      int firstprop, int maxprops, bool dupentries, int flagsexclude)
+{
+ParamEntry *tab,*t;
+int i,n,offs;
+static ExtValue ex;
+int count=0,gcount=1;
+if (!stripgroups) gcount=pi->getGroupCount();
+if (stripproc||flagsexclude)
+for (int i=firstprop;i < pi->getPropCount();i++)
+	{
+	const char*t=pi->type(i);
+	if ((!stripproc)||(strchr("dfsox",*t)))
+		if ((!flagsexclude)||(!(pi->flags(i)&flagsexclude)))
+			if (++count >= maxprops) break;
+	}
+else count=pi->getPropCount()-firstprop;
+t=tab=(ParamEntry*)malloc(sizeof(ParamEntry)*(count+gcount+1));
+t->group=(short)gcount;
+t->flags=(short)count;
+t->name=maybedup(dupentries,pi->getName());
+t->type=maybedup(dupentries,pi->getDescription());
+for(i=0;i<gcount;i++)
+	{
+	t->id=maybedup(dupentries,pi->grname(i));
+	t->offset=0;
+	t++;
+	}
+n=1; offs=1;
+for (i=firstprop;i < pi->getPropCount();i++)
+	{
+	if ((!stripproc)||(strchr("dfsox",*pi->type(i))))
+		{
+		if ((!flagsexclude)||(!(pi->flags(i)&flagsexclude)))
+			{
+			t->offset=offs*sizeof(ExtValue);
+			if (*pi->type(i)!='x') t->offset+=(((char*)&ex.data[0])-((char*)&ex));
+			t->group=(short)(stripgroups?0:pi->group(i));
+			t->flags=(short)pi->flags(i);
+			t->fun1=0;
+			t->fun2=0;
+			t->id=maybedup(dupentries,pi->id(i));
+			t->name=maybedup(dupentries,pi->name(i));
+			t->type=maybedup(dupentries,pi->type(i));
+			t->help=maybedup(dupentries,pi->help(i));
+			t++;
+			n++;
+			if (n>count) break;
+			}
+		offs++;
+		}
+	}
+t->id=0; t->group=0; t->flags=dupentries?MUTPARAM_ALLOCENTRY:0;
+return tab;
+}
+
+void ParamObject::freeParamTab(ParamEntry *pe)
+{
+if (pe[pe->flags+pe->group].flags & MUTPARAM_ALLOCENTRY)
+	{
+	int i;
+	ParamEntry *e;
+	maybefree((void*)pe->name);
+	maybefree((void*)pe->type);
+	for (i=0,e=pe;i<pe->group;i++,e++) 
+		maybefree((void*)e->id);
+	for (i=pe->group,e=pe+i;i<pe->group+pe->flags;i++,e++) 
+		{
+		maybefree((void*)e->id);
+		maybefree((void*)e->name);
+		maybefree((void*)e->type);
+		maybefree((void*)e->help);
+		}
+	}
+free(pe);
+}
+
+bool ParamObject::paramTabEqual(ParamEntry *pe1,ParamEntry *pe2)
+{
+if (pe1->flags != pe2->flags) return false;
+ParamEntry *e1=pe1+pe1->group, *e2=pe2+pe2->group;
+for(int i=0;i<pe1->flags;i++,e1++,e2++)
+	{
+	if (strcmp(e1->id,e2->id)) return false;
+	if (strcmp(e1->name,e2->name)) return false;
+	if (strcmp(e1->type,e2->type)) return false;
+	if (e1->offset!=e2->offset) return false;
+	}
+return true;
+}
+
+void* ParamObject::makeObject(ParamEntry *tab)
+{
+if (!tab) return 0;
+int n=tab->flags;
+if (!n) return 0;
+ExtValue *ret=new ExtValue[n+1];
+ret->setInt(n);
+ExtValue *v=ret+1;
+tab+=tab->group;
+for (;n>0;n--,v++,tab++)
+	switch(*tab->type)
+	{
+	case 'd': v->setInt(0); break;
+	case 'f': v->setDouble(0); break;
+	case 's': v->setString(SString::empty()); break;
+	case 'o': v->setObject(ExtObject()); break;
+	case 'x': break;
+	}
+return ret;
+}
+
+void ParamObject::copyObject(void* dst,void* src)
+{
+if ((!dst)||(!src)) return;
+ExtValue *s=(ExtValue *)src;
+ExtValue *d=(ExtValue *)dst;
+int n=min(s->getInt(),d->getInt());
+s++; d++;
+for (int i=0;i<n;i++,d++,s++)
+	*d=*s;
+}
+
+void* ParamObject::dupObject(void* src)
+{
+if (!src) return NULL;
+ExtValue *s=(ExtValue *)src;
+int n=s->getInt();
+
+ExtValue *ret=new ExtValue[n+1];
+ExtValue *d=ret;
+d->setInt(n);
+d++; s++;
+
+for (int i=0;i<n;i++,d++,s++)
+	*d=*s;
+
+return ret;
+}
+
+void ParamObject::freeObject(void* obj)
+{
+if (!obj) return;
+delete[] (ExtValue *)obj;
+}
+
+
+
+
+
+
+
+
+
Index: /cpp/frams/param/paramobj.h
===================================================================
--- /cpp/frams/param/paramobj.h	(revision 109)
+++ /cpp/frams/param/paramobj.h	(revision 109)
@@ -0,0 +1,50 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _PARAMOBJ_H_
+#define _PARAMOBJ_H_
+
+#include "param.h"
+
+class ParamObject
+{
+  public:
+/** make a ParamEntry* array for use with Param object.
+    offsets in the array are calculated for the ExtObject array as the target.
+    valid array can be created with makeObject().
+    sample code:
+    @code
+    ParamInterface *pi=...; // any param interface
+    ParamEntry *tab=ParamObject::makeParamTab(pi);
+    void* obj=ParamObject::makeObject(tab);
+    void* obj2=ParamObject::makeObject(tab);
+    Param par(tab,obj);
+    par.set(...), par.get(...), par.load(...), par.save(...);
+    par.select(obj);
+    par.select(obj2);
+    ParamObject::freeObject(obj);
+    ParamObject::freeObject(obj2);
+ */
+static ParamEntry* makeParamTab(ParamInterface *pi,bool stripgroups=0,bool stripproc=0,int firstprop=0,int maxprops=9999,bool dupentries=false, int flagsexclude=0);
+
+/** deallocate paramtab obtained from makeParamTab() */
+static void freeParamTab(ParamEntry *pe);
+
+static bool paramTabEqual(ParamEntry *pe1,ParamEntry *pe2);
+
+/** @return the object, suitable for Param.select(...).
+    @return NULL if 'pi' has no usable properties */
+static void* makeObject(ParamEntry *tab);
+
+/** copy data from src to dst  */
+static void copyObject(void* dst,void* src);
+
+/** duplicate object */
+static void* dupObject(void* obj);
+
+/** delete all data in the array and deallocate it  */
+static void freeObject(void* obj);
+};
+
+#endif
Index: /cpp/frams/param/paramtabobj.cpp
===================================================================
--- /cpp/frams/param/paramtabobj.cpp	(revision 109)
+++ /cpp/frams/param/paramtabobj.cpp	(revision 109)
@@ -0,0 +1,38 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "paramtabobj.h"
+
+int ParamTab::measureTab(const ParamEntry *pe)
+{
+int i=0;
+while(pe->id) {i++;pe++;}
+return i;
+}
+
+void ParamTab::resize(int s)
+{
+if (s==siz) return;
+tab=(ParamEntry*)realloc(tab,sizeof(ParamEntry)*(s+1));
+siz=s;
+}
+
+int ParamTab::add(const ParamEntry* p,int count)
+{
+if (count<0) count=measureTab(p);
+resize(siz+count);
+memmove(tab+siz-count,p,sizeof(ParamEntry)*count);
+memset(tab+siz,0,sizeof(ParamEntry));
+if (siz>0) tab[0].flags=(short)(siz-tab[0].group);
+return siz-1;
+}
+
+void ParamTab::remove(int i,int count)
+{
+memmove(tab+i,tab+i+count,sizeof(ParamEntry)*count);
+resize(siz-count);
+memset(tab+siz,0,sizeof(ParamEntry));
+if (siz>0) tab[0].flags=(short)(siz-tab[0].group);
+}
+
Index: /cpp/frams/param/paramtabobj.h
===================================================================
--- /cpp/frams/param/paramtabobj.h	(revision 109)
+++ /cpp/frams/param/paramtabobj.h	(revision 109)
@@ -0,0 +1,34 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _PARAMTABOBJ_H_
+#define _PARAMTABOBJ_H_
+
+#include "param.h"
+
+class ParamTab
+{
+ParamEntry *tab;
+int siz;
+void init() {siz=0; tab=(ParamEntry*)calloc(sizeof(ParamEntry),1);}
+void resize(int s);
+int measureTab(const ParamEntry *pe);
+
+  public:
+ParamTab(const ParamEntry* pe=0,int maxcount=-1) {init(); if (pe) add(pe,maxcount);}
+ParamTab(const ParamTab& src) {init(); add(src);}
+~ParamTab() {clear();}
+
+int size() const {return siz;}
+ParamEntry* getParamTab() const {return tab;}
+ParamEntry* operator()() const {return tab;}
+
+/** @return position of the last added entry */
+int add(const ParamEntry*,int count=1);
+int add(const ParamTab& src) {return add(src.getParamTab(), src.size());}
+void remove(int i,int count=1);
+void clear() {resize(-1);}
+};
+
+#endif
Index: /cpp/frams/param/syntparam.cpp
===================================================================
--- /cpp/frams/param/syntparam.cpp	(revision 109)
+++ /cpp/frams/param/syntparam.cpp	(revision 109)
@@ -0,0 +1,67 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <common/nonstd.h>
+#include "syntparam.h"
+#include "paramobj.h"
+#include <math.h>
+
+SyntParam::SyntParam(ParamEntry *init_pe,SString* autostr,bool handle_defaults_when_saving)
+	:autostring(autostr)
+{
+Param::setParamTab(init_pe);
+pe=ParamObject::makeParamTab(this);
+Param::setParamTab(pe);
+if (handle_defaults_when_saving)
+	{
+	def_obj=ParamObject::makeObject(pe);
+	Param::select(def_obj);
+	Param::setDefault();
+	}
+else
+	def_obj=NULL;
+obj=ParamObject::makeObject(pe);
+Param::select(obj);
+Param::setDefault();
+revert();
+}
+
+SyntParam::SyntParam(const SyntParam& src)
+	:Param(),autostring(src.autostring)
+{
+Param::setParamTab(src.pe);
+pe=ParamObject::makeParamTab(this);
+Param::setParamTab(pe);
+obj=ParamObject::dupObject(src.obj);
+def_obj=src.def_obj ? ParamObject::dupObject(src.def_obj) : NULL;
+Param::select(obj);
+}
+
+SyntParam::~SyntParam()
+{
+update();
+ParamObject::freeParamTab(pe);
+ParamObject::freeObject(obj);
+ParamObject::freeObject(def_obj);
+}
+
+void SyntParam::update(SString *s)
+{
+if (!s) s=autostring;
+if (s) {*s=""; Param::save2(*s,def_obj,0);}
+}
+
+void SyntParam::revert(SString *s)
+{
+if (!s) s=autostring;
+if (s)	{
+	int p=0;
+	Param::load2(*s,p);
+	}
+}
+
+
+
+
+
Index: /cpp/frams/param/syntparam.h
===================================================================
--- /cpp/frams/param/syntparam.h	(revision 109)
+++ /cpp/frams/param/syntparam.h	(revision 109)
@@ -0,0 +1,35 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _SYNTPATAM_H_
+#define _SYNTPATAM_H_
+
+#include "param.h"
+
+/** Creates param + matching temporary object (ParamObject)
+    using the supplied ParamEntry'ies as a template.
+    This is mainly used for manipulating specialized neuron properties
+    (Neuro.d field) in absence of live neuron implementations
+    (these are only available in live creatures, but not when operating
+    on Models and Genotypes).
+
+    See also: gdktest.cpp
+ */
+class SyntParam: public Param
+{
+void* obj,*def_obj;
+ParamEntry *pe;
+SString* autostring;
+  public:
+/** @param handle_defaults_when_saving creates a second object holding the default values so Param::save2 can use it for omitting defaults. can be disabled for compatiblity with previous behavior (defaults were ignored in SyntParam)
+ */
+SyntParam(ParamEntry *pe,SString* autostring=0,bool handle_defaults_when_saving=true);
+SyntParam(const SyntParam& src);
+~SyntParam();
+void setAutoUpdate(SString* autostr) {autostring=autostr;}
+void update(SString *s=0);
+void revert(SString *s=0);
+};
+
+#endif
Index: /cpp/frams/util/3d.cpp
===================================================================
--- /cpp/frams/util/3d.cpp	(revision 109)
+++ /cpp/frams/util/3d.cpp	(revision 109)
@@ -0,0 +1,223 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <common/nonstd_math.h>
+#include <common/framsg.h>
+#include "3d.h"
+
+Pt3D operator+(const Pt3D &p1,const Pt3D &p2) {return Pt3D(p1.x+p2.x,p1.y+p2.y,p1.z+p2.z);}
+Pt3D operator-(const Pt3D &p1,const Pt3D &p2) {return Pt3D(p1.x-p2.x,p1.y-p2.y,p1.z-p2.z);}
+
+Pt3D Pt3D_0(0,0,0);
+
+bool Pt3D::report_errors=true;
+
+double Pt3D::operator()() const
+{
+double q=x*x+y*y+z*z;
+if (q<0) {if (report_errors) FramMessage("Pt3D","operator()","sqrt domain error",3); return 0;}
+return sqrt(q);
+}
+
+bool Pt3D::normalize()
+{
+double len=length();
+if (fabs(len)<1e-50) {if (report_errors) FramMessage("Pt3D","normalize()","vector too small",1); x=1;y=0;z=0; return false;}
+operator/=(len);
+return true;
+}
+
+double Pt3D::distanceTo(const Pt3D& p) const
+{
+return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)+(z-p.z)*(z-p.z));
+}
+
+double Pt3D::manhattanDistanceTo(const Pt3D& p) const
+{
+return fabs(x-p.x)+fabs(y-p.y)+fabs(z-p.z);
+}
+
+Orient Orient_1(Pt3D(1,0,0),Pt3D(0,1,0),Pt3D(0,0,1));
+
+// prosty obrot
+void rotate2D(double k,double &x,double &y)
+{double s=sin(k),c=cos(k);
+double t=c*x-s*y; y=s*x+c*y; x=t;}
+
+void rotate2D(double s,double c,double &x,double &y)
+{double t=c*x-s*y; y=s*x+c*y; x=t;}
+
+int Pt3D::getAngle(double dx,double dy,double &wyn)
+{
+if ((fabs(dx)+fabs(dy))<0.0001) return 0;
+wyn=atan2(dy,dx);
+return 1;
+}
+
+void Pt3D::getAngles(const Pt3D& X,const Pt3D& dir)
+{
+Pt3D	t1(X), t2(dir);
+if (getAngle(t1.x,t1.y,z))
+	{	// nie pionowo
+	rotate2D(-z,t1.x,t1.y);
+	rotate2D(-z,t2.x,t2.y);
+	getAngle(t1.x, t1.z, y);
+	}
+else
+	{	// pionowo
+	z=0;
+	if (t1.z<0)
+		y=-M_PI_2; // w dol
+	else
+		y=M_PI_2; // w gore
+	}
+rotate2D(-y,t2.x,t2.z);
+if (!getAngle(t2.z,-t2.y,x))
+	x=0;// to zly wynik ale dobrego nie ma :P
+}
+
+void Pt3D::getMin(const Pt3D& p)
+{
+if (p.x<x) x=p.x;
+if (p.y<y) y=p.y;
+if (p.z<z) z=p.z;
+}
+void Pt3D::getMax(const Pt3D& p)
+{
+if (p.x>x) x=p.x;
+if (p.y>y) y=p.y;
+if (p.z>z) z=p.z;
+}
+
+void Pt3D::vectorProduct(const Pt3D& a,const Pt3D& b)
+{
+x=a.y*b.z-a.z*b.y;
+y=a.z*b.x-a.x*b.z;
+z=a.x*b.y-a.y*b.x;
+}
+
+void Orient::lookAt(const Pt3D& X,const Pt3D& dir)
+{
+x=X; x.normalize();
+y.vectorProduct(dir,x);
+z.vectorProduct(x,y);
+if ((!y.normalize()) || (!z.normalize()))
+	{
+	y.x=dir.y; y.y=dir.z; y.z=dir.x;
+	z.vectorProduct(x,y);
+	z.normalize();
+	}
+}
+
+// odleglosc 2d
+double d2(double x,double y)
+{
+double q=x*x+y*y;
+if (q<0) {if (Pt3D::report_errors) FramMessage("","d2()","sqrt domain error",3); return 0;}
+return sqrt(q);
+}
+
+Orient::Orient(const Matrix44& m)
+{
+x.x=m[0];  x.y=m[1];  x.z=m[2];
+y.x=m[4];  y.y=m[5];  y.z=m[6];
+z.x=m[8];  z.y=m[9];  z.z=m[10];
+}
+
+void Orient::operator=(const Pt3D &rot)
+{
+*this=Orient_1;
+rotate(rot);
+}
+
+void Orient::rotate(const Pt3D &v)
+{
+double s,c;
+if (fabs(v.x)>0.0001)
+	{
+	s=sin(v.x); c=cos(v.x);
+	rotate2D(s,c,x.y,x.z);
+	rotate2D(s,c,y.y,y.z);
+	rotate2D(s,c,z.y,z.z);
+	}
+if (fabs(v.y)>0.0001)
+	{
+	s=sin(v.y); c=cos(v.y);
+	rotate2D(s,c,x.x,x.z);
+	rotate2D(s,c,y.x,y.z);
+	rotate2D(s,c,z.x,z.z);
+	}
+if (fabs(v.z)>0.0001)
+	{
+	s=sin(v.z); c=cos(v.z);
+	rotate2D(s,c,x.x,x.y);
+	rotate2D(s,c,y.x,y.y);
+	rotate2D(s,c,z.x,z.y);
+	}
+}
+
+void Orient::transform(Pt3D& target,const Pt3D &s) const
+{
+target.x=s.x*x.x+s.y*y.x+s.z*z.x;
+target.y=s.x*x.y+s.y*y.y+s.z*z.y;
+target.z=s.x*x.z+s.y*y.z+s.z*z.z;
+}
+
+void Orient::revTransform(Pt3D& target,const Pt3D &s) const
+{
+target.x=s.x*x.x+s.y*x.y+s.z*x.z;
+target.y=s.x*y.x+s.y*y.y+s.z*y.z;
+target.z=s.x*z.x+s.y*z.y+s.z*z.z;
+}
+
+void Orient::transform(Orient& target,const Orient& src) const
+{
+transform(target.x,src.x);
+transform(target.y,src.y);
+transform(target.z,src.z);
+}
+
+void Orient::revTransform(Orient& target,const Orient& src) const
+{
+revTransform(target.x,src.x);
+revTransform(target.y,src.y);
+revTransform(target.z,src.z);
+}
+
+void Orient::getAngles(Pt3D &angles) const
+{
+angles.getAngles(x,z);
+}
+
+bool Orient::normalize()
+{
+bool ret=1;
+y.vectorProduct(z,x);
+z.vectorProduct(x,y);
+if (!x.normalize()) ret=0;
+if (!z.normalize()) ret=0;
+if (!y.normalize()) ret=0;
+return ret;
+}
+
+Matrix44::Matrix44(const Orient &rot)
+{
+m[0]=rot.x.x;  m[1]=rot.x.y;  m[2]=rot.x.z;  m[3]=0;
+m[4]=rot.y.x;  m[5]=rot.y.y;  m[6]=rot.y.z;  m[7]=0;
+m[8]=rot.z.x;  m[9]=rot.z.y;  m[10]=rot.z.z; m[11]=0;
+m[12]=0;       m[13]=0;       m[14]=0;       m[15]=1;
+}
+
+void Matrix44::operator+=(const Pt3D &)
+{
+
+}
+
+void Matrix44::operator*=(const Pt3D &)
+{
+}
+
+void Matrix44::operator*=(double sc)
+{
+}
Index: /cpp/frams/util/3d.h
===================================================================
--- /cpp/frams/util/3d.h	(revision 109)
+++ /cpp/frams/util/3d.h	(revision 109)
@@ -0,0 +1,140 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _3D_H_
+#define _3D_H_
+
+#ifdef SHP
+#include <string.h> //memcpy
+#else
+#include <memory.h> //memcpy
+#endif
+
+/**********************************
+\file 3d.h 3d.cpp
+
+   basic 3d classes and operators     
+*********************************/
+
+/// point in 3d space
+
+class Pt3D
+{
+public:	double x,y,z;
+static bool report_errors;
+
+Pt3D(double _x,double _y,double _z):x(_x),y(_y),z(_z) {} ///< constructor initializing all coords
+Pt3D(double xyz):x(xyz),y(xyz),z(xyz) {} ///< all coords equal 
+Pt3D() {} ///< coords will be not initialized!
+Pt3D(const Pt3D &p):x(p.x),y(p.y),z(p.z) {} ///< copy from another point
+bool 	operator==(const Pt3D& p)	{return (x==p.x)&&(y==p.y)&&(z==p.z);}
+void 	operator+=(const Pt3D& p)	{x+=p.x;y+=p.y;z+=p.z;}
+void 	operator-=(const Pt3D& p)	{x-=p.x;y-=p.y;z-=p.z;}
+void  	operator*=(double d)	{x*=d;y*=d;z*=d;}
+Pt3D  	operator*(const Pt3D &p) const {return Pt3D(y*p.z-z*p.y, z*p.x-x*p.z, x*p.y-y*p.x);}
+void 	operator/=(double d)	{x/=d; y/=d; z/=d;}
+//Pt3D 	operator+(const Pt3D& p) const {return Pt3D(x+p.x,y+p.y,z+p.z);}
+//Pt3D 	operator-(const Pt3D& p) const {return Pt3D(x-p.x,y-p.y,z-p.z);}
+Pt3D 	operator-() const {return Pt3D(-x,-y,-z);}
+Pt3D 	operator*(double d) const {return Pt3D(x*d,y*d,z*d);}
+Pt3D 	operator/(double d) const {return Pt3D(x/d,y/d,z/d);}
+int	operator<(const Pt3D& p) const {return (x<p.x)&&(y<p.y)&&(z<p.z);}
+	///< check if all coords are below the second point
+int	operator>(const Pt3D& p) const {return (x>p.x)&&(y>p.y)&&(z>p.z);}
+	///< check if all coords are above the second point
+int	operator<=(const Pt3D& p) const {return (x<p.x)||(y<p.y)||(z<p.z);}
+	///< check if some coords are below the second point
+int	operator>=(const Pt3D& p) const {return (x>p.x)||(y>p.y)||(z>p.z);}
+	///< check if some coords are above the second point
+void getMin(const Pt3D& p);
+void getMax(const Pt3D& p);
+/** vector length = \f$\sqrt{x^2+y^2+z^2}\f$  */
+double operator()() const;
+/** vector length = \f$\sqrt{x^2+y^2+z^2}\f$  */
+double length() const {return operator()();}
+double length2() const {return x*x+y*y+z*z;}
+double distanceTo(const Pt3D& p) const;
+double manhattanDistanceTo(const Pt3D& p) const;
+/** calculate angle between (0,0)-(dx,dy), @return 1=ok, 0=can't calculate */
+static int getAngle(double dx,double dy,double &angle);
+/** calculate 3 rotation angles translating (1,0,0) into 'X' and (0,0,1) into 'dir' */
+void getAngles(const Pt3D& X,const Pt3D& dir);
+void vectorProduct(const Pt3D& a,const Pt3D& b);
+bool normalize();
+};
+Pt3D operator+(const Pt3D &p1,const Pt3D &p2);
+Pt3D operator-(const Pt3D &p1,const Pt3D &p2);
+
+class Pt3D_DontReportErrors
+{
+bool state;
+public:
+Pt3D_DontReportErrors() {state=Pt3D::report_errors; Pt3D::report_errors=false;}
+~Pt3D_DontReportErrors() {Pt3D::report_errors=state;}
+};
+
+///  orientation in 3d space = rotation matrix
+
+class Matrix44;
+
+class Orient
+{
+public: Pt3D x,y,z; ///< 3 vectors (= 3x3 matrix)
+
+	Orient() {}
+	Orient(const Orient& src) {x=src.x; y=src.y; z=src.z;}
+	Orient(const Pt3D& a,const Pt3D& b,const Pt3D& c):x(a),y(b),z(c) {}
+//	Orient(const Pt3D& rot) {*this=rot;}
+	Orient(const Matrix44& m);
+	void operator=(const Pt3D &rot);
+	void rotate(const Pt3D &); ///< rotate matrix around 3 axes
+
+	void transform(Pt3D &target,const Pt3D &src) const;	///< transform a vector
+	void revTransform(Pt3D &target,const Pt3D &src) const;	///< reverse transform
+	Pt3D transform(const Pt3D &src) const {Pt3D t; transform(t,src); return t;}
+	Pt3D revTransform(const Pt3D &src) const {Pt3D t; revTransform(t,src); return t;}
+
+	void transform(Orient& target,const Orient& src) const;    ///< transform other orient
+	void revTransform(Orient& target,const Orient& src) const; ///< reverse transform other orient
+	Orient transform(const Orient& src) const {Orient o; transform(o,src); return o;}    ///< transform other orient
+	Orient revTransform(const Orient& src) const {Orient o; revTransform(o,src); return o;} ///< reverse transform other orient
+
+	void transformSelf(const Orient &rot) {Orient tmp; rot.transform(tmp,*this); *this=tmp;}
+	void revTransformSelf(const Orient &rot) {Orient tmp; rot.revTransform(tmp,*this); *this=tmp;}
+
+	void getAngles(Pt3D &) const; ///< calculate rotation from current matrix
+	Pt3D getAngles() const {Pt3D ret; getAngles(ret); return ret;}; ///< calculate rotation from current matrix
+	void lookAt(const Pt3D &X,const Pt3D &dir); ///< calculate orientation matrix from 2 vectors
+
+	bool normalize();
+};
+
+class Matrix44
+{
+public:
+double m[16];
+Matrix44() {}
+Matrix44(const Matrix44& src) {memcpy(m,src.m,sizeof(m));}
+Matrix44(double *srcm) {memcpy(m,srcm,sizeof(m));}
+Matrix44(const Orient &rot);
+
+const double& operator()(int i,int j) const {return m[i+16*j];}
+const double& operator[](int i) const {return m[i];}
+double& operator()(int i,int j) {return m[i+16*j];}
+double& operator[](int i) {return m[i];}
+
+void operator+=(const Pt3D &); ///< translate matrix
+void operator*=(const Pt3D &); ///< scale matrix
+void operator*=(double sc); ///< scale matrix
+};
+
+extern Pt3D Pt3D_0; ///< zero vector
+extern Orient Orient_1; ///< standard unit matrix: 100 010 001
+extern Matrix44 Matrix44_1; ///< standard unit matrix: 1000 0100 0010 0001
+
+void rotate2D(double,double &,double &); ///< rotate 2d vector, given angle
+void rotate2D(double,double,double &,double &); ///< rotate 2d vector, given sin and cos
+double d2(double,double); ///< distance in 2D
+
+#endif
Index: /cpp/frams/util/advlist.cpp
===================================================================
--- /cpp/frams/util/advlist.cpp	(revision 109)
+++ /cpp/frams/util/advlist.cpp	(revision 109)
@@ -0,0 +1,43 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "advlist.h"
+
+void AdvList::remove(int i)
+{
+l_del.action(i);
+SList::remove(i);
+l_postdel.action(i);
+}
+
+void AdvList::clear()
+{
+int i;
+for (i=size()-1;i>=0;i--) remove(i);
+resize(0);
+used=0;
+}
+
+void AdvList::operator-=(void* e)
+{
+int i=find(e);
+if (i>=0) remove(i);
+}
+
+int AdvList::operator+=(void* e)
+{
+int p=size();
+SList::operator+=(e);
+l_add.action(p);
+return p;
+}
+
+void AdvList::mod(int x)
+{
+if (x<-1) x=-1;
+l_mod.action(x);
+}
Index: /cpp/frams/util/advlist.h
===================================================================
--- /cpp/frams/util/advlist.h	(revision 109)
+++ /cpp/frams/util/advlist.h	(revision 109)
@@ -0,0 +1,35 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _ADVLIST_H_
+#define _ADVLIST_H_
+
+#include "list.h"
+#include "common/nonstd.h"
+#include "callbacks.h"
+
+////////////////////////////////////////////
+/// no more DuoList -> now we have Callback class (callbacks.h)
+
+/// list with notification support.
+/// you can register callbacks which will be activated
+/// as the list contents changes
+
+/// sorting functions moved to SortView class
+
+class AdvList: public SList
+{
+public:
+Callback l_add, l_del, l_mod, l_postdel;
+void remove(int);
+virtual void mod(int=-1); ///< call this when you change one or more elements in list (-1 means all elements)
+void clear();
+AdvList() {}
+~AdvList() {clear();}
+int operator+=(void*);
+void operator-=(void*);
+void operator-=(int i) {remove(i);}
+};
+
+#endif
Index: /cpp/frams/util/callbacks.cpp
===================================================================
--- /cpp/frams/util/callbacks.cpp	(revision 109)
+++ /cpp/frams/util/callbacks.cpp	(revision 109)
@@ -0,0 +1,82 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "callbacks.h"
+#include <stdio.h>
+
+#ifdef USEMEMBERCALLBACK
+int MemberCallbackNode::equals(CallbackNode*n)
+{
+if (n==this) return 1;
+MemberCallbackNode *classok=dynamic_cast<MemberCallbackNode*>(n);
+if (!classok) return 0;
+return ((userdata==classok->userdata)&&(object==classok->object)&&(member==classok->member));
+}
+#endif
+
+int FunctionCallbackNode::equals(CallbackNode*n)
+{
+if (n==this) return 1;
+FunctionCallbackNode *classok=dynamic_cast<FunctionCallbackNode*>(n);
+if (!classok) return 0;
+return ((userdata==classok->userdata)&&(fun==classok->fun));
+}
+
+int StatrickCallbackNode::equals(CallbackNode*n)
+{
+if (n==this) return 1;
+StatrickCallbackNode *classok=dynamic_cast<StatrickCallbackNode*>(n);
+if (!classok) return 0;
+return ((object==classok->object)&&(userdata==classok->userdata)&&(fun==classok->fun));
+}
+
+/////////////////
+
+CallbackNode* Callback::add(CallbackNode*n)
+{
+SList::operator+=(n);
+return n;
+}
+
+void Callback::removeNode(CallbackNode*n)
+{
+SList::operator-=(n);
+delete n;
+}
+
+void Callback::remove(CallbackNode*node)
+{
+CallbackNode *n;
+//printf("Hint: removing callbacks (former 'DuoList') is more efficient using removeNode(). (refer to 'callbacks.h')\n");
+for (int i=0;n=(CallbackNode *)operator()(i);i++)
+	if (node->equals(n))
+		{
+		SList::operator-=(i);
+		delete node;
+		if (n!=node) delete n;
+		return;
+		}
+delete node; // tu nie wiem czy na pewno...
+}
+
+void Callback::action(long data)
+{
+FOREACH(CallbackNode*,n,(*this))
+	n->action(data);
+}
+
+Callback::~Callback()
+{
+CallbackNode *n;
+for (int i=size()-1;i>=0;i--)
+	{
+	n=(CallbackNode *)operator()(i);
+	delete n;
+// todo: zrobic zeby kolejnosc delete callbacknode <-> delete callback nie wplywala na poprawne dzialania
+// blad odkryty 24.01 pokazal, ze deletowanie callbacknodow w ~callback
+// moze powodowac problemy, jezeli obiekty sa usuwane w "zlej" kolejnosci
+// ale na razie tak zostanie
+	}
+}
+
Index: /cpp/frams/util/callbacks.h
===================================================================
--- /cpp/frams/util/callbacks.h	(revision 109)
+++ /cpp/frams/util/callbacks.h	(revision 109)
@@ -0,0 +1,120 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _CALLBACKS_H_
+#define _CALLBACKS_H_
+
+#include "list.h"
+#include "statrick.h"
+
+//#define USEMEMBERCALLBACK
+
+class CallbackNode
+{
+public:
+virtual ~CallbackNode() {}
+virtual void action(long calldata)=0;
+virtual int equals(CallbackNode*n) {return (this==n);}
+};
+
+#ifdef USEMEMBERCALLBACK
+class CallBase;
+class MemberCallbackNode :public CallbackNode
+{
+void *userdata;
+CallBase *object;
+void (CallBase::*member)(void*,long);
+  public:
+MemberCallbackNode(CallBase *o,void (CallBase::*m)(void*,long),void *d):object(o),member(m),userdata(d) {}
+void action(long calldata) {(object->*member)(userdata,calldata);}
+int equals(CallbackNode*);
+};
+#define MEMBERCALLBACK(obj,mem,dat) new MemberCallbackNode((CallBase*)(obj),(void (CallBase::*)(void*,long))(mem),(void*)(dat))
+#endif
+
+class FunctionCallbackNode :public CallbackNode
+{
+void *userdata;
+void (*fun)(void*,long);
+  public:
+FunctionCallbackNode(void (*f)(void*,long),void *d):userdata(d),fun(f) {}
+void action(long calldata) {(*fun)(userdata,calldata);}
+int equals(CallbackNode*);
+};
+#define FUNCTIONCALLBACK(fun,dat) new FunctionCallbackNode((void (*)(void*,long))(fun),(void*)(dat))
+
+class StatrickCallbackNode :public CallbackNode
+{
+void *object;
+void *userdata;
+void (*fun)(void*,void*,long);
+  public:
+StatrickCallbackNode(void *o,void (*f)(void*,void*,long),void *d):object(o),userdata(d),fun(f) {}
+void action(long calldata) {(*fun)(object,userdata,calldata);}
+int equals(CallbackNode*);
+};
+#define STATRICKCALLBACK(obj,name,dat) new StatrickCallbackNode((void*)(obj),(void (*)(void*,void*,long))STATRICKNAME(name),(void*)(dat))
+
+/**
+   Like in old 'DuoList' you can register for an event giving function pointer
+      add(Function* fun, void* anydata)
+   'fun' will be called with your pointer as the first argument (void*)
+   and event specific value as the second argument (long)
+      fun(void* anydata,long eventdata)
+
+   'StatrickCallbackNode' uses static functions to emulate object member calls.
+   @see statrick.h
+   
+   Unregistering callbacks:
+   The old remove(...) still works, but removeNode() is more efficient.
+   To use it you have to store what you get from add(...);
+      CallbackNode* node=thelist.l_add.add(&fun,data);
+      // thelist.l_add.remove(&fun,data);
+      thelist.l_add.removeNode(node); // this is better!
+
+ */
+
+class Callback: protected SList
+{
+public:
+~Callback();
+CallbackNode* add(CallbackNode*n);
+CallbackNode* add(void (*f)(void*,long),void *d)
+	{return add(new FunctionCallbackNode(f,d));}
+void remove(void (*f)(void*,long),void *d)
+	{remove(new FunctionCallbackNode(f,d));}
+void remove(CallbackNode*n);
+void removeNode(CallbackNode*n);
+void operator+=(void* fun) {add((void (*)(void*,long))fun,0);}
+void operator-=(void* fun) {remove((void (*)(void*,long))fun,0);}
+void action(long data);
+void action() {action(0);}
+int size() {return SList::size();}
+void clear() {SList::clear();}
+};
+
+///////////////////
+
+#define STCALLBACKDEF(name) STATRICKDEF2(name,void*,long)
+#define STCALLBACKDEFC(cls,name) STATRICKSTUB2(cls,name,void*,long)  \
+	void name(void* arg1,long arg2)
+#define VIRTCALLBACKDEF(name) STATRICKSTUB2(STATRICKCLASS,name,void*,long)  \
+	virtual void name(void* arg1,long arg2)
+#define VIRTCALLBACKDEFC(cls,name) STATRICKSTUB2(cls,name,void*,long)  \
+	virtual void name(void* arg1,long arg2)
+
+/* STCALLBACKDEFC(Class,name)
+
+     |
+     v
+
+   #define STATRICKCLASS Class
+   STCALLBACKDEF(name)
+   #undef STATRICKCLASS
+ */
+
+#define CALLBACKARGS void* arg1,long arg2
+
+#endif
+
Index: /cpp/frams/util/extvalue.cpp
===================================================================
--- /cpp/frams/util/extvalue.cpp	(revision 109)
+++ /cpp/frams/util/extvalue.cpp	(revision 109)
@@ -0,0 +1,882 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "extvalue.h"
+#include <frams/param/param.h>
+#include "sstringutils.h"
+#include <ctype.h>
+#include <frams/vm/classes/collectionobj.h>
+#include <frams/vm/classes/3dobject.h>
+#include <common/nonstd_math.h>
+#include <common/Convert.h>
+
+#ifndef NO_BARRIER
+#include <frams/simul/barrier.h>
+#include <common/threads.h>
+#endif
+
+#ifdef MULTITHREADED
+#include <pthread.h>
+//this lock only protects against ref.counter corruption caused by concurrent reads.
+//read/write conficts and nonatomicity are handled by BarrierObject (at least in theory ;-))
+static pthread_mutex_t extobject_ref_lock=PTHREAD_MUTEX_INITIALIZER;
+#define REF_LOCK pthread_mutex_lock(&extobject_ref_lock)
+#define REF_UNLOCK pthread_mutex_unlock(&extobject_ref_lock)
+#else
+#define REF_LOCK 
+#define REF_UNLOCK 
+#endif
+
+void ExtObject::incref() const
+{
+if (subtype&1)
+	{
+	REF_LOCK;
+	dbobject->refcount++;
+	REF_UNLOCK;
+	}
+}
+
+void ExtObject::decref() const
+{
+if (subtype&1)
+	{
+	REF_LOCK;
+	bool destroy=!--dbobject->refcount;
+	REF_UNLOCK;
+	//another thread can now access the object while we are deleting it
+	//but this is not a bug since we only guarantee read/read safety
+	if (destroy) delete dbobject;
+	}
+}
+
+bool ExtObject::makeUnique()
+{
+if (!(subtype&1)) return false;
+if (dbobject->refcount==1) return false;
+VectorObject* v=VectorObject::fromObject(*this);
+if (v)
+	{
+	VectorObject* n=new VectorObject;
+	n->data.setSize(n->data.size());
+	for(int i=0;i<v->data.size();i++)
+		{
+		ExtValue *x=(ExtValue*)v->data(i);
+		n->data.set(i,x?new ExtValue(*x):NULL);
+		}
+	operator=(n->makeObject());
+	return true;
+	}
+return false;
+}
+
+void* ExtObject::getTarget(const char* classname, bool through_barrier) const
+{
+if (!strcmp(interfaceName(),classname))
+	return getTarget();
+#ifndef NO_BARRIER
+if (through_barrier)
+	{
+	BarrierObject *bo=BarrierObject::fromObject(*this);
+	if (bo)
+		return bo->getSourceObject().getTarget(classname);
+	}
+#endif
+return NULL;
+}
+
+
+SString ExtObject::toString() const
+{
+if (isEmpty()) return SString("<empty object>");
+Param tmp_param;
+ParamInterface *p=getParamInterface(tmp_param);
+int tostr=p->findId("toString");
+if (tostr>=0)
+	{
+	return SString(p->getString(tostr));
+	}
+else
+	{
+	SString tmp("<");
+	tmp+=p->getName();
+	tmp+=SString::sprintf(" object at %p>",object?object:paraminterface);
+	return tmp;
+	}
+}
+
+THREAD_LOCAL_DEF(ExtObject::Serialization,ExtObject::serialization);
+
+void ExtObject::Serialization::begin()
+{
+if (level==0)
+	refs.clear();
+level++;
+}
+
+int ExtObject::Serialization::add(const ExtObject &o)
+{
+if (o.isEmpty()) return -1;
+for(int i=0;i<(int)refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (r==o) return i;
+	}
+refs.push_back(o);
+return -1;
+}
+
+void ExtObject::Serialization::replace(const ExtObject& o,const ExtObject& other)
+{
+if (o.isEmpty()) return;
+for(int i=0;i<(int)refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (r==o)
+		{
+		r=other;
+		return;
+		}
+	}
+}
+
+void ExtObject::Serialization::remove(const ExtObject& o)
+{
+if (o.isEmpty()) return;
+for(int i=0;i<(int)refs.size();i++)
+	{
+	ExtObject& r=refs[i];
+	if (o==r) refs.erase(refs.begin()+i);
+	}
+}
+
+const ExtObject* ExtObject::Serialization::get(int ref)
+{
+if (ref<0) return NULL;
+if (ref>=(int)refs.size()) return NULL;
+return &refs[ref];
+}
+
+void ExtObject::Serialization::end()
+{
+level--;
+if (level==0)
+	refs.clear();
+}
+
+SString ExtObject::serialize_inner() const
+{
+int ref=tlsGetRef(serialization).add(*this);
+if (ref>=0)
+	return SString::sprintf("^%d",ref);
+
+if (isEmpty()) return SString();
+VectorObject *vec=VectorObject::fromObject(*this);
+if (vec)
+	return vec->serialize();
+DictionaryObject *dic=DictionaryObject::fromObject(*this);
+if (dic)
+	return dic->serialize();
+Param tmp_param;
+ParamInterface *p=getParamInterface(tmp_param);
+int m=p->findId("toVector");
+if (m<0) 
+	m=p->findId("toDictionary");
+if (m>=0)
+	{
+	ExtObject o(p->getObject(m));
+	SString ret=SString(interfaceName())+o.serialize();
+	return ret;
+	}
+m=p->findId("toString");
+if (m>=0)
+	{
+	SString str=p->getString(m);
+	sstringQuote(str);
+	SString ret=SString(interfaceName())+"\""+str+"\"";
+	return ret;
+	}
+
+tlsGetRef(serialization).remove(*this);//undo nonserializable reference
+SString ret=interfaceName();
+ret+=SString::sprintf("<%p>",object?object:paraminterface);
+return ret;
+}
+
+SString ExtObject::serialize() const
+{
+tlsGetRef(serialization).begin();
+SString ret=serialize_inner();
+tlsGetRef(serialization).end();
+return ret;
+}
+
+///////////////////////////////////////
+
+void ExtValue::set(const ExtValue& src)
+{
+switch(src.type)
+	{
+	case TString: sets(src.sdata()); break;
+	case TInt: seti(src.idata()); break;
+	case TDouble: setd(src.ddata()); break;
+	case TObj: seto(src.odata()); break;
+	default:type=src.type; break;
+	}
+}
+
+void ExtValue::setEmpty()
+{
+switch(type)
+	{
+#ifdef EXTVALUEUNION
+	case TString: sdata().~SString(); break;
+	case TObj: odata().~ExtObject(); break;
+#else
+	case TString: delete s; break;
+	case TObj: delete o; break;
+#endif
+	default:;
+	}
+type=TUnknown;
+}
+
+static long longsign(long x)
+{
+if (x<0) return -1;
+if (x>0) return 1;
+return 0;
+}
+
+long ExtValue::compare(const ExtValue& src) const
+{
+if (type==TUnknown)
+	{
+	if (src.type==TDouble)
+		return (src.getDouble()!=0.0);
+	if (src.type==TString)
+		return 1;
+	return src.getInt()?1:0;
+	}
+else if (src.type==TUnknown)
+	{
+	if (type==TDouble)
+		return (getDouble()!=0.0);
+	if (type==TString)
+		return 1;
+	return getInt()?1:0;
+	}
+switch(type)
+	{
+	case TInt:
+	{
+	long t=src.getInt();
+	if (idata()>0)
+		{if (t>0) return longsign(idata()-t); else return +1;}
+	else
+		{if (t<=0) return longsign(idata()-t); else return -1;}
+	}
+	case TDouble:
+		{
+		double t=ddata()-src.getDouble();
+		if (t<0) return -1;
+		else if (t>0) return 1;
+		return 0;
+		}
+	case TString:
+	{
+	SString t=src.getString();
+	SString& t2=sdata();
+	const char* s1=(const char*)t2;
+	const char* s2=(const char*)t;
+	return longsign(strcmp(s1,s2));
+	}
+	case TObj: 
+	{
+	if (src.type==TObj)
+		return !(odata()==src.odata());
+	return 1;
+	}
+	default:;
+	}
+return 1;
+}
+
+int ExtValue::operator==(const ExtValue& src) const
+{
+if (type!=src.type) return 0;
+switch(type)
+	{
+	case TInt: return idata()==src.idata();
+	case TDouble: return ddata()==src.ddata();
+	case TString: return sdata()==src.sdata();
+	case TObj: return odata()==src.odata();
+	default:;
+	}
+return 1;
+}
+
+void ExtValue::operator+=(const ExtValue& src)
+{
+switch(type)
+	{
+	case TInt: idata()+=src.getInt(); break;
+	case TDouble: ddata()+=src.getDouble(); break;
+	case TString: sdata()+=src.getString(); break;
+	case TObj:
+		{
+		VectorObject *vec=VectorObject::fromObject(getObject());
+		VectorObject *vec2=VectorObject::fromObject(src.getObject());
+		if (vec && vec2)
+			{
+			for(int i=0;i<vec2->data.size();i++)
+				{
+				ExtValue *s=(ExtValue*)vec2->data(i);
+				ExtValue *d=s?new ExtValue(*s):NULL;
+				vec->data+=d;
+				}
+			}
+		}
+	default:;
+	}
+}
+
+void ExtValue::operator-=(const ExtValue& src)
+{
+switch(type)
+	{
+	case TInt: idata()-=src.getInt(); break;
+	case TDouble: ddata()-=src.getDouble(); break;
+	default:;
+	}
+}
+
+void ExtValue::operator*=(const ExtValue& src)
+{
+switch(type)
+	{
+	case TInt: idata()*=src.getInt(); break;
+	case TDouble: ddata()*=src.getDouble(); break;
+	case TString:
+	{
+	SString t;
+	for(int n=src.getInt();n>0;n--)
+		t+=getString();
+	setString(t);
+	break;
+	}
+	case TObj:
+		{
+		VectorObject *vec=VectorObject::fromObject(getObject());
+		if (vec)
+			{
+			int n=src.getInt();
+			int orig_size=vec->data.size();
+			if (n<=0)
+				{vec->clear();return;}
+			for(;n>1;n--)
+				{
+				for(int i=0;i<orig_size;i++)
+					{
+					ExtValue *s=(ExtValue*)vec->data(i);
+					ExtValue *d=s?new ExtValue(*s):NULL;
+					vec->data+=d;
+					}
+				}
+			}
+		break;
+		}
+	default:;
+	}
+}
+
+#include <common/framsg.h>
+/*#include "fpu_control.h"
+#include <signal.h>
+
+static int fpuexception;
+void mathhandler(int sig)
+{
+printf("fpu exception!\n");
+fpuexception=1;
+signal(SIGFPE,SIG_IGN);
+} */
+
+void ExtValue::operator/=(const ExtValue& src)
+{
+switch(type)
+	{
+	case TInt:
+	{ int a=src.getInt();
+//		idata()/=src.getInt();
+	if (a) idata()/=a;
+	else {FMprintf("ExtValue","divide",FMLV_CRITICAL,"%d/0",idata()); setInvalid();}
+	}
+		break;
+
+	case TDouble:
+		{
+		double d=src.getDouble();
+		if (d==0.0)
+			{
+			FMprintf("ExtValue","divide",FMLV_CRITICAL,"%s/0.0",(const char*)getString());
+			setInvalid();
+			}
+		else
+			{
+			fpExceptDisable();
+			double tmp=ddata()/d;
+			if (!finite(tmp))
+				{ FMprintf("ExtValue","divide",FMLV_CRITICAL,"overflow %s/%s",(const char*)getString(),(const char*)src.getString()); setInvalid(); }
+			else
+				ddata()=tmp;
+			// niby dobrze ale lepiej byloby to robic bardziej systematycznie a nie tylko w dzieleniu?
+			//if (isnan(ddata())) //http://www.digitalmars.com/d/archives/c++/Traping_divide_by_zero_5728.html
+			//	  { FMprintf("ExtValue","divide",FMLV_ERROR,"not-a-number",(const char*)getString()); setInvalid(); }
+			fpExceptEnable();
+			}
+		}
+		break;
+
+	default:;
+	}
+}
+
+SString ExtValue::format(SString& fmt,const ExtValue **values,int count)
+{
+SString ret;
+// "..........%.........%..........%........"
+//  ^_cur     ^_next
+//  ^^^^^^^^^^___sub
+//
+// "..........%.........%..........%........"
+//            ^-cur     ^-next
+//            ^^^^^^^^^^___sub
+const char* begin=(const char*)fmt, *end=begin+fmt.len(), *curr=begin;
+int type=0;
+
+class Args
+{
+const ExtValue **values;
+int count;
+int arg;
+public:
+Args(const ExtValue **v,int c):values(v),count(c),arg(0) {}
+bool finished() {return arg>=count;}
+const ExtValue *getNext() {const ExtValue *ret=NULL; if ((arg<count)&&values[arg]) ret=values[arg]; arg++; return ret;}
+};
+Args args(values,count);
+
+while(curr<end)
+	{
+	const char* next=strchr(curr,'%');
+	if (!next) next=end; else if ((next==curr)&&(curr>begin))
+		{next=strchr(next+1,'%'); if (!next) next=end;}
+	type=0;
+	if (curr>begin)
+		{
+		type=0;
+		for(const char* t=curr;t<next;t++)
+			switch(*t)
+				{
+				case 'd': case 'x': case 'X': case 'u': case 'p': case 'c': type='d'; t=next; break;
+				case 'f': case 'g': case 'e': type='f'; t=next; break;
+				case 's': type='s'; t=next; break;
+				case 't': case 'T': type=*t; t=next; break;
+				case '%': if (t>begin) {type=*t; t=next;} break;
+				}
+		}
+	if (curr>begin) curr--;
+	const ExtValue *a;
+	if (args.finished() && (type!=0) && (type!='%'))
+		{
+		ret+=fmt.substr(curr-begin);
+		break;
+		}
+	SString sub=fmt.substr(curr-begin,next-curr);
+	switch(type)
+		{
+		case 'd': a=args.getNext(); ret+=SString::sprintf((const char*)sub,a?a->getInt():0); break;
+		case 'f': a=args.getNext(); ret+=SString::sprintf((const char*)sub,a?a->getDouble():0); break;
+		case 's': {a=args.getNext(); SString tmp; if (a) tmp=a->getString(); ret+=SString::sprintf((const char*)sub,(const char*)tmp);} break;
+		case 't': case 'T':
+			{
+			a=args.getNext();
+			time_t ti=a?a->getInt():0;
+			struct tm tm=Convert::localtime(ti);
+			SString timtxt;
+			if (type=='T')
+				timtxt=SString::sprintf("%04d-%02d-%02d %02d:%02d:%02d",1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
+			else
+				timtxt=Convert::asctime(tm).c_str();
+			ret+=timtxt;
+			ret+=sub.substr(2);
+			}
+			break;
+		case '%': ret+='%'; ret+=sub.substr(2); break;
+		case 0: ret+=sub; break;
+		}
+	curr=next+1;
+	}
+return ret;
+}
+
+
+void ExtValue::operator%=(const ExtValue& src)
+{
+switch(type)
+	{
+	case TInt: idata()=idata()%src.getInt(); break;
+	case TDouble: ddata()=fmod(ddata(),src.getDouble()); break;
+
+	case TString:
+	{
+	VectorObject *vec=VectorObject::fromObject(src.getObject());
+	if (vec)
+		sdata()=format(sdata(),(const ExtValue**)&vec->data.getref(0),vec->data.size());
+	else
+		{const ExtValue *ptr=&src; sdata()=ExtValue::format(sdata(),&ptr,1);}
+	}
+	break;
+
+	default:;
+	}
+}
+
+long ExtValue::getInt() const
+{
+switch(type)
+	{
+	case TInt: return idata();
+	case TDouble: return (int)ddata();
+	case TString:
+	{
+	const char* s=(const char*)sdata();
+	if ((s[0]=='0')&&(s[1]=='x'))
+		{
+		long val;
+		sscanf(s+2,"%lx",&val);
+		return val;
+		}
+	else
+		{
+		if (strchr(s,'e')||(strchr(s,'E')))
+			return (long)atof(s);
+		else
+			return atol(s);
+		}
+	}
+	case TObj: return (long)odata().param;
+	default:;
+	}
+return 0;
+}
+double ExtValue::getDouble() const
+{
+switch(type)
+	{
+	case TDouble: return ddata();
+	case TInt: return (double)idata();
+	case TString:
+	{
+	const char* s=(const char*)sdata();
+	if ((s[0]=='0')&&(s[1]=='x'))
+		{
+		long val;
+		sscanf(s+2,"%lx",&val);
+		return val;
+		}
+	else
+		return atof(s);
+	}
+	case TObj: return (double)(long)odata().param;
+	default:;
+	}
+return 0.0;
+}
+SString ExtValue::getString() const
+{
+switch(type)
+	{
+	case TString: return sdata();
+	case TInt: return SString::valueOf(idata());
+	case TDouble: return SString::valueOf(ddata());
+	case TObj: return odata().toString();
+	case TInvalid: 	return SString("invalid");
+	default: return SString("null");
+	}
+}
+
+const SString* ExtValue::getStringPtr() const
+{
+if (type==TString)
+	return &sdata();
+return NULL;
+}
+
+SString ExtValue::serialize() const
+{
+switch(type)
+	{
+	case TString:
+		{
+		SString q=sdata();
+		sstringQuote(q);
+		return SString("\"")+q+SString("\"");
+		}
+	case TInt:
+		return SString::valueOf(idata());
+	case TDouble:
+		return SString::valueOf(ddata());
+	case TObj:
+		return odata().serialize();
+	case TInvalid:
+		return SString("invalid");
+	default:
+		return SString("null");
+	}
+}
+
+//returns the first character after the parsed number or NULL if not a number
+const char* ExtValue::parseNumber(const char* in)
+{
+if (isdigit(*in)||((*in=='-')&&(isdigit(in[1]))))
+	{
+	const char* p=in;
+	if (*p=='-') p++;
+	while(isdigit(*p)) p++;
+	bool fp=false;
+	if ((*p=='.') && isdigit(p[1]))
+		{
+		p++;
+		while(isdigit(*p)) p++;
+		fp=true;
+		}
+	if (((*p=='e')||(*p=='E')) && (isdigit(p[1]) || (((p[1]=='-') || (p[1]=='+')) && isdigit(p[2]))))
+		{
+		p++;
+		if ((*p=='-')||(*p=='+')) p++;
+		while(isdigit(*p)) p++;
+		fp=true;
+		}
+
+	if (fp)
+		{
+		setDouble(atof(in));
+		return p;
+		}
+	else
+		{
+		setInt(atol(in));
+		return p;
+		}
+	}
+return NULL;
+}
+
+PtrListTempl<ParamInterface*> ExtValue::deserializable_classes;
+
+void ExtValue::initDeserializableClasses()
+{
+deserializable_classes+=&Pt3D_Ext::getStaticParam();
+deserializable_classes+=&Orient_Ext::getStaticParam();
+}
+
+ParamInterface *ExtValue::findDeserializableClass(const char* name)
+{
+FOREACH(ParamInterface*,cls,deserializable_classes)
+	if (!strcmp(cls->getName(),name))
+		return cls;
+return NULL;
+}
+
+static const char* skipWord(const char* in)
+{
+while(isalpha(*in)||(*in=='_'))
+	in++;
+return in;
+}
+
+//returns the first character after the parsed portion or NULL if invalid format
+const char* ExtValue::deserialize_inner(const char* in)
+{
+const char* ret=parseNumber(in);
+if (ret)
+	return ret;
+else if (*in=='\"')
+	{
+	ret=skipQuoteString(in+1,NULL);
+	SString s(in+1,ret-(in+1));
+	sstringUnquote(s);
+	setString(s);
+	if (*ret=='\"')
+		return ret+1;
+	else
+		return NULL;
+	}
+else if (*in=='[')
+	{
+	VectorObject *vec=new VectorObject;
+	ExtObject o(&VectorObject::par,vec);
+	tlsGetRef(ExtObject::serialization).add(o);
+	const char* p=in+1;
+	ExtValue tmp;
+	while(*p)
+		{
+		if (*p==']') {p++;break;}
+		ret=tmp.deserialize(p);
+		if (ret)
+			{
+			vec->data+=new ExtValue(tmp);
+			p=ret;
+			if (*p==',') p++;
+			}
+		else
+			{
+			p=NULL;
+			break;
+			}
+		}
+	setObject(o);
+	return p;
+	}
+else if (*in=='{')
+	{
+	DictionaryObject *dic=new DictionaryObject;
+	ExtObject o(&DictionaryObject::par,dic);
+	tlsGetRef(ExtObject::serialization).add(o);
+	const char* p=in+1;
+	ExtValue args[2]/*={value,key}*/, dummy_ret;
+	while(*p)
+		{
+		if (*p=='}') {p++;break;}
+		ret=args[1].deserialize(p);
+		if ((!ret)||(args[1].getType()!=TString)) {p=NULL;break;}
+		p=ret;
+		if (*p!=':') {p=NULL;break;}
+		p++;
+		ret=args[0].deserialize(p);
+		if (!ret) {p=NULL;break;}
+		p=ret;
+		dic->p_set(args,&dummy_ret);
+		if (*p==',') p++;
+		}
+	setObject(o);
+	return p;
+	}
+else if (!strncmp(in,"null",4))
+	{
+	setEmpty();
+	return in+4;
+	}
+else if (!strncmp(in,"invalid",9))
+	{
+	setInvalid();
+	return in+9;
+	}
+else if (*in=='<')
+	{ //unserializable object
+	setInvalid();
+	while(*in)
+		if (*in=='>')
+			return in+1;
+		else in++;
+	return in;
+	}
+else if (*in=='^')
+	{
+	in++;
+	ExtValue ref;
+	ret=ref.parseNumber(in);
+	if (ret && (ref.getType()==TInt))
+		{
+		const ExtObject* o=tlsGetRef(ExtObject::serialization).get(ref.getInt());
+		if (o)
+			{
+			setObject(*o);
+			return ret;
+			}
+		}
+	return NULL;
+	}
+else if ((ret=skipWord(in))&&(ret!=in))
+	{
+	SString clsname(in,ret-in);
+	ExtValue tmp;
+	ret=tmp.deserialize(ret);
+	ParamInterface *cls=findDeserializableClass(clsname);
+	if (cls && (tmp.getType()!=TUnknown) && (tmp.getType()!=TInvalid))
+		{
+		VectorObject *vec=VectorObject::fromObject(tmp.getObject());
+		if (vec)
+			{
+			int m=cls->findId("newFromVector");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		DictionaryObject *dic=DictionaryObject::fromObject(tmp.getObject());
+		if (dic)
+			{
+			int m=cls->findId("newFromDictionary");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		if (tmp.getType()==TString)
+			{
+			int m=cls->findId("newFromString");
+			if (m>=0)
+				{
+				cls->call(m,&tmp,this);
+				tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
+				return ret;
+				}
+			}
+		tlsGetRef(ExtObject::serialization).remove(tmp.getObject());
+		setEmpty();
+		}
+	setEmpty();
+	FMprintf("ExtValue","deserialize",FMLV_WARN,"object of class \"%s\" could not be deserialized",(const char*)clsname);
+	return ret;
+	}
+setEmpty();
+return NULL;
+}
+
+const char* ExtValue::deserialize(const char* in)
+{
+tlsGetRef(ExtObject::serialization).begin();
+const char* ret=deserialize_inner(in);
+tlsGetRef(ExtObject::serialization).end();
+return ret;
+}
+
+ExtObject ExtValue::getObject() const
+{
+if (type==TObj) return odata();
+return ExtObject();
+}
+
+ExtValue ExtValue::getExtType()
+{
+if (getType()!=TObj) return ExtValue((long)getType());
+ExtObject& o=odata();
+return ExtValue(SString(o.isEmpty()?"":o.interfaceName()));
+}
+
+SString SString::valueOf(const ExtValue& v)
+{
+return v.getString();
+}
+SString SString::valueOf(const ExtObject& v)
+{
+return v.toString();
+}
Index: /cpp/frams/util/extvalue.h
===================================================================
--- /cpp/frams/util/extvalue.h	(revision 109)
+++ /cpp/frams/util/extvalue.h	(revision 109)
@@ -0,0 +1,187 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _EXTVALUE_H_
+#define _EXTVALUE_H_
+
+#include "sstring.h"
+#include <frams/param/param.h>
+#include <common/nonstd_stl.h>
+#include <common/threads.h>
+
+#define EXTVALUEUNION
+template <int A,int B> struct CompileTimeMax {enum {val=A>B?A:B}; };
+#define EXTVALUEUNIONSIZE CompileTimeMax<sizeof(ExtObject),sizeof(SString)>::val
+
+enum ExtPType
+{TUnknown=0,TInt,TDouble,TString,TObj,TInvalid};
+
+/**
+   destructable object
+ */
+class DestrBase
+{
+public:
+int refcount;
+DestrBase():refcount(0) {}
+void incref() {refcount++;}
+void decref() {refcount--; if (refcount==0) delete this;}
+virtual ~DestrBase() {}
+};
+
+/**
+   object reference.
+ */
+class ExtObject
+{
+int subtype;                    //< 0/1=Generic/DPC Object,  0/2=Standalone/Shared Param
+void incref() const;
+void decref() const;
+  public:
+union { void* object;           //< generic object, will use param
+DestrBase *dbobject;};  //< object with refcounting, will be deleted if refcount goes to 0
+union { Param* param;           //< if object!=0
+	ParamInterface *paraminterface;}; //< if object==0
+
+void copyFrom(const ExtObject& src)  {subtype=src.subtype;object=src.object;param=src.param;}
+
+void* operator new(size_t s, void* mem) {return mem;}
+#ifdef _MSC_VER
+void operator delete(void* mem,void* t) {}
+#endif
+void* operator new(size_t s) {return malloc(sizeof(ExtObject));}
+void operator delete(void* mem) {free(mem);}
+///@param tmp_param can be used for temporary storage, the result ParamInterface* is only valid for as long as tmp_param is valid
+ParamInterface *getParamInterface(Param &tmp_param) const  {if(subtype&2){tmp_param.setParamTab(param->getParamTab());tmp_param.select(object);return &tmp_param;} return paraminterface;}
+const char* interfaceName() const {if (isEmpty()) return "Empty"; return (subtype&2)?param->getName():paraminterface->getName();}
+bool matchesInterfaceName(ParamInterface* pi) const {return !strcmp(interfaceName(),pi->getName());}
+void* getTarget() const {return (subtype&1)?dbobject:object;}
+void* getTarget(const char* classname, bool through_barrier=true) const;
+void setEmpty() {decref();subtype=0;param=NULL;object=NULL;}
+int isEmpty() const {return !param;}
+ExtObject(const ExtObject& src)      {src.incref();copyFrom(src);}
+void operator=(const ExtObject& src) {src.incref();decref();copyFrom(src);}
+bool makeUnique();//< @return false if nothing has changed
+
+int operator==(const ExtObject& src) const {if (object!=src.object) return 0; return (object==0)?(!strcmp(param->getName(),src.param->getName())):1;}
+
+SString toString() const;
+SString serialize_inner() const;
+SString serialize() const;
+
+ExtObject(Param *p,void *o):subtype(2),object(o),param(p){}
+ExtObject(ParamInterface *p=0):subtype(0),object(0),paraminterface(p){}
+ExtObject(Param *p,DestrBase *o):subtype(1+2),dbobject(o),param(p){incref();}
+ExtObject(ParamInterface *p,DestrBase *o):subtype(1),dbobject(o),paraminterface(p){incref();}
+
+~ExtObject(){decref();}
+
+class Serialization
+{
+std::vector<ExtObject> refs;
+int level;
+  public:
+Serialization():level(0) {}
+void begin();
+void end();
+int add(const ExtObject& o);
+void replace(const ExtObject& o,const ExtObject& other);
+void remove(const ExtObject& o);
+const ExtObject* get(int ref);
+};
+
+static THREAD_LOCAL_DECL(Serialization,serialization);
+};
+
+class ExtValue
+{
+public:
+ExtPType type;
+#ifdef EXTVALUEUNION
+long data[(EXTVALUEUNIONSIZE+sizeof(long)-1)/sizeof(long)];
+long& idata() const {return (long&)data[0];};
+double& ddata() const {return *(double*)data;};
+ExtObject& odata() const {return *(ExtObject*)data;};
+SString& sdata() const {return *(SString*)data;};
+#else
+union {
+long i;
+double d;
+SString *s;
+ExtObject *o;
+};
+long& idata() const {return (long&)i;};
+double& ddata() const {return (double&)d;};
+ExtObject& odata() const {return *o;};
+SString& sdata() const {return *s;};
+#endif
+
+void* operator new(size_t s, void* mem) {return mem;}
+void* operator new(size_t s) {return ::operator new(s);}
+
+ExtValue():type(TUnknown){}
+~ExtValue() {setEmpty();}
+ExtValue(long v) {seti(v);}
+ExtValue(double v) {setd(v);}
+ExtValue(const SString &v) {sets(v);}
+ExtValue(const ExtObject &srco) {seto(srco);}
+long compare(const ExtValue& src) const;
+int operator==(const ExtValue& src) const;
+void operator+=(const ExtValue& src);
+void operator-=(const ExtValue& src);
+void operator*=(const ExtValue& src);
+void operator/=(const ExtValue& src);
+void operator%=(const ExtValue& src);
+void operator=(const ExtValue& src)
+	{setr(src);}
+ExtValue(const ExtValue& src)
+	:type(TUnknown) {set(src);}
+void setEmpty();
+void setInvalid() {setEmpty();type=TInvalid;}
+bool makeUnique() {return (type==TObj) && odata().makeUnique();} //< @return false if nothing has changed
+ExtPType getType() {return type;}
+void *getObjectTarget(const char* classname) {return (type==TObj)?getObject().getTarget(classname):0;}
+void setInt(long v) {if (type!=TInt) setri(v); else idata()=v;}
+void setDouble(double v) {if (type!=TDouble) setrd(v); else ddata()=v;}
+void setString(const SString &v) {if (type!=TString) setrs(v); else sdata()=v;}
+void setObject(const ExtObject &src) {if (type!=TObj) setro(src); else odata()=src;}
+long getInt() const;
+double getDouble() const;
+SString getString() const;
+const SString* getStringPtr() const;//< @return pointer to the internal sstring object or NULL if the current type is not string
+SString serialize() const;
+ExtObject getObject() const;
+const char* parseNumber(const char* in);
+const char* deserialize(const char* in);//< @return first character after the succesfully parsed string or NULL if failed
+const char* deserialize_inner(const char* in);
+static PtrListTempl<ParamInterface*> deserializable_classes;
+static ParamInterface *findDeserializableClass(const char* name);
+static void initDeserializableClasses();
+static SString format(SString& fmt,const ExtValue **values,int count);
+
+ExtValue getExtType();
+
+  private: // setrx - release and set, setx - assume released
+void setr(const ExtValue& src){setEmpty();set(src);}
+void set(const ExtValue& src);
+void setri(long v) {setEmpty();seti(v);}
+void setrd(double v) {setEmpty();setd(v);}
+void seti(long v) {type=TInt;idata()=v;}
+void setd(double v) {type=TDouble;ddata()=v;}
+#ifdef EXTVALUEUNION
+void setrs(const SString &v) {setEmpty();sets(v);}
+void setro(const ExtObject &src) {setEmpty();seto(src);}
+void sets(const SString &v) {type=TString;new(data) SString(v);}
+void seto(const ExtObject &src) {type=TObj;new(data) ExtObject(src);}
+#else
+void setrs(const SString &v) {setEmpty();sets(v);}
+void setro(const ExtObject &src) {setEmpty();seto(src);}
+void sets(const SString &v) {type=TString;s=new SString(v);}
+void seto(const ExtObject &src) {type=TObj;o=new ExtObject(src);}
+#endif
+
+};
+
+
+#endif
Index: /cpp/frams/util/hashtable.cpp
===================================================================
--- /cpp/frams/util/hashtable.cpp	(revision 109)
+++ /cpp/frams/util/hashtable.cpp	(revision 109)
@@ -0,0 +1,195 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "hashtable.h"
+
+int HashTable::hash(const char *s)
+{
+int h=0;
+char c;
+while(c=*(s++))
+    h=31*h+c;
+return h&0x7fffffff;
+}
+
+void HashTable::init(int initsize,float lo)
+{
+size=initsize;
+load=lo;
+threshold=(int)(load*(float)size);
+tab=(HashEntry**)calloc(size,sizeof(HashEntry*));
+count=0;
+sync=0;
+}
+
+void HashTable::rehash(int newsize)
+{
+if (newsize==size) return;
+HashEntry **newtab=(HashEntry**)calloc(newsize,sizeof(HashEntry*));
+HashEntry **te=tab,*e,*ne;
+int i;
+for (int s=size;s>0;s--,te++)
+    for (e=*te;e;)
+	{
+	    ne=e; e=e->next;
+	    i=ne->hash%newsize;
+	    ne->next=newtab[i];
+	    newtab[i]=ne;
+	}
+free(tab);
+tab=newtab;
+size=newsize;
+threshold=int(load*(float)size);
+sync++;
+}
+
+void HashTable::clear()
+{
+HashEntry *e,**te,*next;
+int n;
+for (n=size,te=tab;n>0;n--,te++)
+	for (e=*te;e;e=next)
+		{
+		next=e->next;
+		delete e;
+		}
+if (tab) free(tab);
+tab=0; size=0;
+sync++;
+}
+
+HashTable::~HashTable()
+{
+clear();
+}
+
+void* HashTable::put(const SString& key,void *value)
+{
+int h=hash(key);
+int i=h%size;
+for (HashEntry *e=tab[i];e;e=e->next)
+    {
+    if (e->key==key)
+	{
+	void *v=e->value;
+	e->value=value;
+	return v;
+	}
+    }
+if (count>=threshold) { rehash(2*size+1); i=h%size; }
+HashEntry *e=new HashEntry(h,key,value);
+e->next=tab[i];
+tab[i]=e;
+count++;
+sync++;
+return 0;
+}
+
+void* HashTable::remove(const SString& key)
+{
+int i=hash(key)%size;
+HashEntry **ptr=tab+i,*e;
+for (;e=*ptr;ptr=&e->next)
+    {
+    if (e->key==key)
+	{
+	*ptr=e->next;
+	void *v=e->value;
+	delete e;
+	count--;
+	sync++;
+	return v;
+	}
+    }
+return 0;
+}
+
+void* HashTable::remove(HashEntryIterator& it)
+{
+if (!it.entry) return 0;
+HashEntry **ptr=tab+it.hashindex,*e;
+for (;e=*ptr;ptr=&e->next)
+    {
+    if (e == it.entry)
+	{
+	it++;
+	*ptr=e->next;
+	void *v=e->value;
+	delete e;
+	count--;
+	sync++;
+	it.sync++;
+	return v;
+	}
+    }
+   return NULL; 
+}
+
+void* HashTable::get(const SString& key,int *reallygot)
+{
+int i=hash(key)%size;
+for (HashEntry *e=tab[i];e;e=e->next)
+	if (e->key==key) {if (reallygot) *reallygot=1; return e->value;}
+return 0;
+}
+
+/////////
+
+void HashEntryIterator::findNext()
+{
+while (hashindex < (ht->size-1))
+	{
+	hashindex++;
+	if (entry=ht->tab[hashindex]) return;
+	}
+}
+
+
+void HashEntryIterator::operator++()
+{
+if (entry) entry=entry->next;
+if (!entry) findNext();
+}
+
+//////////////////////////
+
+void HashTable::debugprint()
+{
+printf("HashTable: %d/%d (max %d)\n",count,size,threshold);
+HashEntry *e,**te;
+int n;
+for (n=0,te=tab;n<size;n++,te++)
+	if (e=*te)
+	{
+	printf(" %d:",n);
+	for (;e;e=e->next)
+		printf(" (%x)%s=%p",e->hash,(const char*)e->key,e->value);
+	printf("\n");
+	}
+}
+
+/**
+stats[0]=buckets used
+stats[1]=min keys in bucket
+stats[2]=avg keys in bucket
+stats[3]=max keys in bucket
+ */
+void HashTable::getstats(float *stats)
+{
+HashEntry *e,**te;
+int used=0, ma=0, mi=count;
+int c,n;
+for (n=size,te=tab;n>0;n--,te++)
+	{
+	c=0;
+	for (e=*te;e;e=e->next) c++;
+	if (c>ma) ma=c;
+	if ((c<mi)&&(c>0)) mi=c;
+	if (c>0) used++;
+	}
+stats[0]=(float)used;
+stats[1]=(float)((mi==count)?0:mi);
+stats[2]=(float)count/(float)used;
+stats[3]=(float)ma;
+}
Index: /cpp/frams/util/hashtable.h
===================================================================
--- /cpp/frams/util/hashtable.h	(revision 109)
+++ /cpp/frams/util/hashtable.h	(revision 109)
@@ -0,0 +1,84 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _HASHTABLE_H_
+#define _HASHTABLE_H_
+
+#include "sstring.h"
+
+class HashEntry
+{
+public:
+int hash;
+SString key;
+void *value;
+HashEntry *next;
+
+HashEntry(int h,const SString& k,void *v):hash(h),key(k),value(v),next(0){}
+};
+
+class HashEntryIterator;
+
+class HashTable
+{
+friend class HashEntryIterator;
+HashEntry **tab;
+int size;
+int count;
+int threshold;
+float load;
+long sync;
+
+int hash(const char *s);
+void rehash(int newsize);
+public:
+
+HashTable(int initsize,float lo) {init(initsize,lo);}
+HashTable(int initsize) {init(initsize,0.75);}
+HashTable() {init();}
+~HashTable();
+
+/** always use init() after clear() ! */
+void clear();
+void init(int initsize=11,float lo=0.75);
+
+int getSize() {return count;}
+void* put(const SString& key,void *value);
+void* get(const SString& key, int *reallygot=0);
+void* remove(const SString& key);
+/** can be used inside iteration loop:
+    for(HashEntryIterator it(hashtable);it;) hashtable.remove(it);
+    \note iterator is "iterated" to the next entry when the current one is removed (no "it++"!)
+ */
+void* remove(HashEntryIterator& it);
+
+void debugprint();
+void getstats(float *);
+};
+
+/** for(HashEntryIterator it(hashtable);it;it++)
+      {
+      ... it->value
+      ... it->key
+      }
+ */
+class HashEntryIterator
+{
+void findNext();
+  public:
+const HashTable *ht;
+int hashindex;
+HashEntry *entry;
+int sync;
+  HashEntryIterator(const HashTable&t):ht(&t),hashindex(0),entry(t.tab[0]),sync(ht->sync)
+	{if (!entry) findNext();}
+  HashEntryIterator() {}
+void operator++();
+void operator++(int) {operator++();}
+HashEntry* operator->() {return entry;}
+bool isValid() {return (entry&&(sync==ht->sync))?1:0;}
+};
+
+
+#endif
Index: /cpp/frams/util/list.cpp
===================================================================
--- /cpp/frams/util/list.cpp	(revision 109)
+++ /cpp/frams/util/list.cpp	(revision 109)
@@ -0,0 +1,13 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "list.h"
+//#include "nonstd.h"
+
+#ifdef SLISTSTATS
+SListStats SList::stats;
+#endif
Index: /cpp/frams/util/list.h
===================================================================
--- /cpp/frams/util/list.h	(revision 109)
+++ /cpp/frams/util/list.h	(revision 109)
@@ -0,0 +1,173 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _LIST_H_
+#define _LIST_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include <common/nonstd.h>
+
+//#define SLISTSTATS
+
+#ifdef SLISTSTATS
+#include <stdio.h>
+class SListStats
+{
+  public:
+int objects, allocations, copied, totalmem, usedmem;
+SListStats():objects(0),allocations(0),copied(0),totalmem(0),usedmem() {}
+~SListStats()
+	{
+	printf("------------------------\n"
+	       " SListStats:\n"
+	       " objects     = %ld\n"
+	       " allocations = %ld\n"
+	       " copied      = %ld\n"
+	       " total mem   = %ld\n"
+	       " usage       = %ld %%\n"
+	       "------------------------\n",
+	       objects,allocations,copied,totalmem,(usedmem*100)/totalmem);
+	}
+};
+#endif
+
+
+template<class T> class SListTempl
+{
+protected:
+int have,used,pos; // ile,zaj,poz
+T *mem;
+void resize(int x)
+	{
+	if (mem || x)
+		{
+		mem=(T*)realloc(mem,x*sizeof(T));
+#ifdef SLISTSTATS
+		SListStats::stats.allocations++;
+		SListStats::stats.copied+=sizeof(T)*min(x,have);
+#endif	
+		}
+	have=x;
+	}
+
+public:
+
+SListTempl(const SListTempl<T>& src):have(0),used(0),mem(0),pos(0)
+	{(*this)=src;}
+SListTempl():have(0),used(0),pos(0),mem(0)
+	{}
+~SListTempl()
+	{
+#ifdef SLISTSTATS
+	SListStats::stats.objects++;
+	SListStats::stats.totalmem+=sizeof(T)*have;
+	SListStats::stats.usedmem+=sizeof(T)*used;
+#endif
+	hardclear();
+	}
+
+void needSize(int s)
+	{ if (s>have) resize(s+3+s/8); }
+void operator+=(const T& e) ///< append one element
+ {append(e);}
+void operator-=(const T& e) ///< remove one element
+ {int i; if ((i=find(e))>=0) remove(i);}
+void remove(int i,int n=1) ///< remove n elements from position i
+	{
+	if ((i>=size())||(i<0)) return;
+	if (i>(size()-n)) n=size()-i;
+	if (pos>=i) pos-=n;
+	memmove(mem+i,mem+i+n,sizeof(T)*(used-n-i));
+	used-=n;
+	}
+void operator-=(int i) {remove(i);} ///< remove element from position i
+T& operator()(int i) const ///< return element at position i
+	{return mem[i];}
+bool hasMore() {return pos<size();}
+T& operator()() ///< return next element
+	{ return mem[pos++];}
+int operator==(const SListTempl<T>& l) const ///< compare list
+ {
+ if (size() != l.size()) return 0;
+ for (int i=0;i<size();i++) if (l.mem[i] != mem[i]) return 0;
+ return 1;
+ }
+int find(const T& e) const ///< return position of element, or -1 if not found
+	{ for (int i=0;i<size();i++) if (mem[i]==e) return i;   return -1; }
+int start() {return pos=0;} ///< start iteration throgh list
+void append(const T& data) ///< add 1 element
+	{needSize(size()+1); mem[used++]=data;}
+void append(const T* data,int n) ///< add n elements
+	{
+	needSize(size()+n);
+	memcpy(mem+used,data,sizeof(T)*n);
+	used+=n;
+	}
+void insert(int p,T* data,int n) ///< insert n elements at position p
+	{
+	if (p>size()) p=size();
+	needSize(size()+n);
+	memmove(mem+p+n,mem+p,sizeof(T)*(size()-p));
+	memcpy(mem+p,data,sizeof(T)*n);
+	if (pos>p) pos+=n;
+	}
+void insert(int p,const T& data) ///< insert 1 element at position p
+	{
+	if (p>size()) p=size();
+	needSize(size()+1);
+	memmove(mem+p+1,mem+p,sizeof(T)*(size()-p));
+	if (pos>p) pos++;
+	mem[p]=data;
+	used++;
+	}
+void set(int p,const T& d)
+	{ needSize(p+1); mem[p]=d; if (used<(p+1)) used=p+1;}
+void setSize(int s)
+	{ needSize(s); used=s;}
+T& get(int i) const {return operator()(i);}
+T& get() {return operator()();}
+void clear() {used=0;} ///< remove all elements
+void hardclear() {resize(0); used=0;} ///< remove all elements and free mem
+int size() const {return used;} ///< list size 
+int operator!() {return used;} ///< list size
+void operator=(const SListTempl<T>&src) ///duplicate
+ {
+ setSize(src.size());
+ memcpy(mem, src.mem, src.size()*sizeof(T));
+ }
+void operator+=(const SListTempl<T>&src) ///< append src contents
+ {
+ needSize(size()+src.size());
+ memcpy(mem+used, src.mem, src.size()*sizeof(T));
+ used+=src.used;
+ }
+void trim(int newsiz)
+	{if (newsiz<used) used=newsiz;}
+};
+
+#define FOREACH(type,var,list) for(type var=(type)list.start();var=(type)list();)
+
+// usage example: FOREACH(char*,t,thelist) printf("t=%s\n",t);
+
+template<class T> class PtrListTempl: public SListTempl<T>
+{
+   public:
+T operator()() ///< return next element
+     {if (this->pos>=this->size()) return 0; else return 
+this->mem[this->pos++];}
+
+T operator()(int i) const ///< return element at position i
+     {if (i>=this->size()) return 0; else return this->mem[i];}
+
+T get(int i) const {return operator()(i);}
+T get() {return operator()();}
+T& getref(int i) const {return SListTempl<T>::get(i);}
+T& getref() {return SListTempl<T>::get();}
+};
+
+
+typedef PtrListTempl<void*> SList;
+
+#endif
Index: /cpp/frams/util/multimap.cpp
===================================================================
--- /cpp/frams/util/multimap.cpp	(revision 109)
+++ /cpp/frams/util/multimap.cpp	(revision 109)
@@ -0,0 +1,330 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "multimap.h"
+#include <stdio.h>
+
+int MultiMap::getBegin() const
+{
+if (isEmpty()) return 0;
+return getBegin(0);
+}
+
+int MultiMap::getEnd() const
+{
+if (isEmpty()) return -1;
+return getBegin(mappingCount()-1)-1;
+}
+
+int MultiMap::findData(int x) const
+{
+if (isEmpty()) return -1;
+if (getBegin(0)>x) return -1;
+int a,b,c;
+int n=mappingCount()-1;
+a=0; b=n; // find c = last range with begin<=x
+while(1){
+	c=(a+b+1)/2;
+	if (getBegin(c)<=x)
+		{
+		if ((c==n)||(getBegin(c+1)>x)) return c;
+		a=c;
+		}
+	else
+		{
+		b=c-1;
+		}
+	}
+}
+
+int MultiMap::addRange(int &r,const MultiRange& mr)
+{
+// precondition: 0 <= r < (mappingCount()-1)
+// 1.maybe change mapping in this range
+int ret=0;
+SingleMapping *sm=getMapping(r);
+if (sm->to.contains(mr)) return 0; // do nothing
+sm->to.add(mr);
+// after adding mr to this mapping it could be the same as the previous/next one
+if (r>0) // prev exists
+	{
+	SingleMapping *prev=getMapping(r-1);
+	if (prev->to == sm->to)
+		{ // splice with prev
+		removeData(r);
+		delete sm;
+		sm=prev;
+		r--;
+		ret--;
+		}
+	}
+if (r<(mappingCount()-2)) // next exists
+	{
+	SingleMapping *next=getMapping(r+1);
+	if (next->to == sm->to)
+		{ // splice with next
+		removeData(r+1);
+		delete next;
+		ret--;
+		r--;
+		}
+	}
+return ret;
+}
+
+int MultiMap::addRangeBeginEnd(int &r,int begin,int end,const MultiRange& mr)
+{
+// precondition: -1 <= r < mappingCount()
+//               begin <= end
+//               begin >= mapping.begin
+//               end < nextmapping.begin
+SingleMapping *m,*m2,*m1;
+if (r<0)
+	{
+	if (mappingCount())
+		{
+		m=getMapping(0);
+		if (end==(m->begin-1))
+			{ // adjacent to m
+			if (m->to==mr)
+				{ // shift only
+				m->begin=begin;
+				return 0;
+				}
+			// single add
+			SingleMapping *newmap=new SingleMapping(begin,mr);
+			addData(0,newmap);
+			r=0;
+			return 1;
+			}
+		}
+	// double add
+	SingleMapping *newmap=new SingleMapping(begin,mr);
+	SingleMapping *newmap2=new SingleMapping(end+1);
+	addData(0,newmap);
+	addData(1,newmap2);
+	r=1;
+	return 2;
+	}
+
+if (r==(mappingCount()-1))
+	{
+	m=getMapping(r);
+	m1=getMapping(r-1);
+	if (begin==m->begin)
+		{ // adjacent
+		if (m1->to==mr)
+			{ // shift only
+			m->begin=end+1;
+			return 0;
+			}
+		// single add
+		m->begin=end+1;
+		SingleMapping *newmap=new SingleMapping(begin,mr);
+		addData(r,newmap);
+		return 1;
+		}
+	// double add
+	SingleMapping *newmap=new SingleMapping(begin,mr);
+	SingleMapping *newmap2=new SingleMapping(end+1);
+	addData(r+1,newmap);
+	addData(r+2,newmap2);
+	r+=2;
+	return 2;
+	}
+
+m=getMapping(r);
+if (m->to.contains(mr)) return 0;
+if (begin==m->begin) // begin at range boundary
+	{
+	m2=getMapping(r+1);
+	if (end==(m2->begin-1)) return addRange(r,mr);
+
+	SingleMapping *newmap=new SingleMapping(begin,m->to);
+	newmap->to.add(mr);
+	if (r>0)
+		{ // join prev possible...
+		m1=getMapping(r-1);
+		if (m1->to==newmap->to)
+			{ // joint prev = shift
+			delete newmap;
+			m->begin=end+1;
+			return 0;
+			}
+		}
+	// single add:
+	m->begin=end+1;
+	addData(r,newmap);
+	return 1;
+	}
+
+m2=getMapping(r+1);
+if (end==(m2->begin-1)) // end at range boundary
+	{
+	SingleMapping *newmap=new SingleMapping(begin,m->to);
+	newmap->to.add(mr);
+	if (r<(mappingCount()-2))
+		{ // join next possible...
+		if (m2->to==newmap->to)
+			{ // joint next = shift
+			m2->begin=begin;
+			delete newmap;
+			return 0;
+			}
+		}
+	// single add
+	r++;
+	addData(r,newmap);
+	return 1;
+	}
+// double add:
+SingleMapping *newmap=new SingleMapping(begin,m->to);
+newmap->to.add(mr);
+SingleMapping *newmap2=new SingleMapping(end+1,m->to);
+addData(r+1,newmap);
+addData(r+2,newmap2);
+r+=2;
+return 2;
+}
+
+void MultiMap::add(int begin,int end,const MultiRange& to)
+{
+if (to.isEmpty()) return;
+if (end<begin) return;
+int r1=findData(begin);
+int r2=findData(end);
+if (r1==r2)
+	{
+	addRangeBeginEnd(r1,begin,end,to);
+	}
+else
+	{
+	r2+=addRangeBeginEnd(r1,begin,getBegin(r1+1)-1,to);
+	r1++;
+	for (;r1<r2;r1++)
+		r2+=addRange(r1,to);
+	addRangeBeginEnd(r1,getBegin(r1),end,to);
+	}
+}
+
+const MultiRange& MultiMap::map(int x) const
+{
+static MultiRange empty;
+int m=findData(x);
+if (m<0) return empty;
+SingleMapping *sm=getMapping(m);
+return sm->to;
+}
+
+MultiRange MultiMap::map(int begin,int end) const
+{
+MultiRange mr;
+int m=findData(begin);
+for (;m<rangeCount();m++)
+	if (m>=0)
+	{
+	SingleMapping *sm=getMapping(m);
+	if (sm->begin > end) break;
+	mr.add(sm->to);
+	}
+return mr;
+}
+
+MultiRange MultiMap::map(const MultiRange& ranges) const
+{
+MultiRange mr;
+for(int i=0;i<ranges.rangeCount();i++)
+	mr.add(map(ranges.getRange(i)));
+return mr;
+}
+
+IRange MultiMap::getRange(int i) const
+{
+if ((i<0)||(i>(data.size()-1))) return IRange();
+return IRange(getBegin(i),getBegin(i+1)-1);
+}
+
+void MultiMap::operator=(const MultiMap& mm)
+{
+clear();
+for(int i=0;i<mm.mappingCount();i++)
+	addData(i,new SingleMapping(*mm.getMapping(i)));
+}
+
+void MultiMap::addCombined(const MultiMap& m1,const MultiMap& m2)
+{
+for(int i=0;i<m1.rangeCount();i++)
+	{
+	IRange r=m1.getRange(i);
+	add(r,m2.map(m1.getMapping(i)->to));
+	}
+}
+
+void MultiMap::add(const MultiRange& from,const MultiRange& to)
+{
+for(int i=0;i<from.rangeCount();i++)
+	add(from.getRange(i),to);
+}
+
+void MultiMap::addReversed(const MultiMap& m)
+{
+for(int i=0;i<m.rangeCount();i++)
+	{
+	IRange r=m.getRange(i);
+	const MultiRange& mr=m.getMapping(i)->to;
+	for (int j=0;j<mr.rangeCount();j++)
+		add(mr.getRange(j),r);
+	}
+}
+
+MultiMap::~MultiMap()
+{ clear();}
+
+void MultiMap::clear()
+{
+for(int i=0;i<data.size();i++)
+	delete getData(i);
+data.clear();
+}
+
+///////////////////
+
+void MultiMap::print() const
+{
+printf("{ ");
+for (int i=0;i<(mappingCount()-1);i++)
+	{
+	if (i) printf("  ");
+	SingleMapping *sm=getMapping(i);
+	SingleMapping *sm2=getMapping(i+1);
+	if (sm2->begin==sm->begin+1)
+		printf("[%d] -> ",sm->begin);
+	else
+		printf("[%d-%d] -> ",sm->begin,sm2->begin-1);
+	sm->to.print();
+	printf("\n");
+	}
+printf("}\n");
+}
+
+void MultiMap::print2() const
+{
+int y;
+int id=0,id2;
+if (isEmpty())
+	{
+	printf("{ empty }\n");
+	return;
+	}
+printf("{\n");
+for (y=getBegin();y<=getEnd();y++)
+	{
+	id2=findMappingId(y+1);
+	printf("%2d %c",y,(id2!=id)?'_':' ');
+	id=id2;
+	map(y).print2();
+	printf("\n");
+	}
+printf("}\n");
+}
Index: /cpp/frams/util/multimap.h
===================================================================
--- /cpp/frams/util/multimap.h	(revision 109)
+++ /cpp/frams/util/multimap.h	(revision 109)
@@ -0,0 +1,76 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _MULTIMAP_H_
+#define _MULTIMAP_H_
+
+#include "multirange.h"
+
+class SingleMapping
+{
+  public:
+SingleMapping(int b,const MultiRange& t):begin(b),to(t) {}
+SingleMapping(int b):begin(b) {}
+SingleMapping(const SingleMapping& sm):begin(sm.begin),to(sm.to) {}
+int begin;
+MultiRange to;
+};
+
+/** MultiMap - used for conversion mapping.
+    see @ref convmap
+  */
+class MultiMap
+{
+/** list of (SingleMapping*) */
+SList data;
+
+SingleMapping* getData(int i) const {return (SingleMapping*)data(i);}
+void addData(int i,SingleMapping* mapping) {data.insert(i,(void*)mapping);}
+void removeData(int i) {data.remove(i);}
+int findData(int x) const;
+int getBegin(int i) const {return getData(i)->begin;}
+
+// addRangeXXX return the shift for range numbers > r
+int addRange(int &r,const MultiRange& mr);
+// value of 'r' is adjusted according to its range number change
+int addRangeBeginEnd(int &r,int begin,int end,const MultiRange& mr);
+
+  public:
+MultiMap() {}
+MultiMap(const MultiMap& mm) {operator=(mm);}
+~MultiMap();
+void operator=(const MultiMap& mm);
+
+void clear();
+int isEmpty() const {return data.size()==0;}
+int mappingCount() const {return data.size();}
+SingleMapping* getMapping(int i) const {return (SingleMapping*)getData(i);}
+int findMappingId(int x) const {return findData(x);}
+int rangeCount() const {return isEmpty()?0:data.size()-1;}
+IRange getRange(int i) const;
+
+int getBegin() const;
+int getEnd() const;
+
+void add(const IRange& from,const IRange& to) {add(from.begin, from.end, MultiRange(to));}
+void add(const IRange& from,const MultiRange& to) {add(from.begin, from.end, to);}
+void add(int from1,int from2,int to1,int to2) {add(from1,from2,MultiRange(to1,to2));}
+void add(int from1,int from2,const MultiRange& to);
+void add(const MultiRange& from,const MultiRange& to);
+void add(const MultiMap& mm);
+void addCombined(const MultiMap& m1,const MultiMap& m2);
+void addReversed(const MultiMap& mm);
+
+const MultiRange& map(int x) const;
+MultiRange map(int begin,int end) const;
+MultiRange map(const IRange& range) const {return map(range.begin, range.end);}
+MultiRange map(const MultiRange& ranges) const;
+
+void print() const;
+void print2() const;
+};
+
+#endif
+
+
Index: /cpp/frams/util/multirange.cpp
===================================================================
--- /cpp/frams/util/multirange.cpp	(revision 109)
+++ /cpp/frams/util/multirange.cpp	(revision 109)
@@ -0,0 +1,317 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "multirange.h"
+#include <common/nonstd_stl.h>
+
+#include <stdio.h>
+
+int IRange::operator==(const IRange& r)
+{
+if (isEmpty()&&r.isEmpty()) return 1;
+return (begin==r.begin)&&(end==r.end);
+}
+
+void IRange::intersect(const IRange& r)
+{
+if (r.begin>begin) begin=r.begin;
+if (r.end<end) end=r.end;
+}
+void IRange::add(const IRange& r)
+{
+if (r.begin<begin) begin=r.begin;
+if (r.end>end) end=r.end;
+}
+
+void IRange::print() const
+{
+if (begin==end) printf("[%d]",begin);
+else printf("[%d-%d]",begin,end);
+}
+
+////////////////////////////////////////
+
+void MultiRange::print() const
+{
+printf("[");
+for (int i=0;i<data.size();i+=2)
+	{
+	int b=getData(i);
+	int e=getData(i+1);
+	if (i) printf(",");
+	if (b==e) printf("%d",b);
+	else printf("%d-%d",b,e);
+	}
+printf("]");
+}
+
+void MultiRange::print2() const
+{
+const IRange& r=singleRange();
+for(int i=0;i<=r.end;i++)
+	printf(contains(i)?"X":".");
+}
+
+void MultiRange::shift(int delta)
+{
+for(int i=0;i<data.size();i++)
+	data.get(i)+=delta;
+}
+
+int MultiRange::findRange(int x) const
+{
+if (isEmpty()) return -1;
+if (getData(0)>x) return -1;
+int a,b,c;
+int n=rangeCount()-1;
+a=0; b=n; // find c = last range with begin<=x
+while(1){
+	c=(a+b+1)/2;
+	if (getBegin(c)<=x)
+		{
+		if ((c==n)||(getBegin(c+1)>x)) return c;
+		a=c;
+		}
+	else
+		{
+		b=c-1;
+		}
+	}
+}
+
+void MultiRange::addRange(int i,int b,int e)
+{
+data.insert(2*i,b);
+data.insert(2*i+1,e);
+}
+
+void MultiRange::removeRanges(int r1,int r2)
+{
+//data.remove(2*r1,(r2-r1)*2+2);
+for (;r2>=r1;r2--)
+	removeRange(r2);
+}
+
+void MultiRange::removeRange(int i)
+{
+data.remove(2*i,2);
+}
+
+void MultiRange::clear()
+{data.clear();}
+
+int MultiRange::isEmpty() const
+{
+return rangeCount()==0;
+}
+
+IRange MultiRange::singleRange() const
+{
+if (isEmpty()) return IRange();
+int b=getData(0);
+int e=getData(data.size()-1);
+return IRange(b,e);
+}
+
+int MultiRange::rangeCount() const
+{
+return data.size()/2;
+}
+
+IRange MultiRange::getRange(int i) const
+{
+if ((i<0)||(i>=rangeCount())) return IRange();
+return IRange(getData(2*i),getData(2*i+1));
+}
+
+void MultiRange::remove(int begin, int end)
+{
+if (end<begin) return; // NOP
+int count=rangeCount();
+if (!count) return;
+int r1=findRange(begin);
+int r2=findRange(end);
+if (r2<0) return; // NOP
+if (r1==r2)
+	{
+	int e=getEnd(r1);
+	int b=getBegin(r1);
+	if (begin<=e)
+		{
+		if (end>=e)
+			{
+			if (begin>b)
+				setEnd(r1,begin-1);
+			else
+				removeRange(r1);
+			}
+		else
+			{
+			if (begin>b)
+				{ //split
+				addRange(r1+1,end+1,e);
+				setEnd(r1,begin-1);
+				}
+			else
+				{
+				setBegin(r1,end+1);
+				}
+			}
+		}
+	}
+else
+	{
+	if (r1>=0)
+		{
+		int e1=getEnd(r1);
+		int b1=getBegin(r1);
+		if (begin<=e1)
+			{
+			if (begin==b1)
+				{
+				removeRange(r1);
+				r2--;
+				r1--;
+				}
+			else
+				{
+				setEnd(r1,begin-1);
+				}
+			}
+		}
+	int e2=getEnd(r2);
+	if (end<e2)
+		{
+		setBegin(r2,end+1);
+		removeRanges(r1+1,r2-1);
+		}
+	else
+		{
+		removeRanges(r1+1,r2);
+		}
+	}
+}
+
+void MultiRange::add(int begin, int end)
+{
+if (end<begin) return; // NOP
+int count=rangeCount();
+int last=count-1;
+int r1=findRange(begin);
+int r2=findRange(end);
+if (r2<0) // special case: before first range
+	{
+	if (count&&(getData(0)==(end+1)))
+		setData(0,begin);
+	else
+		addRange(0,begin,end);
+	return;
+	}
+if (r1<0) // special case: begin is before first range
+	{
+	setData(0,begin);
+	r1=0;
+	}
+// now r1>=0 and r2>=0
+int joinbegin=(begin<=(getEnd(r1)+1));
+int joinend=(r2<last)&&(end>=(getBegin(r2+1)-1));
+const int SAME=1;
+const int JOINBEGIN=2;
+const int JOINEND=4;
+int action=((r1==r2)?SAME:0)+(joinbegin?JOINBEGIN:0)+(joinend?JOINEND:0);
+switch(action)
+	{
+	case SAME+JOINBEGIN+JOINEND: // remove 1 range
+		setEnd(r1,getEnd(r1+1));
+		removeRange(r1+1);
+		break;
+	case SAME+JOINBEGIN: // extend 1 range
+		setEnd(r1,max(getEnd(r2),end));
+		break;
+	case SAME+JOINEND: // extend 1 range
+		setBegin(r1+1,begin);
+		break;
+	case SAME: // add 1 range
+		addRange(r1+1,begin,end);
+		break;
+
+	case JOINBEGIN+JOINEND: // extend r2+1
+		setBegin(r2+1,getBegin(r1));
+		removeRanges(r1,r2);
+		break;
+	case JOINBEGIN: // extend r1
+		setEnd(r1,max(end,getEnd(r2)));
+		removeRanges(r1+1,r2);
+		break;
+	case JOINEND: // extend r2+1
+		setBegin(r2+1,begin);
+		removeRanges(r1+1,r2);
+		break;
+	case 0: // extend r2
+		setBegin(r2,begin);
+		setEnd(r2,max(end,getEnd(r2)));
+		removeRanges(r1+1,r2-1);
+		break;
+	}
+}
+
+void MultiRange::remove(const MultiRange &mr)
+{
+for(int i=0;i<mr.rangeCount();i++)
+	{
+	IRange r=mr.getRange(i);
+	remove(r);
+	}
+}
+
+void MultiRange::add(const MultiRange &mr)
+{
+for(int i=0;i<mr.rangeCount();i++)
+	{
+	IRange r=mr.getRange(i);
+	add(r);
+	}
+}
+
+void MultiRange::intersect(const MultiRange &mr)
+{
+// = this - (1-mr)
+MultiRange full(singleRange());
+full.remove(mr);
+remove(full);
+}
+
+int MultiRange::contains(const MultiRange &mr) const
+{
+for(int i=0;i<mr.rangeCount();i++)
+	{
+	IRange r=mr.getRange(i);
+	if (!contains(r)) return 0;
+	}
+return 1;
+}
+
+int MultiRange::contains(int x) const
+{
+int r=findRange(x);
+if (r<0) return 0;
+return getEnd(r)>=x;
+}
+
+int MultiRange::contains(const IRange& r) const
+{
+if (r.isEmpty()) return 0;
+int r1=findRange(r.begin);
+if (r1<0) return 0;
+return getRange(r1).contains(r);
+}
+
+void MultiRange::intersect(int begin, int end)
+{
+if (isEmpty()) return;
+if (end<begin) {clear(); return;}
+IRange sr=singleRange();
+remove(sr.begin, begin-1);
+remove(end+1, sr.end);
+}
Index: /cpp/frams/util/multirange.h
===================================================================
--- /cpp/frams/util/multirange.h	(revision 109)
+++ /cpp/frams/util/multirange.h	(revision 109)
@@ -0,0 +1,104 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _MULTIRANGE_H_
+#define _MULTIRANGE_H_
+
+#include <frams/util/list.h>
+
+/**
+   range in integer domain.
+   - (a,a) contains single integer: a
+   - (a,b) contains [b-a+1] integers
+ */
+class IRange
+{
+  public:
+int begin,end;
+IRange():begin(0),end(0) {}
+IRange(int b,int e):begin(b),end(e) {}
+IRange(const IRange& r):begin(r.begin),end(r.end) {}
+int size() const {return end-begin+1;}
+int isEmpty() const {return end<begin;}
+void makeEmpty() {end=begin;}
+void set(int b,int e) {begin=b; end=e;}
+void intersect(const IRange& r);
+void add(const IRange& r);
+/** all empty ranges are equal */
+int operator==(const IRange& r);
+void operator=(const IRange& r) {begin=r.begin; end=r.end;}
+int contains(int x) {return (x>=begin)&&(x<=end);}
+int contains(const IRange& r) {return !r.isEmpty()&&contains(r.begin)&&contains(r.end);}
+void print() const;
+};
+
+/** set of ranges = multiple selection. used in conversion mapping. see @ref convmap */
+class MultiRange
+{
+/** subsequent ranges in array, stored as: {begin1,end1,begin2,end2,...}
+    there are data.size()/2 ranges.
+    all ranges are sorted by 'begin' value.
+    ranges cannot intersect.
+ */
+SListTempl<int> data;
+int getData(int i) const {return (int)data(i);}
+void setData(int i,int x) {data.set(i,x);}
+int getBegin(int i) const {return getData(2*i);}
+int getEnd(int i) const {return getData(2*i+1);}
+void setBegin(int i,int x) {setData(2*i,x);}
+void setEnd(int i,int x) {setData(2*i+1,x);}
+/** find the last range with begin<=x
+ @return -1 if not found */
+int findRange(int x) const;
+void addRange(int i,int b,int e);
+void removeRange(int i);
+void removeRanges(int r1,int r2);
+
+  public:
+MultiRange() {}
+MultiRange(const MultiRange &mr) {data=mr.data;}
+void operator=(const MultiRange &mr) {data=mr.data;}
+MultiRange(const IRange &r) {add(r);}
+MultiRange(int begin,int end) {add(begin,end);}
+MultiRange(int x) {add(x);}
+
+int operator==(const MultiRange &mr) const {return data==mr.data;}
+
+void clear();
+int isEmpty() const;
+
+IRange singleRange() const;
+int rangeCount() const;
+IRange getRange(int i) const;
+
+void add(int x) {add(x,x);}
+void add(const IRange& r) {add(r.begin,r.end);}
+void add(int begin, int end);
+void add(const MultiRange &mr);
+
+void remove(int x) {remove(x,x);}
+void remove(const IRange& r) {remove(r.begin,r.end);}
+void remove(int begin, int end);
+void remove(const MultiRange &mr);
+
+int contains(int x) const;
+int contains(const IRange& r) const;
+int contains(const MultiRange &mr) const;
+
+void intersect(const IRange& r) {intersect(r.begin,r.end);}
+void intersect(int begin, int end);
+void intersect(const MultiRange &mr);
+
+void shift(int delta);
+
+void print() const;
+void print2() const;
+};
+
+#endif
+
+
+
+
+
Index: /cpp/frams/util/rndutil.cpp
===================================================================
--- /cpp/frams/util/rndutil.cpp	(revision 109)
+++ /cpp/frams/util/rndutil.cpp	(revision 109)
@@ -0,0 +1,43 @@
+#include "rndutil.h"
+#include <common/nonstd_math.h>
+#include <stdlib.h>
+
+unsigned short pseudornd(short x)
+{
+static long seed=0;
+long y;
+if (x<=0) {seed=-x; return 0;}
+seed=(y=(3677*seed+3680)&0x7fffffff)-1;
+return (unsigned short)(((unsigned short)y)%(x));//rzutowanie y->unsigned short to pewnie blad bo zmniejsza wartosc ktorej sie potem robi modulo, ale pseudornd sluzy chyba tylko do generowania randomowych world map? i modulo i tak jest tam bardzo male, lepiej niczego nie zmieniac bo po co maja pliki z ustawieniami zmienic swoje przypadkowe znaczenie
+}
+
+double CustomRnd(double *tab)
+{
+double *range=tab+1+2*randomN((int)(0.5+tab[0]));
+return range[0]+rnd0N(range[1]-range[0]);
+};
+
+double RandomGener::Uni(double begin, double end)
+{
+return begin+rnd01*(end-begin);
+}
+
+double RandomGener::GaussStd()
+{
+if (isNextGauss) {isNextGauss=0; return nextGauss;}
+double v1,v2,s;
+do {
+v1=2*rnd01-1; //-1..1
+v2=2*rnd01-1; //-1..1
+s=v1*v1+v2*v2;
+} while (s>=1);
+double mult=sqrt(-2*log(s)/s);
+nextGauss=v2*mult;
+isNextGauss=1;
+return v1*mult;
+}
+
+double RandomGener::Gauss(double m,double s)
+{return m+s*GaussStd();}
+
+RandomGener RndGen;
Index: /cpp/frams/util/rndutil.h
===================================================================
--- /cpp/frams/util/rndutil.h	(revision 109)
+++ /cpp/frams/util/rndutil.h	(revision 109)
@@ -0,0 +1,35 @@
+#ifndef _RNDUTIL_H_
+#define _RNDUTIL_H_
+
+#include <common/nonstd.h>
+#include "math.h"
+
+/** @file rndutil.h various functions */
+
+/** @param x change seed if x<=0
+    @return random value [0..x-1] if x>0 */
+unsigned short pseudornd(short x);
+
+double CustomRnd(double*);
+// the parameter is a table which describes the random distribution:
+// e.g.
+// double x[]={ 3,     // 3 intervals
+//  -10,-1,            // (each of them has always uniform distribution and equal probability of being selected)
+//  -1, 1,
+//   1,10, };
+
+class RandomGener
+{
+   public:
+      RandomGener() {isNextGauss=0;}
+      static double Uni(double begin, double end); ///< uniform excluding 'end' boundary
+      double GaussStd();
+      double Gauss(double m,double s); ///< usually will not return further than 5*stdd
+   private:
+      int isNextGauss;
+      double nextGauss;
+};
+
+extern RandomGener RndGen;
+
+#endif
Index: /cpp/frams/util/sstring.cpp
===================================================================
--- /cpp/frams/util/sstring.cpp	(revision 109)
+++ /cpp/frams/util/sstring.cpp	(revision 109)
@@ -0,0 +1,433 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "sstring.h"
+
+#ifdef SSTRING_SIMPLE
+
+// simple sstring implementation using direct character arrays
+// - duplicate = copy all characters
+// - no mutex needed
+
+#include "sstring-simple.cpp"
+
+#else
+///////////////////////////////////////////////////////////////////////////
+// old sstring implementation using SBuf references
+// - duplicate = copy buffer pointer
+// - mutex required to be thread safe
+
+#include <common/nonstd_stl.h>
+#include "extvalue.h"
+#include <assert.h>
+
+#ifdef MULTITHREADED
+#include <pthread.h>
+static pthread_mutex_t sstring_ref_lock=PTHREAD_MUTEX_INITIALIZER;
+#define REF_LOCK pthread_mutex_lock(&sstring_ref_lock);
+#define REF_UNLOCK pthread_mutex_unlock(&sstring_ref_lock)
+#else
+#define REF_LOCK 
+#define REF_UNLOCK 
+#endif
+
+static int guessMemSize(int request)
+{
+return request+min(request/2,10000)+8;
+}
+
+SBuf::SBuf()
+{
+txt="";
+size=used=0;
+refcount=1;
+}
+
+SBuf::SBuf(int initsize)
+{
+size=guessMemSize(initsize);
+if (size>0)	{ txt=(char*)malloc(size+1); txt[0]=0; }
+	else	txt="";
+used=0;
+refcount=1;
+}
+
+SBuf::~SBuf()
+{
+freeBuf();
+}
+
+void SBuf::initEmpty()
+{
+txt="";
+used=size=0;
+refcount=1;
+}
+
+void SBuf::freeBuf()
+{
+if (!size) return;
+free(txt); used=0;
+}
+
+void SBuf::copyFrom(const char *ch,int chlen)
+{
+if (chlen==-1) chlen=strlen(ch);
+if (chlen>0)
+{
+if (chlen<size)
+	{
+	memcpy(txt,ch,chlen);
+	}
+else
+	{
+	size=guessMemSize(chlen);
+	char *newtxt=(char*)malloc(size+1);
+	memcpy(newtxt,ch,chlen);
+	free(txt);
+	txt=newtxt;
+	}
+}
+txt[chlen]=0;
+used=chlen;
+}
+
+void SBuf::append(const char *ch,int chlen)
+{ // doesn't check anything!
+memcpy(txt+used,ch,chlen);
+used+=chlen;
+txt[used]=0;
+}
+
+void SBuf::ensureSize(int needed)
+{
+if (size>=needed) return;
+needed=guessMemSize(needed);
+txt=(char*)realloc(txt,needed+1);
+size=needed;
+}
+
+/////////////////////////////////////////////
+
+SString::SString()
+{
+initEmpty();
+}
+
+SString::~SString()
+{
+REF_LOCK;
+detach();
+REF_UNLOCK;
+}
+
+SString::SString(int x)
+{
+buf=new SBuf(x);
+}
+
+SString::SString(const char *t,int t_len)
+{
+initEmpty();
+if (!t) return;
+copyFrom(t,t_len);
+}
+
+SString::SString(const SString &from)
+{
+if (from.buf==&SBuf::empty())
+	buf=&SBuf::empty();
+else
+	{
+	REF_LOCK;
+	buf=from.buf;
+	if (buf->size)
+		buf->refcount++;
+	REF_UNLOCK;
+	}
+}
+
+void SString::initEmpty()
+{
+buf=&SBuf::empty();
+}
+
+void SString::memoryHint(int howbig)
+{
+detachCopy(howbig);
+}
+	
+void SString::detachEmpty(int ensuresize)
+{
+if (buf==&SBuf::empty()) { buf=new SBuf(ensuresize); return; }
+if (buf->refcount<2) buf->ensureSize(ensuresize);
+else
+	{
+	buf->refcount--;
+	buf=new SBuf(ensuresize);
+	}
+}
+
+void SString::detach()
+{
+if (buf==&SBuf::empty()) return;
+if (!--buf->refcount) delete buf;
+}
+
+void SString::detachCopy(int ensuresize)
+{
+if (buf==&SBuf::empty()) { buf=new SBuf(ensuresize); return; }
+if (buf->refcount<2)
+	{
+	buf->ensureSize(ensuresize);
+	return;
+	}
+buf->refcount--;
+SBuf *newbuf=new SBuf(ensuresize);
+newbuf->copyFrom(buf->txt,min(ensuresize,buf->used));
+buf=newbuf;
+}
+
+char *SString::directWrite(int ensuresize)
+{
+if (ensuresize<0) ensuresize=len();
+REF_LOCK;
+detachCopy(ensuresize);
+REF_UNLOCK;
+appending=buf->used;
+return buf->txt;
+}
+
+/*
+char *SString::directWrite()
+{
+return directWrite(buf->used);
+}
+*/
+char *SString::directAppend(int maxappend)
+{
+REF_LOCK;
+detachCopy(buf->used+maxappend);
+REF_UNLOCK;
+appending=buf->used;
+return buf->txt+appending;
+}
+
+void SString::endWrite(int newlength)
+{
+if (newlength<0) newlength=strlen(buf->txt);
+else buf->txt[newlength]=0;
+buf->used=newlength;
+}
+
+void SString::endAppend(int newappend)
+{
+if (newappend<0) newappend=strlen(buf->txt+appending);
+else buf->txt[appending+newappend]=0;
+buf->used=appending+newappend;
+}
+
+////////////// append /////////////////
+
+void SString::operator+=(const char *s)
+{
+if (!s) return;
+int x=strlen(s);
+if (!x) return;
+append(s,x);
+}
+
+void SString::append(const char *txt,int count)
+{
+if (!count) return;
+REF_LOCK;
+detachCopy(buf->used+count);
+REF_UNLOCK;
+buf->append(txt,count);
+}
+
+void SString::operator+=(const SString&s)
+{
+append((const char*)s,s.len());
+}
+
+SString SString::operator+(const SString& s) const
+{
+SString ret(*this);
+ret+=s;
+return ret;
+}
+
+/////////////////////////////
+
+void SString::copyFrom(const char *ch,int chlen)
+{
+if (!ch) chlen=0;
+else if (chlen<0) chlen=strlen(ch);
+REF_LOCK;
+detachEmpty(chlen);
+REF_UNLOCK;
+memcpy(buf->txt,ch,chlen);
+buf->txt[chlen]=0; buf->used=chlen;
+}
+
+void SString::operator=(const char *ch)
+{
+copyFrom(ch);
+}
+
+void SString::operator=(const SString&s)
+{
+if (s.buf==buf) return;
+REF_LOCK;
+detach();
+buf=s.buf;
+if (buf->size) buf->refcount++;
+REF_UNLOCK;
+}
+///////////////////////////////////////
+
+SString SString::substr(int begin, int length) const
+{
+if (begin<0) { length+=begin; begin=0; }
+if (length>=(len()-begin)) length=len()-begin;
+if (length<=0) return SString();
+if (length==len()) return *this;
+return SString((*this)(begin),length);
+}
+
+///////////////////////////////////////
+
+int SString::equals(const SString& s) const
+{
+if (s.buf==buf) return 1;
+return !strcmp(buf->txt,s.buf->txt);
+}
+
+///////////////////////////////////////
+
+int SString::indexOf(int character,int start) const
+{
+const char *found=strchr(buf->txt+start,character);
+return found?found-buf->txt:-1;
+}
+
+int SString::indexOf(const char *substring,int start) const
+{
+char *found=strstr(buf->txt+start,substring);
+return found?found-buf->txt:-1;
+}
+
+int SString::indexOf(const SString & substring,int start) const
+{
+char *found=strstr(buf->txt+start,((const char*)substring));
+return found?found-buf->txt:-1;
+}
+
+int SString::getNextToken (int& pos,SString &token,char separator) const
+{
+if (pos>=len()) {token=0;return 0;}
+int p1=pos,p2;
+const char *t1=buf->txt+pos;
+const char *t2=strchr(t1,separator);
+if (t2) pos=(p2=(t2-buf->txt))+1; else p2=pos=len();
+strncpy(token.directWrite(p2-p1),t1,p2-p1);
+token.endWrite(p2-p1);
+return 1;
+}
+
+int SString::startsWith(const char *pattern) const
+{
+const char *t=(const char*)(*this);
+for (;*pattern;pattern++,t++)
+	if (*t != *pattern) return 0;
+return 1;
+}
+
+SString SString::valueOf(int i)
+{
+return SString::sprintf("%d",i);
+}
+SString SString::valueOf(long i)
+{
+return SString::sprintf("%d",i);
+}
+SString SString::valueOf(double d)
+{
+SString tmp=SString::sprintf("%.15g",d);
+if ((!strchr(tmp,'.'))&&(!strchr(tmp,'e'))) tmp+=".0";
+return tmp;
+}
+SString SString::valueOf(const SString& s)
+{
+return s;
+}
+
+#ifdef LINUX
+#define VSNPRINTF_RETURNS_REQUIRED_SIZE
+#endif
+#if defined _WIN32 && !defined __BORLANDC__
+#define USE_VSCPRINTF
+#endif
+
+
+#if 0 //testing _vscprintf
+#define USE_VSCPRINTF
+int _vscprintf(const char *format,va_list argptr)
+{
+return vsnprintf("",0,format,argptr);
+}
+#endif
+
+SString SString::sprintf(const char* format, ...)
+{
+int n, size = 30;
+va_list ap;
+
+SString ret;
+
+#ifdef USE_VSCPRINTF
+va_start(ap, format);
+size=_vscprintf(format, ap);
+va_end(ap);
+#endif
+
+while (1)
+	{
+	char* p=ret.directWrite(size);
+	assert(p!=NULL);
+	size=ret.directMaxLen()+1;
+	/* Try to print in the allocated space. */
+	va_start(ap, format);
+	n = vsnprintf(p, size, format, ap);
+	va_end(ap);
+	/* If that worked, return the string. */
+	if (n > -1 && n < size)
+		{
+		ret.endWrite(n);
+		return ret;
+		}
+	/* Else try again with more space. */
+#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
+	if (n > -1)    /* glibc 2.1 */
+		size = n; /* precisely what is needed */
+	else           /* glibc 2.0 */
+#endif
+		size *= 2;  /* twice the old size */
+	}
+}
+
+SString &SString::empty()
+{
+static SString empty;
+return empty;
+}
+
+SBuf &SBuf::empty()
+{
+static SBuf empty;
+return empty;
+}
+
+#endif //#ifdef SSTRING_SIMPLE
Index: /cpp/frams/util/sstring.h
===================================================================
--- /cpp/frams/util/sstring.h	(revision 109)
+++ /cpp/frams/util/sstring.h	(revision 109)
@@ -0,0 +1,190 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _SSTRING_H_
+#define _SSTRING_H_
+
+//#define SSTRING_SIMPLE
+
+#ifdef SSTRING_SIMPLE
+
+// simple sstring implementation using direct character arrays
+// - duplicate = copy all characters
+// - no mutex needed
+
+#include "sstring-simple.h"
+
+#else
+///////////////////////////////////////////////////////////////////////////
+// old sstring implementation using SBuf references
+// - duplicate = copy buffer pointer
+// - mutex required to be thread safe
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+class ExtValue;  //this include would result in recurrent inclusion: #include "extvalue.h"
+class ExtObject;
+
+class SBuf
+{
+char *txt;
+int used;	///< data size
+int size;	///< buffer size, not including \0, special case: 0==buffer not allocated
+int refcount;	///< buffer is used by 'refcount' objects.
+void initEmpty();
+void ensureSize(int wantsize);
+void copyFrom(const char* ch, int chlen=-1);
+void freeBuf();
+void append(const char* ch, int chlen=-1);
+static SBuf &empty();
+SBuf(int initsize);
+friend class SString;
+SBuf(const SBuf& b) {}
+public:
+SBuf();
+~SBuf();
+};
+
+/// (not so) simple text string class
+
+class SString
+{
+private:
+SBuf *buf;	///< buffer
+int appending;  ///< append mode, changes can occur after character # 'appending'
+//int memhint;
+
+void initEmpty();
+int guessMemSize(int request);
+void copyFrom(SString &from); ///< copy from SString, reference if possible
+void detach(); ///< detach from shared buffer, if any
+void detachEmpty(int ensuresize=0); ///< detach and make empty
+void detachCopy(int ensuresize=0); ///< detach and make private copy
+
+public:
+SString(); ///< make an empty string
+SString(const char*t,int t_len=-1); ///< make a string from char*
+SString(int x); ///< string with initial buffer size
+SString(const SString& from); ///< duplicate string
+~SString();
+
+void copyFrom(const char* ch, int chlen=-1); ///< copy string, length of -1 == unknown
+
+void* operator new(size_t s, void* mem) {return mem;}
+#ifdef _MSC_VER
+void operator delete(void* mem, void* t) {}
+#endif
+void* operator new(size_t s) {return malloc(sizeof(SString));}
+void operator delete(void* mem) {free(mem);}
+
+int len() const {return buf->used;} ///< get string length
+void shrink(); ///< free unnecessary buffer
+
+/// after this call, you can modify sstring directly.
+/// returned value is the pointer to the internal buffer.
+/// <B>ensuresize</B> is minimal value of bytes you need,
+/// the buffer will be resized as needed.
+/// all "direct" operations have to leave the buffer with trailing '\0'
+/// at the end. endWrite() will search for this value in order to determine
+/// new string length.
+/// <P>Sample:<CODE>
+/// SString t;
+/// sprintf(t.directWrite(50),"a=%d,b=%f",a,b);
+/// t.endWrite();</CODE>
+char *directWrite(int ensuresize=-1);
+//char *directWrite();
+/// like directWrite, but it returns the pointer to the first char after current string
+/// for easy appending. <B>maxappend</B> is minimum of character in buffer
+/// that can be appended after this call.
+/// <P>Sample:<CODE>
+/// SString t;
+/// sprintf(t.directAppend(10),"c=%d",c);
+/// t.endAppend();</CODE>
+char *directAppend(int maxappend=0);
+/// update string length, after directWrite.
+/// you don't have to to call endWrite after directWrite if the string's length doesn't change.
+/// optional <B>newlength</B> parameter gives a chance to further optimize
+/// this operation if you know exact length of resulting string.
+/// <P>Sample:<CODE>
+/// SString t("samplestring");
+/// strncpy(t.directWrite(50),src,bytecount);
+/// t.endWrite(bytecount);</CODE>
+void endWrite(int newlength=-1);
+/// update string length, after directAppend.
+/// you will usually need to call endAppend (or endWrite) after directAppend,
+/// because the purpose of directAppend is to change string's length.
+/// optional <B>appendlength</B> parameter gives a chance to further optimize
+/// this operation if you know exact length of the appended string.
+/// <P>Sample:<CODE>
+/// SString t("samplestring");
+/// strncpy(t.directAppend(50),src,bytecount);
+/// t.endAppend(bytecount);</CODE>
+void endAppend(int appendlength=-1);
+/// argument is the amount of memory, that will be probably used
+/// by this string instance. string can use this value
+/// to optimize memory allocation (bigger chunks will be allocated).
+void memoryHint(int howbig);
+int directMaxLen() {return buf->size;} ///< when called after directWrite: max number of characters allowed (can be more than requested)
+
+/// find a character in SString.
+/// return index if the character was found or -1 otherwise.
+int indexOf(int character,int start=0) const;
+
+/// find a substring.
+/// return index if the substring was found or -1 otherwise.
+int indexOf(const char *substring,int start=0) const;
+
+/// find a substring.
+/// return index if the substring was found or -1 otherwise.
+int indexOf(const SString & substring,int start=0) const;
+
+operator const char*() const {return buf->txt;} ///< get SString's readonly buffer
+//operator char*() {detachCopy(len()); return buf->txt;} ///< get SString's writable buffer
+void operator=(const char*t); ///< assign from const char*
+//void operator=(int x) {free(txt);nowy(x);} ///< clear string and make new empty one
+void operator=(const SString &s);
+
+void append(const char *txt,int count);
+SString operator+(const SString &s) const;
+void operator+=(int x); ///< append x spaces after current string
+void operator+=(const char*); ///< append char* contents
+void operator+=(const SString&); ///< append other SString
+
+int equals(const SString &s) const; ///< TRUE if equal
+int operator==(const SString &s) const {return equals(s);} ///< TRUE if equal
+int operator!=(const SString &s) const {return !equals(s);}
+const char* operator()(int p) const {return buf->txt+p;} ///< pointer to p'th character in SString
+char operator[](int i) const {return buf->txt[i];} ///< get char like in array
+
+/// return a substring of the current string
+SString substr(int begin, int length=1<<30) const;
+
+/// simple tokenization:
+/// starting at <B>pos</B>, get next substring delimited by <B>separator</B> character
+/// and put it in output parameter <B>token</B>.
+/// <B>pos</B> is moved accordingly.
+/// returns <B>false</B> if no more tokens are available or <B>true</B> otherwise.
+int getNextToken(int& pos,SString &token,char separator) const;
+
+void operator+=(char ch) {directAppend(1)[0]=ch;endAppend(1);} ///< append single character
+
+int startsWith(const char *pattern) const;
+char charAt(int pos) const {return buf->txt[pos];}
+
+static SString valueOf(int);
+static SString valueOf(long);
+static SString valueOf(double);
+static SString valueOf(const SString&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
+static SString valueOf(const ExtValue&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
+static SString valueOf(const ExtObject&); //tylko do kompletu zeby mozna uzyc tej funkcji nie martwiac sie o typ argumentu
+static SString sprintf(const char* format, ...);
+
+static SString &empty();
+};
+
+#endif //#ifdef SSTRING_SIMPLE
+
+#endif
Index: /cpp/frams/util/sstringutils.cpp
===================================================================
--- /cpp/frams/util/sstringutils.cpp	(revision 109)
+++ /cpp/frams/util/sstringutils.cpp	(revision 109)
@@ -0,0 +1,289 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "sstringutils.h"
+#include <frams/virtfile/virtfile.h>
+#include <common/framsg.h>
+#include <common/nonstd.h>
+
+int loadSString(const char* filename,SString& s,const char* framsgmodule,const char* error)
+{
+VirtFILE *f;
+int ret=0;
+if (f=Vfopen(filename,FOPEN_READ_BINARY))
+	{
+	loadSString(f,s);
+	ret=1;
+	fclose(f);
+	}
+else if (framsgmodule)
+	FMprintf(framsgmodule,"loadSString",FMLV_WARN,error?error:"can't open file \"%s\"",filename);
+return ret;
+}
+
+void loadSString(VirtFILE *f,SString& s)
+{
+char buf[1024];
+int len;
+while(!f->Veof())
+	{
+	len=fread(buf,1,sizeof(buf),f);
+	s.append(buf,len);
+	}
+removeCR(s);
+}
+
+//load single line, discarding any \r or \n found at the end, return false if nothing could be loaded (error or eof)
+bool loadSStringLine(VirtFILE* f,SString& s)
+{
+char buf[100];
+bool eolfound=false;
+bool ret=false;
+s=SString::empty();
+while(!eolfound)
+	{
+	char *r=fgets(buf,sizeof(buf),f);
+	if (r==NULL) break;
+	ret=true;
+	int d=strlen(r);
+	if (d>0)
+		{
+		if (r[d-1]=='\n') {d--; eolfound=true;}
+		if (d>0) if (r[d-1]=='\r') d--;
+		s+=SString(r,d);
+		}
+	}
+return ret;
+}
+
+//////////////////////////
+
+/** "x~xx~xxx" -> "x\~xx\~xxx"  */
+int quoteTilde(SString &target)
+{
+const char* x=target;
+SString tmp;
+char *f;
+while(1)
+	{
+	f=strchr((char*)x,'~');
+	if (f)
+		{
+		tmp.append(x,f-x);
+		tmp+="\\~";
+		x=f+1;
+		}
+	else
+		{
+		if (tmp.len()==0) return 0; // nothing was changed!
+		tmp+=x;
+		target=tmp;
+		return 1;
+		}
+	}
+}
+
+/** "x\~xx\~xxx" -> "x~xx~xxx"  */
+int unquoteTilde(SString &target)
+{
+const char* x=target;
+SString tmp;
+char *f;
+while(1)
+	{
+	f=strchr((char*)x,'\\');
+	if (f)
+		{
+		tmp.append(x,f-x);
+		if (f[1]=='~')
+			{
+			tmp+='~';
+			x=f+2;
+			}
+		else
+			{
+			tmp+="\\";
+			x=f+1;
+			}
+		}
+	else
+		{
+		if (tmp.len()==0) return 0; // nothing was changed!
+		tmp+=x;
+		target=tmp;
+		return 1;
+		}
+	}
+}
+
+/////////////////
+
+bool strContainsOneOf(const char* str,const char* chars)
+{
+while(*str)
+	{
+	if (strchr(chars,*str)) return 1;
+	str++;
+	}
+return 0;
+}
+
+//////////////
+
+bool sstringQuote(SString& target)
+{
+const char* x=target;
+bool changed=0;
+SString tmp;
+tmp.memoryHint(target.len());
+while(*x)
+	{
+	switch(*x)
+		{
+		case '\n': tmp+="\\n"; changed=1; break;
+		case '\r': tmp+="\\r"; changed=1; break;
+		case '\t': tmp+="\\t"; changed=1; break;
+		case '\"': tmp+="\\\""; changed=1; break;
+		case '\\': tmp+="\\\\"; changed=1; break;
+		default: tmp+=*x;
+		}
+	x++;
+	}
+if (changed) target=tmp;
+return changed;
+}
+
+const char* skipQuoteString(const char* txt, const char* limit)
+{
+while(*txt)
+	{
+	if (*txt=='\"') return txt;
+	if (*txt=='\\') txt++;
+	txt++;
+	if (txt==limit) break;
+	}
+return txt;
+}
+
+int sstringUnquote(SString &target)
+{
+const char* x=target;
+SString tmp;
+char *f;
+while(1)
+	{
+	f=strchr((char*)x,'\\');
+	if (f)
+		{
+		tmp.append(x,f-x);
+		switch(f[1])
+			{
+			case 'n': tmp+='\n'; break;
+			case 'r': tmp+='\r'; break;
+			case 't': tmp+='\t'; break;
+			case '\"': tmp+='\"'; break;
+			default: tmp+=f[1];
+			}
+		x=f+2;
+		}
+	else
+		{
+		if (tmp.len()==0) return 0; // nothing was changed!
+		tmp+=x;
+		target=tmp;
+		return 1;
+		}
+	}
+}
+
+int strFindField(const SString& txt,const SString& name,int &end)
+{
+const char* t=txt,*n;
+int pos=0;
+while(1)
+	{
+	n=strchr(t+pos,',');
+	if ((!strncmp(t+pos,name,name.len()))&&(t[pos+name.len()]=='='))
+		{
+		if (n) end=n-t; else end=txt.len();
+		return pos;
+		}
+	if (n) pos=n-t+1; else break;
+	}
+return -1;
+}
+
+SString strGetField(const SString& txt,const SString& name)
+{
+int p,e;
+p=strFindField(txt,name,e);
+if (p<0) return SString();
+p+=name.len()+1;
+return SString(txt.substr(p,e-p));
+}
+
+void strSetField(SString& txt,const SString& name,const SString& value)
+{
+int p,e;
+p=strFindField(txt,name,e);
+if (p<0)
+	{
+	if (!value.len()) return;
+	char *t=txt.directAppend(1+name.len()+value.len());
+	char *b=t;
+	if (txt.len()) *(t++)=',';
+	strcpy(t,name); t+=name.len();
+	*(t++)='=';
+	strcpy(t,value); t+=value.len();
+	txt.endAppend(t-b);
+	}
+else
+	{
+	if (!value.len())
+		{
+		if (p>0) p--; else if (e<txt.len()) e++;
+		char *t=txt.directWrite(0);
+		memmove(t+p,t+e,txt.len()-e);
+		txt.endWrite(txt.len()+value.len()-(e-p));
+		}
+	else
+		{
+		p+=name.len()+1;
+		char *t=txt.directWrite(txt.len()+value.len()-(e-p));
+		memmove(t+p+value.len(),t+e,txt.len()-e);
+		memmove(t+p,value,value.len());
+		txt.endWrite(txt.len()+value.len()-(e-p));
+		}
+	}
+}
+
+SString trim(SString& s)
+{
+const unsigned char*b=(const unsigned char*)(const char*)s;
+const unsigned char*e=b+s.len();
+while((b<e)&&(*b<=' ')) b++;
+while((b<e)&&(e[-1]<=' ')) e--;
+if ((e-b)==s.len()) return s;
+SString newstring;
+char* t=newstring.directWrite(e-b);
+memmove(t,b,e-b);
+newstring.endWrite(e-b);
+return newstring;
+}
+
+bool removeCR(SString& s)
+{
+const char* p=(const char*)s;
+const char* cr=strchr(p,'\r');
+if (!cr) return false;
+char* begin=s.directWrite();
+char* src=begin+(cr-p),*dst=src;
+while(*src)
+	if (*src=='\r')
+		src++;
+	else
+		*(dst++)=*(src++);
+s.endWrite(dst-begin);
+return true;
+}
Index: /cpp/frams/util/sstringutils.h
===================================================================
--- /cpp/frams/util/sstringutils.h	(revision 109)
+++ /cpp/frams/util/sstringutils.h	(revision 109)
@@ -0,0 +1,32 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _SSTRINGUTILS_H_
+#define _SSTRINGUTILS_H_
+
+#include "sstring.h"
+#include <frams/virtfile/virtfile.h>
+
+/// return: 1=ok 0=error
+int loadSString(const char* filename,SString& s,const char* framsgmodule=0,const char* error=0);
+void loadSString(VirtFILE *f,SString& s);
+bool loadSStringLine(VirtFILE* f,SString &s);
+
+int quoteTilde(SString &target);
+int unquoteTilde(SString &target);
+
+bool strContainsOneOf(const char* str,const char* chars);
+bool sstringQuote(SString& target);
+const char* skipQuoteString(const char* txt, const char* limit);
+int sstringUnquote(SString &target);
+
+int strFindField(const SString& txt,const SString& name,int &end);
+SString strGetField(const SString& txt,const SString& name);
+void strSetField(SString& txt,const SString& name,const SString& value);
+
+SString trim(SString& s); //remove leading/trailing whitespace
+bool removeCR(SString& s); //remove '\r' return true if changed
+
+#endif
+
Index: /cpp/frams/util/statrick.h
===================================================================
--- /cpp/frams/util/statrick.h	(revision 109)
+++ /cpp/frams/util/statrick.h	(revision 109)
@@ -0,0 +1,117 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _STATRICK_H_
+#define _STATRICK_H_
+
+/**
+ @file
+ CALLING METHODS WITH "STATIC TRICK" (="STATRICK")
+
+ each trick-callable method has the static counterpart defined by the STATRICKDEF macro
+
+\code
+ static void Class::method_statrick(Class* instance,otherparams)
+    { instance->method(otherparams); }
+
+------ class.h -----------
+ class Class
+ {
+ #define STATRICKCLASS Class
+ STATRICKDEF(method_x)
+ STATRICKDEF1(method_y,argtype1)
+ STATRICKDEF2(method_z,argtype1,argtype2)
+ STATRICKIMPL(method_a)
+   {
+   ...implementation...
+   }
+ STATRICKIMPL1(method_b,argtype1)
+   {
+   ...implementation...
+   }
+ STATRICKIMPL2(method_c,argtype1,argtype2)
+   {
+   ...implementation...
+   }
+ #undef STATRICKCLASS
+ }
+------ class.cpp ---------
+ Class::method_x()
+  {
+  ...implementation
+  }
+ Class::method_y(type1 arg1)
+  {
+  ...implementation
+  }
+ Class::method_z(type1 arg1,type2 arg2)
+  {
+  ...implementation
+  }
+--------------------------
+\endcode
+
+ usage:
+
+\code 
+ void* methodptr=STATRICKPTR(Class::method);
+ STATRICKCALL(method,anyobject);
+\endcode
+
+ */
+
+#define STATRICKNAME(name) (name ## _statrick)
+#define STATRICKCALL(method,anyobject) (*method(anyobject))
+#define STATRICKCALL1(method,anyobject,a) (*method(anyobject,a))
+#define STATRICKCALL2(method,anyobject,a,b) (*method(anyobject,a,b))
+
+#define STATRICKSTUB(cls,method)				\
+static void method ## _statrick (cls * instance)	\
+{ instance -> method (); }
+
+#define STATRICKSTUB1(cls,method,type1) \
+static void method ## _statrick (cls * instance, type1 arg1)	\
+{ instance -> method (arg1); }
+
+#define STATRICKSTUB2(cls,method,type1,type2)							\
+static void method ## _statrick (cls * instance, type1 arg1, type2 arg2) \
+{ instance -> method (arg1,arg2); }
+
+#define STATRICKRSTUB(cls,ret,method)				\
+static ret method ## _statrick (cls * instance)	\
+{ return instance -> method (); }
+
+#define STATRICKRSTUB1(cls,ret,method,type1) \
+static ret method ## _statrick (cls * instance, type1 arg1)	\
+{ return instance -> method (arg1); }
+
+#define STATRICKRSTUB2(cls,ret,method,type1,type2)							\
+static ret method ## _statrick (cls * instance, type1 arg1, type2 arg2) \
+{ return instance -> method (arg1,arg2); }
+
+#define STATRICKDEF(method)			\
+ STATRICKSTUB(STATRICKCLASS,method)		 \
+void method ()
+
+#define STATRICKRDEF(ret,method)	\
+ STATRICKRSTUB(STATRICKCLASS,ret,method)		 \
+ret method ()
+
+#define STATRICKDEF1(method,type1)	\
+ STATRICKSTUB1(STATRICKCLASS,method,type1)             \
+void method (type1 arg1)
+
+#define STATRICKRDEF1(ret,method,type1)	\
+ STATRICKRSTUB1(STATRICKCLASS,ret,method,type1)             \
+ret method (type1 arg1)
+
+#define STATRICKDEF2(method,type1,type2)	\
+STATRICKSTUB2(STATRICKCLASS,method,type1,type2)                \
+void method (type1 arg1,type2 arg2)
+
+#define STATRICKRDEF2(ret,method,type1,type2)	\
+STATRICKRSTUB2(STATRICKCLASS,ret,method,type1,type2)                \
+ret method (type1 arg1,type2 arg2)
+
+#endif
Index: /cpp/frams/util/usertags.h
===================================================================
--- /cpp/frams/util/usertags.h	(revision 109)
+++ /cpp/frams/util/usertags.h	(revision 109)
@@ -0,0 +1,71 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _USERTAGS_H_
+#define _USERTAGS_H_
+
+/**
+  UserTags is the fast way to associate custom data with general purpose objects.
+  Objects don't have to know about their users and we can add more users later without recompilation.
+  For example: MechObject is general physical object in MechaStick engine.
+  In Framsticks simulator every MechObject is connected with Model object, MechParts and MechJoints are
+  connected with their counterparts in the Model as well, but the MechObject doesn't even know about it.
+  If the SSG visualisation is used, the same MechObject is also connected with SSGFramObject.
+  UserTags class is implemented as array, therefore the access is very fast but we have to define
+  maximum possible number of tags for every object:
+
+  UserTags<anyclass,datatype,maximumtags> object;
+  (all UserTags object for a given 'anyclass' will share registered id's)
+
+  usage:
+    1.allocate private id:
+       int id=object->userdata.newID();
+    'id' is shared across all objects of that class (in this example)
+    and therefore you need to allocate it only once.
+    0 is illegal here and means that we run out of id values.
+       
+    2.now you can use id to access your private data inside UserTags:
+       object->userdata[id]=...;
+       ...=object->userdata[id];
+       ...
+    3.you can also assign/access object->userdata (which is a shortcut for 
+    object->userdata[0]). no id here, so in that case it is your responsibility
+    to avoid conflicts.
+       object->userdata=...;
+       ...=object->userdata;
+       ...
+
+    4.free your id when finished:
+       object->userdata.freeID(id);
+ */
+
+template<class ID,class T,int N> class UserTags
+{
+static char reg[N];
+T data[N];
+  public:
+UserTags() {memset(data,0,sizeof(data));}
+
+/** allocate new id */
+static int newID() 
+	{ for(int i=1;i<N;i++) if (!reg[i]) {reg[i]=1; return i;}
+	DB(printf("Warning: UserTags run out of ids!\n"));
+	  return 0; }
+static void freeID(int id) 
+	{ reg[id]=0; }
+T& operator[](int id)
+	{
+	DB(if (!id) printf("Warning: UserTags @ %p is using id=0\n",this);)
+	return data[id]; 
+	}
+operator T() { return data[0]; }
+void operator=(T x) { data[0]=x; }
+};
+
+#endif
+
+
+
+
+
Index: /cpp/frams/virtfile/stdiofile-autoinit.cpp
===================================================================
--- /cpp/frams/virtfile/stdiofile-autoinit.cpp	(revision 109)
+++ /cpp/frams/virtfile/stdiofile-autoinit.cpp	(revision 109)
@@ -0,0 +1,20 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "stdiofile.h"
+
+static StdioFileSystem filesys;
+
+class StdioFile_autoinit
+{
+public:
+StdioFile_autoinit()
+	{
+   //StdioFILE::setStdio();
+	VirtFILE::selectFileSystem(&filesys);
+	}
+};
+
+
+static StdioFile_autoinit ai;
Index: /cpp/frams/virtfile/stdiofile.cpp
===================================================================
--- /cpp/frams/virtfile/stdiofile.cpp	(revision 109)
+++ /cpp/frams/virtfile/stdiofile.cpp	(revision 109)
@@ -0,0 +1,45 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2014  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "stdiofile.h"
+#include <common/nonstd_dir.h>
+#include <common/nonstd_stdio.h>
+#include <common/framsg.h>
+
+VirtFILE* StdioFileSystem::Vfopen(const char* path,const char*mode)
+{
+	//printFM("Vfopen %s %s",path,mode);
+	FILE *f=fopen(path,mode);
+	//printFM("%p",f);
+	if (f) return new StdioFILE(f,path); else return 0;
+}
+
+VirtDIR* StdioFileSystem::Vopendir(const char* path)
+{
+	//printFM("Vopendir %s",path);
+	DIR *d=opendir(path);
+	//printFM("%p",d);
+	if (d) return new StdioDIR(d); else return 0;
+}
+
+int StdioFileSystem::Vfexists(const char* path)
+{
+	return fileExists(path);
+}
+
+void StdioFILE::setStdio()
+{
+	static StdioFILEDontClose si(stdin);
+	static StdioFILEDontClose so(stdout);
+	static StdioFILEDontClose se(stderr);
+	setVstdin(&si);
+	setVstdout(&so);
+	setVstderr(&se);
+}
+
+dirent* StdioDIR::Vreaddir()
+{
+	//printFM("Vreaddir %s",dir);
+	return readdir(dir);
+}
Index: /cpp/frams/virtfile/stdiofile.h
===================================================================
--- /cpp/frams/virtfile/stdiofile.h	(revision 109)
+++ /cpp/frams/virtfile/stdiofile.h	(revision 109)
@@ -0,0 +1,63 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _STDIOFILE_H_
+#define _STDIOFILE_H_
+
+#include "virtfile.h"
+#include <frams/util/sstring.h>
+#include <stdio.h>
+#include <common/nonstd_dir.h>
+
+class StdioFileSystem: public VirtFileSystem
+{
+public:
+VirtFILE *Vfopen(const char* path,const char*mode);
+int Vfexists(const char* path);
+VirtDIR *Vopendir(const char* path);
+};
+
+class StdioFILE: public VirtFILE
+{
+  protected:
+FILE *file;
+SString path;
+  public:
+StdioFILE(FILE *f) {file=f;}
+StdioFILE(FILE *f,const SString& p) {file=f;path=p;}
+static void setStdio();
+int Vread(void *ptr, size_t size, size_t nmemb) {return fread(ptr,size,nmemb,file);}
+int Vwrite(const void *ptr, size_t size, size_t nmemb) {return fwrite(ptr,size,nmemb,file);}
+int Veof() {return feof(file);}
+int Vputc(int c) {return fputc(c,file);}
+int Vputs(const char *s) {return fputs(s,file);}
+int Vgetc() {return fgetc(file);}
+char *Vgets(char *s, int size) {return fgets(s,size,file);}
+int Vprintf(const char *format, va_list args) { return vfprintf(file,format,args); }
+int Vseek(long offset, int whence) {return fseek(file,offset,whence);}
+int Vtell() {return ftell(file);}
+void Vrewind() {rewind(file);}
+int Vflush() {return fflush(file);}
+const char* VgetPath() {return path;}
+
+~StdioFILE() {if (file) fclose(file);}
+};
+
+class StdioDIR: public VirtDIR
+{
+DIR *dir;
+  public:
+StdioDIR(DIR* d):dir(d) {}
+~StdioDIR() {if (dir) closedir(dir);}
+dirent* Vreaddir();
+};
+
+class StdioFILEDontClose: public StdioFILE
+{
+  public:
+StdioFILEDontClose(FILE *f):StdioFILE(f) {}
+~StdioFILEDontClose() {file=0;}
+};
+
+#endif
Index: /cpp/frams/virtfile/stringfile.cpp
===================================================================
--- /cpp/frams/virtfile/stringfile.cpp	(revision 109)
+++ /cpp/frams/virtfile/stringfile.cpp	(revision 109)
@@ -0,0 +1,83 @@
+#include "stringfile.h"
+#include <stdio.h>
+#include <errno.h> //EINVAL
+
+int StringFILE::Vread(void *ptr, size_t size, size_t nmemb)
+{
+int have=str.len()-pos;
+if (have<=0) return 0;
+int need=size*nmemb;
+if (need>have) {nmemb=have/size; need=size*nmemb;}
+memcpy(ptr,((const char*)str)+pos,need);
+pos+=need;
+return nmemb;
+}
+
+int StringFILE::Vgetc()
+{
+if (pos >= str.len()) //...i znowu byl bug roku! :O
+	return EOF;
+else
+	return str[pos++];
+}
+
+char *StringFILE::Vgets(char *s, int size)
+{
+int have=str.len()-pos;
+if (have<=0) return 0;
+if (size<0) size=0;
+if (have>size) have=size-1;
+const char* src=((const char*)str)+pos;
+char *dest=s;
+while(have-- > 0)
+	{
+	*(dest++) = *(src++); pos++;
+	if (dest[-1]=='\n') break;
+	}
+*dest=0;
+return s;
+}
+
+int StringFILE::Vprintf(const char *format, va_list args)
+{
+char *buf=str.directAppend(10000);
+vsnprintf(buf,10000,format,args);
+int n=strlen(buf); // todo: strlen moze wyjsc poza siebie jezeli printf nie da 0S
+str.endAppend(n);
+return n;
+}
+
+int StringFILE::Vseek(long offset, int whence)
+{
+switch(whence)
+	{
+	case SEEK_SET: pos=offset; break;
+	case SEEK_CUR: pos+=offset; break;
+	case SEEK_END: pos=str.len()-offset; break;
+	default: return EINVAL;
+	}
+if (pos < 0) pos=0; else if (pos>str.len()) pos=str.len();
+return 0;
+}
+
+const char StringFileSystem::PREFIX[]="string://";
+
+bool StringFileSystem::isStringPath(const char* path)
+{
+return !strncmp(path,PREFIX,sizeof(PREFIX)-1);
+}
+
+VirtFILE *StringFileSystem::Vfopen(const char* path,const char*mode)
+{
+if ((*mode=='r') && isStringPath(path))
+	{
+	return new StringFILE2(SString(path+sizeof(PREFIX)-1));
+	}
+return chain->Vfopen(path,mode);
+}
+
+int StringFileSystem::Vfexists(const char* path)
+{ return chain->Vfexists(path); }
+
+VirtDIR *StringFileSystem::Vopendir(const char* path)
+{ return chain->Vopendir(path); }
Index: /cpp/frams/virtfile/stringfile.h
===================================================================
--- /cpp/frams/virtfile/stringfile.h	(revision 109)
+++ /cpp/frams/virtfile/stringfile.h	(revision 109)
@@ -0,0 +1,50 @@
+#ifndef _STRINGFILE_H_
+#define _STRINGFILE_H_
+
+#include "virtfile.h"
+#include <frams/util/sstring.h>
+
+class StringFILE: public VirtFILE
+{
+  protected:
+SString& str;
+long pos;
+  public:
+StringFILE(SString& s):str(s),pos(0) {}
+int Vread(void *ptr, size_t size, size_t nmemb);
+int Vwrite(const void *ptr, size_t size, size_t nmemb) {str.append((const char*)ptr,size*nmemb); return size*nmemb;}
+int Veof() {return pos>=str.len();}
+int Vputc(int c) {str+=(char)c; return c;}
+int Vputs(const char *s) {str.append(s,strlen(s)); return 0;}
+int Vgetc();
+char *Vgets(char *s, int size);
+int Vprintf(const char *format, va_list args);
+int Vseek(long offset, int whence);
+int Vtell() {return pos;}
+void Vrewind() {pos=0;}
+int Vflush() {return 0;}
+};
+
+/** this version owns the string object */
+class StringFILE2: public StringFILE
+{
+SString string;
+  public:
+StringFILE2(const SString& s):StringFILE(string),string(s) {}
+StringFILE2():StringFILE(string) {}
+const SString& getString() {return string;}
+};
+
+class StringFileSystem: public VirtFileSystem
+{
+  public:
+VirtFileSystem *chain;
+StringFileSystem(VirtFileSystem *_chain):chain(_chain) {}
+VirtFILE *Vfopen(const char* path,const char*mode);
+int Vfexists(const char* path);
+VirtDIR *Vopendir(const char* path);
+static const char PREFIX[];
+static bool isStringPath(const char* path);
+};
+
+#endif
Index: /cpp/frams/virtfile/virtfile.cpp
===================================================================
--- /cpp/frams/virtfile/virtfile.cpp	(revision 109)
+++ /cpp/frams/virtfile/virtfile.cpp	(revision 109)
@@ -0,0 +1,96 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "virtfile.h"
+
+VirtFILE *VirtFILE::Vstdin=0;
+VirtFILE *VirtFILE::Vstdout=0;
+VirtFILE *VirtFILE::Vstderr=0;
+
+VirtFileSystem *VirtFILE::vfs=0;
+
+VirtFILE *Vfopen(const char* path,const char*mode)
+{
+return VirtFILE::vfs ? VirtFILE::vfs->Vfopen(path,mode) : 0;
+}
+
+VirtDIR *Vopendir(const char* path)
+{
+return VirtFILE::vfs ? VirtFILE::vfs->Vopendir(path) : 0;
+}
+
+int Vfexists(const char* path)
+{
+return VirtFILE::vfs ? VirtFILE::vfs->Vfexists(path) : 0;
+}
+
+VirtFILE::~VirtFILE()
+{}
+
+void VirtFILE::selectFileSystem(VirtFileSystem *s) {vfs=s;}
+
+int VirtFILE::printf(const char *format, ...)
+{
+int ret; va_list argptr;
+va_start(argptr,format);
+ret=Vprintf(format,argptr);
+va_end(argptr);
+return ret;
+}
+
+void VirtFILE::setVstdin(VirtFILE *f) {Vstdin=f;}
+void VirtFILE::setVstdout(VirtFILE *f) {Vstdout=f;}
+void VirtFILE::setVstderr(VirtFILE *f) {Vstderr=f;}
+VirtFILE* VirtFILE::getVstdin() {return Vstdin;}
+VirtFILE* VirtFILE::getVstdout() {return Vstdout;}
+VirtFILE* VirtFILE::getVstderr() {return Vstderr;}
+//////////////////////////////////////////////////////////////////////////
+
+VirtFILE* VirtFileSystem::Vfopen(const char* path,const char*mode) {return 0;}
+int VirtFileSystem::Vfexists(const char* path) {return 0;}
+VirtDIR* VirtFileSystem::Vopendir(const char* path) {return 0;}
+
+//////////////////////////////////////////////////////////////////////////
+
+
+
+int fread(void *ptr, size_t size, size_t nmemb, VirtFILE* f) {return f->Vread(ptr,size,nmemb);}
+int fwrite(const void *ptr, size_t size, size_t nmemb, VirtFILE* f) {return f->Vwrite(ptr,size,nmemb);}
+#ifdef __BORLANDC__
+#undef feof
+#endif
+#ifdef _MSC_VER
+#undef feof
+#endif
+#ifdef SHP
+#undef feof
+#endif
+int feof(VirtFILE* f) {return f->Veof();}
+#ifdef __BORLANDC__
+#define feof(__f)     ((__f)->flags & _F_EOF)
+#endif
+#ifdef _MSC_VER
+#define feof(_stream)     ((_stream)->_flag & _IOEOF)
+#endif
+
+int fputc(int c,VirtFILE* f) {return f->Vputc(c);}
+int fputs(const char *s,VirtFILE* f) {return f->Vputs(s);}
+int fgetc(VirtFILE* f) {return f->Vgetc();}
+int fseek(VirtFILE* f,long offset, int whence) {return f->Vseek(offset,whence);}
+int ftell(VirtFILE* f) {return f->Vtell();}
+void rewind(VirtFILE* f) {f->Vrewind();}
+int fflush(VirtFILE* f) {return f->Vflush();}
+char *fgets(char *s, int size, VirtFILE* f) {return f->Vgets(s,size);}
+int fprintf(VirtFILE* f,const char *format, ...)
+	{
+	int ret; va_list argptr;
+	va_start(argptr,format);
+	ret=f->Vprintf(format,argptr);
+	va_end(argptr);
+	return ret;
+	}
+int fclose(VirtFILE* f) {delete f; return 0;}
+
+int closedir(VirtDIR* d) {delete d; return 0;}
+dirent* readdir(VirtDIR* d) {return d->Vreaddir();}
Index: /cpp/frams/virtfile/virtfile.h
===================================================================
--- /cpp/frams/virtfile/virtfile.h	(revision 109)
+++ /cpp/frams/virtfile/virtfile.h	(revision 109)
@@ -0,0 +1,104 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _VIRTFILE_H_
+#define _VIRTFILE_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <common/nonstd_dir.h>
+//#include <dirent.h> //to jest inkludowane przez powyzsze 
+//struct dirent; //kiedys byla ta linia jak nie bylo jeszcze implementacji windowsowej dirent, ale borlandowi sie nie podoba jak s¹ obie
+
+#ifdef DLLEXPORTACTIVE  //tylko w tym pliku uzyte
+#define DLLEXP __declspec(dllexport)
+#else
+#define DLLEXP
+#endif
+
+class DLLEXP VirtFileSystem;
+
+class DLLEXP VirtFILE
+{
+  public:
+virtual int Vread(void *ptr, size_t size, size_t nmemb)=0;
+virtual int Vwrite(const void *ptr, size_t size, size_t nmemb)=0;
+virtual int Veof()=0;
+virtual int Vputc(int c)=0;
+virtual int Vputs(const char *s)=0;
+virtual int Vgetc()=0;
+virtual int Vseek(long offset, int whence)=0;
+virtual int Vtell()=0;
+virtual void Vrewind()=0;
+virtual int Vflush()=0;
+virtual char *Vgets(char *s, int size)=0;
+virtual int Vprintf(const char *format, va_list args)=0;
+int printf(const char *format, ...);
+virtual const char *VgetPath() {return 0;} // 0=unspecified path
+virtual ~VirtFILE();
+static VirtFILE *Vstdin,*Vstdout,*Vstderr;
+static void setVstdin(VirtFILE *);
+static void setVstdout(VirtFILE *);
+static void setVstderr(VirtFILE *);
+static VirtFILE* getVstdin();
+static VirtFILE* getVstdout();
+static VirtFILE* getVstderr();
+static VirtFileSystem *vfs;
+static void selectFileSystem(VirtFileSystem *s);
+};
+
+class DLLEXP VirtDIR
+{
+  public:
+virtual ~VirtDIR() {}
+virtual dirent* Vreaddir() {return 0;}
+};
+
+class DLLEXP VirtFileSystem
+{
+public:
+virtual VirtFILE *Vfopen(const char* path,const char*mode);
+virtual int Vfexists(const char* path);
+virtual VirtDIR *Vopendir(const char* path);
+};
+
+DLLEXP VirtFILE *Vfopen(const char* path,const char*mode);
+DLLEXP VirtDIR *Vopendir(const char* path);
+DLLEXP int Vfexists(const char* path);
+
+DLLEXP int fread(void *ptr, size_t size, size_t nmemb, VirtFILE* f);
+DLLEXP int fwrite(const void *ptr, size_t size, size_t nmemb, VirtFILE* f);
+#ifdef __BORLANDC__
+#undef feof
+#endif
+#ifdef _MSC_VER
+#undef feof
+#endif
+#ifdef SHP //nie wiem co to robi i jak ma byc, ale sie kompiluje z tym undefem a bez nie. tak samo w cpp
+#undef feof
+#endif
+DLLEXP int feof(VirtFILE* f);// {return f->Veof();}
+#ifdef __BORLANDC__
+#define feof(__f)     ((__f)->flags & _F_EOF)
+#endif
+#ifdef _MSC_VER
+#define feof(_stream)     ((_stream)->_flag & _IOEOF)
+#endif
+
+DLLEXP int fputc(int c,VirtFILE* f);
+DLLEXP int fputs(const char *s,VirtFILE* f);
+DLLEXP int fgetc(VirtFILE* f);
+DLLEXP int fseek(VirtFILE* f,long offset, int whence);
+DLLEXP int ftell(VirtFILE* f);
+DLLEXP void rewind(VirtFILE* f);
+DLLEXP int fflush(VirtFILE* f);
+DLLEXP char *fgets(char *s, int size, VirtFILE* f);
+DLLEXP int fprintf(VirtFILE* f,const char *format, ...);
+DLLEXP int fclose(VirtFILE* f);
+
+DLLEXP dirent* readdir(VirtDIR* d);
+DLLEXP int closedir(VirtDIR* d);
+
+#endif
+
Index: /cpp/frams/vm/classes/3dobject.cpp
===================================================================
--- /cpp/frams/vm/classes/3dobject.cpp	(revision 109)
+++ /cpp/frams/vm/classes/3dobject.cpp	(revision 109)
@@ -0,0 +1,574 @@
+#include <frams/util/3d.h>
+#include "3dobject.h"
+#include <frams/param/param.h>
+#include "collectionobj.h"
+
+ParamEntry* Pt3D_Ext::getStaticParamtab()
+{
+#define FIELDSTRUCT Pt3D_Ext
+static ParamEntry paramtab[]=
+{
+{"XYZ",1,18,"XYZ","3D vector"},
+
+{"x",0,PARAM_NOSTATIC,"x","f",FIELD(p.x),},
+{"y",0,PARAM_NOSTATIC,"y","f",FIELD(p.y),},
+{"z",0,PARAM_NOSTATIC,"z","f",FIELD(p.z),},
+{"new",0,0,"create new XYZ object","p oXYZ(f x,f y,f z)",PROCEDURE(p_new),"3D vectors objects can be also created using the (x,y,z) notation, i.e. var v=(1,2,3) is the same as var v=XYZ.new(1,2,3);",},
+{"newFromVector",0,0,"create new XYZ object","p oXYZ(oVector)",PROCEDURE(p_newFromVector),"used for deserialization"},
+{"clone",0,PARAM_NOSTATIC,"create new XYZ object copying the coordinates","p oXYZ()",PROCEDURE(p_clone),"Note: copying object references does not create new objects. Use clone() if a new object is needed.\n\nExample:\nvar o1=(1,2,3), o2=o1, o3=o1.clone();\no1.y=9999;\n//o2 is now (1,9999,3) but o3 is still (1,2,3)",},
+{"set",0,PARAM_NOSTATIC,"set (copy) coordinates from another XYZ object","p(oXYZ)",PROCEDURE(p_set),},
+{"set3",0,PARAM_NOSTATIC,"set individual 3 coordinates","p(f x,f y,f z)",PROCEDURE(p_set3),},
+{"add",0,PARAM_NOSTATIC,"add","p(oXYZ)",PROCEDURE(p_addvec),"Note: it does not return a new object, just modifies the existing one"},
+{"sub",0,PARAM_NOSTATIC,"subtract","p(oXYZ)",PROCEDURE(p_subvec),"Note: it does not return a new object, just modifies the existing one"},
+{"scale",0,PARAM_NOSTATIC,"multiply by scalar","p(f)",PROCEDURE(p_scale),},
+{"length",0,PARAM_READONLY+PARAM_NOSTATIC,"length","f",GETONLY(length),},
+{"normalize",0,PARAM_NOSTATIC,"normalize","p()",PROCEDURE(p_normalize),"scales the vector length to 1.0"},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{"toVector",0,PARAM_READONLY+PARAM_NOSTATIC,"vector of [x,y,z]","oVector",GETONLY(toVector),},
+{"rotate",0,PARAM_NOSTATIC,"rotate using Orient object","p(oOrient)",PROCEDURE(p_rotate),},
+{"revRotate",0,PARAM_NOSTATIC,"reverse rotate using Orient object","p(oOrient)",PROCEDURE(p_revrotate),},
+{"get",0,PARAM_NOSTATIC,"get one of coordinates","p f(d index)",PROCEDURE(p_get),"this function makes the XYZ objects \"indexable\" (so you can use [] for accessing subsequent fields, like in Vector)",},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+void Pt3D_Ext::p_new(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Pt3D_Ext(args[2].getDouble(),args[1].getDouble(),args[0].getDouble()));
+}
+
+static double doubleFromVec(VectorObject *vec,int i)
+{
+if (i>=vec->data.size()) return 0;
+ExtValue *v=(ExtValue*)vec->data.get(i);
+if (v)
+	return v->getDouble();
+return 0;
+}
+
+static Pt3D pt3DFromVec(VectorObject* v,int offset=0)
+{
+return Pt3D(doubleFromVec(v,offset),doubleFromVec(v,offset+1),doubleFromVec(v,offset+2));
+}
+
+void Pt3D_Ext::p_newFromVector(ExtValue *args,ExtValue *ret)
+{
+VectorObject *vec=VectorObject::fromObject(args->getObject());
+if (vec)
+	*ret=makeDynamicObject(new Pt3D_Ext(pt3DFromVec(vec)));
+else
+	ret->setEmpty();
+}
+
+void Pt3D_Ext::p_clone(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Pt3D_Ext(p.x,p.y,p.z));
+}
+
+void Pt3D_Ext::p_set3(ExtValue *args,ExtValue *ret)
+{
+p.x=args[2].getDouble();
+p.y=args[1].getDouble();
+p.z=args[0].getDouble();
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_set(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::get_length(ExtValue *ret)
+{
+ret->setDouble(p.length());
+}
+
+void Pt3D_Ext::get_toString(ExtValue *ret)
+{
+SString s="(";
+ExtValue v;
+v.setDouble(p.x); s+=v.getString();
+s+=",";
+v.setDouble(p.y); s+=v.getString();
+s+=",";
+v.setDouble(p.z); s+=v.getString();
+s+=")";
+ret->setString(s);
+}
+
+static void add3Coords(VectorObject* vec,const Pt3D& p)
+{
+vec->data+=new ExtValue(p.x);
+vec->data+=new ExtValue(p.y);
+vec->data+=new ExtValue(p.z);
+}
+
+void Pt3D_Ext::get_toVector(ExtValue *ret)
+{
+VectorObject *vec=new VectorObject;
+add3Coords(vec,p);
+ret->setObject(ExtObject(&VectorObject::par,vec));
+}
+
+void Pt3D_Ext::p_addvec(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p+=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_subvec(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *other=fromObject(args[0]);
+if (other)
+	p-=other->p;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_scale(ExtValue *args,ExtValue *ret)
+{
+double d=args[0].getDouble();
+p.x*=d; p.y*=d; p.z*=d;
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_normalize(ExtValue *args,ExtValue *ret)
+{
+p.normalize();
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_rotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o=Orient_Ext::fromObject(args[0]);
+if (o)
+	{
+	Pt3D tmp=p;
+	o->o.transform(p,tmp);
+	}
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_revrotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o=Orient_Ext::fromObject(args[0]);
+if (o)
+	{
+	Pt3D tmp=p;
+	o->o.revTransform(p,tmp);
+	}
+ret->setEmpty();
+}
+
+void Pt3D_Ext::p_get(ExtValue *args,ExtValue *ret)
+{
+int index=args->getInt();
+if ((index<0)||(index>2))
+	ret->setEmpty();
+else
+	ret->setDouble((&p.x)[index]);
+}
+
+Param& Pt3D_Ext::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static Pt3D_Ext static_pt3dobj;
+static Param static_pt3dparam(getStaticParamtab(),&static_pt3dobj);
+#else
+static Param static_pt3dparam(getStaticParamtab());
+#endif
+return static_pt3dparam;
+}
+
+Pt3D_Ext* Pt3D_Ext::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (Pt3D_Ext*)o.getTarget(getStaticParam().getName());
+}
+
+ParamInterface* Pt3D_Ext::getInterface() {return &getStaticParam();}
+
+ExtObject Pt3D_Ext::makeStaticObject(Pt3D* p)
+{return ExtObject(&getStaticParam(),((char*)p)+(((char*)&p->x)-((char*)&((Pt3D_Ext*)p)->p.x)));}
+
+ExtObject Pt3D_Ext::makeDynamicObject(Pt3D_Ext* p)
+{return ExtObject(&getStaticParam(),p);}
+
+//////////////////////////////////////
+
+ParamEntry* Orient_Ext::getStaticParamtab()
+{
+#define FIELDSTRUCT Orient_Ext
+static ParamEntry paramtab[]=
+{
+{"Orient",1,27,"Orient","3D orientation, stored as 3x3 matrix."},
+
+{"xx",1,PARAM_NOSTATIC,"orientation.x.x","f",FIELD(o.x.x),},
+{"xy",1,PARAM_NOSTATIC,"orientation.x.y","f",FIELD(o.x.y),},
+{"xz",1,PARAM_NOSTATIC,"orientation.x.z","f",FIELD(o.x.z),},
+{"yx",1,PARAM_NOSTATIC,"orientation.y.x","f",FIELD(o.y.x),},
+{"yy",1,PARAM_NOSTATIC,"orientation.y.y","f",FIELD(o.y.y),},
+{"yz",1,PARAM_NOSTATIC,"orientation.y.z","f",FIELD(o.y.z),},
+{"zx",1,PARAM_NOSTATIC,"orientation.z.x","f",FIELD(o.z.x),},
+{"zy",1,PARAM_NOSTATIC,"orientation.z.y","f",FIELD(o.z.y),},
+{"zz",1,PARAM_NOSTATIC,"orientation.z.z","f",FIELD(o.z.z),},
+
+{"x",0,PARAM_NOSTATIC,"x vector","oXYZ",GETONLY(x),},
+{"y",0,PARAM_NOSTATIC,"y vector","oXYZ",GETONLY(y),},
+{"z",0,PARAM_NOSTATIC,"z vector","oXYZ",GETONLY(z),},
+
+{"new",0,0,"create new Orient object","p oOrient()",PROCEDURE(p_new),},
+{"newFromVector",0,0,"create new Orient object","p oOrient(oVector)",PROCEDURE(p_newFromVector),},
+{"toVector",0,PARAM_READONLY+PARAM_NOSTATIC,"vector representation","oVector",GETONLY(toVector),"for serialization"},
+{"clone",0,PARAM_NOSTATIC,"create new Orient object","p oOrient()",PROCEDURE(p_clone),},
+{"set",0,PARAM_NOSTATIC,"copy from another Orient object","p(oOrient)",PROCEDURE(p_set),},
+{"reset",0,PARAM_NOSTATIC,"set identity matrix","p()",PROCEDURE(p_reset),},
+{"rotate3",0,PARAM_NOSTATIC,"rotate around 3 axes","p(f x,f y,f z)",PROCEDURE(p_rotate3),},
+{"rotate",0,PARAM_NOSTATIC,"rotate using Orient object","p(oOrient)",PROCEDURE(p_rotate),},
+{"revRotate",0,PARAM_NOSTATIC,"reverse rotate using Orient object","p(oOrient)",PROCEDURE(p_revrotate),},
+{"lookAt",0,PARAM_NOSTATIC,"calculate rotation from 2 vectors","p(oXYZ direction,oXYZ up)",PROCEDURE(p_lookat),},
+{"normalize",0,PARAM_NOSTATIC,"normalize","p()",PROCEDURE(p_normalize),},
+{"between2",0,PARAM_NOSTATIC,"interpolate orientation","p(oOrient,oOrient,f amount)",PROCEDURE(p_between2),"The calling Orient receives the orientation interpolated from 2 input orientations.\nExample:\n"
+	"var o1=Orient.new(), o2=Orient.new(), o3=Orient.new();\n"
+	"o2.rotate3(0,Math.pi/2,0);\n"
+	"o3.between2(o1,o2,0); // o3 equals o2\n"
+	"o3.between2(o1,o2,1); // o3 equals o1\n"
+	"o3.between2(o1,o2,0.5); // o3 is halfway between o1 and o2\n"},
+{"betweenOV",0,PARAM_NOSTATIC,"interpolate orientation","p(oOrient,oXYZ,f amount)",PROCEDURE(p_betweenOV),"Like between2(), but the second Orient is composed of the supplied XYZ vector (X component) and Y Z vectors from the calling object.\n"
+	"Example:\n"
+	"var o=Orient.new();\n"
+	"o.betweenOV(o,(0,1,0),1); //no change, o remains 100 010 001\n"
+	"o.betweenOV(o,(0,1,0),0.9); //o is slightly rotated towards (0,1,0)\n"
+	"o.betweenOV(o,(0,1,0),0); //o is completely transformed, o.x=(0,1,0)\n"
+	},
+{"localToWorld",0,PARAM_NOSTATIC,"transform coordinates","p XYZ(oXYZ point,oXYZ center)",PROCEDURE(p_localToWorld),},
+{"worldToLocal",0,PARAM_NOSTATIC,"transform coordinates","p XYZ(oXYZ point,oXYZ center)",PROCEDURE(p_worldToLocal),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+void Orient_Ext::p_new(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Orient_Ext());
+}
+
+void Orient_Ext::p_newFromVector(ExtValue *args,ExtValue *ret)
+{
+VectorObject *vec=VectorObject::fromObject(args->getObject());
+if (vec)
+	*ret=makeDynamicObject(new Orient_Ext(Orient(pt3DFromVec(vec,0),pt3DFromVec(vec,3),pt3DFromVec(vec,6))));
+else
+	ret->setEmpty();
+}
+
+void Orient_Ext::get_toVector(ExtValue *ret)
+{
+VectorObject *vec=new VectorObject;
+add3Coords(vec,o.x);
+add3Coords(vec,o.y);
+add3Coords(vec,o.z);
+ret->setObject(ExtObject(&VectorObject::par,vec));
+}
+
+void Orient_Ext::p_clone(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new Orient_Ext(o));
+}
+
+void Orient_Ext::p_set(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *other=fromObject(args[0]);
+if (other)
+	o=other->o;
+ret->setEmpty();
+}
+
+void Orient_Ext::p_reset(ExtValue *args,ExtValue *ret)
+{
+o=Orient_1;
+ret->setEmpty();
+}
+
+void Orient_Ext::get_x(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.x);
+}
+
+void Orient_Ext::get_y(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.y);
+}
+
+void Orient_Ext::get_z(PARAMGETARGS)
+{
+*ret=Pt3D_Ext::makeStaticObject(&o.z);
+}
+
+void Orient_Ext::p_lookat(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *dir=Pt3D_Ext::fromObject(args[1]),*up=Pt3D_Ext::fromObject(args[0]);
+if (dir&&up)
+	o.lookAt(dir->p,up->p);
+ret->setEmpty();
+}
+
+void Orient_Ext::p_rotate3(ExtValue *args,ExtValue *ret)
+{
+Pt3D p(args[2].getDouble(),args[1].getDouble(),args[0].getDouble());
+o.rotate(p);
+ret->setEmpty();
+}
+
+void Orient_Ext::p_rotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *obj=Orient_Ext::fromObject(args[0]);
+if (!obj)
+	{
+	Orient tmp=o;
+	obj->o.transform(o,tmp);
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_revrotate(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *obj=Orient_Ext::fromObject(args[0]);
+if (obj)
+	{
+	Orient tmp=o;
+	obj->o.revTransform(o,tmp);
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_normalize(ExtValue *args,ExtValue *ret)
+{
+o.normalize();
+ret->setEmpty();
+}
+
+void Orient_Ext::p_between2(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o1=Orient_Ext::fromObject(args[2]);
+Orient_Ext *o2=Orient_Ext::fromObject(args[1]);
+if (o1&&o2)
+	{
+	double q1=args[0].getDouble(),q2=1.0-q1;
+	o.x.x=q1*o1->o.x.x+q2*o2->o.x.x;
+	o.x.y=q1*o1->o.x.y+q2*o2->o.x.y;
+	o.x.z=q1*o1->o.x.z+q2*o2->o.x.z;
+	o.y.x=q1*o1->o.y.x+q2*o2->o.y.x;
+	o.y.y=q1*o1->o.y.y+q2*o2->o.y.y;
+	o.y.z=q1*o1->o.y.z+q2*o2->o.y.z;
+	o.z.x=q1*o1->o.z.x+q2*o2->o.z.x;
+	o.z.y=q1*o1->o.z.y+q2*o2->o.z.y;
+	o.z.z=q1*o1->o.z.z+q2*o2->o.z.z;
+	o.normalize();
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_betweenOV(ExtValue *args,ExtValue *ret)
+{
+Orient_Ext *o1=Orient_Ext::fromObject(args[2]);
+Pt3D_Ext *p2=Pt3D_Ext::fromObject(args[1]);
+if (o1&&p2)
+	{
+	double q1=args[0].getDouble(),q2=1.0-q1;
+	o.x.x=q1*o1->o.x.x+q2*p2->p.x;
+	o.x.y=q1*o1->o.x.y+q2*p2->p.y;
+	o.x.z=q1*o1->o.x.z+q2*p2->p.z;
+	o.normalize();
+	}
+ret->setEmpty();
+}
+
+void Orient_Ext::p_localToWorld(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *center,*point;
+point=Pt3D_Ext::fromObject(args[1]);
+center=Pt3D_Ext::fromObject(args[0]);
+if (center && point)
+	{
+	Pt3D d;
+	Pt3D src=point->p;
+	o.transform(d,src);
+	d+=center->p;
+	*ret=Pt3D_Ext::makeDynamicObject(new Pt3D_Ext(d));
+	}
+else
+	ret->setEmpty();
+}
+
+void Orient_Ext::p_worldToLocal(ExtValue *args,ExtValue *ret)
+{
+Pt3D_Ext *center,*point;
+point=Pt3D_Ext::fromObject(args[1]);
+center=Pt3D_Ext::fromObject(args[0]);
+if (center && point)
+	{
+	Pt3D d;
+	Pt3D src=point->p;
+	d-=center->p;
+	o.revTransform(d,src);
+	*ret=Pt3D_Ext::makeDynamicObject(new Pt3D_Ext(d));
+	}
+else
+	ret->setEmpty();
+}
+
+Param& Orient_Ext::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static Orient_Ext static_orientobj;
+static Param static_orientparam(getStaticParamtab(),&static_orientobj);
+#else
+static Param static_orientparam(getStaticParamtab());
+#endif
+return static_orientparam;
+}
+
+Orient_Ext* Orient_Ext::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (Orient_Ext*)o.getTarget(getStaticParam().getName());
+}
+
+ParamInterface* Orient_Ext::getInterface() {return &getStaticParam();}
+
+ExtObject Orient_Ext::makeStaticObject(Orient* o)
+{return ExtObject(&getStaticParam(),((char*)o)+(((char*)&o->x)-((char*)&((Orient_Ext*)o)->o.x)));}
+
+ExtObject Orient_Ext::makeDynamicObject(Orient_Ext* p)
+{return ExtObject(&getStaticParam(),p);}
+
+ParamEntry* ReferenceObj::getStaticParamtab()
+{
+#define FIELDSTRUCT ReferenceObj
+static ParamEntry paramtab[]=
+{
+{"Ref",1,5,"Ref","Reference objects. Useful for returning things from functions.\n\nExample:\nvar x=111;\nsquare(&x);// '&' creates the Reference object\nSimulator.print(x);//x is now 12321\n\nfunction square(r)\n{r.value=r.value*r.value;}\n//square receives the Reference objects and changes its 'value' field"},
+
+{"value",0,PARAM_NOSTATIC,"value","x",GETSET(value),},
+{"newS",0,0,"create new reference","p",PROCEDURE(p_newS),"(for internal use only) use &variablename to create Ref objects.",},
+{"newO",0,0,"create new reference","p",PROCEDURE(p_newO),"(for internal use only) use &variablename to create Ref objects.",},
+{"copyFrom",0,0,"copy the reference","p(oRef)",PROCEDURE(p_copyFrom),"make the reference point to the same target,"},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+return paramtab;
+}
+
+Param& ReferenceObj::getStaticParam()
+{
+#ifdef __CODEGUARD__
+static ReferenceObj static_referenceobj;
+static Param static_refobjectparam(getStaticParamtab(),&static_referenceobj);
+#else
+static Param static_refobjectparam(getStaticParamtab());
+#endif
+return static_refobjectparam;
+}
+
+void ReferenceObj::p_newS(ExtValue *args,ExtValue *ret)
+{
+*ret=makeDynamicObject(new ReferenceObj((ExtValue*)args->getInt()));
+}
+
+void ReferenceObj::p_newO(ExtValue *args,ExtValue *ret)
+{
+if (args[0].type==TInt)
+	*ret=makeDynamicObject(new ReferenceObj(args[1].getObject(),args[0].getInt()));
+else
+	*ret=makeDynamicObject(new ReferenceObj(args[1].getObject(),args[0].getString()));
+}
+
+void ReferenceObj::p_copyFrom(ExtValue *args,ExtValue *ret)
+{
+ReferenceObj* other=fromObject(args[0]);
+if (other)
+	{
+	value=other->value;
+	obj=other->obj;
+	prop=other->prop;
+	}
+}
+
+void ReferenceObj::get_toString(ExtValue *ret)
+{
+SString s="(";
+static SListTempl<ReferenceObj*> trace;
+if (trace.find(this)>=0)
+	s+="...";
+else
+	{
+	trace+=this;
+	if (value)
+		s+=value->getString();
+	else
+		{
+		ExtValue v;
+		Param tmp_param;
+		ParamInterface *pi=obj.getParamInterface(tmp_param);
+		pi->get(prop,v);
+		s+=v.getString();
+		}
+	trace-=this;
+	}
+s+=")";
+ret->setString(s);
+}
+
+void ReferenceObj::get_value(ExtValue *ret)
+{
+if (value)
+	*ret=*value;
+else
+	{
+	Param tmp_param;
+	ParamInterface *pi=obj.getParamInterface(tmp_param);
+	pi->get(prop,*ret);
+	}
+}
+
+int ReferenceObj::set_value(const ExtValue *val)
+{
+if (value)
+	*value=*val;
+else
+	{
+	Param tmp_param;
+	ParamInterface *pi=obj.getParamInterface(tmp_param);
+	pi->set(prop,*val);
+	}
+return PSET_CHANGED;
+}
+
+ReferenceObj::ReferenceObj(const ExtObject &o,const SString &p)
+	:value(0),obj(o)
+{
+Param tmp_param;
+ParamInterface *pi=obj.getParamInterface(tmp_param);
+prop=pi->findId(p);
+}
+
+ExtObject ReferenceObj::makeDynamicObject(ReferenceObj* r)
+{return ExtObject(&getStaticParam(),r);}
+
+ReferenceObj* ReferenceObj::fromObject(const ExtValue& v)
+{
+if (v.type!=TObj) return 0;
+const ExtObject& o=v.getObject();
+return (ReferenceObj*)o.getTarget(getStaticParam().getName());
+}
Index: /cpp/frams/vm/classes/3dobject.h
===================================================================
--- /cpp/frams/vm/classes/3dobject.h	(revision 109)
+++ /cpp/frams/vm/classes/3dobject.h	(revision 109)
@@ -0,0 +1,105 @@
+#ifndef _3DOBJECT_H_
+#define _3DOBJECT_H_
+
+#include <frams/util/3d.h>
+#include <frams/util/extvalue.h>
+
+class Pt3D_Ext: public DestrBase
+{
+  public:
+Pt3D p;
+
+Pt3D_Ext(double x,double y,double z):p(x,y,z) {}
+Pt3D_Ext():p(0,0,0) {}
+Pt3D_Ext(const Pt3D &i):p(i) {}
+#define STATRICKCLASS Pt3D_Ext
+PARAMPROCDEF(p_new);
+PARAMPROCDEF(p_newFromVector);
+PARAMPROCDEF(p_clone);
+PARAMGETDEF(length);
+PARAMPROCDEF(p_addvec);
+PARAMPROCDEF(p_subvec);
+PARAMPROCDEF(p_scale);
+PARAMPROCDEF(p_rotate);
+PARAMPROCDEF(p_revrotate);
+PARAMPROCDEF(p_normalize);
+PARAMGETDEF(toString);
+PARAMGETDEF(toVector);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_set3);
+PARAMPROCDEF(p_get);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeStaticObject(Pt3D* p);
+static ExtObject makeDynamicObject(Pt3D_Ext* p);
+static Pt3D_Ext* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+class Orient_Ext: public DestrBase
+{
+  public:
+Orient o;
+
+Orient_Ext():o(Orient_1) {}
+Orient_Ext(const Orient& other):o(other) {}
+#define STATRICKCLASS Orient_Ext
+PARAMPROCDEF(p_new);
+PARAMPROCDEF(p_newFromVector);
+PARAMPROCDEF(p_clone);
+PARAMGETDEF(toString);
+PARAMGETDEF(toVector);
+PARAMGETDEF(x);
+PARAMGETDEF(y);
+PARAMGETDEF(z);
+PARAMPROCDEF(p_rotate3);
+PARAMPROCDEF(p_rotate);
+PARAMPROCDEF(p_revrotate);
+PARAMPROCDEF(p_lookat);
+PARAMPROCDEF(p_normalize);
+PARAMPROCDEF(p_between2);
+PARAMPROCDEF(p_betweenOV);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_reset);
+PARAMPROCDEF(p_localToWorld);
+PARAMPROCDEF(p_worldToLocal);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeStaticObject(Orient* p);
+static ExtObject makeDynamicObject(Orient_Ext* p);
+static Orient_Ext* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+class ReferenceObj: public DestrBase
+{
+  public:
+ExtValue *value;
+ExtObject obj;
+int prop;
+
+ReferenceObj(ExtValue *val):value(val) {}
+ReferenceObj() {}
+ReferenceObj(const ExtObject &o,int p):value(0),obj(o),prop(p) {}
+ReferenceObj(const ExtObject &o,const SString &p);
+#define STATRICKCLASS ReferenceObj
+PARAMPROCDEF(p_newS);
+PARAMPROCDEF(p_newO);
+PARAMPROCDEF(p_copyFrom);
+PARAMGETDEF(toString);
+PARAMGETDEF(value);
+PARAMSETDEF(value);
+#undef STATRICKCLASS
+
+static ParamInterface* getInterface();
+static ExtObject makeDynamicObject(ReferenceObj* r);
+static ReferenceObj* fromObject(const ExtValue& v);
+static Param& getStaticParam();
+static ParamEntry* getStaticParamtab();
+};
+
+#endif
Index: /cpp/frams/vm/classes/collectionobj.cpp
===================================================================
--- /cpp/frams/vm/classes/collectionobj.cpp	(revision 109)
+++ /cpp/frams/vm/classes/collectionobj.cpp	(revision 109)
@@ -0,0 +1,470 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "collectionobj.h"
+#include <common/nonstd_math.h> //sqrt in borland
+#include <frams/errmgr/stderrors.h>
+#include <common/nonstd_stl.h>
+#include <frams/util/sstringutils.h>
+#ifndef NO_VMACHINE
+#include <frams/vm/vmachine.h>
+#endif
+
+#define FIELDSTRUCT VectorObject
+ParamEntry vector_paramtab[]=
+{
+{"Vector",1,13,"Vector","Vector is 1-dimensional array, indexed by integer value (starting from 0). "
+ "Multidimensional arrays can be simulated by putting other Vector objects into the Vector.\n"
+ "Example:\nvar v=Vector.new();\nv.add(123); v.add(\"string\");",},
+{"clear",0,PARAM_NOSTATIC,"clear data","p()",PROCEDURE(p_clear),},
+{"size",0,PARAM_READONLY+PARAM_NOSTATIC,"element count","d",GETONLY(size),},
+{"remove",0,PARAM_NOSTATIC,"remove at position","p(d position)",PROCEDURE(p_remove),},
+{"get",0,PARAM_NOSTATIC,"get value at position","p x(d position)",PROCEDURE(p_get),},
+{"set",0,PARAM_NOSTATIC,"set value at position","p(d position,x value)",PROCEDURE(p_set),},
+{"add",0,PARAM_NOSTATIC,"append at the end","p(x value)",PROCEDURE(p_add),},
+{"find",0,PARAM_NOSTATIC,"find","p d(x value)",PROCEDURE(p_find),"returns the element index or -1 if not found"},
+{"avg",0,PARAM_READONLY+PARAM_NOSTATIC,"average","f",GETONLY(avg)},
+{"stdev",0,PARAM_READONLY+PARAM_NOSTATIC,"standard deviation","f",GETONLY(stdev),"=sqrt(sum((element[i]-avg)^2)/(size-1)) which is estimated population std.dev. from sample std.dev."},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{"new",0,0,"create new Vector","p oVector()",STATICPROCEDURE(p_new),},
+{"sort",0,PARAM_NOSTATIC,"sort elements (in place)","p(o comparator)",PROCEDURE(p_sort),"comparator can be null, giving the \"natural\" sorting order (depending on element type), otherwise it must be a function reference obtained by the \"function FUNCTIONNAME\" operator.\n\nExample:\nfunction compareLastDigit(a,b) {return (a%10)<(b%10);}\nvar v=[16,23,35,42,54,61];\nv.sort(function compareLastDigit);"},
+{"iterator",0,PARAM_NOSTATIC,"iterator","o",GETONLY(iterator),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+#define FIELDSTRUCT DictionaryObject
+ParamEntry dictionary_paramtab[]=
+{
+{"Dictionary",1,9,"Dictionary","Dictionary associates stored values with string keys "
+ "(\"key\" is the first argument in get/set/remove functions). Integer \"key\" can be "
+ "used to enumerate all elements (the sequence of elements is not preserved).\n"
+ "Example:\nvar d=Dictionary.new(); d.set(\"name\",\"John\"); d.set(\"age\",44);\n"
+ "var i;\nfor(i=0;i<d.size;i++) Simulator.print(d.getKey(i)+\" is \"+d.get(i));",},
+{"clear",0,PARAM_NOSTATIC,"clear data","p()",PROCEDURE(p_clear),},
+{"size",0,PARAM_NOSTATIC,"element count","d",GETONLY(size),},
+{"remove",0,PARAM_NOSTATIC,"remove named or indexed element","p(x key)",PROCEDURE(p_remove),},
+{"get",0,PARAM_NOSTATIC,"get named or indexed element","p x(x key)",PROCEDURE(p_get),},
+{"getKey",0,PARAM_NOSTATIC,"get a key of the indexed element","p s(d index)",PROCEDURE(p_getKey),},
+{"set",0,PARAM_NOSTATIC,"set named or indexed element","p(x key,x value)",PROCEDURE(p_set),},
+{"find",0,PARAM_NOSTATIC,"find","p s(x value)",PROCEDURE(p_find),"returns the element key or null if not found"},
+{"new",0,0,"create new Dictionary","p oDictionary()",STATICPROCEDURE(p_new),},
+{"toString",0,PARAM_READONLY+PARAM_NOSTATIC,"textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+Param VectorObject::par(vector_paramtab);
+Param DictionaryObject::par(dictionary_paramtab);
+
+/////////////////////////////////////////
+
+VectorObject::VectorObject(Pt3D &pt)
+	:readonly(0),owndata(1)
+{
+set(0,ExtValue(pt.x));
+set(1,ExtValue(pt.y));
+set(2,ExtValue(pt.z));
+}
+
+void VectorObject::clear()
+{
+if (owndata)
+for(int i=data.size()-1;i>=0;i--)
+	{
+	ExtValue *v=(ExtValue*)data.get(i);
+	if (v) delete v;
+	}
+//nie tak spektakularny jak rasowe bugi roku ale tez idiotyczny :>
+//wszystko przez to ze poczatkowo bylo tylko uzywane w destruktorze...
+data.clear();
+}
+
+void VectorObject::p_remove(PARAMPROCARGS)
+{
+if (readonly) return;
+int i=args->getInt();
+if ((i<0)||(i>=data.size())) return;
+ExtValue *v=(ExtValue*)data.get(i);
+if (v) delete v;
+data-=i;
+}
+
+void VectorObject::set(int i,const ExtValue& val)
+{
+int oldsize=data.size();
+if (i<0) return;
+ExtValue *v=(ExtValue*)data.get(i);
+if (v) delete v;
+data.set(i,new ExtValue(val));
+i--;
+while(i>=oldsize)
+	{
+	data.set(i,0);
+	i--;
+	}
+}
+
+void VectorObject::p_get(PARAMPROCARGS)
+{
+int i=args->getInt();
+if (listIndexCheck(&data,i,"VectorObject","get"))
+	{
+	ExtValue *v=get(i);
+	if (v)
+		{
+		*ret=*v;
+		return;
+		}
+	}
+*ret=ExtValue();
+}
+
+void VectorObject::get_avg(ExtValue* ret)
+{
+if (!data.size()) {ret->setEmpty(); return;}
+double s=0.0;
+for(int i=data.size()-1;i>=0;i--)
+	s+=((ExtValue*)data.get(i))->getDouble();
+s/=data.size();
+ret->setDouble(s);
+}
+
+SString VectorObject::serialize() const
+{
+SString out="[";
+	{
+	for(int i=0;i<data.size();i++)
+		{
+		ExtValue* v=(ExtValue*)data.get(i);
+		if (i) out+=",";
+		if (v)
+			out+=v->serialize();
+		else
+			out+="null";
+		}
+	}
+out+="]";
+//sprintf(out.directAppend(20),"<Vector@%p>",this);out.endAppend();
+return out;
+}
+
+static THREAD_LOCAL_DEF(SList,tostring_trace);
+
+void VectorObject::get_toString(ExtValue* ret)
+{
+SString out="[";
+//static SListTempl<VectorObject*> trace;
+if (tlsGetRef(tostring_trace).find(this)>=0)
+	out+="...";
+else
+	{
+	tlsGetRef(tostring_trace)+=this;
+	for(int i=0;i<data.size();i++)
+		{
+		ExtValue* v=(ExtValue*)data.get(i);
+		if (i) out+=",";
+		if (v)
+			out+=v->getString();
+		else
+			out+="null";
+		}
+	tlsGetRef(tostring_trace)-=this;
+	}
+out+="]";
+ret->setString(out);
+}
+
+void VectorObject::get_stdev(ExtValue* ret)
+{
+if (!data.size()) {ret->setEmpty(); return;}
+get_avg(ret);
+double a=ret->getDouble();
+double s=0.0;
+for(int i=data.size()-1;i>=0;i--)
+	{
+	double d=a-((ExtValue*)data.get(i))->getDouble();
+	s+=d*d;
+	}
+ret->setDouble(sqrt(s/max(1,data.size()-1)));
+}
+
+void VectorObject::p_find(PARAMPROCARGS)
+{
+short i;
+for(i=0;i<data.size();i++)
+	{
+	if ((*args)==(*get(i)))
+		{ret->setInt(i);return;}
+	}
+ret->setInt(-1);
+}
+
+class VEComparator
+{
+public:
+bool operator()(const ExtValue *a,const ExtValue *b) {return a->compare(*b)<0;}
+};
+
+#ifndef NO_VMACHINE
+class VMVEComparator
+{
+public:
+VMachine::JumpTargetObject *jto;
+VMachine *vm;
+VMVEComparator(VMachine::JumpTargetObject *_jto):jto(_jto),vm(jto->vm) {}
+bool operator()(const ExtValue *a,const ExtValue *b);
+};
+
+bool VMVEComparator::operator()(const ExtValue *a,const ExtValue *b)
+{
+if (!VMCode::prepareDynamicJumpTarget(jto->pc,jto->code))
+	return false;
+
+vm->push(*a);
+vm->push(*b);
+vm->pushNewCallState();
+vm->jumpDynamicJumpTarget(jto->pc);
+vm->run();
+vm->popCallState();
+bool ret;
+ExtValue& retval=vm->getValue();
+if (retval.type==TInvalid)
+	{
+	ret=false;
+	FMprintf("VectorElementComparator","",FMLV_ERROR,"Comparison function returned no value");
+	}
+else
+	ret=(retval.getInt()!=0);
+vm->drop(2);
+return ret;
+}
+#endif
+
+void VectorObject::p_sort(PARAMPROCARGS)
+{
+#ifndef NO_VMACHINE
+VMachine::JumpTargetObject *jto=VMachine::JumpTargetObject::fromObject(args->getObject());
+if (jto)
+	{
+	VMVEComparator cmp(jto);
+	ExtValue **first=(ExtValue**)&data.getref();
+	std::sort(first,first+data.size(),cmp);
+	}
+else
+#endif
+	{
+	VEComparator cmp;
+	ExtValue **first=(ExtValue**)&data.getref();
+	std::sort(first,first+data.size(),cmp);
+	}
+ret->setEmpty();
+}
+
+void VectorObject::get_iterator(ExtValue* ret)
+{
+ret->setObject(VectorIterator::makeFrom(this));
+}
+
+VectorObject* VectorObject::fromObject(const ExtObject& o)
+{
+return (VectorObject*)o.getTarget(par.getName());
+}
+
+/////////////////////////////
+
+void DictionaryObject::clear()
+{
+for(HashEntryIterator it(hash);it.isValid();)
+	{
+        ExtValue *v=(ExtValue*)hash.remove(it);
+	if (v) delete v;
+	}
+hash.clear();
+hash.init();
+}
+
+void DictionaryObject::p_find(PARAMPROCARGS)
+{
+for(HashEntryIterator it(hash);it.isValid();it++)
+	{
+	if ((*args)==(*((ExtValue*)it->value)))
+		{
+		ret->setString(it->key);
+		return;
+		}
+	}
+ret->setEmpty();
+}
+
+HashEntryIterator* DictionaryObject::getIndexIterator(int i)
+{
+if (i<0) return 0;
+if (i>=hash.getSize()) return 0;
+
+if ((!it.isValid())||(it_index>i))
+	{
+	it=HashEntryIterator(hash);
+	it_index=0;
+	}
+while(it.isValid())
+	{
+	if (it_index==i)
+		return &it;
+	it_index++;
+	it++;
+	}
+return 0;
+}
+
+void DictionaryObject::p_remove(PARAMPROCARGS)
+{
+if ((args->type==TInt)||(args->type==TDouble))
+	{
+	HashEntryIterator* iter=getIndexIterator(args->getInt());
+	if (iter)
+		{
+		ExtValue *oldval=(ExtValue*)hash.remove(*iter);
+		if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+		}
+	}
+else
+	{
+	ExtValue *oldval=(ExtValue*)hash.remove(args[0].getString());
+	if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+	}
+}
+
+void DictionaryObject::p_get(PARAMPROCARGS)
+{
+if ((args->type==TInt)||(args->type==TDouble))
+	{
+	HashEntryIterator* iter=getIndexIterator(args->getInt());
+	if (iter && (*iter)->value)
+		{
+		*ret=*((ExtValue*)(*iter)->value);
+		return;
+		}
+	}
+else
+	{
+	ExtValue *val=(ExtValue*)hash.get(args[0].getString());
+	if (val)
+		{
+		*ret=*val;
+		return;
+		}
+	}
+*ret=ExtValue();
+}
+
+void DictionaryObject::p_getKey(PARAMPROCARGS)
+{
+HashEntryIterator* iter=getIndexIterator(args->getInt());
+if (iter)
+	{
+	*ret=(*iter)->key;
+	return;
+	}
+*ret=ExtValue();
+}
+
+void DictionaryObject::p_set(PARAMPROCARGS)
+{
+ExtValue *newval=(args[0].getType()==TUnknown)?0:new ExtValue(args[0]);
+ExtValue *oldval=(ExtValue*)hash.put(args[1].getString(),newval);
+if (oldval) {*ret=*oldval; delete oldval;} else *ret=ExtValue();
+}
+
+SString DictionaryObject::serialize() const
+{
+SString out="{";
+	{
+	for(HashEntryIterator it(hash);it.isValid();)
+		{
+		out+="\"";
+		SString q=it->key; sstringQuote(q);
+		out+=q;
+		out+="\":";
+		out+=((ExtValue*)it->value)->serialize();
+		it++;
+		if (it.isValid()) out+=",";
+		}
+	}
+out+="}";
+return out;
+}
+
+void DictionaryObject::get_toString(ExtValue* ret)
+{
+SString out="{";
+//static SListTempl<DictionaryObject*> trace;
+if (tlsGetRef(tostring_trace).find(this)>=0)
+	out+="...";
+else
+	{
+	tlsGetRef(tostring_trace)+=this;
+	for(HashEntryIterator it(hash);it.isValid();)
+		{
+		out+=it->key;
+		out+=":";
+		out+=((ExtValue*)it->value)->getString();
+		it++;
+		if (it.isValid()) out+=",";
+		}
+	tlsGetRef(tostring_trace)-=this;
+	}
+out+="}";
+ret->setString(out);
+}
+
+DictionaryObject* DictionaryObject::fromObject(const ExtObject& o)
+{
+return (DictionaryObject*)o.getTarget(par.getName());
+}
+
+////////////////
+
+VectorIterator::VectorIterator(VectorObject* v)
+{
+vec=v;
+vec->incref();
+pos=-1;
+}
+
+#define FIELDSTRUCT VectorIterator
+ParamEntry vectoriterator_paramtab[]=
+{
+ {"VectorIterator",1,2,"VectorIterator","VectorIterator"},
+{"next",0,PARAM_READONLY+PARAM_NOSTATIC,"next","d 0 1",GETONLY(next),},
+{"value",0,PARAM_READONLY+PARAM_NOSTATIC,"value","x",GETONLY(value),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+ExtObject VectorIterator::makeFrom(VectorObject *v)
+{
+static Param par(vectoriterator_paramtab);
+return ExtObject(&par,new VectorIterator(v));
+}
+
+VectorIterator::~VectorIterator()
+{
+vec->decref();
+}
+
+void VectorIterator::get_next(ExtValue* ret)
+{
+pos++;
+ret->setInt((pos < vec->data.size()) ? 1 : 0);
+}
+
+void VectorIterator::get_value(ExtValue* ret)
+{
+ExtValue *v=(ExtValue*) (((pos>=0)&&(pos<vec->data.size())) ? vec->data(pos) : NULL );
+if (v)
+	*ret=*v;
+else
+	ret->setEmpty();
+}
Index: /cpp/frams/vm/classes/collectionobj.h
===================================================================
--- /cpp/frams/vm/classes/collectionobj.h	(revision 109)
+++ /cpp/frams/vm/classes/collectionobj.h	(revision 109)
@@ -0,0 +1,94 @@
+// This file is a part of the Framsticks GDK library.
+// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _COLLECTIONOBJ_H_
+#define _COLLECTIONOBJ_H_
+
+#include <frams/param/param.h>
+#include <frams/util/extvalue.h>
+#include <frams/util/hashtable.h>
+#include <frams/util/3d.h>
+
+/** object collection, indexed by int */
+class VectorObject: public DestrBase
+{
+  public:
+SList data;
+unsigned int readonly:1, owndata:1;
+void clear();
+ExtValue *get(int i) {return (ExtValue*)data.get(i);}
+void set(int i,const ExtValue& val);
+
+static Param par;
+VectorObject(Pt3D& pt);
+VectorObject():readonly(0),owndata(1) {}
+~VectorObject() {clear();}
+#define STATRICKCLASS VectorObject
+PARAMPROCDEF(p_clear) {if (readonly) return; clear();}
+PARAMGETDEF(size) {arg1->setInt(data.size());}
+PARAMGETDEF(avg);
+PARAMGETDEF(stdev);
+PARAMGETDEF(iterator);
+PARAMPROCDEF(p_remove);
+PARAMPROCDEF(p_get);
+PARAMPROCDEF(p_find);
+PARAMPROCDEF(p_set) {if (!readonly) set(arg1[1].getInt(),arg1[0]);}
+PARAMPROCDEF(p_add) {if (readonly) return; /*ExtValue tmp; get_toString(&tmp); printf("%s += %s",(const char*)tmp.getString(),(const char*)arg1[0].getString());*/ data+=new ExtValue(arg1[0]); /*get_toString(&tmp); printf(" -> %s\n",(const char*)tmp.getString());*/ arg2->setInt(data.size()-1);}
+PARAMGETDEF(toString);
+PARAMPROCDEF(p_sort);
+#undef STATRICKCLASS
+static void p_new(void*,ExtValue*args,ExtValue*ret)
+	{ret->setObject(ExtObject(&par,new VectorObject));}
+SString serialize() const;
+ExtObject makeObject() {return ExtObject(&par,this);}
+
+static VectorObject* fromObject(const ExtObject& o);
+};
+
+/** object collection, indexed by name */
+class DictionaryObject: public DestrBase
+{
+  public:
+HashTable hash;
+HashEntryIterator it;
+int it_index;
+
+void clear();
+HashEntryIterator* getIndexIterator(int i);
+
+static Param par;
+DictionaryObject():it(hash),it_index(-1) {}
+~DictionaryObject() {clear();}
+#define STATRICKCLASS DictionaryObject
+PARAMPROCDEF(p_clear) {clear();}
+PARAMGETDEF(size) {arg1->setInt(hash.getSize());}
+PARAMPROCDEF(p_remove);
+PARAMPROCDEF(p_get);
+PARAMPROCDEF(p_getKey);
+PARAMPROCDEF(p_set);
+PARAMPROCDEF(p_find);
+PARAMGETDEF(toString);
+#undef STATRICKCLASS
+SString serialize() const;
+static void p_new(void*,ExtValue*args,ExtValue*ret)
+	{ret->setObject(ExtObject(&par,new DictionaryObject));} //NOWY KANDYDAT NA BUGA ROKU!!!
+static DictionaryObject* fromObject(const ExtObject& v);
+ExtObject makeObject() {return ExtObject(&par,this);}
+};
+
+class VectorIterator: public DestrBase
+{
+  public:
+VectorObject *vec;
+int pos;
+VectorIterator(VectorObject* v);
+~VectorIterator();
+#define STATRICKCLASS VectorIterator
+PARAMGETDEF(next);
+PARAMGETDEF(value);
+#undef STATRICKCLASS
+static ExtObject makeFrom(VectorObject *v);
+};
+
+#endif
