Index: cpp/frams/genetics/f9/conv_f9.cpp
===================================================================
--- cpp/frams/genetics/f9/conv_f9.cpp	(revision 120)
+++ cpp/frams/genetics/f9/conv_f9.cpp	(revision 120)
@@ -0,0 +1,159 @@
+// This file is a part of the Framsticks GDK.
+// Copyright (C) 2002-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "conv_f9.h"
+#include <frams/model/model.h>
+#include <string.h>
+#include <common/nonstd_stl.h> //ARRAY_LENGTH
+
+//#define APPLY_DETERMINISTIC_BODY_NOISE
+
+GenoConv_F90::GenoConv_F90()
+{
+	name = "Turtle3D-ortho encoding";
+	in_format  = '9';
+	out_format = '0';
+	mapsupport = 0; //would be easy and nice to add!
+}
+
+
+const char* turtle_commands_f9 ="LRDUBF";
+
+//const char* turtle_commandsX_f9="-+0000";
+//const char* turtle_commandsY_f9="00-+00";
+//const char* turtle_commandsZ_f9="0000-+";
+
+SString GenoConv_F90::convert(SString &in, MultiMap *map)
+{
+	vector<XYZ_LOC> vertices;
+	XYZ_LOC current;
+	Model m;
+	m.open();
+	int recently_added=addSegment(m,vertices,current,0xDead);
+	for(int i=0;i<in.len();i++)
+	{
+		char command=in[i];
+		char *ptr=strchr((char*)turtle_commands_f9,command);
+		if (ptr)
+		{
+			int delta[]={0,0,0};
+			int pos=ptr-turtle_commands_f9;
+			int axis=pos/2;
+			int dir=pos%2;
+			(*(delta+axis))+=dir*2-1; //+1 or -1 in the given axis
+			current.add(delta);
+			recently_added=addSegment(m,vertices,current,recently_added);
+		}
+	}
+#ifdef APPLY_DETERMINISTIC_BODY_NOISE
+	perturbPartLocations(m);
+#endif
+	setColors(m);
+	m.close();
+	return m.getF0Geno().getGene();
+}
+
+int GenoConv_F90::addSegment(Model &m,vector<XYZ_LOC> &vertices,const XYZ_LOC &new_vertex,int recently_added)
+{
+	if (vertices.size()<1)
+	{
+		return addNewVertex(m,vertices,new_vertex);
+	} else
+	{
+		int vertex_here=findVertexAt(vertices,new_vertex);
+		if (vertex_here<0) //need to create a new Part
+		{
+			vertex_here=addNewVertex(m,vertices,new_vertex);
+		} //else there already exists a Part in new_vertex; new Joint may or may not be needed
+		Part *p1=m.getPart(recently_added);
+		Part *p2=m.getPart(vertex_here);
+		if (m.findJoint(p1,p2)<0 && m.findJoint(p2,p1)<0) //new Joint needed? should always be true if we just created a new Part (vertex_here was <0)
+		{
+			m.addNewJoint(p1,p2);
+		}
+		return vertex_here;
+	}
+}
+
+int GenoConv_F90::findVertexAt(vector<XYZ_LOC> &vertices,const XYZ_LOC &vertex)
+{
+	for(int i=0;i<vertices.size();i++)
+		if (vertices[i].same_coordinates(vertex)) return i;
+	return -1;
+}
+
+
+int GenoConv_F90::addNewVertex(Model &m,vector<XYZ_LOC> &vertices,const XYZ_LOC &new_vertex)
+{
+	Part *p=new Part;
+	p->p.x=new_vertex.x;
+	p->p.y=new_vertex.y;
+	p->p.z=new_vertex.z;
+	m.addPart(p);
+
+	vertices.push_back(new_vertex);
+	return vertices.size()-1;
+}
+
+double mix(int *colortab,int maxind,double ind)
+{
+	int indpre=(int)ind;
+	int indpost=indpre+1;
+	if (indpost>maxind) indpost=maxind;
+	int v1=colortab[indpre];
+	int v2=colortab[indpost];
+	double d1=ind-indpre;
+	double d2=indpost-ind;
+	double v=indpre==indpost?v1:d2*v1+d1*v2; //d1+d2==1
+	return v;
+}
+
+void GenoConv_F90::setColors(Model &m) //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
+{
+	//a rainbow on Joints: from the first one red, through middle green, to blue or violet - last
+	static int r[]={1,1,0,0,0,1};
+	static int g[]={0,1,1,1,0,0};
+	static int b[]={0,0,0,1,1,1};
+	int maxind=ARRAY_LENGTH(r)-1;
+
+	int joints_count=m.getJointCount();
+	for(int i=0;i<joints_count;i++)
+	{
+		Joint *j=m.getJoint(i);
+		double x=joints_count<2?0:(double)i/(joints_count-1); //0..1, postion in the rainbow
+		double ind=x*maxind;
+		j->vcolor.x=mix(r,maxind,ind);
+		j->vcolor.y=mix(g,maxind,ind);
+		j->vcolor.z=mix(b,maxind,ind);
+	}
+
+	int parts_count=m.getPartCount();
+	SList jlist;
+	for(int i=0;i<parts_count;i++)
+	{
+		Part *p=m.getPart(i);
+		jlist.clear();
+		int count=m.findJoints(jlist,p);
+		Pt3D averagecolor(0,0,0); //Parts will get averaged colors from all attached Joints
+		FOREACH(Joint*,j,jlist)
+			averagecolor+=j->vcolor;
+		if (count>5) count=5; //avoid too fat...
+		p->vsize=0.3+count/15.0; //the more Joints is attached to a Part, the fatter it is
+		p->vcolor=averagecolor/count;
+	}
+}
+
+void GenoConv_F90::perturbPartLocations(Model &m) //deterministic "body noise"
+{
+	for(int i=0;i<m.getPartCount();i++)
+	{
+		Part *p=m.getPart(i);
+		Pt3D noise(
+			((i+1)  %10)-4.5  ,
+			((3*i+5)%10)-4.5  ,
+			((7*i+2)%10)-4.5
+			); //-4.5 .. 4.5 in each axis
+		p->p+=noise/1000;
+	}
+}
Index: cpp/frams/genetics/f9/conv_f9.h
===================================================================
--- cpp/frams/genetics/f9/conv_f9.h	(revision 120)
+++ cpp/frams/genetics/f9/conv_f9.h	(revision 120)
@@ -0,0 +1,48 @@
+// This file is a part of the Framsticks GDK.
+// Copyright (C) 2002-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _CONV_F9_H_
+#define _CONV_F9_H_
+
+#include <ctype.h>
+#include <common/nonstd_math.h>
+#include <frams/model/modelparts.h>
+#include <frams/util/multimap.h>
+#include <frams/util/sstring.h>
+#include <frams/genetics/genoconv.h>
+#include <vector>
+using std::vector;
+
+
+extern const char* turtle_commands_f9;
+
+
+struct XYZ_LOC
+{
+	int x,y,z; //coordinates xyz of a vertex - represented as int's so that it is easy and safe to check identity. Could also be done using lists of Model's Parts, but that would involve comparing floats
+	XYZ_LOC() {x=y=z=0;}
+	void add(int delta[3]) {x+=delta[0]; y+=delta[1]; z+=delta[2];}
+	bool same_coordinates(const XYZ_LOC &loc) {return x==loc.x && y==loc.y && z==loc.z;}
+};
+
+
+// The f9->f0 converter
+class GenoConv_F90: public GenoConverter
+{
+public:
+	GenoConv_F90();
+
+	//implementation GenoConverter method
+	SString convert(SString &in, MultiMap *map);
+
+protected:
+	//auxiliary methods
+	int addSegment(Model &m,vector<XYZ_LOC> &vertices,const XYZ_LOC &new_vertex,int recently_added);
+	int findVertexAt(vector<XYZ_LOC> &vertices,const XYZ_LOC &new_vertex);
+	int addNewVertex(Model &m,vector<XYZ_LOC> &punkty,const XYZ_LOC &nowypunkt);
+	void setColors(Model &m); //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
+	void perturbPartLocations(Model &m); //deterministic "body noise"
+};
+
+#endif
Index: cpp/frams/genetics/f9/oper_f9.cpp
===================================================================
--- cpp/frams/genetics/f9/oper_f9.cpp	(revision 120)
+++ cpp/frams/genetics/f9/oper_f9.cpp	(revision 120)
@@ -0,0 +1,122 @@
+// This file is a part of the Framsticks GDK.
+// Copyright (C) 2002-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#include "oper_f9.h"
+#include <frams/genetics/f9/conv_f9.h>
+#include <common/nonstd.h> //randomN, rnd01
+
+
+#define FIELDSTRUCT GenoOper_f9
+static ParamEntry GENOf9param_tab[]=
+{
+	{"Genetics: f9",1,1,},
+	{"f9_mut",0,0,"Mutation probability","f 0 1 0.1",FIELD(mut_prob),"How many genes should be mutated during single mutation (1=all genes, 0.1=ten percent)",},
+	{0,},
+};
+#undef FIELDSTRUCT
+
+
+GenoOper_f9::GenoOper_f9()
+{
+	par.setParamTab(GENOf9param_tab);
+	par.select(this);
+	par.setDefault();
+	supported_format='9';
+}
+
+int GenoOper_f9::checkValidity(const char* gene)
+{
+	if (!gene[0]) return 1; //empty is not valid
+	bool ok=true;
+	int i;
+	for(i=0;i<strlen(gene);i++) if (!strchr(turtle_commands_f9,gene[i])) {ok=false; break;}
+	return ok ? GENOPER_OK : i+1;
+}
+
+///Remove all invalid letters from the genotype
+int GenoOper_f9::validate(char *&gene)
+{
+	SString validated; //new genotype (everything except turtle_commands_f9 is skipped)
+	for(int i=0;i<strlen(gene);i++)
+		if (strchr(turtle_commands_f9,gene[i])) validated+=gene[i];  //validated contains only turtle_commands_f9
+	free(gene);
+	gene=strdup(validated); //reallocate
+	return GENOPER_OK;
+}
+
+///Very simple mutation
+int GenoOper_f9::mutate(char *&gene,float &chg,int &method)
+{
+	method=0;
+	int changes=0,len=strlen(gene);
+	int symbols=strlen(turtle_commands_f9);
+
+	for(int i=0;i<len;i++)
+	{
+		if (rnd01<mut_prob) //normalize prob with the length of the genotype
+		{
+			char oldgene=gene[i];
+			gene[i]=turtle_commands_f9[randomN(symbols)];
+			if (gene[i]!=oldgene) changes++;
+		}
+	}
+
+	if (rnd01<mut_prob) //add or delete a random char
+	{
+		SString newgeno(gene);
+		if (randomN(2)==0) //add
+		{
+			int symbols=strlen(turtle_commands_f9);
+			int p=randomN(len+1);  //random location
+			//printf("before add: %s\n",(const char*)newgeno);
+			newgeno=newgeno.substr(0,p)+SString(turtle_commands_f9+randomN(symbols),1)+newgeno.substr(p);
+			//printf("after add: %s\n",(const char*)newgeno);
+			changes++;
+		} else if (len>1) //delete
+		{
+			int p=randomN(len);  //random location
+			//printf("before delete: %s\n",(const char*)newgeno);
+			newgeno=newgeno.substr(0,p)+newgeno.substr(p+1);
+			//printf("after delete: %s\n",(const char*)newgeno);
+			changes++;
+		}
+		free(gene);
+		gene=strdup(newgeno); //reallocate
+	}
+
+	chg=(float)changes/len;
+	return changes>0?GENOPER_OK:GENOPER_OPFAIL; //no changes => OPFAIL so that genman will call mutate again
+}
+
+///A simple one-point crossover
+int GenoOper_f9::crossOver(char *&g1,char *&g2,float& chg1,float& chg2)
+{
+	int len1=strlen(g1),len2=strlen(g2);
+	int p1=randomN(len1);  //random cut point for first genotype
+	int p2=randomN(len2);  //random cut point for second genotype
+	char *child1=(char*)malloc(p1+len2-p2+1);
+	char *child2=(char*)malloc(p2+len1-p1+1);
+	strncpy(child1,g1,p1);   strcpy(child1+p1,g2+p2);
+	strncpy(child2,g2,p2);   strcpy(child2+p2,g1+p1);
+	free(g1); g1=child1;
+	free(g2); g2=child2;
+	chg1=(float)p1/strlen(child1);
+	chg2=(float)p2/strlen(child2);
+	return GENOPER_OK;
+}
+
+///Applying some colors and font styles...
+unsigned long GenoOper_f9::style(const char *g, int pos)
+{
+	char ch=g[pos];
+	unsigned long style=GENSTYLE_CS(0,GENSTYLE_INVALID); //default, should be changed below
+	char *ptr=strchr((char*)turtle_commands_f9,ch);
+	if (ptr)
+	{
+		int pos=ptr-turtle_commands_f9;
+		int axis=pos/2;
+		style=GENSTYLE_RGBS(axis==0?200:0,axis==1?200:0,axis==2?200:0,GENSTYLE_NONE);
+	}
+	return style;
+}
Index: cpp/frams/genetics/f9/oper_f9.h
===================================================================
--- cpp/frams/genetics/f9/oper_f9.h	(revision 120)
+++ cpp/frams/genetics/f9/oper_f9.h	(revision 120)
@@ -0,0 +1,25 @@
+// This file is a part of the Framsticks GDK.
+// Copyright (C) 2002-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
+// Refer to http://www.framsticks.com/ for further information.
+
+#ifndef _GENOOPER_F9_H_
+#define _GENOOPER_F9_H_
+
+#include "../oper_fx.h"
+
+
+class GenoOper_f9 : public GenoOperators
+{
+public:
+	GenoOper_f9();
+	int checkValidity(const char *);
+	int validate(char *&);
+	int mutate(char *&g,float& chg,int &method);
+	int crossOver(char *&g1,char *&g2,float& chg1,float& chg2);
+	unsigned long style(const char *g, int pos);
+	char* getSimplest() {return "R";}
+
+	double mut_prob; //mutation probability
+};
+
+#endif
