Index: cpp/frams/model/defassign-f0-SDK.h
===================================================================
--- cpp/frams/model/defassign-f0-SDK.h	(revision 726)
+++ cpp/frams/model/defassign-f0-SDK.h	(revision 732)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2016  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
Index: cpp/frams/model/f0-SDK-classes.h
===================================================================
--- cpp/frams/model/f0-SDK-classes.h	(revision 726)
+++ cpp/frams/model/f0-SDK-classes.h	(revision 732)
@@ -155,5 +155,5 @@
  {"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),},
+ {"classObject",0,1+2,"neuron class","oNeuroClass",GETONLY(classObject),},
  {0,0,0,}
 };
Index: cpp/frams/model/model.cpp
===================================================================
--- cpp/frams/model/model.cpp	(revision 726)
+++ cpp/frams/model/model.cpp	(revision 732)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -9,4 +9,6 @@
 #include <common/loggers/loggers.h>
 
+#define F0_CHECKPOINT_LINE "checkpoint:"
+
 Model::Model()
 {
@@ -18,4 +20,6 @@
 {
 	partmappingchanged = 0;
+	using_checkpoints = false;
+	is_checkpoint = false;
 	buildstatus = empty;
 	modelfromgenotype = 0;
@@ -47,10 +51,8 @@
 	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++)
+	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);
@@ -58,6 +60,6 @@
 		addJoint(j);
 		j->attachToParts(oldj->part1->refno, oldj->part2->refno);
-	}}
-	{for (int i = 0; i < mod.getNeuroCount(); i++)
+	}
+	for (int i = 0; i < mod.getNeuroCount(); i++)
 	{
 		Neuro *oldn = mod.getNeuro(i);
@@ -66,25 +68,32 @@
 		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)
+	}
+	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);
+		}
+	}
+	updateRefno();
+	if (using_checkpoints)
+		for (vector<Model*>::const_iterator it = mod.checkpoints.begin(); it != mod.checkpoints.end(); it++)
+		{
+		Model *m = *it;
+		Model *n = new Model(*m, m->autobuildmaps, false, true);
+		checkpoints.push_back(n);
+		}
+}
+
+
+Model::Model(const Geno &src, bool buildmaps, bool _using_checkpoints, bool _is_checkpoint)
 	:autobuildmaps(buildmaps)
 {
-	init(src);
+	init(src, _using_checkpoints, _is_checkpoint);
 }
 
@@ -92,21 +101,30 @@
 {
 	clear();
-	open();
+	open(mod.isUsingCheckpoints(), mod.isCheckpoint());
 	internalCopy(mod);
 	buildstatus = mod.buildstatus;
 }
 
-Model::Model(const Model &mod, bool buildmaps)
+Model::Model(const Model &mod, bool buildmaps, bool _using_checkpoints, bool _is_checkpoint)
 	:autobuildmaps(buildmaps)
 {
 	init();
-	open();
+	open(_using_checkpoints, _is_checkpoint);
 	internalCopy(mod);
-	buildstatus = mod.buildstatus;
-}
-
-void Model::init(const Geno &src)
+	if (is_checkpoint)
+		close();
+	else
+		buildstatus = mod.buildstatus;
+	if (mod.map)
+		map = new MultiMap(*mod.map);
+	if (mod.f0map)
+		f0map = new MultiMap(*mod.f0map);
+}
+
+void Model::init(const Geno &src, bool _using_checkpoints, bool _is_checkpoint)
 {
 	init();
+	using_checkpoints = _using_checkpoints;
+	is_checkpoint = _is_checkpoint;
 	modelfromgenotype = 1;
 	geno = src;
@@ -146,4 +164,7 @@
 	geno = Geno();
 	f0geno = Geno();
+	for (vector<Model*>::iterator it = checkpoints.begin(); it != checkpoints.end(); it++)
+		delete *it;
+	checkpoints.clear();
 }
 
