Index: cpp/frams/genetics/f1/conv_f1.cpp
===================================================================
--- cpp/frams/genetics/f1/conv_f1.cpp	(revision 534)
+++ cpp/frams/genetics/f1/conv_f1.cpp	(revision 671)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -10,240 +10,243 @@
 #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 };
+//#define v1f1COMPATIBLE //as in ancient Framsticks 1.x
+
+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),first_part_mapping(NULL),energ(0),energ_div(0) {}
-~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)
-		{
+	Builder(const char*g, int mapping = 0) :invalid(0), genbegin(g), usemapping(mapping), first_part_mapping(NULL), model_energy(0), model_energy_max(0) {}
+	~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 model_energy, model_energy_max;
+	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) logPrintf("GenoConvF1","addInput",LOG_WARN,
-					    "illegal neuron connection %d <- %d (ignored)",n1,n2);
+		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) logPrintf("GenoConvF1", "addInput", LOG_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);
+		}
+		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);
-			}
-		}
+	}
+	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;
+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::normalizeBiol4()
+{
+	double sum = muscle_power + assimilation + stamina + ingestion;
+	muscle_power /= sum;
+	assimilation /= sum;
+	stamina /= sum;
+	ingestion /= sum;
 }
 
 /** main conversion function - with conversion map support */
-SString GenoConv_f1::convert(SString &i,MultiMap *map)
-{
-const char* g=i.c_str();
-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().getGenes();
-}
-
-void Builder::setPartMapping(int p,const char* g)
-{
-if (!usemapping) return;
-const MultiRange *r=makeRange(g);
-if (p<0)
+SString GenoConv_f1::convert(SString &i, MultiMap *map)
+{
+	const char* g = i.c_str();
+	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.model_energy_max > 0) ? (builder.model_energy / builder.model_energy_max) : 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().getGenes();
+}
+
+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;
+		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.twist, 0, c.curvedness);
+	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.twist += (1.58 - c.twist)*0.3; setPartMapping(part1, g); break;
+		case 'q': c.twist += (-1.58 - c.twist)*0.3; setPartMapping(part1, g); break;
 #ifdef v1f1COMPATIBLE
-	case 'L': c.dlug+=(3.0-c.dlug)*0.3; setPartMapping(part1,g); break;
+		case 'L': c.length += (3.0 - c.length)*0.3; setPartMapping(part1, g); break;
 #else
