#include <iostream>
#include <fstream>
#include <map>

#include <i3d/draw.h>
#include <i3d/morphology.h>
#include <i3d/transform.h>
#include <i3d/DistanceTransform.h>
#include <i3d/filters.h>
#include <i3d/convolution.h>

#include "TriangleMesh.h"
#include "rnd_generators.h"
#include "texture/texture.h"

#undef min
#undef max

//some debug-code enabling triggers
// #define SAVE_INTERMEDIATE_IMAGES

#define TEXTURE_QUANTIZATION 8	
#define SQR(x) ((x)*(x))

#define SKIP GetRandomUniform(0.0, 1.0)

//'multiple' should be ideally 10^desired_decimal_accuracy
int inline RoundTo(const float val, const float multiple=1000.f)
{
	return ( int(floorf(val*multiple)) );
}

//puts v1 into Pos, with mPos being a helper structure preventing having
//v1 multiple times inside the Pos
long unsigned int Enlist(
	const Vector3FC& v1,
	std::vector<Vector3FC>& Pos,
	std::map< int,std::map< int,std::map< int,long unsigned int > > >& mPos)
{
	long unsigned int o1; //ret val

	std::map< int,std::map< int,long unsigned int > >& mY=mPos[RoundTo(v1.x)];
	if (mY.empty())
	{
		Pos.push_back(v1);
		o1=Pos.size();

		//add reference to this vertex in the mPos structure
		std::map< int,long unsigned int > mZ;
		mZ[RoundTo(v1.z)]=o1;
		std::map< int,std::map< int,long unsigned int > > my;
		my[RoundTo(v1.y)]=mZ;
		mPos[RoundTo(v1.x)]=my;
	}
	else
	{
		std::map< int,long unsigned int >& mZ=mY[RoundTo(v1.y)];
		if (mZ.empty())
		{
			Pos.push_back(v1);
			o1=Pos.size();

			//add reference to this vertex in the mPos structure
			std::map< int,long unsigned int > mZ;
			mZ[RoundTo(v1.z)]=o1;
			mY[RoundTo(v1.y)]=mZ;
		}
		else
		{
			if (mZ[RoundTo(v1.z)] == 0)
			{
				Pos.push_back(v1);
				o1=Pos.size();

				//add reference to this vertex in the mPos structure
				mZ[RoundTo(v1.z)]=o1;
			}
			else
			{
				o1=mZ[RoundTo(v1.z)];
			}
		}
	}

	return o1;
}


int ActiveMesh::ImportSTL(const char *filename)
{
	Pos.clear();
	ID.clear();
	norm.clear();

	//a helper map to (efficiently) search for already stored vertices inside Pos
	std::map< int,std::map< int,std::map< int,long unsigned int > > > mPos;
	//         x             y             z  offset+1 in Pos

	//try to open the file
	std::ifstream file(filename);
	if (!file.is_open()) return 1;

	//read the "header" line
	char tmp[1024];
	file >> tmp; //dangerous...
	//check tmp for "solid" or complain
	if (tmp[0] != 's'
	 || tmp[1] != 'o'
	 || tmp[2] != 'l'
	 || tmp[3] != 'i'
	 || tmp[4] != 'd') { file.close(); return(2); }
	//read (and skip) the rest of the header line
	file.ignore(10240,'\n');

	//read facet by facet
	while (file >> tmp)
	{
		//check tmp for "facet" or "endsolid" or complain
		if (tmp[0] != 'f'
		 || tmp[1] != 'a'
		 || tmp[2] != 'c'
		 || tmp[3] != 'e'
		 || tmp[4] != 't')
		{
		//no new face starting, end of file then?
			if (tmp[0] != 'e'
			 || tmp[1] != 'n'
			 || tmp[2] != 'd'
			 || tmp[3] != 's'
			 || tmp[4] != 'o') { file.close(); return(3); }
			else break;
		}

		//read normal
		file >> tmp; //"normal" keyword
		float x,y,z;
		file >> x >> y >> z;
		Vector3F normal(x,y,z);

		//read triangle vertices
		file >> tmp;
		//check tmp for "outer" or complain
		if (tmp[0] != 'o'
		 || tmp[1] != 'u'
		 || tmp[2] != 't'
		 || tmp[3] != 'e'
		 || tmp[4] != 'r') { file.close(); return(4); }
		file >> tmp; //"loop" keyword

		file >> tmp; //"vertex" keyword
		file >> x >> y >> z;
		Vector3FC v1(x,y,z);

		file >> tmp;
		file >> x >> y >> z;
		Vector3FC v2(x,y,z);

		file >> tmp;
		file >> x >> y >> z;
		Vector3FC v3(x,y,z);

		file >> tmp; //"endloop" keyword
		file >> tmp; //"endfacet" keyword

		//add this triangle to the ActiveMesh data structures
		//we need to:
		// scale, round and use this for comparison against already
		// discovered vertices to avoid for having the same vertex saved twice
		long unsigned int o1,o2,o3;
		o1=Enlist(v1,Pos,mPos);
		o2=Enlist(v2,Pos,mPos);
		o3=Enlist(v3,Pos,mPos);
		// 
		// three offsets to the Pos array should be output
		// add them to the ID array
		ID.push_back(o1-1);
		ID.push_back(o2-1);
		ID.push_back(o3-1);
		// add normal to the norm array
		norm.push_back(normal);

		/*
		std::cout << "v1: " << v1.x << "," << v1.y << "," << v1.z << " -- o1=" << o1 << "\n";
		std::cout << "v2: " << v2.x << "," << v2.y << "," << v2.z << " -- o2=" << o2 << "\n";
		std::cout << "v3: " << v3.x << "," << v3.y << "," << v3.z << " -- o3=" << o3 << "\n";
		std::cout << "normal: " << normal.x << "," << normal.y << "," << normal.z << "\n\n";
		*/
	}
	
	file.close();
	return(0);
}

int ActiveMesh::ExportSTL(const char *filename)
{
	//try to open the file
	std::ofstream file(filename);
	if (!file.is_open()) return(1);

	file << "solid from ISBI_Compactor app by Vladimir Ulman (ulman@fi.muni.cz)\n";

	//cell body
	for (unsigned int i=0; i < ID.size(); i+=3)
	{
		file << "facet normal " << norm[i/3].x << " " << norm[i/3].y << " " << norm[i/3].z << "\n";
		file << "outer loop\n";
		file << "vertex " << Pos[ID[i+0]].x << " " << Pos[ID[i+0]].y << " " << Pos[ID[i+0]].z << "\n";
		file << "vertex " << Pos[ID[i+1]].x << " " << Pos[ID[i+1]].y << " " << Pos[ID[i+1]].z << "\n";
		file << "vertex " << Pos[ID[i+2]].x << " " << Pos[ID[i+2]].y << " " << Pos[ID[i+2]].z << "\n";
		file << "endloop\nendfacet\n";
	}

	//filopodia
	for (size_t f=0; f < Ftree.size(); ++f)
	{
		mesh_t& filoMesh=Ftree[f];
		for (unsigned int i=0; i < filoMesh.ID.size(); i+=3)
		{
			file << "facet normal " << filoMesh.norm[i/3].x << " " << filoMesh.norm[i/3].y << " " << filoMesh.norm[i/3].z << "\n";
			file << "outer loop\n";
			file << "vertex " << filoMesh.Pos[filoMesh.ID[i+0]].x << " " << filoMesh.Pos[filoMesh.ID[i+0]].y << " " << filoMesh.Pos[filoMesh.ID[i+0]].z << "\n";
			file << "vertex " << filoMesh.Pos[filoMesh.ID[i+1]].x << " " << filoMesh.Pos[filoMesh.ID[i+1]].y << " " << filoMesh.Pos[filoMesh.ID[i+1]].z << "\n";
			file << "vertex " << filoMesh.Pos[filoMesh.ID[i+2]].x << " " << filoMesh.Pos[filoMesh.ID[i+2]].y << " " << filoMesh.Pos[filoMesh.ID[i+2]].z << "\n";
			file << "endloop\nendfacet\n";
		}
	}

	file.close();
	return(0);
}


int ActiveMesh::ImportVTK(const char *filename) //surface version
{
	Pos.clear();
	ID.clear();
	norm.clear();

	//try to open the file
	std::ifstream file(filename);
	if (!file.is_open()) return 1;

	//read the "header" line
	char tmp[1024];
	file >> tmp >> tmp; //dangerous...
	//check tmp for "vtk" or complain
	if (tmp[0] != 'v'
	 || tmp[1] != 't'
	 || tmp[2] != 'k') { file.close(); return(2); }
	//read (and skip) the rest of the header line
	file.ignore(10240,'\n');

	//ignore "vtk output"
	file.ignore(10240,'\n');

	//read "ASCII"
	file >> tmp;
	if (tmp[0] != 'A'
	 || tmp[1] != 'S'
	 || tmp[2] != 'C'
	 || tmp[3] != 'I'
	 || tmp[4] != 'I') { file.close(); return(3); }
	file.ignore(10240,'\n');

	//search until DATASET lines is found
	int counter=0;
	file >> tmp;
	file.ignore(10240,'\n');
	while (tmp[0] != 'D' || tmp[1] != 'A' || tmp[2] != 'T'
	    || tmp[3] != 'A' || tmp[4] != 'S' || tmp[5] != 'E')
	{
		file >> tmp;
		file.ignore(10240,'\n');
		++counter;
		if (counter == 10) { file.close(); return(35); }
	}

	//read points header
	unsigned int itemCount;
	file >> tmp >> itemCount;;
	if (tmp[0] != 'P'
	 || tmp[1] != 'O'
	 || tmp[2] != 'I'
	 || tmp[3] != 'N'
	 || tmp[4] != 'T'
	 || tmp[5] != 'S') { file.close(); return(4); }
	file.ignore(10240,'\n');

	//std::cout << "reading " << itemCount << " point coordinates\n";
	Pos.reserve(itemCount);

	//read all points...
	float x,y,z;
	while (itemCount > 0 && file >> x)
	{
		file >> y >> z;

		//... and save them
		Vector3FC v1(x,y,z);
		//v1*=100.f;
		Pos.push_back(v1);

		--itemCount;
	}
	//std::cout << "last coordinate was: " << x << "," << y << "," << z << "\n";

	//prepare "information about faces normals"
	Vector3F fictiveNormal(1.f,0.f,0.f);

	//read polyhedra header
	file >> tmp >> itemCount;
	if ((tmp[0] != 'P'
	  || tmp[1] != 'O'
	  || tmp[2] != 'L'
	  || tmp[3] != 'Y'
	  || tmp[4] != 'G'
	  || tmp[5] != 'O'
	  || tmp[6] != 'N'
	  || tmp[7] != 'S')
	         &&
	    (tmp[0] != 'C'
	  || tmp[1] != 'E'
	  || tmp[2] != 'L'
	  || tmp[3] != 'L'
	  || tmp[4] != 'S')) { file.close(); return(5); }
	file.ignore(10240,'\n');

	//std::cout << "reading " << itemCount << " triangles\n";
	ID.reserve(3*itemCount);
	norm.reserve(itemCount);

	//read all polyhedra vertices
	unsigned int ignore,v1,v2,v3;
	while (itemCount > 0 && file >> ignore && ignore == 3)
	{
		file >> v1 >> v2 >> v3;

		//save v1,v2,v3 (TODO: if not already saved...)
		//make triangles use CW winding order
		ID.push_back(v1);
		ID.push_back(v2);
		ID.push_back(v3);
		norm.push_back(fictiveNormal);

		--itemCount;
	}
	//std::cout << "last triangle was: " << v1 << "," << v2 << "," << v3 << "\n";

	file.close();
	return(0);
}