@@ -293,15 +314,15 @@
 Model::ItemType Model::itemTypeFromLinePrefix(const char* line)
 {
-struct PrefixAndItem { const char* prefix; ItemType type; };
-static const PrefixAndItem types[]={ {"m:",ModelType},{"p:",PartType},{"j:",JointType},{"n:",NeuronType},{"c:",NeuronConnectionType},{NULL} };
-for(const PrefixAndItem *t=types;t->prefix!=NULL;t++)
-	{
-	const char* in=line;
-	const char* pattern=t->prefix;
-	for(;*in==*pattern;in++,pattern++)
-		if (*pattern==':')
-			return t->type;
-	}
-return UnknownType;
+	struct PrefixAndItem { const char* prefix; ItemType type; };
+	static const PrefixAndItem types[] = { { "m:", ModelType }, { "p:", PartType }, { "j:", JointType }, { "n:", NeuronType }, { "c:", NeuronConnectionType }, { F0_CHECKPOINT_LINE, CheckpointType }, { NULL } };
+	for (const PrefixAndItem *t = types; t->prefix != NULL; t++)
+	{
+		const char* in = line;
+		const char* pattern = t->prefix;
+		for (; *in == *pattern; in++, pattern++)
+			if (*pattern == ':')
+				return t->type;
+	}
+	return UnknownType;
 }
 
@@ -311,5 +332,5 @@
 	f0warnposition = -1;
 	MultiMap *convmap = autobuildmaps ? new MultiMap() : NULL;
-	f0geno = (geno.getFormat() == '0') ? geno : geno.getConverted('0', convmap);
+	f0geno = (geno.getFormat() == '0') ? geno : geno.getConverted('0', convmap, using_checkpoints);
 	f0genoknown = 1;
 	if (f0geno.isInvalid())
@@ -331,7 +352,8 @@
 	MultiRange frommap;
 	LoggerToMemory mh(LoggerBase::Enable | LoggerBase::DontBlock);
+	Model *current_model = this;
 	for (; f0txt.getNextToken(pos, line, '\n'); lnum++)
 	{
-		const char* line_ptr=line.c_str();
+		const char* line_ptr = line.c_str();
 		for (; *line_ptr; line_ptr++)
 			if (!strchr(" \r\t", *line_ptr)) break;
@@ -339,16 +361,16 @@
 		if (!*line_ptr) continue;
 
-		const char* colon=strchr(line_ptr,':');
-		ItemType type=UnknownType;
+		const char* colon = strchr(line_ptr, ':');
+		ItemType type = UnknownType;
 		SString excluding_prefix;
-		if (colon!=NULL)
-			{
+		if (colon != NULL)
+		{
 			colon++;
-			type=itemTypeFromLinePrefix(line_ptr);
+			type = itemTypeFromLinePrefix(line_ptr);
 			for (; *colon; colon++)
 				if (!strchr(" \r\t", *colon)) break;
-			excluding_prefix=colon;
-			}
-		
+			excluding_prefix = colon;
+		}
+
 		if (autobuildmaps)
 		{
@@ -357,5 +379,12 @@
 		}
 		mh.reset();
-		if (addFromString(type, excluding_prefix, lnum, autobuildmaps ? (&frommap) : 0) == -1)
+		if (type == CheckpointType)
+		{
+			current_model->close();
+			current_model = new Model;
+			current_model->open(false, true);
+			checkpoints.push_back(current_model);
+		}
+		else if (current_model->addFromString(type, excluding_prefix, lnum, autobuildmaps ? (&frommap) : 0) == -1)
 		{
 			buildstatus = invalid;
@@ -371,5 +400,5 @@
 	}
 	mh.disable();
-	close();
+	current_model->close();
 	if (convmap)
 	{
@@ -503,4 +532,12 @@
 		}
 	}
+
+	for (vector<Model*>::const_iterator it = checkpoints.begin(); it != checkpoints.end(); it++)
+	{
+		Geno g = (*it)->getF0Geno();
+		gen += F0_CHECKPOINT_LINE "\n";
+		gen += g.getGenes();
+	}
+
 	g = Geno(gen.c_str(), '0');
 }
@@ -508,7 +545,9 @@
 //////////////
 
