Index: cpp/frams/genetics/fS/fS_oper.cpp
===================================================================
--- cpp/frams/genetics/fS/fS_oper.cpp	(revision 967)
+++ cpp/frams/genetics/fS/fS_oper.cpp	(revision 969)
@@ -15,6 +15,5 @@
 				{"fS_mut_rem_part",         0, 0, "Remove part",              "f 0 100 10", FIELD(prob[FS_REM_PART]),             "mutation: probability of deleting a part",},
 				{"fS_mut_mod_part",         0, 0, "Modify part",              "f 0 100 10", FIELD(prob[FS_MOD_PART]),             "mutation: probability of changing the part type",},
-				{"fS_mut_add_joint",        0, 0, "Add joint",                "f 0 100 10", FIELD(prob[FS_ADD_JOINT]),            "mutation: probability of adding a joint",},
-				{"fS_mut_rem_joint",        0, 0, "Remove joint",             "f 0 100 10", FIELD(prob[FS_REM_JOINT]),            "mutation: probability of removing a joint",},
+				{"fS_mut_change_joint",        0, 0, "Change joint",                "f 0 100 10", FIELD(prob[FS_CHANGE_JOINT]),            "mutation: probability of changing a joint",},
 				{"fS_mut_add_param",        0, 0, "Add param",                "f 0 100 10", FIELD(prob[FS_ADD_PARAM]),            "mutation: probability of adding a parameter",},
 				{"fS_mut_rem_param",        0, 0, "Remove param",             "f 0 100 10", FIELD(prob[FS_REM_PARAM]),            "mutation: probability of removing a parameter",},
@@ -41,5 +40,5 @@
 	par.select(this);
 	par.setDefault();
-	supported_format = "S";
+	supported_format = 'S';
 }
 
@@ -53,5 +52,5 @@
 		{
 			logPrintf("GenoOper_fS", "checkValidity", LOG_ERROR, "Invalid part size");
-			return 1 + errorPosition;
+			return errorPosition;
 		}
 	}
@@ -92,9 +91,6 @@
 			result = changePartType(genotype, availableTypes);
 			break;
-		case FS_ADD_JOINT:
-			result = addJoint(genotype);
-			break;
-		case FS_REM_JOINT:
-			result = removeJoint(genotype);
+		case FS_CHANGE_JOINT:
+			result = changeJoint(genotype);
 			break;
 		case FS_ADD_PARAM:
@@ -214,5 +210,5 @@
 const char* GenoOper_fS::getSimplest()
 {
-	return "S:C{x=0.80599;y=0.80599;z=0.80599}";
+	return "C{x=0.80599;y=0.80599;z=0.80599}";
 }
 
@@ -233,9 +229,9 @@
 		style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
 	}
-	else if (isdigit(ch) || strchr(".=", ch)) // Numerical value
+	else if (isdigit(ch) || strchr(".", ch)) // Numerical value
 	{
 		style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_NONE);
 	}