int ActiveMesh::ImportVTK_Volumetric(const char *filename,bool saveAlsoTetrahedra)
{
	Pos.clear();
	ID.clear();
	norm.clear();

	if (saveAlsoTetrahedra) VolID.clear();

	//try to open the file
	std::ifstream file(filename);
	if (!file.is_open()) return 1;

	//read the "header" line
	char tmp[1024];
	file >> tmp >> tmp; //dangerous...
	//check tmp for "vtk" or complain
	if (tmp[0] != 'v'
	 || tmp[1] != 't'
	 || tmp[2] != 'k') { file.close(); return(2); }
	//read (and skip) the rest of the header line
	file.ignore(10240,'\n');

	//ignore "vtk output"
	file.ignore(10240,'\n');

	//read "ASCII"
	file >> tmp;
	if (tmp[0] != 'A'
	 || tmp[1] != 'S'
	 || tmp[2] != 'C'
	 || tmp[3] != 'I'
	 || tmp[4] != 'I') { file.close(); return(3); }
	file.ignore(10240,'\n');

	//search until DATASET lines is found
	int counter=0;
	file >> tmp;
	file.ignore(10240,'\n');
	while (tmp[0] != 'D' || tmp[1] != 'A' || tmp[2] != 'T'
	    || tmp[3] != 'A' || tmp[4] != 'S' || tmp[5] != 'E')
	{
		file >> tmp;
		file.ignore(10240,'\n');
		++counter;
		if (counter == 10) { file.close(); return(35); }
	}

	//read points header
	unsigned int itemCount;
	file >> tmp >> itemCount;;
	if (tmp[0] != 'P'
	 || tmp[1] != 'O'
	 || tmp[2] != 'I'
	 || tmp[3] != 'N'
	 || tmp[4] != 'T'
	 || tmp[5] != 'S') { file.close(); return(4); }
	file.ignore(10240,'\n');

	//std::cout << "reading " << itemCount << " point coordinates\n";
	Pos.reserve(itemCount);

	//read all points...
	float x,y,z;
	while (itemCount > 0 && file >> x)
	{
		file >> y >> z;

		//... and save them
		Vector3FC v1(x,y,z);
		//v1*=100.f;
		Pos.push_back(v1);

		--itemCount;
	}
	//std::cout << "last coordinate was: " << x << "," << y << "," << z << "\n";

	//prepare "information about faces normals"
	Vector3F fictiveNormal(1.f,0.f,0.f);

	//read polyhedra header
	file >> tmp >> itemCount;
	if (tmp[0] != 'C'
	 || tmp[1] != 'E'
	 || tmp[2] != 'L'
	 || tmp[3] != 'L'
	 || tmp[4] != 'S') { file.close(); return(5); }
	file.ignore(10240,'\n');

	//std::cout << "reading " << itemCount << " polyhedra\n";
	ID.reserve(3*itemCount);
	norm.reserve(itemCount);

	//read all polyhedra vertices
	unsigned int ignore,v1,v2,v3,v4;
	while (itemCount > 0 && file >> ignore && ignore == 4)
	{
		file >> v1 >> v2 >> v3 >> v4;

		//save v1,v2,v3
		ID.push_back(v1);
		ID.push_back(v2);
		ID.push_back(v3);
		norm.push_back(fictiveNormal);

		//save v1,v2,v4
		ID.push_back(v1);
		ID.push_back(v2);
		ID.push_back(v4);
		norm.push_back(fictiveNormal);

		//save v1,v4,v3
		ID.push_back(v1);
		ID.push_back(v4);
		ID.push_back(v3);
		norm.push_back(fictiveNormal);

		//save v4,v2,v3
		ID.push_back(v4);
		ID.push_back(v2);
		ID.push_back(v3);
		norm.push_back(fictiveNormal);

		if (saveAlsoTetrahedra)
		{
			VolID.push_back(v1);
			VolID.push_back(v2);
			VolID.push_back(v3);
			VolID.push_back(v4);
		}

		--itemCount;
	}
	//std::cout << "last polyhedron was: " << v1 << "," << v2 << "," << v3 << "," << v4 << "\n";

	file.close();
	return(0);
}


void PrintVector(const Vector3d<float>& v)
{
	std::cout << "(" << v.x << "," << v.y << "," << v.z << ")\n";
}

bool FindIndexInSkeletons(const std::vector<struct mesh_t>& Ftree, //filopodia container
                          const std::vector<bool>& which,          //consider-only flag
                          const unsigned int index,                //the vertex index to search
                          const unsigned int stop_idx,             //up to filopodium index idx (skeleton id)
                          const unsigned int stop_pos,             //and up to pos/offset within it (skeleton offset)
                          unsigned int &ret_idx,unsigned int &ret_pos) //return filopodium index, next occurence position of the (same) index
{
	//search for index in segFromPoint lists from the Ftree,
	//consider only those segFromPoint lists for which the which flag is on
	//but not further than till idx list (filopodium) and pos (offset) within it
	//
	//in this way we look for some previous occurence of the same index
	//whose another instance should be sitting at Ftree[stop_idx].segFromPoint[stop_pos]
	//
	//note: the function returns the first matching occurence (ret_idx,ret_pos)
	//that contains the searched for index in its skeleton; if filopodia were
	//added first the main branch, later its offsprings, and more offsprings are
	//branching-out from the same point on the main branch; than the
	//reference (index) of the main branch is returned (as it is found
	//earlier than index of an offsprings filopodium)

	//besides for vertex indicies, we also check the spatial positions of the
	//considered points and their distance...
	//
	//coordinate/position of the 'index' point
	const Vector3FC& indexPos=Ftree[stop_idx].fPoints[index];
	Vector3FC testPos;

	//search over filopodia of interest
	for (unsigned int idx=0; idx <= stop_idx && idx < which.size(); ++idx)
	if (which[idx] && idx<Ftree.size())
	{
		//this filopodium is of interest (flag is on)
		const mesh_t& filoMesh=Ftree[idx];

		//search over segment points within it
		for (unsigned int i=0; i < filoMesh.segFromPoint.size(); ++i)
		{
			if (filoMesh.segFromPoint[i] == index
			   && (idx < stop_idx || i < stop_pos))
			{
				ret_idx=idx;
				ret_pos=i;
				return(true);
			}

			//also check the distance...
			testPos=filoMesh.fPoints[filoMesh.segFromPoint[i]];
			//std::cout << "  testing vector: "; PrintVector(testPos);
			//std::cout << "reference vector: "; PrintVector(indexPos);
			testPos-=indexPos;
			//std::cout << "sq. distance = " << testPos.LenQ() << "\n";
			//std::cout << "    distance = " << testPos.Len() << "\n";
			if (testPos.LenQ() < 0.0000000001f  //is distance less than 10^-5 ?
			   && (idx < stop_idx || i < stop_pos))
			{
				ret_idx=idx;
				ret_pos=i;
				return(true);
			}
		}
	}

	//not found special index
	return(false);
}

//enable to see debug outputs for filopodia reading from VTKs
//#define DEBUG_BRANCHING