-void Model::open()
+void Model::open(bool _using_checkpoints, bool _is_checkpoint)
 {
 	if (buildstatus == building) return;
+	using_checkpoints = _using_checkpoints;
+	is_checkpoint = _is_checkpoint;
 	buildstatus = building;
 	modelfromgenotype = 0;
@@ -518,6 +557,21 @@
 }
 
+int Model::getCheckpointCount()
+{
+	return checkpoints.size();
+}
+
+Model* Model::getCheckpoint(int i)
+{
+	return checkpoints[i];
+}
+
 void Model::checkpoint()
-{}
+{
+	if (!using_checkpoints) return;
+	updateRefno();
+	Model *m = new Model(*this, false, false, true);
+	checkpoints.push_back(m);
+}
 
 void Model::setGeno(const Geno& newgeno)
@@ -544,5 +598,5 @@
 	if (buildstatus != building)
 		logPrintf("Model", "close", LOG_WARN, "Unexpected close() - no open()");
-	if (internalcheck(building_live_model ? LIVE_CHECK : FINAL_CHECK) > 0)
+	if (internalcheck(is_checkpoint ? CHECKPOINT_CHECK : (building_live_model ? LIVE_CHECK : FINAL_CHECK)) > 0)
 	{
 		buildstatus = valid;
@@ -589,5 +643,5 @@
 int Model::addFromString(ItemType item_type, const SString &singleline, const MultiRange* srcrange)
 {
-	return addFromString(item_type,singleline, 0, srcrange);
+	return addFromString(item_type, singleline, 0, srcrange);
 }
 
@@ -612,8 +666,8 @@
 	error_message = SString::empty();
 	ParamInterface::LoadOptions opts;
-	switch(item_type)
-		{
-		case PartType:
-		{
+	switch (item_type)
+	{
+	case PartType:
+	{
 		Param partparam(f0_part_paramtab);
 		Part *p = new Part();
@@ -626,8 +680,8 @@
 		if (srcrange) p->setMapping(*srcrange);
 		return getPartCount() - 1;
-		}
-
-		case ModelType:
-		{
+	}
+
+	case ModelType:
+	{
 		Param modelparam(f0_model_paramtab);
 		modelparam.select(this);
@@ -635,8 +689,8 @@
 		if (opts.parse_failed) { error_message = "Invalid 'm:'"; return -1; }
 		return 0;
-		}
-
-		case JointType:
-		{
+	}
+
+	case JointType:
+	{
 		Param jointparam(f0_joint_paramtab);
 		Joint *j = new Joint();
@@ -665,8 +719,8 @@
 			return -1;
 		}
-		}
-
-		case NeuronType:
-		{
+	}
+
+	case NeuronType:
+	{
 		Param neuroparam(f0_neuro_paramtab);
 		Neuro *nu = new Neuro();
@@ -714,8 +768,8 @@
 			return neurons.size() - 1;
 		}
-		}
-
-		case NeuronConnectionType:			
-		{
+	}
+
+	case NeuronConnectionType:
+	{
 		Param ncparam(f0_neuroconn_paramtab);
 		NeuroConn c;
@@ -736,9 +790,9 @@
 		error_message = SString::sprintf("Invalid reference to Neuro #%d", n1_ok ? c.n2_refno : c.n1_refno);
 		return -1;
-		}
-
-		case UnknownType: //handled by addFromString for uniform error handling
-			return -1;
-		}
+	}
+
+	case CheckpointType: case UnknownType: //handled by addFromString for uniform error handling
+		return -1;
+	}
 	return -1;
 }
@@ -747,24 +801,20 @@
 /////////////
 