-	else if(strchr("()_;[],", ch))
+	else if(strchr("()_;[],=", ch))
 	{
 		style = GENSTYLE_CS(0, GENSTYLE_BOLD); // Important char
@@ -292,5 +288,5 @@
 
 	Substring substring(&partType, 0, 1);
-	Node *newNode = new Node(substring, node->modifierMode, node->paramMode, node->cycleMode, node);
+	Node *newNode = new Node(substring, node);
 	// Add random rotation
 	string rotationParams[]{ROT_X, ROT_Y, ROT_Z};
@@ -298,10 +294,10 @@
 	{
 		for(int i=0; i < 3; i++)
-			newNode->params[rotationParams[i]] = RndGen.Uni(-90, 90);
+			newNode->params[rotationParams[i]] = RndGen.Uni(-M_PI / 2, M_PI / 2);
 	}
 	else
 	{
 		string selectedParam = rotationParams[rndUint(3)];
-		newNode->params[selectedParam] = RndGen.Uni(-90, 90);
+		newNode->params[selectedParam] = RndGen.Uni(-M_PI / 2, M_PI / 2);
 	}
 	string rParams[]{RX, RY, RZ};
@@ -309,10 +305,10 @@
 	{
 		for(int i=0; i < 3; i++)
-			newNode->params[rParams[i]] = RndGen.Uni(-90, 90);
+			newNode->params[rParams[i]] = RndGen.Uni(-M_PI / 2, M_PI / 2);
 	}
 	else
 	{
 		string selectedParam = rParams[rndUint(3)];
-		newNode->params[selectedParam] = RndGen.Uni(-90, 90);
+		newNode->params[selectedParam] = RndGen.Uni(-M_PI / 2, M_PI / 2);
 	}
 	// Assign part size to default value
@@ -324,5 +320,5 @@
 	double relativeVolume = volume / volumeMultiplier;    // Volume without applying modifiers
 
-	double newRadius = Node::calculateRadiusFromVolume(newNode->partType, relativeVolume);
+	double newRadius = std::cbrt(relativeVolume / volumeMultipliers.at(newNode->partType));
 	newNode->params[SIZE_X] = newRadius;
 	newNode->params[SIZE_Y] = newRadius;
@@ -333,7 +329,7 @@
 	{
 		geno.getState();
-		newNode->changeSizeParam(SIZE_X, fS_Genotype::randomParamMultiplier(), true);
-		newNode->changeSizeParam(SIZE_Y, fS_Genotype::randomParamMultiplier(), true);
-		newNode->changeSizeParam(SIZE_Z, fS_Genotype::randomParamMultiplier(), true);
+		newNode->changeSizeParam(SIZE_X, true);
+		newNode->changeSizeParam(SIZE_Y, true);
+		newNode->changeSizeParam(SIZE_Z, true);
 	}
 	return true;
@@ -374,5 +370,5 @@
 		int index = rndUint(availTypesLength);
 		if (availTypes[index] == SHAPETYPE_TO_GENE.at(randomNode->partType))
-			index = (index + 1 + rndUint(availTypesLength)) % availTypesLength;
+			index = (index + 1 + rndUint(availTypesLength - 1)) % availTypesLength;
 		char newTypeChr = availTypes[index];
 
@@ -385,17 +381,32 @@
 #endif
 
-		if (ensureCircleSection)
-		{
-			geno.getState();
-			if (randomNode->partType == Part::Shape::SHAPE_CUBOID
-				|| (randomNode->partType == Part::Shape::SHAPE_CYLINDER && newType == Part::Shape::SHAPE_ELLIPSOID))
-			{
-				double sizeMultiplier = randomNode->getParam(SIZE) * randomNode->state->s;
-				double relativeVolume = randomNode->calculateVolume() / pow(sizeMultiplier, 3.0);
-				double newRelativeRadius = Node::calculateRadiusFromVolume(newType, relativeVolume);
-				randomNode->params[SIZE_X] = newRelativeRadius;
-				randomNode->params[SIZE_Y] = newRelativeRadius;
-				randomNode->params[SIZE_Z] = newRelativeRadius;
-			}
+		geno.getState();
+		double sizeMultiplier = randomNode->getParam(SIZE) * randomNode->state->s;
+		double relativeVolume = randomNode->calculateVolume() / pow(sizeMultiplier, 3.0);
+
+		if(!ensureCircleSection || newType == Part::Shape::SHAPE_CUBOID || (randomNode->partType == Part::Shape::SHAPE_ELLIPSOID && newType == Part::Shape::SHAPE_CYLINDER))
+		{
+			double radiusQuotient = std::cbrt(volumeMultipliers.at(randomNode->partType) / volumeMultipliers.at(newType));
+			randomNode->params[SIZE_X] = randomNode->getParam(SIZE_X) * radiusQuotient;
+			randomNode->params[SIZE_Y] = randomNode->getParam(SIZE_Y) * radiusQuotient;
+			randomNode->params[SIZE_Z] = randomNode->getParam(SIZE_Z) * radiusQuotient;
+		}
+		else if(randomNode->partType == Part::Shape::SHAPE_CUBOID && newType == Part::Shape::SHAPE_CYLINDER)
+		{
+			double newRadius = 0.5 * (randomNode->getParam(SIZE_X) + randomNode->getParam(SIZE_Y));
+			randomNode->params[SIZE_X] = newRadius;
+			randomNode->params[SIZE_Y] = newRadius;
+			randomNode->params[SIZE_Z] = 0.5 * relativeVolume / (M_PI * newRadius * newRadius);
+		}
+		else if(newType == Part::Shape::SHAPE_ELLIPSOID)
+		{
+			double newRelativeRadius = cbrt(relativeVolume / volumeMultipliers.at(newType));
+			randomNode->params[SIZE_X] = newRelativeRadius;
+			randomNode->params[SIZE_Y] = newRelativeRadius;
+			randomNode->params[SIZE_Z] = newRelativeRadius;
+		}
+		else
+		{
+			throw fS_Exception("Invalid part type", 1);
 		}
 		randomNode->partType = newType;
@@ -405,41 +416,17 @@
 }
 
-bool GenoOper_fS::addJoint(fS_Genotype &geno)
+bool GenoOper_fS::changeJoint(fS_Genotype &geno)
 {
 	if (geno.startNode->children.empty())
 		return false;
 
-	Node *randomNode;
-	for (int i = 0; i < mutationTries; i++)
-	{
-		char randomJoint = JOINTS[rndUint(JOINT_COUNT)];
-		randomNode = geno.chooseNode(1);        // First part does not have joints
-		if (randomNode->joint == DEFAULT_JOINT)
-		{
-			randomNode->joint = randomJoint;
-			return true;
-		}
-	}
-	return false;
-}
-
-
-bool GenoOper_fS::removeJoint(fS_Genotype &geno)
-{
-	// This operator may can lower success rate that others, as it does not work when there is only one node
-	if (geno.startNode->children.size() < 1) // Only one node; there are no joints
-		return false;
-
-	// Choose a node with joints
-	for (int i = 0; i < mutationTries; i++)
-	{
-		Node *randomNode = geno.chooseNode(1);    // First part does not have joints
-		if (randomNode->joint != DEFAULT_JOINT)
-		{
-			randomNode->joint = DEFAULT_JOINT;
-			return true;
-		}
-	}
-	return false;
+	Node *randomNode = geno.chooseNode(1);        // First part does not have joints
+	int jointLen  = ALL_JOINTS.length();
+	int index = rndUint(jointLen);
+	if (ALL_JOINTS[index] == randomNode->joint)
+		index = (index + 1 + rndUint(jointLen - 1)) % jointLen;
+
+	randomNode->joint = ALL_JOINTS[index];
+	return true;
 }
 
@@ -450,13 +437,10 @@
 	if (paramCount == int(PARAMS.size()))
 		return false;
-	string selectedParam = PARAMS[rndUint(PARAMS.size())];
-	// Not allow 'j' parameter when the cycle mode is not on
-	if (selectedParam == JOINT_DISTANCE && !geno.startNode->cycleMode)
-		return false;
-	if (randomNode->params.count(selectedParam) > 0)
+	string key = PARAMS[rndUint(PARAMS.size())];
+	if (randomNode->params.count(key) > 0)
 		return false;
 	// Do not allow invalid changes in part size
-	bool isRadiusOfBase = selectedParam == SIZE_X || selectedParam == SIZE_Y;
-	bool isRadius = isRadiusOfBase || selectedParam == SIZE_Z;
+	bool isRadiusOfBase = key == SIZE_X || key == SIZE_Y;
+	bool isRadius = isRadiusOfBase || key == SIZE_Z;
 	if (ensureCircleSection && isRadius)
 	{
@@ -467,5 +451,5 @@
 	}
 	// Add modified default value for param
-	randomNode->params[selectedParam] = defaultParamValues.at(selectedParam);
+	randomNode->params[key] = mutateCreep('f', defaultValues.at(key), minValues.at(key), maxValues.at(key), true);
 	return true;
 }