int ActiveMesh::ImportVTK_Ftree(const char *filename,unsigned int idx,
                                const float stretch,bool resetMesh)
{
	const float radiusCorrection=stretch;

	//handle the undecided case...
	if (idx == 999999) idx=(unsigned int)Ftree.size();

	//make sure the F-tree array is long enough to host the requested index
	while (Ftree.size() < idx+1) Ftree.push_back(mesh_t());
	mesh_t* filoMesh=&(Ftree[idx]);

	if (resetMesh)
	{
		filoMesh->Pos.clear();
		filoMesh->ID.clear();
		filoMesh->norm.clear();
	}

	//resets the branchID_to_FtreeIndices mapping
	tipOffsets.clear();

	//setup a list of indices that were populated from this file,
	//the list becomes interesting only if the filopodium branches
	//(the list is actually a flag-array.... lazy programmer)
	std::vector<bool> populatedIndices(1000,false);
	populatedIndices[idx]=true;
	
	//local/temporary list of vertices of segments that make up the f-tree
	//later, we convert these to triangles and save these guys instead
	filoMesh->fPoints.clear();
	filoMesh->segFromPoint.clear();
	filoMesh->segToPoint.clear();
	filoMesh->segFromRadius.clear();
	filoMesh->parent=-1;

	//try to open the file
	std::ifstream file(filename);
	if (!file.is_open()) return 1;

	//read the "header" line
	char tmp[1024];
	file >> tmp >> tmp; //dangerous...
	//check tmp for "vtk" or complain
	if (tmp[0] != 'v'
	 || tmp[1] != 't'
	 || tmp[2] != 'k') { file.close(); return(2); }
	//read (and skip) the rest of the header line
	file.ignore(10240,'\n');

	//ignore "vtk output"
	file.ignore(10240,'\n');

	//read "ASCII"
	file >> tmp;
	if (tmp[0] != 'A'
	 || tmp[1] != 'S'
	 || tmp[2] != 'C'
	 || tmp[3] != 'I'
	 || tmp[4] != 'I') { file.close(); return(3); }
	file.ignore(10240,'\n');

	//search until DATASET lines is found
	int counter=0;
	file >> tmp;
	file.ignore(10240,'\n');
	while (tmp[0] != 'D' || tmp[1] != 'A' || tmp[2] != 'T'
	    || tmp[3] != 'A' || tmp[4] != 'S' || tmp[5] != 'E')
	{
		file >> tmp;
		file.ignore(10240,'\n');
		++counter;
		if (counter == 10) { file.close(); return(35); }
	}

	//read points header
	unsigned int itemCount;
	file >> tmp >> itemCount;;
	if (tmp[0] != 'P'
	 || tmp[1] != 'O'
	 || tmp[2] != 'I'
	 || tmp[3] != 'N'
	 || tmp[4] != 'T'
	 || tmp[5] != 'S') { file.close(); return(4); }
	file.ignore(10240,'\n');

#ifdef DEBUG_BRANCHING
	std::cout << "reading " << itemCount << " point coordinates\n";
#endif
	filoMesh->fPoints.reserve(itemCount);

	//read all points...
	float x,y,z;
	while (itemCount > 0 && file >> x)
	{
		file >> y >> z;

		//... and save them
		Vector3FC v1(x,y,z);
		filoMesh->fPoints.push_back(v1);
		//std::cout << "reading coordinate: "; PrintVector(v1);

		--itemCount;
	}
#ifdef DEBUG_BRANCHING
	std::cout << "last coordinate was: " << x << "," << y << "," << z << "\n";
#endif

	//read segments header
	file >> tmp >> itemCount;
	if (tmp[0] != 'C'
	 || tmp[1] != 'E'
	 || tmp[2] != 'L'
	 || tmp[3] != 'L'
	 || tmp[4] != 'S') { file.close(); return(5); }
	file.ignore(10240,'\n');

#ifdef DEBUG_BRANCHING
	std::cout << "reading " << itemCount << " segments\n";
#endif
	filoMesh->segFromPoint.reserve(itemCount);
	filoMesh->segToPoint.reserve(itemCount);
	filoMesh->segFromRadius.reserve(itemCount);

	//read all segments vertices
	unsigned int ignore,v1,v2;
	while (itemCount > 0 && file >> ignore && ignore == 2)
	{
		file >> v1 >> v2;

		filoMesh->segFromPoint.push_back(v1);
		filoMesh->segToPoint.push_back(v2);

		--itemCount;
	}
#ifdef DEBUG_BRANCHING
	std::cout << "last segment was: " << v1 << "," << v2 << "\n";
#endif

	//"ignore" cell types header, body 
	file >> tmp >> itemCount;
	if (tmp[0] != 'C'
	 || tmp[1] != 'E'
	 || tmp[2] != 'L'
	 || tmp[3] != 'L'
	 || tmp[4] != '_'
	 || tmp[5] != 'T'
	 || tmp[6] != 'Y') { file.close(); return(6); }
	file.ignore(10240,'\n');
	while (itemCount > 0)
	{
		file.ignore(10240,'\n');
		--itemCount;
	}

	//read radii, i.e. POINT_DATA header
	file >> tmp >> itemCount;
	if (tmp[0] != 'P'
	 || tmp[1] != 'O'
	 || tmp[2] != 'I'
	 || tmp[3] != 'N'
	 || tmp[4] != 'T'
	 || tmp[5] != '_'
	 || tmp[6] != 'D'
	 || tmp[7] != 'A') { file.close(); return(7); }
	file.ignore(10240,'\n');
	file.ignore(10240,'\n'); //ignore SCALARS..
	file.ignore(10240,'\n'); //ignore LOOKUP_TABLE

	//temporal storage of radii which are given for every point
	//(but we store them for beginning of every skeleton segment)
	std::vector<float> tmpValAtPoints;
	tmpValAtPoints.reserve(itemCount);

	//read the radii
	while (itemCount > 0 && file >> x)
	{
		tmpValAtPoints.push_back(x*radiusCorrection);
		//std::cout << "reading radius: " << x*radiusCorrection << "\n";

		--itemCount;
	}

	//now, store the radii per skeleton segments
	for (unsigned int qq=0; qq < filoMesh->segFromPoint.size(); ++qq)
		filoMesh->segFromRadius.push_back(tmpValAtPoints[filoMesh->segFromPoint[qq]]);
	tmpValAtPoints.clear();

	//read branch IDs, i.e. POINT_DATA header
	file >> tmp >> itemCount;
	if (tmp[0] != 'P'
	 || tmp[1] != 'O'
	 || tmp[2] != 'I'
	 || tmp[3] != 'N'
	 || tmp[4] != 'T'
	 || tmp[5] != '_'
	 || tmp[6] != 'D'
	 || tmp[7] != 'A') { file.close(); return(8); }
	file.ignore(10240,'\n');
	file.ignore(10240,'\n'); //ignore SCALARS..
	file.ignore(10240,'\n'); //ignore LOOKUP_TABLE

	//read the IDs
	while (itemCount > 0 && file >> x)
	{
		tmpValAtPoints.push_back(x);
		//std::cout << "reading ID: " << x << "\n";

		--itemCount;
	}

	file.close();

	//note the first binding: the main branch
	tipOffsets[(int)tmpValAtPoints.front()]=idx;
#ifdef DEBUG_BRANCHING
	std::cout << "detected main branchID=" << tmpValAtPoints.front() << "\n";
#endif

	//now detect offspring branches, test if segIndex is candidate for a branching point
	//on the current filopodium (which is referenced with filoMesh and idx)
	unsigned int segIndex=1;
	while (segIndex < filoMesh->segFromPoint.size())
	{
		unsigned int ret_idx,ret_pos;

#ifdef DEBUG_BRANCHING
		std::cout << "processing pos=" << segIndex
		          << " with radius " << filoMesh->segFromRadius[segIndex]
		          << " on filo idx " << idx << "\n";
#endif

		//is the current index repeating?
		if (FindIndexInSkeletons(this->Ftree,populatedIndices,
		                         filoMesh->segFromPoint[segIndex],
		                         idx,segIndex,
		                         ret_idx,ret_pos) == true)
		{
#ifdef DEBUG_BRANCHING
			std::cout << "found branching point at pos=" << segIndex
			          << " matching pos=" << ret_pos << " at branch idx " << ret_idx << "\n";
#endif

			//we have found another instance of repeating starting segment,
			//this is a branching point
			//
			//let's move the whole data from this instance to the end into a new
			//filopodium structure and continue searching from that new filopodia
			//from its beginning

			//create a new filopodium instance
			Ftree.push_back(mesh_t());
			//enlarging the Ftree may result in re-allocating it, invalidating the pointer...
			//hence, refresh the pointer
			filoMesh=&(Ftree[idx]);

			//the new index of the offspring branch
			idx=(unsigned int)Ftree.size()-1;
			//NB: filoMesh points on the source segment, idx references the new segment
#ifdef DEBUG_BRANCHING
			std::cout << "new filo idx " << idx << "\n";
#endif

			populatedIndices[idx]=true;
			Ftree[idx].parent=(signed)ret_idx;
			tipOffsets[ (int)tmpValAtPoints[filoMesh->segFromPoint[segIndex]] ]=idx;
#ifdef DEBUG_BRANCHING
			std::cout << "detected offspring branchID=" << tmpValAtPoints[filoMesh->segFromPoint[segIndex]] << "\n";
#endif

			//copy the positions vector
			Ftree[idx].fPoints=filoMesh->fPoints;

			//move the segment data:
			//first, alloc arrays for the new data
			size_t keepSize=filoMesh->segFromPoint.size()-segIndex;
			Ftree[idx].segFromPoint.reserve(keepSize);
			Ftree[idx].segToPoint.reserve(keepSize);
			Ftree[idx].segFromRadius.reserve(keepSize);
			//
			//remember how much to keep from the source data
			keepSize=segIndex;
			while (segIndex < filoMesh->segFromPoint.size())
			{
#ifdef DEBUG_BRANCHING
				std::cout << "adding segment from pos=" << segIndex
				          << " with radius " << filoMesh->segFromRadius[segIndex]
				          << " to filo idx " << idx << "\n";
#endif
				Ftree[idx].segFromPoint.push_back(filoMesh->segFromPoint[segIndex]);
				Ftree[idx].segToPoint.push_back(filoMesh->segToPoint[segIndex]);
				Ftree[idx].segFromRadius.push_back(filoMesh->segFromRadius[segIndex]);
				++segIndex;
			}
			//
			//resize the source data in the end
			filoMesh->segFromPoint.resize(keepSize);
			filoMesh->segToPoint.resize(keepSize);
			filoMesh->segFromRadius.resize(keepSize);

			//shortening the offspring skeleton:
			//(but only if the mother skeleton continues)
			if ((ret_pos) < Ftree[ret_idx].segFromRadius.size())
			//NB: THIS IS USELESS AS WE ARE MATCHING segFromPoints POINTS AGAINST segFromPoints,
			//not also against segToPoints -- so we are not able to detect an offspring that is
			//budding away from the very tip of the main branch; in other words, we can only detect
			//offsprings that budding away only from skeleton segments starting points, i.e., only
			//along inside the main branch, not anywhere along the last skeleton segment (except
			//for its starting point)
			{
#ifdef DEBUG_BRANCHING
				std::cout << "going to shorten the offspring skeleton\n";
#endif
				//determine "branching triangle" vertices
				const unsigned int A=Ftree[ret_idx].segFromPoint[ret_pos];
				const unsigned int B=Ftree[ret_idx].segToPoint[ret_pos];
				const unsigned int C=Ftree[idx].segToPoint[0];

				//"branching triangle" edges, and their lengths
				float xx=filoMesh->fPoints[B].x - filoMesh->fPoints[A].x;
				float yy=filoMesh->fPoints[B].y - filoMesh->fPoints[A].y;
				float zz=filoMesh->fPoints[B].z - filoMesh->fPoints[A].z;
				const float ab=xx*xx + yy*yy + zz*zz;

				xx=filoMesh->fPoints[C].x - filoMesh->fPoints[B].x;
				yy=filoMesh->fPoints[C].y - filoMesh->fPoints[B].y;
				zz=filoMesh->fPoints[C].z - filoMesh->fPoints[B].z;
				float bc=xx*xx + yy*yy + zz*zz;

				xx=filoMesh->fPoints[C].x - filoMesh->fPoints[A].x;
				yy=filoMesh->fPoints[C].y - filoMesh->fPoints[A].y;
				zz=filoMesh->fPoints[C].z - filoMesh->fPoints[A].z;
				const float ac=xx*xx + yy*yy + zz*zz;

				//angle between the vectors
				bc=acosf((ab + ac - bc)/(2*sqrtf(ab)*sqrtf(ac)));
				//displacement distance of the initial offspring branch vertex
				bc=Ftree[ret_idx].segFromRadius[ret_pos+1] / sinf(bc);

				//displacement relative to the length of AC vector
				bc=bc/sqrtf(ac);

				//create a new starting point (of the offspring skeleton)
				Vector3FC point;
				point.x=filoMesh->fPoints[A].x + bc*xx;
				point.y=filoMesh->fPoints[A].y + bc*yy;
				point.z=filoMesh->fPoints[A].z + bc*zz;
				//add the original skeleton vertex on the list of vertices
				Ftree[idx].fPoints.push_back(filoMesh->fPoints[A]);
				//add the new shifted skeleton vertex on the list of vertices
				Ftree[idx].fPoints.push_back(point);
				//and make the skeleton start from the new shifted vertex
				Ftree[idx].segFromPoint[0]=(unsigned int)(Ftree[idx].fPoints.size()-1);
			}
			else
			{
				//if not shorted, we are still an offspring (parent != 0)
				//and thus special vertices are expected to exist (at mesh generation...)
				//add the original skeleton vertex on the list of vertices
				const unsigned int A=Ftree[idx].segFromPoint[0];
				Ftree[idx].fPoints.push_back(filoMesh->fPoints[A]);
				//add the new shifted skeleton vertex on the list of vertices, in fact still the same
				Ftree[idx].fPoints.push_back(filoMesh->fPoints[A]);
				//and make the skeleton start from the new shifted vertex
				Ftree[idx].segFromPoint[0]=(unsigned int)(Ftree[idx].fPoints.size()-1);
			}

			//restart this searching algorithm
			segIndex=1;
			filoMesh=&(Ftree[idx]);
#ifdef DEBUG_BRANCHING
			std::cout << "new filoMesh remaining length=" << filoMesh->segFromPoint.size() << "\n";
#endif
		}
		else ++segIndex;
	}

	//now widen added skeleton(s) and save triangles...
	for (unsigned int idxx=0; idxx < populatedIndices.size(); ++idxx)
	if (populatedIndices[idxx])
		PopulateSurfTriangles_Ftree(idxx,true);
		//NB: change to false to have meshes starting from the current skeleton first vertex

	return(0);
}