-/** 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
-}
-
-////////////
-
-void Model::updateNeuroRefno()
-{
+void Model::updateRefno()
+{
+	for (int i = 0; i < parts.size(); i++)
+		getPart(i)->refno = i;
+	for (int i = 0; i < joints.size(); i++)
+	{
+		Joint *j = getJoint(i);
+		j->refno = i;
+		if (j->part1 && j->part2 && (j->part1 != j->part2))
+		{
+			j->p1_refno = j->part1->refno;
+			j->p2_refno = j->part2->refno;
+		}
+	}
 	for (int i = 0; i < neurons.size(); i++)
-	{
-		Neuro* n = (Neuro*)neurons(i);
-		n->refno = i;
-	}
+		getNeuro(i)->refno = i;
 }
 
@@ -779,12 +829,4 @@
 #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;
-}
-
 SString Model::nameForErrors() const
 {
@@ -802,4 +844,5 @@
 	int ret = 1;
 	shape = SHAPE_UNKNOWN;
+	updateRefno();
 	if ((parts.size() == 0) && (neurons.size() == 0)) return 0;
 	if (parts.size() == 0)
@@ -812,5 +855,4 @@
 			p = (Part*)parts(i);
 			p->owner = this;
-			p->refno = i;
 			if (checklevel > 0)
 				p->mass = 0.0;
@@ -907,5 +949,5 @@
 					}
 				}
-				if (check != LIVE_CHECK)
+				if ((check != LIVE_CHECK) && (check != CHECKPOINT_CHECK))
 				{
 					if (j->shape != Joint::SHAPE_FIXED)
@@ -935,6 +977,4 @@
 	}
 
-	updateNeuroRefno(); // valid refno is important for n-n connections check (later)
-
 	for (i = 0; i < neurons.size(); i++)
 	{
@@ -944,73 +984,78 @@
 	}
 
-	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 (check != CHECKPOINT_CHECK)
+	{
+
+		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++)
 				{
-					if (!(j->part2->flags&LINKFLAG))
+					j = (Joint*)joints(i);
+					if (j->part1->flags&LINKFLAG)
 					{
-						change = 1;
-						j->part2->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;
+						}
+						}
 				}
-				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))
-			{
-				logPrintf("Model", "internalCheck", LOG_ERROR, "Not all parts connected (eg. Part #0 and Part #%d)%s", i, nameForErrors().c_str());
+			}
+			for (i = 0; i < parts.size(); i++)
+			{
+				p = (Part*)parts(i);
+				if (!(p->flags&LINKFLAG))
+				{
+					logPrintf("Model", "internalCheck", LOG_ERROR, "Not all parts connected (eg. Part #0 and Part #%d)%s", i, nameForErrors().c_str());
+					ret = 0;
+					break;
+				}
+			}
+		}
+
+		for (i = 0; i < joints.size(); i++)
+		{
+			j = (Joint*)joints(i);
+			if (j->p1_refno == j->p2_refno)
+			{
+				logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal self connection, Joint #%d%s", i, nameForErrors().c_str());
 				ret = 0;
 				break;
 			}
-		}
-	}
-
-	for (i = 0; i < joints.size(); i++)
-	{
-		j = (Joint*)joints(i);
-		if (j->p1_refno == j->p2_refno)
-		{
-			logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal self connection, Joint #%d%s", i, nameForErrors().c_str());
-			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)))
-			{
-				logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal duplicate Joint #%d and Joint #%d%s", i, k, nameForErrors().c_str());
-				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)))
+				{
+					logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal duplicate Joint #%d and Joint #%d%s", i, k, nameForErrors().c_str());
+					ret = 0;
+					break;
+				}
+			}
+		}
+	}
+
 	if (shape == SHAPE_ILLEGAL)
 		ret = 0;
@@ -1250,4 +1295,36 @@
 //////////////////////
 
+int Model::elementToMap(ItemType type, int index)
+{
+	switch (type)
+	{
+	case PartType: return partToMap(index);
+	case JointType: return jointToMap(index);
+	case NeuronType: return neuroToMap(index);
+	default: return -1;
+	}
+}
+
+Model::TypeAndIndex Model::mapToElement(int map_index)
+{
+	if ((map_index >= 0) && (map_index < MODEL_MAPPING_OFFSET))
+		return TypeAndIndex(PartType, mapToPart(map_index));
+	if ((map_index >= MODEL_MAPPING_OFFSET) && (map_index < 2 * MODEL_MAPPING_OFFSET))
+		return TypeAndIndex(JointType, mapToJoint(map_index));
+	if ((map_index >= 2 * MODEL_MAPPING_OFFSET) && (map_index < 3 * MODEL_MAPPING_OFFSET))
+		return TypeAndIndex(NeuronType, mapToNeuro(map_index));
+	return TypeAndIndex();
+}
+
+int Model::partToMap(int i) { return MODEL_MAPPING_OFFSET + i; }
+int Model::jointToMap(int i) { return 2 * MODEL_MAPPING_OFFSET + i; }
+int Model::neuroToMap(int i) { return 3 * MODEL_MAPPING_OFFSET + i; }
+int Model::mapToPart(int i) { return i - MODEL_MAPPING_OFFSET; }
+int Model::mapToJoint(int i) { return i - 2 * MODEL_MAPPING_OFFSET; }
+int Model::mapToNeuro(int i) { return i - 3 * MODEL_MAPPING_OFFSET; }
+
+
+//////////////////////
+
 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(); } };
Index: cpp/frams/model/model.h
===================================================================
--- cpp/frams/model/model.h	(revision 726)
+++ cpp/frams/model/model.h	(revision 732)
@@ -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-2018  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -15,6 +15,4 @@
 
 extern ParamEntry f0_model_paramtab[];
-
-//#define EASYMAPDEBUG
 
 enum ModelBuildStatus { empty, building, invalid, valid };
@@ -64,4 +62,8 @@
 	/// make model map in build()
 	bool autobuildmaps;
+	/// supports adding checkpoints
+	bool using_checkpoints;
+	/// means less strict validation
+	bool is_checkpoint;
 	/// valid if build from f0 genotype
 	int f0errorposition;
@@ -75,4 +77,5 @@
 	SList parts, joints, neurons;
 	char partmappingchanged;
+	vector<Model*> checkpoints;
 
 	void internalCopy(const Model &mod);
@@ -95,16 +98,14 @@
 		EDITING_CHECK, ///< Used in Model::validate(). Default validation - does not modify elements of the Model.
 		FINAL_CHECK,   ///< Used in Model::close() when a Model is built from a genotype. Like EDITING_CHECK, but also calculates Joint::d and Joint::rot.
-		LIVE_CHECK     ///< used in Model::close() when a Model is built from a Creature. Like FINAL_CHECK but does not limit joint length which could make some liveModels invalid.
+		LIVE_CHECK,     ///< used in Model::close() when a Model is built from a Creature. Like FINAL_CHECK but does not limit joint length which could make some liveModels invalid.
+		CHECKPOINT_CHECK     ///< used when storing checkpoint models. Like LIVE_CHECK, excluding consistency check (disjoint parts are acceptable)
 	};
 protected:
 	ShapeType shape;
 
-	void updateNeuroRefno(); // set Neuro::refno for all neurons
 	SString nameForErrors() const;
 	int internalcheck(CheckType check);
 
-	void moveNeuro(int oldpos, int newpos);
-
-	void init(const Geno &srcgen);
+	void init(const Geno &srcgen, bool _using_checkpoints, bool _is_checkpoint);
 	void init();
 
@@ -134,6 +135,11 @@
 	int getErrorPosition(bool includingwarnings = false);
 	ShapeType getShapeType() const { return shape; }
+	bool isUsingCheckpoints() const { return using_checkpoints; }
+	bool isCheckpoint() const { return is_checkpoint; }
 
 	void updateRefno(); // set ::refno for all elements
+
+	int getCheckpointCount();
+	Model* getCheckpoint(int i);
 
 	/// The bounding box size. Valid if the model is valid. Read only.
@@ -153,6 +159,6 @@
 	   @see getMap()
 	   */