@@ -501,14 +485,11 @@
 			advance(it, rndUint(paramCount));
 
-			double multiplier = fS_Genotype::randomParamMultiplier();
-
-
 			// Do not allow invalid changes in part size
 			if (it->first != SIZE_X && it->first != SIZE_Y && it->first != SIZE_Z)
 			{
-				it->second *= multiplier;
+				it->second = GenoOperators::mutateCreep('f', it->second, minValues.at(it->first), maxValues.at(it->first), true);
 				return true;
 			} else
-				return randomNode->changeSizeParam(it->first, multiplier, ensureCircleSection);
+				return randomNode->changeSizeParam(it->first, ensureCircleSection);
 		}
 	}
@@ -607,5 +588,5 @@
 			advance(it, rndUint(inputCount));
 
-			it->second = GenoOperators::getMutatedNeuroClassProperty(it->second, selectedNeuron, -1);
+			it->second = GenoOperators::getMutatedNeuronConnectionWeight(it->second);
 			return true;
 		}
@@ -673,27 +654,4 @@
 
 	fS_Neuron *neu = neurons[rndUint(neurons.size())];
-	SyntParam par = neu->classProperties();
-
-	if (par.getPropCount() > 0)
-	{
-		int i = rndUint(par.getPropCount());
-		if (*par.type(i) == 'f')
-		{
-			double change = GenoOperators::getMutatedNeuroClassProperty(par.getDouble(i), neu, GenoOperators::NEUROCLASS_PROP_OFFSET + i);
-			par.setDouble(i, change);
-		}
-		SString line;
-		int tmp = 0;
-		par.update(&line);
-		SString props;
-		line.getNextToken(tmp, props, '\n'); // removal of newline character
-		if (props != "")
-		{
-			SString det = neu->getClass()->name + ": " + props;
-			neu->setDetails(det);
-			return true;
-		}
-	}
-
-	return false;
-}
+	return GenoOperators::mutateRandomNeuroClassProperty(neu);
+}