void ActiveMesh::PopulateSurfTriangles_Ftree(const unsigned int idx,const bool enforce)
{
	//check for index bounds
	if (idx >= Ftree.size()) return;

	//shortcut...
	mesh_t& filoMesh=Ftree[idx];

	//how much fine sample the surface (more = finer)
	const size_t PointsOnRadiusPeriphery=10;
	const unsigned int AdvancePointsAlongFilopodiaSkeleton=1; //1 = normal operation

	//from where to take the first skeleton vertex:
	//use the current one unless we are an offspring asked to use the original vertex
	if (enforce && filoMesh.parent > -1) --filoMesh.segFromPoint[0];

	//prepare "information about faces normals"
	Vector3F fictiveNormal(1.f,0.f,0.f);

	//if filopodium can be imagined as a cone and we need to render its surface,
	//we need to render the circular-like base (as triangle fan),
	//and the cylinder-like "cone" towards the tip; this point is for the triangle fan
	const size_t fanCentrePoint=filoMesh.Pos.size();
	filoMesh.Pos.push_back(filoMesh.fPoints[filoMesh.segFromPoint[0]]);

	//bookmark the beginning of the cylinder-like surface points
	size_t firstRadiusPoint=filoMesh.Pos.size();

	//will store a former nXaxis
	Vector3F pXaxis;

	//for all bending points except the last one (tip)
	for (unsigned int i=0; i < filoMesh.segFromPoint.size(); i+=AdvancePointsAlongFilopodiaSkeleton)
	{
		//we need to construct rotation matrix that would
		//rotate points on a circle which lays in the XZ plane
		//(Y axis is normal to it then) such that the points
		//would lay in the plane to which this new Y axis
		//would be normal
		//
		//new Y axis 
		Vector3F nYaxis=filoMesh.fPoints[filoMesh.segToPoint[i]];
		if (i == 0) nYaxis-=filoMesh.fPoints[filoMesh.segFromPoint[i]];
		else
		{
			nYaxis-=filoMesh.fPoints[filoMesh.segFromPoint[i-1]];
			nYaxis/=2.f; //unnecessary scaling
		}

		Vector3F nZaxis(0.f,1.f,0.f); //in fact it is original Yaxis _for now_
		Vector3F nXaxis;
		//new X axis is perpendicular to the original and new Y axis
		Mul(nYaxis,nZaxis,nXaxis);

		//nXaxis will likely fail is nYaxis is practically identical to nZaxis (a real y-axis)
		//NB: scalar product is precisely nYaxis.y (as nZaxis.y is 1.0, .x and .z are 0.f)
		//NB: cos(5deg) is approx. 0.996
		if (nYaxis.Ort().y > 0.99f) { nXaxis.x=1.0f; nXaxis.y=nXaxis.z=0.0f; }

		//new Z axis is perpendicular to the new X and Y axes
		Mul(nYaxis,nXaxis,nZaxis);

		//normalize...
		nXaxis/=nXaxis.Len();
		nZaxis/=nZaxis.Len();

		//make sure nXaxis is somewhat in the same direction as is pXaxis (=former nXaxis)
		//(but obviously not for the first plane...)
		if (i > 0)
		{
			//calc: ang=acos(pXaxis*nXaxis) and then x=cos(ang), z=sin(ang)
			//note: pXaxis and nXaxis are normalized
			float x=pXaxis*nXaxis;
			if (x > 0.99999f) x=1.0f;
			const float z=sinf(acosf(x));
			if ((pXaxis*nZaxis) > 0.f)
			{
				//rotate one direction
				nXaxis =x*nXaxis;
				nXaxis+=z*nZaxis;
			}
			else
			{
				//rotate the opposite direction
				nXaxis =x*nXaxis;
				nXaxis-=z*nZaxis;
			}
			//find new nZaxis and normalize again
			Mul(nYaxis,nXaxis,nZaxis);
			nZaxis/=nZaxis.Len();
		}
		//update pXaxis (=new former nXaxis)
		pXaxis=nXaxis;

		//now render the points on the circle and project them into the scene
		for (size_t p=0; p < PointsOnRadiusPeriphery; ++p)
		{
			//the point in its original position
			float x=filoMesh.segFromRadius[i]*cosf(6.28f*float(p)/float(PointsOnRadiusPeriphery));
			float z=filoMesh.segFromRadius[i]*sinf(6.28f*float(p)/float(PointsOnRadiusPeriphery));

			//rotate
			Vector3FC V(x*nXaxis);
			V+=z*nZaxis;

			//shift to the centre and save
			V+=filoMesh.fPoints[filoMesh.segFromPoint[i]];
			filoMesh.Pos.push_back(V);
		}
	}

	//finally, add the tip point
	filoMesh.Pos.push_back(filoMesh.fPoints[filoMesh.segToPoint.back()]);

	//now, add the triangle fan - aka filopodia base
	for (size_t p=0; p < PointsOnRadiusPeriphery-1; ++p)
	{
		filoMesh.ID.push_back(fanCentrePoint);
		filoMesh.ID.push_back(firstRadiusPoint+p+1);
		filoMesh.ID.push_back(firstRadiusPoint+p);
		filoMesh.norm.push_back(fictiveNormal);
	}
	filoMesh.ID.push_back(fanCentrePoint);
	filoMesh.ID.push_back(firstRadiusPoint);
	filoMesh.ID.push_back(firstRadiusPoint+PointsOnRadiusPeriphery-1);
	filoMesh.norm.push_back(fictiveNormal);

	//if necessary, restore the offspring's skeleton first vertex
	if (enforce && filoMesh.parent > -1) ++filoMesh.segFromPoint[0];

	//now create triangles for all segment strips
	//except the last one (that leads to the tip)
	for (unsigned int i=AdvancePointsAlongFilopodiaSkeleton; i < filoMesh.segFromPoint.size(); i+=AdvancePointsAlongFilopodiaSkeleton)
	{
		size_t p=0;
		for (; p < PointsOnRadiusPeriphery-1; ++p)
		{
			filoMesh.ID.push_back(firstRadiusPoint+p);
			filoMesh.ID.push_back(firstRadiusPoint+p+1);
			filoMesh.ID.push_back(firstRadiusPoint+p+PointsOnRadiusPeriphery);
			filoMesh.norm.push_back(fictiveNormal);

			filoMesh.ID.push_back(firstRadiusPoint+p+PointsOnRadiusPeriphery+1);
			filoMesh.ID.push_back(firstRadiusPoint+p+PointsOnRadiusPeriphery);
			filoMesh.ID.push_back(firstRadiusPoint+p+1);
			filoMesh.norm.push_back(fictiveNormal);
		}

		filoMesh.ID.push_back(firstRadiusPoint+p);
		filoMesh.ID.push_back(firstRadiusPoint);
		filoMesh.ID.push_back(firstRadiusPoint+p+PointsOnRadiusPeriphery);
		filoMesh.norm.push_back(fictiveNormal);

		filoMesh.ID.push_back(firstRadiusPoint  +PointsOnRadiusPeriphery);
		filoMesh.ID.push_back(firstRadiusPoint+p+PointsOnRadiusPeriphery);
		filoMesh.ID.push_back(firstRadiusPoint);
		filoMesh.norm.push_back(fictiveNormal);

		firstRadiusPoint+=PointsOnRadiusPeriphery;
	}

	//the last segment...
	size_t p=0;
	for (; p < PointsOnRadiusPeriphery-1; ++p)
	{
		filoMesh.ID.push_back(firstRadiusPoint+p);
		filoMesh.ID.push_back(firstRadiusPoint+p+1);
		filoMesh.ID.push_back(firstRadiusPoint+PointsOnRadiusPeriphery);
		filoMesh.norm.push_back(fictiveNormal);
	}

	filoMesh.ID.push_back(firstRadiusPoint+p);
	filoMesh.ID.push_back(firstRadiusPoint);
	filoMesh.ID.push_back(firstRadiusPoint+PointsOnRadiusPeriphery);
	filoMesh.norm.push_back(fictiveNormal);
}


void ActiveMesh::RenderMask_bodyVol(i3d::Image3d<i3d::GRAY16>& mask,const bool showTriangles)
{
	//time savers...
	const float xRes=mask.GetResolution().GetX();
	const float yRes=mask.GetResolution().GetY();
	const float zRes=mask.GetResolution().GetZ();
	const float xOff=mask.GetOffset().x;
	const float yOff=mask.GetOffset().y;
	const float zOff=mask.GetOffset().z;

	//time-savers for boundary checking...
	const int maxX=(int)mask.GetSizeX()-1;
	const int maxY=(int)mask.GetSizeY()-1;
	const int maxZ=(int)mask.GetSizeZ()-1;

	//time-savers for accessing neigbors...
	const long xLine=(signed)mask.GetSizeX();
	const long Slice=(signed)mask.GetSizeY() *xLine;
	i3d::GRAY16* const pM=mask.GetFirstVoxelAddr();

	//over all tetrahedra
	for (size_t i=0; i < VolID.size(); i+=4)
	{
		//tetrahedron to drive positions in the FF
		//(positions tetrahedron)
		const Vector3F& v1=Pos[VolID[i+0]];
		const Vector3F& v2=Pos[VolID[i+1]];
		const Vector3F& v3=Pos[VolID[i+2]];
		const Vector3F& v4=Pos[VolID[i+3]];

		//determine stepping factor to iterate somewhat efficiently over all pixels
		//which this tetrahedron spans over: calc AABB, get longest AA dimension,
		//use resolution-driven magic factor...
		//(assumes the resolution is isotropic)

		Vector3F tmp;
		//AA dimensions of AABB
		tmp.x=  std::max(std::max(v1.x,v2.x),std::max(v3.x,v4.x));
		float t=std::min(std::min(v1.x,v2.x),std::min(v3.x,v4.x));
		tmp.x-=t;

		tmp.y=std::max(std::max(v1.y,v2.y),std::max(v3.y,v4.y));
		t    =std::min(std::min(v1.y,v2.y),std::min(v3.y,v4.y));
		tmp.y-=t;

		tmp.z=std::max(std::max(v1.z,v2.z),std::max(v3.z,v4.z));
		t    =std::min(std::min(v1.z,v2.z),std::min(v3.z,v4.z));
		tmp.z-=t;

		//longest AA dimension
		t=std::max(tmp.x,std::max(tmp.y,tmp.z));

		//the same dimension in pixels
		t*=xRes;

		//the magic constant...
		t*=1.6f;

		//portion the tetrahedron at least into t steps -> step size is inverse
		t=1.f/t;

		//now iterate through the tetrahedron:
		for (float d=0.f; d <= 1.0f; d += t)
		for (float c=0.f; c <= (1.0f-d); c += t)
		for (float b=0.f; b <= (1.0f-d-c); b += t)
		{
			float a=1.0f -b -c -d;

			//float-point coordinate:
			tmp =a*v1;
			tmp+=b*v2;
			tmp+=c*v3;
			tmp+=d*v4;

			//pixel (integer) coordinate:
			const long x=long((tmp.x-xOff) *xRes);
			const long y=long((tmp.y-yOff) *yRes);
			const long z=long((tmp.z-zOff) *zRes);

			//inside the image?
			if ((x > 0) && (y > 0) && (z > 0)
				&& (x < maxX) && (y < maxY) && (z < maxZ))
			{
				//a value to insert here
				short val=(showTriangles)? short(i%5 *30 +50) : 50;

				//insert:
				pM[z*Slice +y*xLine +x]=std::max( pM[z*Slice +y*xLine +x],(i3d::GRAY16)val );
			}
		} //iterate over inside tetrahedra
	} //iterate over all tetrahedra
} //function end