-	case 'L': c.dlug+=(2.0-c.dlug)*0.3; setPartMapping(part1,g); break;
+		case 'L': c.length += (2.0 - c.length)*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
-			{
-			logMessage("GenoConv_F1","grow",1,"Illegal neuron position (ignored)");
-			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++;
+		case 'l': c.length += (0.33 - c.length)*0.3; setPartMapping(part1, g); break;
+		case 'A': c.assimilation += (1 - c.assimilation)*0.8;	c.normalizeBiol4(); setPartMapping(part1, g);  break;
+		case 'a': c.assimilation -= c.assimilation*0.4;	c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'I': c.ingestion += (1 - c.ingestion)*0.8;	c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'i': c.ingestion -= c.ingestion*0.4;	c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'S': c.stamina += (1 - c.stamina)*0.8; c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 's': c.stamina -= c.stamina*0.4; c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'M': c.muscle_power += (1 - c.muscle_power)*0.8;	c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'm': c.muscle_power -= c.muscle_power*0.4;	c.normalizeBiol4(); setPartMapping(part1, g); break;
+		case 'C': c.curvedness += (2.0 - c.curvedness)*0.25;	setPartMapping(part1, g); break;
+		case 'c': c.curvedness += (-2.0 - c.curvedness)*0.25; setPartMapping(part1, g); break;
+		case 'F': c.friction += (4 - c.friction)*0.2; setPartMapping(part1, g); break;
+		case 'f': c.friction -= c.friction*0.2; setPartMapping(part1, g); break;
+		case 'W': c.weight += (2.0 - c.weight)*0.3; setPartMapping(part1, g); break;
+		case 'w': c.weight += (0.5 - c.weight)*0.3; setPartMapping(part1, g); break;
+		case 'E': c.energy += (10.0 - c.energy)*0.1; setPartMapping(part1, g); break;
+		case 'e': c.energy -= c.energy*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.visual_size += (0.7 - c.visual_size)*0.25; setPartMapping(part1, g); break;
+		case 'h': c.visual_size += (0.05 - c.visual_size)*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
+			{
+				logMessage("GenoConv_F1", "grow", 1, "Illegal neuron position (ignored)");
+				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.weight) / (part->mass + 1.0); // v=m*d
+				//			part->volume+=1.0/c.weight;
+				part->mass += 1.0;
+			}
+			model_energy += 0.9*c.energy + 0.1;
+			model_energy_max += 1.0;
+
+			int part2 = growPart(c, g);
+			growJoint(part1, part2, k, c, g);
+			//		est* e = new est(*s,*s2,k,c,zz,this);
+
+			// attenuate properties as they are propagated along the structure
+			c.length = 0.5*c.length + 0.5*stdprops.length;
+			c.visual_size = 0.5*c.visual_size + 0.5*stdprops.visual_size;
+			c.curvedness = 0.66*c.curvedness;
+			c.twist = 0.66*c.twist;
+			c.friction = 0.8*c.friction + 0.2*stdprops.friction;
+
+			c.assimilation = 0.8*c.assimilation + 0.2*stdprops.assimilation;
+			c.stamina = 0.8*c.stamina + 0.2*stdprops.stamina;
+			c.muscle_power = 0.8*c.muscle_power + 0.2*stdprops.muscle_power;
+			c.ingestion = 0.8*c.ingestion + 0.2*stdprops.ingestion;
+			c.weight += (stdprops.weight - c.weight)*0.5;
+			c.normalizeBiol4();
+
+			if (c.muscle_reset_range) c.muscle_bend_range = 1.0; else c.muscle_reset_range = true;
+			grow(part2, g + 1, Pt3D_0, c);
+			return;
+		}
+		case '(':
+		{
+			setPartMapping(part1, g);
+			SList ga;
+			int i, count;
+			count = countBranches(g + 1, ga);
+			c.muscle_reset_range = false;
+			c.muscle_bend_range = 1.0 / count;
+			for (i = 0; i < count; i++)
+				grow(part1, (char*)ga(i), k + Pt3D(0, 0, -M_PI + (i + 1)*(2*M_PI / (count + 1))), c);
+			return;
+		}
+		case ' ': case '\t': case '\n': case '\r': break;
+		default: invalid = 1; return;
+		}
+		g++;
 	}
 }
@@ -251,239 +254,239 @@
 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;
+	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.size();
-}
-
-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: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));
+		case ',': if (!gl) out += (void*)(g + 1);
+		}
+		g++;
+	}
+	return out.size();
+}
+
+int Builder::growJoint(int part1, int part2, Pt3D &angle, F1Props &c, const char *g)
+{
+	double len = min(2.0, c.length);
+	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.stamina, c.cred, c.cgreen, c.cblue);
+	lastjoint_muscle_power = c.muscle_power;
+	return model.singleStepBuild(tmp, makeRange(g));
+}
+
+int Builder::growPart(F1Props &c, const char *g)
+{
+	sprintf(tmp, "p:dn=%lg,fr=%lg,ing=%lg,as=%lg,vs=%g,vr=%g,vg=%g,vb=%g",
+		1.0 / c.weight, c.friction, c.ingestion, c.assimilation, c.visual_size, 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;
+	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.muscle_bend_range);
+				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 )
+			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)
 				{
-				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)
+					if (cls->getPreferredLocation() == 2)
 						last_f1_neuro->attachToJoint(getLastJoint());