-	Model(const Geno &src, bool buildmaps = false);
-	Model(const Model &mod, bool buildmaps = false);
+	Model(const Geno &src, bool buildmaps = false, bool _using_checkpoints = false, bool _is_checkpoint = false);
+	Model(const Model &mod, bool buildmaps = false, bool _using_checkpoints = false, bool _is_checkpoint = false);
 	/** duplicate the model.
 		the resulting object's status is 'building' (opened).
@@ -255,5 +261,5 @@
 	void clear();
 
-	enum ItemType { UnknownType,ModelType,PartType,JointType,NeuronType,NeuronConnectionType };
+	enum ItemType { UnknownType, ModelType, PartType, JointType, NeuronType, NeuronConnectionType, CheckpointType };
 	static ItemType itemTypeFromLinePrefix(const char* line);
 	/** Execute single line of <B>f0</B> genotype.
@@ -264,9 +270,9 @@
 		@param srcrange source genotype range which will be mapped to this element
 		*/
-	int addFromString(ItemType item_type,const SString &singleline, int line_num, const MultiRange* srcrange = NULL);
+	int addFromString(ItemType item_type, const SString &singleline, int line_num, const MultiRange* srcrange = NULL);
 	/** Execute single line of <B>f0</B> genotype - compatiblity variant */