void ActiveMesh::RenderMask_filo(i3d::Image3d<i3d::GRAY16>& mask,const unsigned int idx,
	const unsigned short color,const bool showTriangles,
	const bool checkCollisionOfTip)
{
	//if index out of range, do nothing
	if (idx >= Ftree.size()) return;

	//time savers: resolution
	const float xRes=mask.GetResolution().GetX();
	const float yRes=mask.GetResolution().GetY();
	const float zRes=mask.GetResolution().GetZ();

	//time savers: offset
	const float xOff=mask.GetOffset().x;
	const float yOff=mask.GetOffset().y;
	const float zOff=mask.GetOffset().z;

	if (checkCollisionOfTip)
	{
		//std::cout << "RENDERING ID " << color << " with collision detection enabled\n";
		//determine the position of the tip in microns
		const Vector3FC& realPos=this->Ftree[idx].fPoints[this->Ftree[idx].segToPoint.back()];

		//convert it to pixel coordinate
		const size_t x=size_t((realPos.x-xOff) *xRes);
		const size_t y=size_t((realPos.y-yOff) *yRes);
		const size_t z=size_t((realPos.z-zOff) *zRes);

		//check the mask value
		unsigned short origMaskVal = mask.GetVoxel(x,y,z);

		//report problem if necessary (when mask value is not zero)
		if (origMaskVal > 0)
			std::cout << "POSSIBLE COLLISION DETECTED"
			  << " of the tip of filopodium ID " << color
			  << " bumping into filopodium ID " << origMaskVal << "\n";
	}

	//create "aside" empty image similar to the input-output one
	i3d::Image3d<i3d::GRAY16> tmpI;
	tmpI.CopyMetaData(mask);
	tmpI.GetVoxelData()=0;

	//over all triangles of the given filopodia
	const mesh_t& filoMesh=Ftree[idx];
	for (unsigned int i=0; i < filoMesh.ID.size()/3; ++i)
	{
		const Vector3F& v1=filoMesh.Pos[filoMesh.ID[3*i+0]];
		const Vector3F& v2=filoMesh.Pos[filoMesh.ID[3*i+1]];
		const Vector3F& v3=filoMesh.Pos[filoMesh.ID[3*i+2]];

		//sweeping (and rendering) the triangle
		for (float c=0.f; c <= 1.0f; c += 0.01f)
		for (float b=0.f; b <= (1.0f-c); b += 0.01f)
		{
			float a=1.0f -b -c;

			Vector3F v=a*v1;
			v+=b*v2;
			v+=c*v3;
			
			//get pixel coordinate
			const int x=int((v.x-xOff) *xRes);
			const int y=int((v.y-yOff) *yRes);
			const int z=int((v.z-zOff) *zRes);

			unsigned short val=(showTriangles)? (unsigned short)(i%5 *30 +101) : color;
			if (tmpI.Include(x,y,z)) tmpI.SetVoxel((unsigned)x,(unsigned)y,(unsigned)z,val);
		}
	}

	//this floods cell exterior from the image corner
	i3d::FloodFill(tmpI,(i3d::GRAY16)50,0);

	//this removes the flooding while filling
	//everything else (and closing holes in this way)
	i3d::GRAY16* pI=tmpI.GetFirstVoxelAddr();
	i3d::GRAY16* pM=mask.GetFirstVoxelAddr();
	i3d::GRAY16* const pL=pM+mask.GetImageSize();
	while (pM != pL)
	{
		*pM=(*pI != 50 && *pM == 0)? std::max(*pI,color) : *pM;
		++pI; ++pM;
	}
}


void ActiveMesh::UpdateMeshBBox(Vector3F& min,Vector3F& max)
{
	//first, for the cell body
	for (unsigned int i=0; i < Pos.size(); ++i)
	{
		min.x=std::min(min.x,Pos[i].x);
		min.y=std::min(min.y,Pos[i].y);
		min.z=std::min(min.z,Pos[i].z);

		max.x=std::max(max.x,Pos[i].x);
		max.y=std::max(max.y,Pos[i].y);
		max.z=std::max(max.z,Pos[i].z);
	}

	//second, for all filopodia
	for (size_t f=0; f < Ftree.size(); ++f)
	{
		const mesh_t& filoMesh=Ftree[f];
		for (unsigned int i=0; i < filoMesh.Pos.size(); ++i)
		{
			min.x=std::min(min.x,filoMesh.Pos[i].x);
			min.y=std::min(min.y,filoMesh.Pos[i].y);
			min.z=std::min(min.z,filoMesh.Pos[i].z);

			max.x=std::max(max.x,filoMesh.Pos[i].x);
			max.y=std::max(max.y,filoMesh.Pos[i].y);
			max.z=std::max(max.z,filoMesh.Pos[i].z);
		}
	}
}


void ActiveMesh::CenterMesh(const float safeConstant)
{
	//calc geom. centre,
	double x=0.,y=0.,z=0.;
	//and extremal points
	float xMin=0.,yMin=0.,zMin=0.;
	float xMax=0.,yMax=0.,zMax=0.;

	//only from the cell body!
	for (unsigned int i=0; i < Pos.size(); ++i)
	{
		x+=Pos[i].x;
		y+=Pos[i].y;
		z+=Pos[i].z;

		xMin=std::min(xMin,Pos[i].x);
		yMin=std::min(yMin,Pos[i].y);
		zMin=std::min(zMin,Pos[i].z);

		xMax=std::max(xMax,Pos[i].x);
		yMax=std::max(yMax,Pos[i].y);
		zMax=std::max(zMax,Pos[i].z);
	}
	x/=double(Pos.size());
	y/=double(Pos.size());
	z/=double(Pos.size());

	meshPlaygroundSize.x=safeConstant*(xMax-xMin);
	meshPlaygroundSize.y=safeConstant*(yMax-yMin);
	meshPlaygroundSize.z=safeConstant*(zMax-zMin);

	//assume image offset is zero,
	//calc mesh shift vector to the image centre
	meshShift.x=meshPlaygroundSize.x/2.f - (float)x;
	meshShift.y=meshPlaygroundSize.y/2.f - (float)y;
	meshShift.z=meshPlaygroundSize.z/2.f - (float)z;

	std::cout << "playground size: " << meshPlaygroundSize.x << " x "
	          << meshPlaygroundSize.y << " x "
	          << meshPlaygroundSize.z << " microns\n";
}


void ActiveMesh::TranslateMesh(const Vector3F& shift)
{
	//cell body
	for (unsigned int i=0; i < Pos.size(); ++i)
	{
		Pos[i].x+=shift.x;
		Pos[i].y+=shift.y;
		Pos[i].z+=shift.z;
	}

	//filopodia
	for (size_t f=0; f < Ftree.size(); ++f)
	{
		mesh_t& filoMesh=Ftree[f];
		for (unsigned int i=0; i < filoMesh.Pos.size(); ++i)
		{
			filoMesh.Pos[i].x+=shift.x;
			filoMesh.Pos[i].y+=shift.y;
			filoMesh.Pos[i].z+=shift.z;
		}
		for (unsigned int i=0; i < filoMesh.fPoints.size(); ++i)
		{
			filoMesh.fPoints[i].x+=shift.x;
			filoMesh.fPoints[i].y+=shift.y;
			filoMesh.fPoints[i].z+=shift.z;
		}
	}
}


void ActiveMesh::InitDots_body(const i3d::Image3d<i3d::GRAY16>& mask)
{
	i3d::Image3d<float> dt;
	i3d::GrayToFloat(mask,dt);

	i3d::EDM(dt,0,50.f,false);
#ifdef SAVE_INTERMEDIATE_IMAGES
	dt.SaveImage("1_DTAlone.ics");
#endif

	i3d::Image3d<float> perlinInner,perlinOutside;
	perlinInner.CopyMetaData(mask);
	float coarseness = GetRandomUniform(this->configFile.coarsenessLower, 
													this->configFile.coarsenessUpper);
	DoPerlin3D(perlinInner,coarseness,0.3,1.0,6);
	Stretch(perlinInner, 0.0f, 1.0f, 4.0f);
#ifdef SAVE_INTERMEDIATE_IMAGES
	perlinInner.SaveImage("2_PerlinAlone_Inner.ics");
#endif

	perlinOutside.CopyMetaData(mask);
	DoPerlin3D(perlinOutside,2.5,0.2,1.0,6);
	Stretch(perlinOutside, 0.0f, 1.0f, 2.0f);
#ifdef SAVE_INTERMEDIATE_IMAGES
	perlinOutside.SaveImage("2_PerlinAlone_Outside.ics");
#endif

	i3d::Image3d<i3d::GRAY16> erroded;
	i3d::ErosionO(mask,erroded,1);

	//initial object intensity levels
	float* p=dt.GetFirstVoxelAddr();
	float* const pL=p+dt.GetImageSize();
	const float* pI=perlinInner.GetFirstVoxelAddr();
	const float* pO=perlinOutside.GetFirstVoxelAddr();
	const i3d::GRAY16* er=erroded.GetFirstVoxelAddr();

	// intensity values
	const float innerBg = 0.0;
	const float innerPerlinStrength = 20.0f;
	const float coronaBg = 1.0f;
	const float coronaPerlinStrength = 330.0f;

	// distances (in microns)
	const float coronaWidth = 0.1f;
	const float interfaceCoronaInterior = 0.05f;

	while (p != pL)
	{
		//are we within the mask?
		if (*p > 0.f)
		{
			//close to the surface?
			if (*p < coronaWidth || *er == 0) 
				 *p=coronaBg + coronaPerlinStrength*(*pO); // corona
			else if (*p < (coronaWidth + interfaceCoronaInterior))
			{
				 // linear interpolation factor (between corona and interior)
				 float factor = (*p - coronaWidth)/(interfaceCoronaInterior); 

				 *p=(innerBg + innerPerlinStrength*(*pI))*factor +  
				 	 (coronaBg + coronaPerlinStrength*(*pO))*(1.0f-factor);
			}
			else
				 *p=innerBg  + innerPerlinStrength*(*pI);  //inside

			if (*er == 0) *p=coronaBg; //corona envelope (was 1500)

			if (*p < 0.f) *p=0.f;
		}
		++p; ++pI; ++pO; ++er;
	}
#ifdef SAVE_INTERMEDIATE_IMAGES
	dt.SaveImage("3_texture.ics");
#endif

	perlinInner.DisposeData();
	perlinOutside.DisposeData();
	erroded.DisposeData();

	//now, read the "molecules"
	dots.clear();
	dots.reserve(1<<23);

	//time savers...
	const float xRes=mask.GetResolution().GetX();
	const float yRes=mask.GetResolution().GetY();
	const float zRes=mask.GetResolution().GetZ();
	const float xOff=mask.GetOffset().x;
	const float yOff=mask.GetOffset().y;
	const float zOff=mask.GetOffset().z;

	p=dt.GetFirstVoxelAddr();
	for (int z=0; z < (signed)dt.GetSizeZ(); ++z)
	for (int y=0; y < (signed)dt.GetSizeY(); ++y)
	for (int x=0; x < (signed)dt.GetSizeX(); ++x, ++p)
   for (int v=0; v < floorf(*p); v+=TEXTURE_QUANTIZATION)
	{
		//convert px coords into um, use centres of voxels
		const float X=(float(x)+0.5f)/xRes + xOff;
		const float Y=(float(y)+0.5f)/yRes + yOff;
		const float Z=(float(z)+0.5f)/zRes + zOff;

		dots.push_back(Vector3F(X,Y,Z));
		if (dots.size() == dots.capacity())
		{
			std::cout << "reserving more at position: " << x << "," << y << "," << z << "\n";
			dots.reserve(dots.size()+(1<<20));
		}
	}
//#ifdef SAVE_INTERMEDIATE_IMAGES
	std::cout << "initiated " << dots.size() << " fl. molecules (capacity is for "
	          << dots.capacity() << ")\n";
//#endif
}