-					else if (cls->getPreferredLocation()==1)
+					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().c_str(),"|"))
-						{
-						neuro_cls_param->setDoubleById("p",lastjoint_muscle_power);
-						neuro_cls_param->setDoubleById("r",props.bendrange);
-						}
-					else if (!strcmp(cls->getName().c_str(),"@"))
-						{
-						neuro_cls_param->setDoubleById("p",lastjoint_muscle_power);
-						}
+					if (!strcmp(cls->getName().c_str(), "|"))
+					{
+						neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
+						neuro_cls_param->setDoubleById("r", props.muscle_bend_range);
+					}
+					else if (!strcmp(cls->getName().c_str(), "@"))
+					{
+						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;
+			}
+			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=ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str());
-	paInt relative=ExtValue::getInt(trim(SString(begin,colon-begin)).c_str(),false);
-	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,ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str()));
-	if (usemapping) receptor->addMapping(*makeRange(begin,end-1));
-	}
-else if ((begin[0]=='>')&&(begin[1]))
-	{
-	Neuro *out=model.addNewNeuro();
-	out->addInput(last_f1_neuro,ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str()));
-	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",ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str()));
-else if (*begin=='=') addClassParam("in",ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str()));
-else if (*begin=='/') addClassParam("si",ExtValue::getDouble(trim(SString(colon+1,end-(colon+1))).c_str()));
-else if (islower(begin[0]))
-	{
-	SString name(begin,colon-begin);
-	SString value(colon+1,end-(colon+1));
-	addClassParam(name.c_str(),value.c_str());
-	}
-}
+		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 conn_weight = ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str());
+		paInt relative = ExtValue::getInt(trim(SString(begin, colon - begin)).c_str(), false);
+		int this_refno = neuro_f1_to_f0.size() - 1;
+		addOrRememberInput(this_refno, this_refno + relative, conn_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, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+		if (usemapping) receptor->addMapping(*makeRange(begin, end - 1));
+	}
+	else if ((begin[0] == '>') && (begin[1]))
+	{
+		Neuro *out = model.addNewNeuro();
+		out->addInput(last_f1_neuro, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+		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.muscle_bend_range);
+			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", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (*begin == '=') addClassParam("in", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (*begin == '/') addClassParam("si", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
+	else if (islower(begin[0]))
+	{
+		SString name(begin, colon - begin);
+		SString value(colon + 1, end - (colon + 1));
+		addClassParam(name.c_str(), value.c_str());
+	}
+}
Index: cpp/frams/genetics/f1/conv_f1.h
===================================================================
--- cpp/frams/genetics/f1/conv_f1.h	(revision 534)
+++ cpp/frams/genetics/f1/conv_f1.h	(revision 671)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -11,9 +11,9 @@
 struct F1Props
 {
-	double dlug,skr,masa,tarcie,ruch,asym,odpor,wchl,rot,energ;
-	double bendrange;
-	int resetrange;
-	double grub,cred,cgreen,cblue;
-	void wykluczanie();
+	double length, curvedness, weight, friction, muscle_power, assimilation, stamina, ingestion, twist, energy;
+	double muscle_bend_range;
+	bool muscle_reset_range;
+	double visual_size, cred, cgreen, cblue;
+	void normalizeBiol4();
 };
 
@@ -49,5 +49,5 @@
  Another example:
 
- "X[G][-1:2.3][-2:3.4]" - The first neuron is the standalone G receptor. Other neuron can use its output 
+ "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
@@ -57,18 +57,18 @@
  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
+ \image html nn-ex4.gif
+
+ */
+class GenoConv_f1 : public GenoConverter
 {
 public:
-GenoConv_f1()
+	GenoConv_f1()
 	{
-	name="Recursive encoding";
-	in_format='1';
-	mapsupport=1;
+		name = "Recursive encoding";
+		in_format = '1';
+		mapsupport = 1;
 	}
-SString convert(SString &i,MultiMap *map);
-~GenoConv_f1() {}
+	SString convert(SString &i, MultiMap *map);
+	~GenoConv_f1() {}
 };
 