-	int addFromString(ItemType item_type,const SString &singleline, const MultiRange* srcrange = NULL);
+	int addFromString(ItemType item_type, const SString &singleline, const MultiRange* srcrange = NULL);
 	/** Execute single line of <B>f0</B> genotype - low level variant, used by Model::build(), error messages returned as string instead of calling logger */
-	int addFromStringNoLog(ItemType item_type,const SString &singleline, SString& error_message, const MultiRange* srcrange = 0);
+	int addFromStringNoLog(ItemType item_type, const SString &singleline, SString& error_message, const MultiRange* srcrange = 0);
 
 	/// separate build stages (for future use)
@@ -289,5 +295,5 @@
 	/// or modify the model created from the genotype.
 	/// Between open() and close() the model is not fully usable.
-	void open();
+	void open(bool _using_checkpoints = false, bool _is_checkpoint = false);
 
 	/// Current model written as f0 genotype while building
@@ -379,19 +385,21 @@
 	void buildUsingSolidShapeTypes(const Model& src_ballandstick_shapes, Part::Shape use_shape = Part::SHAPE_CYLINDER, float thickness = 0.2);
 
-#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
+protected:
+	static const int MODEL_MAPPING_OFFSET = 0x10000000;
+public:
+	static int elementToMap(ItemType t, int i);
+	struct TypeAndIndex
+	{
+		ItemType type; int index;
+		TypeAndIndex() :type(UnknownType), index(0) {}
+		TypeAndIndex(ItemType _type, int _index) :type(_type), index(_index) {}
+	};
+	static TypeAndIndex mapToElement(int i);
+	static int partToMap(int i);
+	static int jointToMap(int i);
+	static int neuroToMap(int i);
+	static int mapToPart(int i);
+	static int mapToJoint(int i);
+	static int mapToNeuro(int i);
 
 	static void makeGenToGenMap(MultiMap& result, const MultiMap& gen1tomodel, const MultiMap& gen2tomodel);