void ActiveMesh::InitDots_filo(const i3d::Image3d<i3d::GRAY16>& mask,const unsigned int idx)
{
   // get the resolution of image
	i3d::Vector3d<float> res = mask.GetResolution().GetRes();
	float max_res = std::max(res.x, std::max(res.y, res.z)); // ... pixels/micron
	
	//syntactic short-cut to the proper filopodium geometry data
	mesh_t& filoMesh=Ftree[idx];

	//render the filopodium texture points along its skeleton
	//the amount of points is determined from the radius, from circle area -> square of radius
	for (size_t i=0; i < filoMesh.segFromPoint.size(); ++i)
	{
		//time-savers...
		const Vector3F& fP=filoMesh.fPoints[filoMesh.segFromPoint[i]];
		const Vector3F& tP=filoMesh.fPoints[filoMesh.segToPoint[i]];

		const float length=(tP-fP).Len(); // segment length in microns

		// discrete stepping, in fact: how many steps there will be for this particular one segment
		const int S = (int) ceil(length * max_res * (this->configFile.filoAxisOrientedDensity));

		for (int s=0; s < S; ++s)
		{
			//ratios "from" and "to"
			const float fS=float(S-s)/float(S);
			const float tS=float(s)/float(S);

			//target radius
			const float target_r=(i+1 < filoMesh.segFromPoint.size())? filoMesh.segFromRadius[i+1] : 0.f;

			//current radius
			float r=fS*filoMesh.segFromRadius[i] + tS*target_r;

			//current position
			Vector3F p =fS*fP;
			         p+=tS*tP;

			// The number of dots should correspond the width (radius) of filopodium
			// multiplied by given density. The density should be a large number, as the
			// radius is typically very small, to obtain many dots overhere.
			// Also, the number of dotsCount here is cca 5-times downscaled to the very final count.
		   float dotsCount = (this->configFile.filoContourOrientedDensity) * r;

			// prevent data overflow (5 - multiplicity of addition)
			int upper_limit = std::numeric_limits<i3d::GRAY16>::max() / (5 * TEXTURE_QUANTIZATION);
			int qM=(dotsCount > upper_limit)? upper_limit : (int)dotsCount;

			// establish 4 vectors  - all orthogonal to the direction of filopodium
			Vector3F mainDirection = tP-fP;
			Vector3F vector1(-mainDirection.y, mainDirection.x, 0.0f);
			Vector3F vector2(-vector1.x, -vector1.y, -vector1.z);

			Vector3F vector3;
			Mul(mainDirection, vector1, vector3);

			Vector3F vector4;
			Mul(mainDirection, vector2, vector4);

			// make them as small as the size of the radius in the given position
			vector1 /= vector1.Len();
			vector2 /= vector2.Len();
			vector3 /= vector3.Len();
			vector4 /= vector4.Len();

		   int radiusInPixels = (int) ceil(r*max_res);

			for (int q=0; q < qM; ++q)
			{
				dots_filo.push_back(p);

				// The following cycle guarantees, that the dots will fill the filopodium
				// smoothly - from the center to the outer envelope. 
				for (int step=0; step<=radiusInPixels; step++)
				{
				   // how much the given vector should enlarge
				   float scale = (r*(step+1.0f))/(radiusInPixels+1.0f);

					dots_filo.push_back(p+scale*vector1);
					dots_filo.push_back(p+scale*vector2);
					dots_filo.push_back(p+scale*vector3);
					dots_filo.push_back(p+scale*vector4);
				}
			}
		}
	}
}


void ActiveMesh::BrownDots(const i3d::Image3d<i3d::GRAY16>& mask)
{
	//time-savers: 6*sigma = voxel size (in the given axis)
	//if a dot is placed in the centre of some voxel, such sigma
	//setting should not displace it outside of its original voxel
	const float xSigma=1.f/(6.f*mask.GetResolution().GetX());
	const float ySigma=1.f/(6.f*mask.GetResolution().GetY());
	const float zSigma=1.f/(6.f*mask.GetResolution().GetZ());

	//cell body
	//NB: re-seed at every 2k-th point, i.e., after 6k rnd numbers are drawn
	for (size_t i=0; i < dots.size(); ++i)
	{
		dots[i].x+=GetRandomGauss(0.f,xSigma,(i%2000)==0);
		dots[i].y+=GetRandomGauss(0.f,ySigma);
		dots[i].z+=GetRandomGauss(0.f,zSigma);
	}

	//filopodia
	for (size_t i=0; i < dots_filo.size(); ++i)
	{
		dots_filo[i].x+=GetRandomGauss(0.f,xSigma,(i%2000)==0);
		dots_filo[i].y+=GetRandomGauss(0.f,ySigma);
		dots_filo[i].z+=GetRandomGauss(0.f,zSigma);
	}
}


template <class VT>
VT GetPixel(i3d::Image3d<VT> const &img,const float x,const float y,const float z)
{
	//nearest not-greater integer coordinate, "o" in the picture in docs
	//X,Y,Z will be coordinate of the voxel no. 2
	const int X=static_cast<int>(floorf(x));
	const int Y=static_cast<int>(floorf(y));
	const int Z=static_cast<int>(floorf(z));
	//now we can write only to pixels at [X or X+1,Y or Y+1,Z or Z+1]

	//quit if too far from the "left" borders of the image
	//as we wouldn't be able to draw into the image anyway
	if ((X < -1) || (Y < -1) || (Z < -1)) return (0);

	//residual fraction of the input coordinate
	const float Xfrac=x - static_cast<float>(X);
	const float Yfrac=y - static_cast<float>(Y);
	const float Zfrac=z - static_cast<float>(Z);

	//the weights
	float A=0.0f,B=0.0f,C=0.0f,D=0.0f; //for 2D

	//x axis:
	A=D=Xfrac;
	B=C=1.0f - Xfrac;

	//y axis:
	A*=1.0f - Yfrac;
	B*=1.0f - Yfrac;
	C*=Yfrac;
	D*=Yfrac;

	//z axis:
	float A_=A,B_=B,C_=C,D_=D;
	A*=1.0f - Zfrac;
	B*=1.0f - Zfrac;
	C*=1.0f - Zfrac;
	D*=1.0f - Zfrac;
	A_*=Zfrac;
	B_*=Zfrac;
	C_*=Zfrac;
	D_*=Zfrac;

	//portions of the value in a bit more organized form, w[z][y][x]
	const float w[2][2][2]={{{B ,A },{C ,D }},
                           {{B_,A_},{C_,D_}}};

	//the return value
	float v=0;

	//reading from the input image,
	//for (int zi=0; zi < 2; ++zi) if (Z+zi < (signed)img.GetSizeZ()) { //shortcut for 2D cases to avoid some computations...
	for (int zi=0; zi < 2; ++zi)
	  for (int yi=0; yi < 2; ++yi)
	    for (int xi=0; xi < 2; ++xi)
		if (img.Include(X+xi,Y+yi,Z+zi)) {
			//if we got here then we can safely change coordinate types
			v+=(float)img.GetVoxel(size_t(X+xi),size_t(Y+yi),size_t(Z+zi)) * w[zi][yi][xi];
		}
	//}

	return ( static_cast<VT>(v) );
}

void ActiveMesh::FFDots(const i3d::Image3d<i3d::GRAY16>& mask,
                        const FlowField<float> &FF)
{
	//TODO: tests: FF consistency, same size as mask?

	//time savers...
	const float xRes=mask.GetResolution().GetX();
	const float yRes=mask.GetResolution().GetY();
	const float zRes=mask.GetResolution().GetZ();
	const float xOff=mask.GetOffset().x;
	const float yOff=mask.GetOffset().y;
	const float zOff=mask.GetOffset().z;

	//apply FF on the this->dots (no boundary checking)
	for (size_t i=0; i < dots.size(); ++i)
	{
		//turn micron position into pixel one
		const float X=(dots[i].x -xOff) *xRes;
		const float Y=(dots[i].y -yOff) *yRes;
		const float Z=(dots[i].z -zOff) *zRes;

		//note: GetPixel() returns 0 in case we ask for value outside the image
		//TODO: check against mask
		dots[i].x += GetPixel(*FF.x, X,Y,Z);
		dots[i].y += GetPixel(*FF.y, X,Y,Z);
		dots[i].z += GetPixel(*FF.z, X,Y,Z);
	}
}

void PopulateCube(i3d::Image3d<i3d::GRAY16>& texture,
                  const float x,const float y,const float z,  //real-valued voxel coordinate, centre of the cube
                  int quantum)                                //how many dots into one voxel cube
{
	//it is assumed that [x,y,z] fits into the image and is at least
	//1px away from the image border -> no checks needed anymore

	//lengths of AA edges of one voxel (AA = Axis Aligned)
	float xSize=1.0f/texture.GetResolution().GetX();
	float ySize=1.0f/texture.GetResolution().GetY();
	float zSize=1.0f/texture.GetResolution().GetZ();

	//std::cout << "quantum=" << quantum << "\n";
	//std::cout << "xyzSize==" << xSize << "," << ySize << "," << zSize << "\n";

	//density in dots/um^3
	quantum/=xSize*ySize*zSize;

	//scaling factor per one axis...
	quantum=(int)cbrtf(quantum);

	//how many dots per axis, honours anisotropy (if present)
	const int xn=(int)ceilf(xSize * quantum);
	const int yn=(int)ceilf(ySize * quantum);
	const int zn=(int)ceilf(zSize * quantum);
	//should hold xn*yn*zn >= original input quantum, ideally closest possible to the equality

	//std::cout << "xyzn=" << xn << "," << yn << "," << zn << "\n";
	//std::cout << "xn*yn*zn=" << xn*yn*zn << "\n";

	const long xLine=(signed)texture.GetSizeX();
	const long Slice=(signed)texture.GetSizeY() *xLine;

	for (int iz=0; iz < zn; ++iz)
	{
		const int Z=int(z-0.5f+float(iz)/float(zn));
		for (int iy=0; iy < yn; ++iy)
		{
			const int Y=int(y-0.5f+float(iy)/float(yn));
			i3d::GRAY16* T=texture.GetFirstVoxelAddr();
			T+=Z*Slice + Y*xLine;

			for (int ix=0; ix < xn; ++ix)
			{
				const int X=int(x-0.5f+float(ix)/float(xn));
				++T[X];

				//std::cout << "populating at [" << X << "," << Y << "," << Z << "]\n";
			}
		}
	}
}

void PopulateCube(i3d::Image3d<i3d::GRAY16>& texture,
                  const float x,const float y,const float z,  //real-valued voxel coordinate, centre of the cube
                  const int xn,const int yn,const int zn)     //how many dots per axis into one voxel cube
{
	//it is assumed that [x,y,z] fits into the image and is at least
	//1px away from the image border -> no checks needed anymore

	const long xLine=(signed)texture.GetSizeX();
	const long Slice=(signed)texture.GetSizeY() *xLine;

	for (int iz=0; iz < zn; ++iz)
	{
		const int Z=int(z-0.5f+float(iz)/float(zn));
		for (int iy=0; iy < yn; ++iy)
		{
			const int Y=int(y-0.5f+float(iy)/float(yn));
			i3d::GRAY16* T=texture.GetFirstVoxelAddr();
			T+=Z*Slice + Y*xLine;

			for (int ix=0; ix < xn; ++ix)
			{
				const int X=int(x-0.5f+float(ix)/float(xn));
				++T[X];

				//std::cout << "populating at [" << X << "," << Y << "," << Z << "]\n";
			}
		}
	}
}

void ActiveMesh::RenderDots(const i3d::Image3d<i3d::GRAY16>& mask,
                            i3d::Image3d<i3d::GRAY16>& texture)
{
	texture.CopyMetaData(mask);
	texture.GetVoxelData()=0;

	//how many fl. dots (image side) is represented with one dot (simulation side)
	int quantum=TEXTURE_QUANTIZATION;

	//time savers...
	const float xRes=texture.GetResolution().GetX();
	const float yRes=texture.GetResolution().GetY();
	const float zRes=texture.GetResolution().GetZ();
	const float xOff=texture.GetOffset().x;
	const float yOff=texture.GetOffset().y;
	const float zOff=texture.GetOffset().z;

	//time-savers for boundary checking...
	const int maxX=(int)texture.GetSizeX()-1;
	const int maxY=(int)texture.GetSizeY()-1;
	const int maxZ=(int)texture.GetSizeZ()-1;

	//density in dots/um^3
	quantum*=xRes*yRes*zRes;

	//scaling factor per one axis...
	quantum=(int)cbrtf(quantum);

	//how many dots per axis, honours anisotropy (if present)
	const int xn=(int)ceilf(quantum / xRes);
	const int yn=(int)ceilf(quantum / yRes);
	const int zn=(int)ceilf(quantum / zRes);
	//should hold xn*yn*zn >= original input quantum, ideally closest possible to the equality

	//std::cout << "xyzn=" << xn << "," << yn << "," << zn << "\n";
	//std::cout << "xn*yn*zn=" << xn*yn*zn << "\n";

	//render the points into the texture image
	for (size_t i=0; i < dots.size(); ++i)
	{
		//real-valued pixel coordinate
		const float x=(dots[i].x-xOff) *xRes;
		const float y=(dots[i].y-yOff) *yRes;
		const float z=(dots[i].z-zOff) *zRes;

		//is it too far from boundary?
		const int X=(int)roundf(x);
		const int Y=(int)roundf(y);
		const int Z=(int)roundf(z);
		if ((X > 0) && (Y > 0) && (Z > 0)
		   && (X < maxX) && (Y < maxY) && (Z < maxZ))
				PopulateCube(texture,x,y,z,xn,yn,zn);
	}
	for (size_t i=0; i < dots_filo.size(); ++i)
	{
		//real-valued pixel coordinate
		const float x=(dots_filo[i].x-xOff) *xRes;
		const float y=(dots_filo[i].y-yOff) *yRes;
		const float z=(dots_filo[i].z-zOff) *zRes;

		//is it too far from boundary?
		const int X=(int)roundf(x);
		const int Y=(int)roundf(y);
		const int Z=(int)roundf(z);
		if ((X > 0) && (Y > 0) && (Z > 0)
		   && (X < maxX) && (Y < maxY) && (Z < maxZ))
				PopulateCube(texture,x,y,z,xn,yn,zn);
	}
}

///------------------------------------------------------------------------
void ActiveMesh::PhaseII(const i3d::Image3d<i3d::GRAY16>& texture,
	                      i3d::Image3d<float>& blurred_texture)
{
	// convert the image into the float data type (to enable convolution
	// with PSF)
	i3d::Image3d<float> fimg;
	i3d::GrayToFloat(texture, fimg);

	// load the image of suitable PSF
	i3d::Image3d<float> psf(this->configFile.texture_PSFpath.c_str());

	// If the PSF has different resolution from the processed image, 
	// resampling is required.
	i3d::Vector3d<float> 
			  res_psf = psf.GetResolution().GetRes(),
			  res_fimg = fimg.GetResolution().GetRes();

	std::cout << "* res img: " << res_fimg << std::endl;
	std::cout << "* res psf: " << res_psf << std::endl;

	if (res_fimg != res_psf)
		{
			 std::cout << "resampling psf" <<  std::endl;
			 i3d::ResampleToDesiredResolution(psf, res_fimg, i3d::LANCZOS);
		}

	std::cout << "* image size: " << fimg.GetSize() << std::endl;
	std::cout << "* psf size: " << psf.GetSize() << std::endl;

	// convolution with real confocal PSF
	i3d::Convolution<double>(fimg, psf, blurred_texture);

#ifdef SAVE_INTERMEDIATE_IMAGES
	blurred_texture.SaveImage("4_texture_blurred.ics");
#endif

	// Let us add the uneven illumination (with its brightest loci in the cell
	// centre, which happens to be in the image centre)
	const int xC=(int)(texture.GetSizeX()/2);
	const int yC=(int)(texture.GetSizeY()/2);
	float* p=blurred_texture.GetFirstVoxelAddr();
	for (int z=0; z < (signed)blurred_texture.GetSizeZ(); ++z)
		 for (int y=0; y < (signed)blurred_texture.GetSizeY(); ++y)
			  for (int x=0; x < (signed)blurred_texture.GetSizeX(); ++x, ++p)
			  {
			   // max distance
				float maxDist = SQR((float)xC) + SQR((float)yC);
				// background signal (inverted parabola)
				float distSq = 
						  -(SQR((float)(x-xC)) + SQR((float)(y-yC)))/maxDist + 1.f;
				*p *= distSq;
			  }
#ifdef SAVE_INTERMEDIATE_IMAGES
	blurred_texture.SaveImage("5_texture_blurred_illuminated.ics");
#endif
}

void ActiveMesh::PhaseIII(i3d::Image3d<float>& blurred,
	                       i3d::Image3d<i3d::GRAY16>& texture)
{
	// NONSPECIFIC BACKGROUND
   i3d::Image3d<float> bgImg;
	bgImg.CopyMetaData(blurred);
	DoPerlin3D(bgImg,10.0,7.0,1.0,10); // very smooth a wide coherent noise

	// reset the maximum and minimin intesity levels of the background
	// to the expected values
	const float oldMaxI = bgImg.GetVoxelData().max();
	const float oldMinI = bgImg.GetVoxelData().min();
	const float newMaxI = 2.2f;
	const float newMinI = 2.0f;
	const float scale = (newMaxI-newMinI)/(oldMaxI-oldMinI);

	for (size_t i=0; i<bgImg.GetImageSize(); i++)
	{
		float value = bgImg.GetVoxel(i);
		value = scale*(value - oldMinI) + newMinI;
	   bgImg.SetVoxel(i, value);
	}
	
   // given gain of EMCCD camera
   const int EMCCDgain = 1000;

	float* p=blurred.GetFirstVoxelAddr();
	for (size_t i=0; i<blurred.GetImageSize(); i++, p++)
	{
		// shift the signal (simulates non-ideal black background)
		// ?reflection of medium?
		*p+=bgImg.GetVoxel(i);

		// ENF ... Excess Noise Factor (stochasticity of EMCCD gain)
		// source of information:
		// https://www.qimaging.com/resources/pdfs/emccd_technote.pdf
		const float ENF = GetRandomUniform(1.0f, 1.4f);

		// PHOTON NOISE 
		// uncertainty in the number of incoming photons
		// from statistics: shot noise mean = sqrt(signal) 
		const float noiseMean = sqrtf(*p);

		// SKIP avoid strong discretization of intenzity histogram due
		// to the Poisson probabilty function
 		*p += ENF * ((float)GetRandomPoisson(noiseMean) - noiseMean + SKIP);

		// EMCCD GAIN
      // amplification of signal (and inevitably also the noise)
	   *p *= EMCCDgain; 

		// DARK CURRENT
		// constants are parameters of Andor iXon camera provided from vendor:
		*p += ENF*EMCCDgain*(float)GetRandomPoisson(0.06f);

		// BASELINE (camera electron level)
		*p += 400.0f;

		// READOUT NOISE
		// variance up to 25.f (old camera on ILBIT)
		// variance about 1.f (for the new camera on ILBIT)
		*p+=GetRandomGauss(0.0f,1.0f);

		// ADC (analogue-digital converter)
		// ADCgain ... how many electrons correcpond one intensity value
		// ADCoffset ... how much intensity levels are globally added to each pixel
		const float ADCgain = 28.0f; 
		*p/=ADCgain;

		const float ADCoffset = 400.0f;
		*p+=ADCoffset;
	}
#ifdef SAVE_INTERMEDIATE_IMAGES
	blurred.SaveImage("6_texture_finalized.ics");
#endif

	//obtain final GRAY16 image
	i3d::FloatToGrayNoWeight(blurred,texture);
}


void ExtendSmooth(i3d::Image3d<i3d::GRAY16>& img, //reference to see the image dimensions
                  const i3d::GRAY16* const ffIM,
                  const float* const ffID,
                  i3d::GRAY16* const ffOM,
                  float* const ffOD)
{
	const size_t xLine=img.GetSizeX();
	const size_t Slice=img.GetSizeY() *xLine;

	for (size_t z=1; z < img.GetSizeZ()-1; ++z)
	for (size_t y=1; y < img.GetSizeY()-1; ++y)
	for (size_t x=1; x < img.GetSizeX()-1; ++x)
	{
		const size_t offset=z*Slice +y*xLine +x;
		//outside? -> calculate average
		if (ffIM[offset] == 0)
		{
			float val=0.f;
			int count=0;

			size_t off=offset-Slice;
			if (ffIM[off-xLine]) { ++count; val+=ffID[off-xLine]; }
			if (ffIM[off-1])     { ++count; val+=ffID[off-1]; }
			if (ffIM[off])       { ++count; val+=ffID[off]; }
			if (ffIM[off+1])     { ++count; val+=ffID[off+1]; }
			if (ffIM[off+xLine]) { ++count; val+=ffID[off+xLine]; }

			off=offset;
			if (ffIM[off-xLine-1]) { ++count; val+=ffID[off-xLine-1]; }
			if (ffIM[off-xLine])   { ++count; val+=ffID[off-xLine]; }
			if (ffIM[off-xLine+1]) { ++count; val+=ffID[off-xLine+1]; }
			if (ffIM[off-1])       { ++count; val+=ffID[off-1]; }
			//if (ffIM[off])         { ++count; val+=ffID[off]; }
			if (ffIM[off+1])       { ++count; val+=ffID[off+1]; }
			if (ffIM[off+xLine-1]) { ++count; val+=ffID[off+xLine-1]; }
			if (ffIM[off+xLine])   { ++count; val+=ffID[off+xLine]; }
			if (ffIM[off+xLine+1]) { ++count; val+=ffID[off+xLine+1]; }

			off=offset+Slice;
			if (ffIM[off-xLine]) { ++count; val+=ffID[off-xLine]; }
			if (ffIM[off-1])     { ++count; val+=ffID[off-1]; }
			if (ffIM[off])       { ++count; val+=ffID[off]; }
			if (ffIM[off+1])     { ++count; val+=ffID[off+1]; }
			if (ffIM[off+xLine]) { ++count; val+=ffID[off+xLine]; }

			//have we found some neighbor?
			if (count)
			{
				//yes, then we are extending the original mask
				val/=float(count);
				ffOM[offset]=1;
			}
			else
			{
				ffOM[offset]=0;
			}
			ffOD[offset]=val;
		}
		//inside? -> copy original data
		else
		{
			ffOM[offset]=1;
			ffOD[offset]=ffID[offset];
		}
	}
}