Index: cpp/frams/model/modelobj.cpp
===================================================================
--- cpp/frams/model/modelobj.cpp	(revision 726)
+++ cpp/frams/model/modelobj.cpp	(revision 732)
@@ -7,16 +7,17 @@
 ParamEntry modelobj_paramtab[] =
 {
-	{ "Model", 1, 16, "Model", },
+	{ "Model", 1, 21, "Model", },
 	{ "se", 0, PARAM_NOSTATIC, "startenergy", "f", FIELD(startenergy), },
 	{ "Vstyle", 0, PARAM_NOSTATIC, "vis_style", "s", FIELD(vis_style), },
 
-	{ "geno", 0, PARAM_NOSTATIC | PARAM_READONLY, "geno", "o Geno", GETONLY(geno), },
-	{ "newFromString", 0, 0, "create new object", "p oModel(s genotype)", PROCEDURE(p_newfromstring), },
-	{ "newFromGeno", 0, 0, "create new object", "p oModel(oGeno)", PROCEDURE(p_newfromgeno), },
+	{ "geno", 0, PARAM_NOSTATIC | PARAM_READONLY, "Geno", "oGeno", GETONLY(geno), },
+	{ "newFromString", 0, 0, "Create a new object", "p oModel(s genotype)", PROCEDURE(p_newfromstring), },
+	{ "newFromGeno", 0, 0, "Create a new object", "p oModel(oGeno)", PROCEDURE(p_newfromgeno), },
+	{ "newWithCheckpoints", 0, 0, "Create a new object", "p oModel(x Geno object or string genotype)", PROCEDURE(p_newwithcheckpoints), "Creates a Model with the \"Checkpoints\" option enabled. Genotype converters supporting Checkpoints provide a sequence of Models that reflects development stages of the creature (this sequence is used purely for debugging and visualization of phenotype growth/development). Checkpoint Models can be accessed using getCheckpoint(i) for i ranging from 0 to numcheckpoints-1. Models created without the Checkpoint option and Models coming from unsupported converters have numcheckpoints=0." },
 
-	{ "numparts", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "number of parts", "d", GETONLY(numparts), },
-	{ "numjoints", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "number of joints", "d", GETONLY(numjoints), },
-	{ "numneurons", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "number of neurons", "d", GETONLY(numneurons), },
-	{ "numconnections", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "number of neuron connections", "d", GETONLY(numconnections), },
+	{ "numparts", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Number of parts", "d", GETONLY(numparts), },
+	{ "numjoints", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Number of joints", "d", GETONLY(numjoints), },
+	{ "numneurons", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Number of neurons", "d", GETONLY(numneurons), },
+	{ "numconnections", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Number of neuron connections", "d", GETONLY(numconnections), },
 
 	{ "getPart", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "getPart (static model information)", "p oPart(d index)", PROCEDURE(p_getpart), },
@@ -24,8 +25,20 @@
 	{ "getNeuroDef", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "getNeuroDef", "p oNeuroDef(d index)", PROCEDURE(p_getneuro), },
 
-	{ "size_x", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "bounding box x size", "f", FIELD(size.x), "(size_x,size_y,size_z) are dimensions of the axis-aligned bounding box of the creature, including imaginary Part sizes (Part.s, usually 1.0). A creature consisting of a single default part has the size of (2.0,2.0,2.0) - twice the Part.s value (like a sphere diameter is twice its radius).\nSee also: Creature.moveAbs" },
-	{ "size_y", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "bounding box y size", "f", FIELD(size.y), "See Model.size_x" },
-	{ "size_z", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "bounding box z size", "f", FIELD(size.z), "See Model.size_x" },
+	{ "size_x", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "Bounding box x size", "f", FIELD(size.x), "(size_x,size_y,size_z) are dimensions of the axis-aligned bounding box of the creature, including imaginary Part sizes (Part.s, usually 1.0). A creature consisting of a single default part has the size of (2.0,2.0,2.0) - twice the Part.s value (like a sphere diameter is twice its radius).\nSee also: Creature.moveAbs" },
+	{ "size_y", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "Bounding box y size", "f", FIELD(size.y), "See Model.size_x" },
+	{ "size_z", 0, PARAM_READONLY | PARAM_NOSTATIC | PARAM_DEPRECATED, "Bounding box z size", "f", FIELD(size.z), "See Model.size_x" },
 	{ "bboxSize", 0, PARAM_READONLY | PARAM_NOSTATIC, "Bounding box size", "oXYZ", GETONLY(bboxsize) },
+	{ "numcheckpoints", 0, PARAM_DONTSAVE | PARAM_READONLY | PARAM_NOSTATIC, "Number of checkpoints", "d", GETONLY(numcheckpoints) },
+	{ "getCheckpoint", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "getCheckpoint", "p oModel(d index)", PROCEDURE(p_getcheckpoint),
+	"Checkpoint Model objects are only valid as long as the parent Model object exists.\n"
+	"See also: Model.newWithCheckpoints()\n\n"
+	"// incorrect usage - calling getCheckpoint() on a temporary object:\n"
+	"var c=Model.newWithCheckpoints(\"XXX\").getCheckpoint(1).genotype.geno;\n\n"
+	"// correct usage - keeping the parent Model reference in 'm':\n"
+	"var m=Model.newWithCheckpoints(\"XXX\");\n"
+	"var c=m.getCheckpoint(1).genotype.geno;\n"
+	},
+	{ "shape_type", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Shape type", "d 0 3 ~Unknown~Illegal~Ball-and-stick~Solids", GETONLY(shape_type) },
+	{ "solid_model", 0, PARAM_DONTSAVE | PARAM_NOSTATIC | PARAM_READONLY, "Solid shapes model", "oModel", GETONLY(solid_model), "Conversion of this Model to solid shapes. Note! Only available when this Model has shape_type==2 (Ball-and-stick)." },
 
 	{ 0, 0, 0, },
@@ -57,9 +70,29 @@
 }
 
+void ModelObj::p_newwithcheckpoints(ExtValue *args, ExtValue *ret)
+{
+	Model *m = NULL;
+	if (args[0].getType() == TString)
+		m = new Model(Geno(args[0].getString()), false, true);
+	else
+	{
+		Geno *g = GenoObj::fromObject(args[0].getObject(), false);
+		if (g)
+			m = new Model(*g, false, true);
+		else
+			logPrintf("Model", "newWithCheckpoints", LOG_ERROR, "Geno or string expected, %s found", args[0].typeDescription().c_str());
+	}
+
+	if (m != NULL)
+		*ret = makeDynamicObject(m);
+	else
+		ret->setEmpty();
+}
+
 Param& ModelObj::getStaticParam()
 {
 #ifdef __CODEGUARD__
 	static ModelObj static_modelobj;
-	static Param static_modelparam(modelobj_paramtab,&static_modelobj);
+	static Param static_modelparam(modelobj_paramtab, &static_modelobj);
 #else
 	static Param static_modelparam(modelobj_paramtab);
@@ -131,2 +164,29 @@
 	*ret = Pt3D_Ext::makeDynamicObject(new Pt3D_Ext(size));
 }
+
+void ModelObj::p_getcheckpoint(PARAMPROCARGS)
+{
+	int i = args->getInt();
+	if ((i < 0) || (i >= getCheckpointCount()))
+	{
+		ret->setEmpty();
+		return;
+	}
+	ret->setObject(makeStaticObject(getCheckpoint(i)));
+}
+
+void ModelObj::get_solid_model(ExtValue *ret)
+{
+	if (getShapeType() != Model::SHAPE_BALL_AND_STICK)
+		ret->setEmpty();
+	Model *m = new Model;
+	m->open();
+	m->buildUsingSolidShapeTypes(*this);
+	m->close();
+	*ret = makeDynamicObject(m);
+}
+
+void ModelObj::get_shape_type(ExtValue *ret)
+{
+	ret->setInt(getShapeType());
+}
Index: cpp/frams/model/modelobj.h
===================================================================
--- cpp/frams/model/modelobj.h	(revision 726)
+++ cpp/frams/model/modelobj.h	(revision 732)
@@ -14,4 +14,5 @@
 	PARAMPROCDEF(p_newfromstring);
 	PARAMPROCDEF(p_newfromgeno);
+	PARAMPROCDEF(p_newwithcheckpoints);
 
 #define GETDELEGATE(name,type,value) PARAMGETDEF(name) {arg1->set ## type (value);}
@@ -20,4 +21,5 @@
 	GETDELEGATE(numneurons, Int, getNeuroCount())
 	GETDELEGATE(numconnections, Int, getConnectionCount())
+	GETDELEGATE(numcheckpoints, Int, getCheckpointCount())
 #undef GETDELEGATE
 
@@ -26,4 +28,7 @@
 	PARAMPROCDEF(p_getneuro);
 	PARAMGETDEF(bboxsize);
+	PARAMPROCDEF(p_getcheckpoint);
+	PARAMGETDEF(shape_type);
+	PARAMGETDEF(solid_model);
 
 #undef STATRICKCLASS