void ActiveMesh::ConstructFF_T(FlowField<float> &FF)
{
	//erase the flow field
	//add displacement vectors by "rendering" displacement tetrahedra
	//smooth

	//tests:
	//FF must be consistent
	if (FF.isConsistent() == false)
	{
		std::cout << "ConstructFF_T(): input FF is not consistent.\n";
		return;
	}
	//oldVolPos and newVolPos must be of the same length
	if (oldVolPos.size() != newVolPos.size())
	{
		std::cout << "ConstructFF_T(): (volumetric) vertex arrays are not consistent.\n";
		return;
	}

	//erase
	FF.x->GetVoxelData()=0;
	FF.y->GetVoxelData()=0;
	FF.z->GetVoxelData()=0;

	i3d::Image3d<i3d::GRAY16> imgCounts;
	imgCounts.CopyMetaData(*FF.x);
	imgCounts.GetVoxelData()=0;

	//time savers...
	const float xRes=FF.x->GetResolution().GetX();
	const float yRes=FF.x->GetResolution().GetY();
	const float zRes=FF.x->GetResolution().GetZ();
	const float xOff=FF.x->GetOffset().x;
	const float yOff=FF.x->GetOffset().y;
	const float zOff=FF.x->GetOffset().z;

	//time-savers for boundary checking...
	const int maxX=(int)FF.x->GetSizeX()-1;
	const int maxY=(int)FF.x->GetSizeY()-1;
	const int maxZ=(int)FF.x->GetSizeZ()-1;

	//time-savers for accessing neigbors...
	const long xLine=(signed)FF.x->GetSizeX();
	const long Slice=(signed)FF.x->GetSizeY() *xLine;
	float* const ffx=FF.x->GetFirstVoxelAddr();
	float* const ffy=FF.y->GetFirstVoxelAddr();
	float* const ffz=FF.z->GetFirstVoxelAddr();
	i3d::GRAY16* const ffC=imgCounts.GetFirstVoxelAddr();

	//over all tetrahedra
	for (size_t i=0; i < VolID.size(); i+=4)
	{
		//tetrahedron to drive positions in the FF
		//(positions tetrahedron)
		const Vector3F& v1=oldVolPos[VolID[i+0]];
		const Vector3F& v2=oldVolPos[VolID[i+1]];
		const Vector3F& v3=oldVolPos[VolID[i+2]];
		const Vector3F& v4=oldVolPos[VolID[i+3]];

		//tetrahedron to drive values to place at these positions
		//(values tetrahedron)
		const Vector3F dv1=newVolPos[VolID[i+0]] - v1;
		const Vector3F dv2=newVolPos[VolID[i+1]] - v2;
		const Vector3F dv3=newVolPos[VolID[i+2]] - v3;
		const Vector3F dv4=newVolPos[VolID[i+3]] - v4;

		//determine stepping factor to iterate somewhat efficiently over all pixels
		//which this tetrahedron spans over: calc AABB, get longest AA dimension,
		//use resolution-driven magic factor...
		//(assumes the resolution is isotropic)

		Vector3F tmp;
		//AA dimensions of AABB
		tmp.x=  std::max(std::max(v1.x,v2.x),std::max(v3.x,v4.x));
		float t=std::min(std::min(v1.x,v2.x),std::min(v3.x,v4.x));
		tmp.x-=t;

		tmp.y=std::max(std::max(v1.y,v2.y),std::max(v3.y,v4.y));
		t    =std::min(std::min(v1.y,v2.y),std::min(v3.y,v4.y));
		tmp.y-=t;

		tmp.z=std::max(std::max(v1.z,v2.z),std::max(v3.z,v4.z));
		t    =std::min(std::min(v1.z,v2.z),std::min(v3.z,v4.z));
		tmp.z-=t;

		//longest AA dimension
		t=std::max(tmp.x,std::max(tmp.y,tmp.z));

		//the same dimension in pixels
		t*=xRes;

		//the magic constant...
		t*=1.6f;

		//portion the tetrahedron at least into t steps -> step size is inverse
		t=1.f/t;

		//now iterate through the tetrahedron:
		for (float d=0.f; d <= 1.0f; d += t)
		for (float c=0.f; c <= (1.0f-d); c += t)
		for (float b=0.f; b <= (1.0f-d-c); b += t)
		{
			float a=1.0f -b -c -d;

			//float-point coordinate:
			tmp =a*v1;
			tmp+=b*v2;
			tmp+=c*v3;
			tmp+=d*v4;

			//pixel (integer) coordinate:
			const long x=long((tmp.x-xOff) *xRes);
			const long y=long((tmp.y-yOff) *yRes);
			const long z=long((tmp.z-zOff) *zRes);

			//inside the image?
			if ((x > 0) && (y > 0) && (z > 0)
				&& (x < maxX) && (y < maxY) && (z < maxZ))
			{
				//a value to insert here
				tmp =a*dv1;
				tmp+=b*dv2;
				tmp+=c*dv3;
				tmp+=d*dv4;

				//insert:
				ffx[z*Slice +y*xLine +x]+=tmp.x;
				ffy[z*Slice +y*xLine +x]+=tmp.y;
				ffz[z*Slice +y*xLine +x]+=tmp.z;
				ffC[z*Slice +y*xLine +x]+=1;
			}
		}
	}

	//finish the averaging of FF
	for (size_t i=0; i < imgCounts.GetImageSize(); ++i)
	if (*(ffC+i))
	{
		*(ffx+i)/=float(*(ffC+i));
		*(ffy+i)/=float(*(ffC+i));
		*(ffz+i)/=float(*(ffC+i));
	}
	//imgCounts.SaveImage("counts.ics"); //TODO REMOVE

	//sanity checker: //TODO REMOVE
	size_t issueCounter=0;
	for (long z=1; z < (signed)imgCounts.GetSizeZ()-1; ++z)
	for (long y=1; y < (signed)imgCounts.GetSizeY()-1; ++y)
	for (long x=1; x < (signed)imgCounts.GetSizeX()-1; ++x)
	{
		const long offset=z*Slice +y*xLine +x;
		if (ffC[offset-1]>0 && ffC[offset]==0 && ffC[offset+1]>0)
		{
			//necessary condition met, check the sufficient as well
			//necessary condition: pattern occurs in x-axis
			//sufficient: pattern occurs in all three axes
			//
			//y axis:
			if (ffC[offset-xLine]>0 && ffC[offset+xLine]>0)
			{
				//z axis:
				if (ffC[offset-Slice]>0 && ffC[offset+Slice]>0)
					++issueCounter;
			}
		}
	}
	if (issueCounter)
		std::cout << "ConstructFF_T(): " << issueCounter << " HOLES LEFT IN THE FF.\n";

	//a bit more of fine-tunning:
	// "extrude" 2px wide outer/added boundary by smoothing edge values

	//touch/modify only the original empty spaces,
	//empty spaces are indicated with the imgCounts image
	i3d::Image3d<i3d::GRAY16> maskA,maskB; //temporary OUTPUT MASKS
	maskA.CopyMetaData(imgCounts);
	maskB.CopyMetaData(imgCounts);
	i3d::GRAY16* const ffMA=maskA.GetFirstVoxelAddr();
	i3d::GRAY16* const ffMB=maskB.GetFirstVoxelAddr();

	i3d::Image3d<float> shell;
	shell.CopyMetaData(imgCounts);
	float* const ffD=shell.GetFirstVoxelAddr(); //temporary OUTPUT DATA

	//x
	ExtendSmooth(imgCounts,imgCounts.GetFirstVoxelAddr(),ffx,ffMA,ffD);
	ExtendSmooth(imgCounts,ffMA,ffD,ffMB,ffx);

	//y
	ExtendSmooth(imgCounts,imgCounts.GetFirstVoxelAddr(),ffy,ffMA,ffD);
	ExtendSmooth(imgCounts,ffMA,ffD,ffMB,ffy);

	//z
	ExtendSmooth(imgCounts,imgCounts.GetFirstVoxelAddr(),ffz,ffMA,ffD);
	ExtendSmooth(imgCounts,ffMA,ffD,ffMB,ffz);
}


void ActiveMesh::configDataSubClass::getValueOrDefault(const char* key,const std::string& defValue,std::string& var)
{
	std::string sKey(key);
	std::map<std::string,std::string>::const_iterator it = keyVal_map.find(sKey);
	if (it == keyVal_map.end())
	{
		std::cout << "INCOMPLETE CONFIG FILE, add the following line to your config file:\n";
		std::cout << key << "  " << defValue << "\n";
		var = defValue;
	}
	else
		var = it->second;
}

void ActiveMesh::configDataSubClass::getValueOrDefault(const char* key,const int defValue,int& var)
{
	std::string sKey(key);
	std::map<std::string,std::string>::const_iterator it = keyVal_map.find(sKey);
	if (it == keyVal_map.end())
	{
		std::cout << "INCOMPLETE CONFIG FILE, add the following line to your config file:\n";
		std::cout << key << "  " << defValue << "\n";
		var = defValue;
	}
	else
		var = atoi(it->second.c_str());
}

void ActiveMesh::configDataSubClass::getValueOrDefault(const char* key,const float defValue,float& var)
{
	std::string sKey(key);
	std::map<std::string,std::string>::const_iterator it = keyVal_map.find(sKey);
	if (it == keyVal_map.end())
	{
		std::cout << "INCOMPLETE CONFIG FILE, add the following line to your config file:\n";
		std::cout << key << "  " << defValue << "\n";
		var = defValue;
	}
	else
		var = (float)atof(it->second.c_str());
}

int ActiveMesh::configDataSubClass::ParseConfigFile(const char* fileName)
{
	//clear before parsing
	keyVal_map.clear();

	//try to open the config file
	std::ifstream configFile(fileName);
	if (!configFile.is_open())
	{
		std::cout << "Unable to read the config file: " << fileName << "\n";
		return(-1);
	}

	//read the config file lines as long we can
	std::string key,value;
	while (configFile >> key)
	{
		//skip empty lines
		if (key.length() == 0) continue;

		//skip comments
		if (key.at(0) == ';')
		{ configFile.ignore(10240,'\n'); continue; }

		//otherwise, read also the value part and store it
		configFile >> value;
		keyVal_map[key]=value;
	}
	configFile.close();

	//provide default values, if necessary
	getValueOrDefault("texture_res_x", 8.0, texture_Resolution.x);
	getValueOrDefault("texture_res_y", 8.0, texture_Resolution.y);
	getValueOrDefault("texture_res_z", 1.0, texture_Resolution.z);

	getValueOrDefault("border_width_x", 5.0, texture_borderWidth.x);
	getValueOrDefault("border_width_y", 5.0, texture_borderWidth.y);
	getValueOrDefault("border_width_z", 5.0, texture_borderWidth.z);

	getValueOrDefault("PSF",
	  "../psf/2013-07-25_1_1_9_0_2_0_0_1_0_0_0_0_9_12.ics", texture_PSFpath);

	getValueOrDefault("filoAxisOrientedDensity", 10, filoAxisOrientedDensity);
	getValueOrDefault("filoContourOrientedDensity", 15, filoContourOrientedDensity);

	getValueOrDefault("coarsenessLower", 1.0, coarsenessLower);
	getValueOrDefault("coarsenessUpper", 2.0, coarsenessUpper);

	//clear to save some memory
	keyVal_map.clear();
	return (0);
}
