#ifdef GTGEN_DEBUG
  #include <sys/time.h>
#endif

#include <i3d/draw.h>
#include <i3d/morphology.h>

#include "flowfields.h"
#include "bp_lists.h"

template <class MV>
size_t ResetBoundaryPoints(std::vector< i3d::Vector3d<float> /**/> &BPList,
			 i3d::Vector3d<float> &BPCentre,
			 i3d::Image3d<MV> const &mask,
			 const MV ID)
{
	//plan: make a copy of the mask to make sure only the object of interest
	//is shown in the new mask; the new mask is then slightly eroded and the
	//disappeared pixels are considered to form the outer boundary of the object
	//in the mask
	//
	//see docs to learn details about outer and inner boundary points

	DEBUG_REPORT("source image at " << mask.GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(mask.GetSize(),mask.GetResolution())  << " in microns");

	//will hold a copy of the original mask image
	i3d::Image3d<bool> myMask;

	myMask.CopyMetaData(mask);
	const MV *m=mask.GetFirstVoxelAddr();
	bool *mM=myMask.GetFirstVoxelAddr();

	//do the copy
	if (ID == 0) for (size_t i=0; i < mask.GetImageSize(); ++i, ++m, ++mM) *mM=(*m > 0);
	else         for (size_t i=0; i < mask.GetImageSize(); ++i, ++m, ++mM) *mM=(*m == ID);

	//erosion by 1 pixel line
	i3d::Image3d<bool> myMaskEroded;
	i3d::Erosion<bool>(myMask,myMaskEroded,i3d::nb3D_26);

	//clear the old list of points
	BPList.clear();

	//the future return value, the number of outer boundary points
	size_t NoofOuterPoints=0;

	//fill the list and compute the geometric centre at the same time
	double xC=0; //centre positions
	double yC=0;
	double zC=0;

	//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;

	//working pointers
	mM=myMask.GetFirstVoxelAddr();
	bool *mME=myMaskEroded.GetFirstVoxelAddr();

	//find outer boundary points - these are good for collision detection
	for (size_t z=0; z < myMask.GetSizeZ(); ++z)
	  for (size_t y=0; y < myMask.GetSizeY(); ++y)
	    for (size_t x=0; x < myMask.GetSizeX(); ++x, ++mM, ++mME)
		if ((*mM) && (!*mME)) {
			//x,y,z is position of a boundary pixel,
			//turn it into an absolute micron coordinate
			const float X=(float)x/xRes + xOff;
			const float Y=(float)y/yRes + yOff;
			const float Z=(float)z/zRes + zOff;

			//to lessen a bit the effect of iterative increasing of the list by one point,
			//as the list tends to increase always by one and with every such increase
			//of capacity, the whole list is copied over into a new (longer) one
			if (BPList.size() == BPList.capacity())
				BPList.reserve(BPList.size()+10000);

			//add on the list
			BPList.push_back(i3d::Vector3d<float>(X,Y,Z));

			//computation of the centre
			xC+=X; yC+=Y; zC+=Z;
		}

	//remember how many outer boundary points we have;
	//these points will used for collision detections...
	NoofOuterPoints=BPList.size();

	//finalize the new centre coordinate, the buddies were not accumulated...
	BPCentre.x=static_cast<float>( xC/(double)BPList.size() );
	BPCentre.y=static_cast<float>( yC/(double)BPList.size() );
	BPCentre.z=static_cast<float>( zC/(double)BPList.size() );

	//one-fourth pixel offsets
	const float xBuddyOffset=0.25f / xRes; //in microns
	const float yBuddyOffset=0.25f / yRes;
	const float zBuddyOffset=0.25f / zRes;

	//time-savers for accessing neigbors...
	const size_t xLine=mask.GetSizeX();
	const size_t Slice=mask.GetSizeY() *xLine;

	mM=myMask.GetFirstVoxelAddr();
	mME=myMaskEroded.GetFirstVoxelAddr();

	//find outer boundary points again - these are here only to support rendering
	for (size_t z=0; z < (myMask.GetSizeZ()-1); ++z) {
	  for (size_t y=0; y < (myMask.GetSizeY()-1); ++y) {
	    for (size_t x=0; x < (myMask.GetSizeX()-1); ++x, ++mM, ++mME)
		if ((*mM) && (!*mME)) {
			//x,y,z is position of a boundary pixel,
			//turn it into an absolute micron coordinate
			const float X=(float)x/xRes + xOff;
			const float Y=(float)y/yRes + yOff;
			const float Z=(float)z/zRes + zOff;

			//to lessen a bit the effect of iterative increasing of the list by one point,
			//as the list tends to increase always by one and with every such increase
			//of capacity, the whole list is copied over into a new (longer) one
			if (BPList.size() == BPList.capacity())
				BPList.reserve(BPList.size()+30000);

			//add on the list iff we are _interpolating_,
			//i.e. there exists a neighbor in the given direction
			//FULL VERSION
			/*
			if (*(mM+1)) {
				//just x-Line
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z));
			}
			if (*(mM+xLine)) {
				//just y-Line
				BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z));
			}
			if (*(mM+Slice)) {
				//just z-Line
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+2.f*zBuddyOffset));
			}

			if ( (*(mM+1)) && (*(mM+xLine)) && (*(mM+xLine+1)) ) {
				//xy-Face
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z));
			}
			if ( (*(mM+1)) && (*(mM+Slice)) && (*(mM+Slice+1)) ) {
				//xz-Face
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z+2.f*zBuddyOffset));
			}
			if ( (*(mM+xLine)) && (*(mM+Slice)) && (*(mM+xLine+Slice)) ) {
				//yz-Face
				BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
			}

			if ( (*(mM+1)) && (*(mM+xLine)) && (*(mM+xLine+1)) 
			  && (*(mM+Slice)) && (*(mM+Slice+1)) && (*(mM+Slice+xLine)) && (*(mM+Slice+xLine+1)) ) {
				//xyz-Volume
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
			}
			*/

			//OPTIMIZED VERSION
			/*
			if (*(mM+1)) {
				//just x-Line
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z));

				if ( (*(mM+xLine)) && (*(mM+xLine+1)) ) {
					//xy-Face
					BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z));
					BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z));
					BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z));
					BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z));
				}
				if ( (*(mM+Slice)) && (*(mM+Slice+1)) ) {
					//xz-Face
					BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z+    zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z+    zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z+2.f*zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z+2.f*zBuddyOffset));

					if ( (*(mM+xLine)) && (*(mM+xLine+1)) && (*(mM+Slice+xLine)) && (*(mM+Slice+xLine+1)) ) {
						//xyz-Volume
						BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z+    zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z+    zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
						BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
					}
				}
			}

			if (*(mM+xLine)) {
				//just y-Line
				BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z));

				if ( (*(mM+Slice)) && (*(mM+xLine+Slice)) ) {
					//yz-Face
					BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z+    zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z+    zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z+2.f*zBuddyOffset));
					BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z+2.f*zBuddyOffset));
				}
			}

			if (*(mM+Slice)) {
				//just z-Line
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+2.f*zBuddyOffset));
			}
			*/

			//just x-Line, in both directions
			if (*(mM-1)) {
				BPList.push_back(i3d::Vector3d<float>(X-    xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X-2.f*xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X-3.f*xBuddyOffset,Y,Z));
			}
			if (*(mM+1)) {
				BPList.push_back(i3d::Vector3d<float>(X+    xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X+2.f*xBuddyOffset,Y,Z));
				BPList.push_back(i3d::Vector3d<float>(X+3.f*xBuddyOffset,Y,Z));
			}

			//just y-Line, in both directions
			if (*(mM-xLine)) {
				BPList.push_back(i3d::Vector3d<float>(X,Y-    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y-2.f*yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y-3.f*yBuddyOffset,Z));
			}
			if (*(mM+xLine)) {
				BPList.push_back(i3d::Vector3d<float>(X,Y+    yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y+2.f*yBuddyOffset,Z));
				BPList.push_back(i3d::Vector3d<float>(X,Y+3.f*yBuddyOffset,Z));
			}

			//just z-Line, in both directions
			if (*(mM-Slice)) {
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z-    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z-2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z-3.f*zBuddyOffset));
			}
			if (*(mM+Slice)) {
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+    zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+2.f*zBuddyOffset));
				BPList.push_back(i3d::Vector3d<float>(X,Y,Z+3.f*zBuddyOffset));
			}
			//this produces many doublings of points, but it is a good sacrifice for stable rendering

		}
	    ++mM; //move over the last pixel in the line
	    ++mME;
	  }
	  mM+=xLine; //move over the whole last line
	  mME+=xLine;
	}

	REPORT("new list contains " << BPList.size() << " points (" << NoofOuterPoints << " outer) with centre at " << BPCentre);
	return(NoofOuterPoints);
}


void MoveBPListAndCentre(std::vector< i3d::Vector3d<float> /**/> &BPList,
			 i3d::Vector3d<float> &BPCentre,
			 const i3d::Vector3d<float> &shift)
{
	for (size_t i=0; i < BPList.size(); ++i) 
	{
		 BPList[i] += shift;
	}

	BPCentre += shift;
}


void MoveBPListAndCentre(std::vector< i3d::Vector3d<float> /**/> &BPList,
			 i3d::Vector3d<float> &BPCentre,
			 FlowField<float> const &FF)
{
	//sanitary checks...
	if (!FF.isConsistent())
		throw ERROR_REPORT("Flow field is not consistent.");
	if (!FF.z)
		throw ERROR_REPORT("The input flow field is not 3D.");
	if (BPList.size() == 0) {
		DEBUG_REPORT("The input BPList is empty, quitting...");
		return;
	}

	DEBUG_REPORT("moving " << BPList.size() << " dots with flow field at " << FF.x->GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(FF.x->GetSize(),FF.x->GetResolution())  << " in microns");

	//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;
#ifdef GTGEN_DEBUG
	char NoMoreDbgReportsB=0; //Before movement
	char NoMoreDbgReportsA=0; //After movement
#endif

	//centre positions
	double xC=0;
	double yC=0;
	double zC=0;

	for (size_t i=0; i < BPList.size(); ++i) {
		//turn micron position into pixel one
		const float X=(BPList[i].x -xOff) *xRes;
		const float Y=(BPList[i].y -yOff) *yRes;
		const float Z=(BPList[i].z -zOff) *zRes;

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

		xC+=BPList[i].x;
		yC+=BPList[i].y;
		zC+=BPList[i].z;

#ifdef GTGEN_DEBUG
		//consistency checks... these indicate problem with size of local mask
		if (!FF.x->Include((size_t)roundf(X),
				   (size_t)roundf(Y),
				   (size_t)roundf(Z))) {
			if (!NoMoreDbgReportsB)
			  REPORT("Warning: Boundary point outside of the scope of the current flow field.");
			NoMoreDbgReportsB=1;
		}
		if ( !FF.x->Include(i3d::MicronsToPixels(
			BPList[i]-FF.x->GetOffset(),
			FF.x->GetResolution()
			)) ) {
			if (!NoMoreDbgReportsA)
			  REPORT("Warning: Moved outside of the scope of the current flow field.");
			NoMoreDbgReportsA=1;
		}
#endif
	}

	//finalize the new centre coordinate
	BPCentre.x=static_cast<float>( xC/(double)BPList.size() );
	BPCentre.y=static_cast<float>( yC/(double)BPList.size() );
	BPCentre.z=static_cast<float>( zC/(double)BPList.size() );

	//note: the function does not change the number of points on the list,
	//nor it changes the number of the outer boundary points -> no need to adjust Cell::scmCellOuterBPNumber

	DEBUG_REPORT("new object mask centre is " << BPCentre);
}


template <class MV>
void RenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP)
{
	DEBUG_REPORT("source image at " << mask.GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(mask.GetSize(),mask.GetResolution())  << " in microns");

	if (BPList.size() == 0) {
		REPORT("Warning, empty list given, nothing really rendered.");
		return;
	}

#ifdef GTGEN_DEBUG
	char NoMoreDbgReports=0;

	//time-measurement
	struct timeval t1,t2,t3;
	float T1,T2;
	gettimeofday(&t1,NULL);
#endif

	//determine AABB of the cell, so that we can
	//render it into reasonably small auxiliary image later
	i3d::Vector3d<float> minCorner(BPList[0]);
	i3d::Vector3d<float> maxCorner(BPList[0]);

	//scan all BP points
#ifndef GTGEN_WITH_SOFA
	for (size_t i=0; i < BPList.size(); ++i) {
#else
	for (int i=0; i < OuterBP; ++i) {
#endif
		if (BPList[i].x < minCorner.x) minCorner.x=BPList[i].x;
		else
		if (BPList[i].x > maxCorner.x) maxCorner.x=BPList[i].x;

		if (BPList[i].y < minCorner.y) minCorner.y=BPList[i].y;
		else
		if (BPList[i].y > maxCorner.y) maxCorner.y=BPList[i].y;

		if (BPList[i].z < minCorner.z) minCorner.z=BPList[i].z;
		else
		if (BPList[i].z > maxCorner.z) maxCorner.z=BPList[i].z;
	}

	//setup the AABB and prepare the auxiliary image:
	//maxCorner holds the 2um longer diagonal
	maxCorner-=minCorner;
	maxCorner+=i3d::Vector3d<float>(2.f);
	//minCorner is offset within the 'mask' image...
	minCorner-=mask.GetOffset();
#ifdef GTGEN_DEBUG
	if ((minCorner.x < 0) || (minCorner.y < 0) || (minCorner.z < 0))
		throw ERROR_REPORT("pixelShift has some coordinate negative.");
#endif
	//... and 1um "shorter" (aka further from the cell)
	minCorner-=i3d::Vector3d<float>(1.f);

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

	//now, make the minCorner to be multiple of voxel grid
	i3d::Vector3d<int> pixelShift;
	pixelShift.x=(int)floorf(minCorner.x*xRes);
	pixelShift.y=(int)floorf(minCorner.y*yRes);
	pixelShift.z=(int)floorf(minCorner.z*zRes);
#ifdef GTGEN_DEBUG
	if ((pixelShift.x > (int)mask.GetSizeX())
	 || (pixelShift.y > (int)mask.GetSizeY())
	 || (pixelShift.z > (int)mask.GetSizeZ()))
		throw ERROR_REPORT("pixelShift has some coordinate outside the mask image.");

	REPORT("pixelShift=" << pixelShift);
#endif

	//time savers: myMask offset
	const float xOff=mask.GetOffset().x + (float)pixelShift.x/xRes;;
	const float yOff=mask.GetOffset().y + (float)pixelShift.y/yRes;;
	const float zOff=mask.GetOffset().z + (float)pixelShift.z/zRes;;

	//a copy into which the cell will be rendered first
	//(because we will render it dilated and we need to perform erosion
	//in the end)
	i3d::Image3d<i3d::GRAY8> myMask;
	myMask.SetResolution(mask.GetResolution());
	myMask.SetOffset(i3d::Offset(xOff,yOff,zOff));
	myMask.MakeRoom(ceilf(maxCorner.x*xRes),
	                ceilf(maxCorner.y*yRes),
						 ceilf(maxCorner.z*zRes));
	myMask.Clear();

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

	//time-savers for accessing neigbors...
	const int xLine=myMask.GetSizeX();
	const int Slice=myMask.GetSizeY() *xLine;
	i3d::GRAY8* const mB=myMask.GetFirstVoxelAddr();

	//draw all points dilated
#ifndef GTGEN_WITH_SOFA
	for (size_t i=0; i < BPList.size(); ++i) {
#else
	for (int i=0; i < OuterBP; ++i) {
#endif
		//nearest neighbor pixel coordinate
		const int x=(int)roundf( (BPList[i].x-xOff) *xRes);
		const int y=(int)roundf( (BPList[i].y-yOff) *yRes);
		const int z=(int)roundf( (BPList[i].z-zOff) *zRes);

		//if (mask.Include(x,y,z)) mask.SetVoxel(x,y,z,value);
		if ((x > 0) && (y > 0) && (z > 0)
		   && (x < maxX) && (y < maxY) && (z < maxZ)) {
		   	i3d::GRAY8 *mb=mB +z*Slice +y*xLine +x;
#ifdef GTGEN_DEBUG
			//translate local myMask coordinates to the mask coordinates by +pixelShift
			if ( (! *mb) && (mask.GetOffset().x == 0.f)
			   && (mask.GetVoxel(x+pixelShift.x,y+pixelShift.y,z+pixelShift.z)) )
					REPORT("Warning: overwriting value "
					  << mask.GetVoxel(i3d::Vector3d<int>(x,y,z)+pixelShift)
					  << " at pixel " << i3d::Vector3d<int>(x,y,z)+pixelShift);
#endif
			//dilation of this pixel
#ifndef GTGEN_WITH_SOFA
			*(mb-Slice)=200;
			*(mb-xLine)=200;
			*(mb-1)=200;
			*mb=200;
			*(mb+1)=200;
			*(mb+xLine)=200;
			*(mb+Slice)=200;
#else
			*(mb-1-xLine-Slice)=200;
			*(mb  -xLine-Slice)=200;
			*(mb+1-xLine-Slice)=200;
			*(mb-1      -Slice)=200;
			*(mb        -Slice)=200;
			*(mb+1      -Slice)=200;
			*(mb-1+xLine-Slice)=200;
			*(mb  +xLine-Slice)=200;
			*(mb+1+xLine-Slice)=200;

			*(mb-1-xLine)=200;
			*(mb  -xLine)=200;
			*(mb+1-xLine)=200;
			*(mb-1      )=200;
			*mb          =200;
			*(mb+1      )=200;
			*(mb-1+xLine)=200;
			*(mb  +xLine)=200;
			*(mb+1+xLine)=200;

			*(mb-1-xLine+Slice)=200;
			*(mb  -xLine+Slice)=200;
			*(mb+1-xLine+Slice)=200;
			*(mb-1      +Slice)=200;
			*(mb        +Slice)=200;
			*(mb+1      +Slice)=200;
			*(mb-1+xLine+Slice)=200;
			*(mb  +xLine+Slice)=200;
			*(mb+1+xLine+Slice)=200;
#endif
		}
#ifdef GTGEN_DEBUG
		else if (!NoMoreDbgReports) {
			REPORT("Warning: Asked to render outside or at the boundary of the mask image.");
			NoMoreDbgReports=1;
		}
#endif
	}

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T1=((float)t1.tv_usec / 1000000.0f);
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("rendering (dilated) object took " << T2-T1 << " seconds");
#endif

	//this floods cell exterior from the image corner
	i3d::FloodFill(myMask,(i3d::GRAY8)100,0);

#ifdef GTGEN_DEBUG
	gettimeofday(&t3,NULL);
	t3.tv_sec-=t1.tv_sec;
	T1=(float)t3.tv_sec + ((float)t3.tv_usec / 1000000.0f);
	REPORT("flooding outside of (dilated) object took " << T1-T2 << " seconds");
#endif
	const int mP_xLine=mask.GetSizeX();
	const int mP_Slice=mask.GetSizeY() *mP_xLine;
	MV* const mP=mask.GetFirstVoxelAddr() + pixelShift.z*mP_Slice
	             + pixelShift.y*mP_xLine + pixelShift.x;

	//now, do the erosion with just i3d::nb3D_6,
	//it is not filtered near image boundary
	for (size_t z=2; z < myMask.GetSizeZ(); ++z)
	  for (size_t y=2; y < myMask.GetSizeY(); ++y) {
	    //sweeping pointers
	    const i3d::GRAY8 *mb=mB +(z-1)*Slice +(y-1)*xLine +1;
			  //MV *mp=mask.GetVoxelAddr(pixelShift+i3d::Vector3d<size_t>(1,y-1,z-1));
			  MV *mp=mP +(z-1)*mP_Slice +(y-1)*mP_xLine +1;

	    for (size_t x=2; x < myMask.GetSizeX(); ++x, ++mb, ++mp)
		if ( (*(mb-Slice) != 100) && 
		     (*(mb-xLine) != 100) && 
		     (*(mb-    1) != 100) && 
		     (*(mb      ) != 100) && 
		     (*(mb+    1) != 100) &&
		     (*(mb+xLine) != 100) && 
		     (*(mb+Slice) != 100) ) *mp=value; //survived erosion
		//if (*mb) *mp=value; //direct copy
	  }

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("finishing (eroding) object took " << T2-T1 << " seconds");
#endif
}


template <class MV>
void RenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP,
			  FlowField<float> const &FF)
{
	if (!FF.isConsistent())
		throw ERROR_REPORT("Flow field is not consistent.");
	if (!FF.z)
		throw ERROR_REPORT("The input flow field is not 3D.");

	DEBUG_REPORT("source image at " << mask.GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(mask.GetSize(),mask.GetResolution())  << " in microns");

	if (BPList.size() == 0) {
		REPORT("Warning, empty list given, nothing really rendered.");
		return;
	}

	//a copy into which the cell will be rendered first
	//(because we will render it dilated and we need to perform erosion
	//in the end)
	i3d::Image3d<i3d::GRAY8> myMask;
	myMask.CopyMetaData(mask);
	myMask.Clear();

#ifdef GTGEN_DEBUG
	char NoMoreDbgReports=0;

	//time-measurement
	struct timeval t1,t2,t3;
	float T1,T2;
	gettimeofday(&t1,NULL);
#endif

	//flow field related time savers:
	const float xResFF=FF.x->GetResolution().GetX();
	const float yResFF=FF.x->GetResolution().GetY();
	const float zResFF=FF.x->GetResolution().GetZ();
	const float xOffFF=FF.x->GetOffset().x;
	const float yOffFF=FF.x->GetOffset().y;
	const float zOffFF=FF.x->GetOffset().z;

	//mask image related 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 int xLine=mask.GetSizeX();
	const int Slice=mask.GetSizeY() *xLine;
	i3d::GRAY8* const mB=myMask.GetFirstVoxelAddr();

	//draw all points dilated
#ifndef GTGEN_WITH_SOFA
	for (size_t i=0; i < BPList.size(); ++i) {
#else
	for (int i=0; i < OuterBP; ++i) {
#endif
		//working copy of the BP point, in microns
		float xFF=BPList[i].x;
		float yFF=BPList[i].y;
		float zFF=BPList[i].z;

		//turn micron position into flow field pixel one
		const float X=(xFF -xOffFF) *xResFF;
		const float Y=(yFF -yOffFF) *yResFF;
		const float Z=(zFF -zOffFF) *zResFF;

		//move the working copy micron position of the BP point
		//note: GetPixel() returns 0 in case we ask for value outside the image
		xFF += GetPixel(*FF.x, X,Y,Z);
		yFF += GetPixel(*FF.y, X,Y,Z);
		zFF += GetPixel(*FF.z, X,Y,Z);

		//now, turn the micron position back to mask pixel one
		//nearest neighbor pixel coordinate
		const int x=(int)roundf( (xFF-xOff) *xRes);
		const int y=(int)roundf( (yFF-yOff) *yRes);
		const int z=(int)roundf( (zFF-zOff) *zRes);

		//if (mask.Include(x,y,z)) mask.SetVoxel(x,y,z,value);
		if ((x > 0) && (y > 0) && (z > 0)
		   && (x < maxX) && (y < maxY) && (z < maxZ)) {
		   	i3d::GRAY8 *mb=mB +z*Slice +y*xLine +x;
#ifdef GTGEN_DEBUG
			if ((! *mb) && (mask.GetVoxel(x,y,z)) && (mask.GetOffset().x == 0.f))
				REPORT("Warning: overwriting value " << mask.GetVoxel(x,y,z)
				  << " at pixel (" << x << "," << y << "," << z << ")");
#endif
			//dilation of this pixel
#ifndef GTGEN_WITH_SOFA
			*(mb-Slice)=200;
			*(mb-xLine)=200;
			*(mb-1)=200;
			*mb=200;
			*(mb+1)=200;
			*(mb+xLine)=200;
			*(mb+Slice)=200;
#else
			*(mb-1-xLine-Slice)=200;
			*(mb  -xLine-Slice)=200;
			*(mb+1-xLine-Slice)=200;
			*(mb-1      -Slice)=200;
			*(mb        -Slice)=200;
			*(mb+1      -Slice)=200;
			*(mb-1+xLine-Slice)=200;
			*(mb  +xLine-Slice)=200;
			*(mb+1+xLine-Slice)=200;

			*(mb-1-xLine)=200;
			*(mb  -xLine)=200;
			*(mb+1-xLine)=200;
			*(mb-1      )=200;
			*mb          =200;
			*(mb+1      )=200;
			*(mb-1+xLine)=200;
			*(mb  +xLine)=200;
			*(mb+1+xLine)=200;

			*(mb-1-xLine+Slice)=200;
			*(mb  -xLine+Slice)=200;
			*(mb+1-xLine+Slice)=200;
			*(mb-1      +Slice)=200;
			*(mb        +Slice)=200;
			*(mb+1      +Slice)=200;
			*(mb-1+xLine+Slice)=200;
			*(mb  +xLine+Slice)=200;
			*(mb+1+xLine+Slice)=200;
#endif
		}
#ifdef GTGEN_DEBUG
		else if (!NoMoreDbgReports) {
			REPORT("Warning: Asked to render outside or at the boundary of the mask image.");
			NoMoreDbgReports=1;
		}
#endif
	}

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T1=((float)t1.tv_usec / 1000000.0f);
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("rendering (dilated) object took " << T2-T1 << " seconds");
#endif
	//this floods cell exterior from the image corner
	i3d::FloodFill(myMask,(i3d::GRAY8)100,0);

#ifdef GTGEN_DEBUG
	gettimeofday(&t3,NULL);
	t3.tv_sec-=t1.tv_sec;
	T1=(float)t3.tv_sec + ((float)t3.tv_usec / 1000000.0f);
	REPORT("flooding outside of (dilated) object took " << T1-T2 << " seconds");
#endif
	MV* const mP=mask.GetFirstVoxelAddr();

	//now, do the erosion with just i3d::nb3D_6,
	//it is not filtered near image boundary
	for (size_t z=2; z < mask.GetSizeZ(); ++z)
	  for (size_t y=2; y < mask.GetSizeY(); ++y) {
	    //sweeping pointers
	    const i3d::GRAY8 *mb=mB +(z-1)*Slice +(y-1)*xLine +1;
			  MV *mp=mP +(z-1)*Slice +(y-1)*xLine +1;

	    for (size_t x=2; x < mask.GetSizeX(); ++x, ++mb, ++mp)
		if ( (*(mb-Slice) != 100) && 
		     (*(mb-xLine) != 100) && 
		     (*(mb-    1) != 100) && 
		     (*(mb      ) != 100) && 
		     (*(mb+    1) != 100) &&
		     (*(mb+xLine) != 100) && 
		     (*(mb+Slice) != 100) ) *mp=value; //survived erosion
		//if (*mb) *mp=value; //direct copy
	  }

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("finishing (eroding) object took " << T2-T1 << " seconds");
#endif
}


template <class MV>
void SoftRenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP)
{
	DEBUG_REPORT("source image at " << mask.GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(mask.GetSize(),mask.GetResolution())  << " in microns");

	if (BPList.size() == 0) {
		REPORT("Warning, empty list given, nothing really rendered.");
		return;
	}

#ifdef GTGEN_DEBUG
	char NoMoreDbgReports=0;

	//time-measurement
	struct timeval t1,t2,t3;
	float T1,T2;
	gettimeofday(&t1,NULL);
#endif

	//determine AABB of the cell, so that we can
	//render it into reasonably small auxiliary image later
	i3d::Vector3d<float> minCorner(BPList[0]);
	i3d::Vector3d<float> maxCorner(BPList[0]);

	//scan all BP points
#ifndef GTGEN_WITH_SOFA
	for (size_t i=0; i < BPList.size(); ++i) {
#else
	for (int i=0; i < OuterBP; ++i) {
#endif
		if (BPList[i].x < minCorner.x) minCorner.x=BPList[i].x;
		else
		if (BPList[i].x > maxCorner.x) maxCorner.x=BPList[i].x;

		if (BPList[i].y < minCorner.y) minCorner.y=BPList[i].y;
		else
		if (BPList[i].y > maxCorner.y) maxCorner.y=BPList[i].y;

		if (BPList[i].z < minCorner.z) minCorner.z=BPList[i].z;
		else
		if (BPList[i].z > maxCorner.z) maxCorner.z=BPList[i].z;
	}

	//setup the AABB and prepare the auxiliary image:
	//maxCorner holds the 2um longer diagonal
	maxCorner-=minCorner;
	maxCorner+=i3d::Vector3d<float>(2.f);
	//minCorner is offset within the 'mask' image...
	minCorner-=mask.GetOffset();
#ifdef GTGEN_DEBUG
	if ((minCorner.x < 0) || (minCorner.y < 0) || (minCorner.z < 0))
		throw ERROR_REPORT("pixelShift has some coordinate negative.");
#endif
	//... and 1um "shorter" (aka further from the cell)
	minCorner-=i3d::Vector3d<float>(1.f);

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

	//now, make the minCorner to be multiple of voxel grid
	i3d::Vector3d<int> pixelShift;
	pixelShift.x=(int)floorf(minCorner.x*xRes);
	pixelShift.y=(int)floorf(minCorner.y*yRes);
	pixelShift.z=(int)floorf(minCorner.z*zRes);
#ifdef GTGEN_DEBUG
	if ((pixelShift.x > (int)mask.GetSizeX())
	 || (pixelShift.y > (int)mask.GetSizeY())
	 || (pixelShift.z > (int)mask.GetSizeZ()))
		throw ERROR_REPORT("pixelShift has some coordinate outside the mask image.");

	REPORT("pixelShift=" << pixelShift);
#endif

	//time savers: myMask offset
	const float xOff=mask.GetOffset().x + (float)pixelShift.x/xRes;;
	const float yOff=mask.GetOffset().y + (float)pixelShift.y/yRes;;
	const float zOff=mask.GetOffset().z + (float)pixelShift.z/zRes;;

	//a copy into which the cell will be rendered first
	//(because we will render it dilated and we need to perform erosion
	//in the end)
	i3d::Image3d<i3d::GRAY8> myMask;
	myMask.SetResolution(mask.GetResolution());
	myMask.SetOffset(i3d::Offset(xOff,yOff,zOff));
	myMask.MakeRoom(ceilf(maxCorner.x*xRes),
	                ceilf(maxCorner.y*yRes),
						 ceilf(maxCorner.z*zRes));
	myMask.Clear();

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

	//time-savers for accessing neigbors...
	const int xLine=myMask.GetSizeX();
	const int Slice=myMask.GetSizeY() *xLine;
	i3d::GRAY8* const mB=myMask.GetFirstVoxelAddr();

	//draw all points dilated
#ifndef GTGEN_WITH_SOFA
	for (size_t i=0; i < BPList.size(); ++i) {
#else
	for (int i=0; i < OuterBP; ++i) {
#endif
		//nearest neighbor pixel coordinate
		const int x=(int)roundf( (BPList[i].x-xOff) *xRes);
		const int y=(int)roundf( (BPList[i].y-yOff) *yRes);
		const int z=(int)roundf( (BPList[i].z-zOff) *zRes);

		//if (mask.Include(x,y,z)) mask.SetVoxel(x,y,z,value);
		if ((x > 0) && (y > 0) && (z > 0)
		   && (x < maxX) && (y < maxY) && (z < maxZ)) {
		   	i3d::GRAY8 *mb=mB +z*Slice +y*xLine +x;
#ifdef GTGEN_DEBUG
			//translate local myMask coordinates to the mask coordinates by +pixelShift
			if ( (! *mb) && (mask.GetOffset().x == 0.f)
			   && (mask.GetVoxel(x+pixelShift.x,y+pixelShift.y,z+pixelShift.z)) )
					REPORT("Warning: overwriting value "
					  << mask.GetVoxel(i3d::Vector3d<int>(x,y,z)+pixelShift)
					  << " at pixel " << i3d::Vector3d<int>(x,y,z)+pixelShift);
#endif
			//dilation of this pixel
#ifndef GTGEN_WITH_SOFA
			*(mb-Slice)=200;
			*(mb-xLine)=200;
			*(mb-1)=200;
			*mb=200;
			*(mb+1)=200;
			*(mb+xLine)=200;
			*(mb+Slice)=200;
#else
			*(mb-1-xLine-Slice)=200;
			*(mb  -xLine-Slice)=200;
			*(mb+1-xLine-Slice)=200;
			*(mb-1      -Slice)=200;
			*(mb        -Slice)=200;
			*(mb+1      -Slice)=200;
			*(mb-1+xLine-Slice)=200;
			*(mb  +xLine-Slice)=200;
			*(mb+1+xLine-Slice)=200;

			*(mb-1-xLine)=200;
			*(mb  -xLine)=200;
			*(mb+1-xLine)=200;
			*(mb-1      )=200;
			*mb          =200;
			*(mb+1      )=200;
			*(mb-1+xLine)=200;
			*(mb  +xLine)=200;
			*(mb+1+xLine)=200;

			*(mb-1-xLine+Slice)=200;
			*(mb  -xLine+Slice)=200;
			*(mb+1-xLine+Slice)=200;
			*(mb-1      +Slice)=200;
			*(mb        +Slice)=200;
			*(mb+1      +Slice)=200;
			*(mb-1+xLine+Slice)=200;
			*(mb  +xLine+Slice)=200;
			*(mb+1+xLine+Slice)=200;
#endif
		}
#ifdef GTGEN_DEBUG
		else if (!NoMoreDbgReports) {
			REPORT("Warning: Asked to render outside or at the boundary of the mask image.");
			NoMoreDbgReports=1;
		}
#endif
	}

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T1=((float)t1.tv_usec / 1000000.0f);
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("rendering (dilated) object took " << T2-T1 << " seconds");
#endif

	//this floods cell exterior from the image corner
	i3d::FloodFill(myMask,(i3d::GRAY8)100,0);

#ifdef GTGEN_DEBUG
	gettimeofday(&t3,NULL);
	t3.tv_sec-=t1.tv_sec;
	T1=(float)t3.tv_sec + ((float)t3.tv_usec / 1000000.0f);
	REPORT("flooding outside of (dilated) object took " << T1-T2 << " seconds");
#endif
	const int mP_xLine=mask.GetSizeX();
	const int mP_Slice=mask.GetSizeY() *mP_xLine;
	MV* const mP=mask.GetFirstVoxelAddr() + pixelShift.z*mP_Slice
	             + pixelShift.y*mP_xLine + pixelShift.x;

	//now, do the erosion with just i3d::nb3D_6,
	//it is not filtered near image boundary
	for (size_t z=2; z < myMask.GetSizeZ(); ++z)
	  for (size_t y=2; y < myMask.GetSizeY(); ++y) {
	    //sweeping pointers
	    const i3d::GRAY8 *mb=mB +(z-1)*Slice +(y-1)*xLine +1;
			  //MV *mp=mask.GetVoxelAddr(pixelShift+i3d::Vector3d<size_t>(1,y-1,z-1));
			  MV *mp=mP +(z-1)*mP_Slice +(y-1)*mP_xLine +1;

	    for (size_t x=2; x < myMask.GetSizeX(); ++x, ++mb, ++mp)
		if ( (*(mb-Slice) != 100) && 
		     (*(mb-xLine) != 100) && 
		     (*(mb-    1) != 100) && 
		     (*(mb      ) != 100) && 
		     (*(mb+    1) != 100) &&
		     (*(mb+xLine) != 100) && 
		     (*(mb+Slice) != 100) &&
			  (*mp == 0) ) *mp=value; //survived erosion
		//if (*mb) *mp=value; //direct copy
	  }

#ifdef GTGEN_DEBUG
	gettimeofday(&t2,NULL);
	t2.tv_sec-=t1.tv_sec;
	T2=(float)t2.tv_sec + ((float)t2.tv_usec / 1000000.0f);
	REPORT("finishing (eroding) object took " << T2-T1 << " seconds");
#endif
}


template <class MV>
void RenderBPListIntoNewMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP,
			  const size_t noFrames,
			  const float cellLookDistance,
			  const float perX,
			  const float perY,
			  const float perZ)
{
	//prepare the output image
	AllocateAndZeroNewMask(mask,BPList,noFrames,cellLookDistance,perX,perY,perZ);

	//now call the real drawer
	RenderBPListIntoMask(mask,value, BPList,BPCentre,OuterBP);
}


template <class MV>
void AllocateAndZeroNewMask(i3d::Image3d<MV> &mask,
			    std::vector< i3d::Vector3d<float> /**/> const &BPList,
			    const size_t noFrames,
				 const float cellLookDistance,
			    const float perX,
			    const float perY,
			    const float perZ)
{
	//find the (rectangular coordinate-system axis-aligned) bounding box
	//in microns...
	float xFrom=100000000000000.f;
	float yFrom=100000000000000.f;
	float zFrom=100000000000000.f;
	float xTo= -100000000000000.f;
	float yTo= -100000000000000.f;
	float zTo= -100000000000000.f;

	if (BPList.size() == 0) {
		REPORT("Warning, empty list given, nothing really rendered. Input image not resized!");
		return;
	}

	//sweep over the list and seek extremal coordinates
	for (size_t i=0; i < BPList.size(); ++i) {
		//tested position
		const float x=BPList[i].x;
		const float y=BPList[i].y;
		const float z=BPList[i].z;

		if (x < xFrom) xFrom=x;
		if (y < yFrom) yFrom=y;
		if (z < zFrom) zFrom=z;
		if (x > xTo) xTo=x;
		if (y > yTo) yTo=y;
		if (z > zTo) zTo=z;
	}

	DEBUG_REPORT("points span x=" << xFrom << " to " << xTo << "; y="
	       << yFrom << " to " << yTo << "; z=" << zFrom << " to " << zTo);

	//first, compute ideal parameters when considering only per[XYZ] parameters
	//new size of the image
	size_t xSize=(size_t)ceilf( (2.f*perX+1.f)*(xTo-xFrom) * mask.GetResolution().GetX() );
	size_t ySize=(size_t)ceilf( (2.f*perY+1.f)*(yTo-yFrom) * mask.GetResolution().GetY() );
	size_t zSize=(size_t)ceilf( (2.f*perZ+1.f)*(zTo-zFrom) * mask.GetResolution().GetZ() );

	//second, add boundary required when considering only motion of cell
	const float xySize=std::max((xTo-xFrom), (yTo-yFrom));
	const float maxTranslationXY=2.f*((float)noFrames+0.1f)*cellLookDistance;
	xSize+=(size_t)ceilf( (maxTranslationXY + xySize-(xTo-xFrom)) * mask.GetResolution().GetX() );
	ySize+=(size_t)ceilf( (maxTranslationXY + xySize-(yTo-yFrom)) * mask.GetResolution().GetY() );
	zSize+=(size_t)ceilf( 4.f*(float)noFrames+0.2f );

			  //cell_centre  minus  half_of_the_size_in_microns
	//const float xOff=(xTo+xFrom)/2.f - 0.5*xSize/mask.GetResolution().GetX();
	float xOff=( (xTo+xFrom) - (float)xSize/mask.GetResolution().GetX() )/2.f;
	float yOff=( (yTo+yFrom) - (float)ySize/mask.GetResolution().GetY() )/2.f;
	float zOff=( (zTo+zFrom) - (float)zSize/mask.GetResolution().GetZ() )/2.f;

	//now, align the mask voxel grid onto the scenemasks one
	xOff=floorf((xOff-mask.GetOffset().x)*mask.GetResolution().GetX())
			/ mask.GetResolution().GetX()  + mask.GetOffset().x;
	yOff=floorf((yOff-mask.GetOffset().y)*mask.GetResolution().GetY())
			/ mask.GetResolution().GetY()  + mask.GetOffset().y;
	zOff=floorf((zOff-mask.GetOffset().z)*mask.GetResolution().GetZ())
			/ mask.GetResolution().GetZ()  + mask.GetOffset().z;

	//emergency break when the suggested image should be larger
	//than is the scene size -- TMI paper only!
	// (or add sceneMask reference as the reference limit example)
	if ((xSize > 700) || (ySize > 700))
	{
		xOff=-5.f / mask.GetResolution().GetX()  + mask.GetOffset().x;
		yOff=-5.f / mask.GetResolution().GetY()  + mask.GetOffset().y;
		zOff=-5.f / mask.GetResolution().GetZ()  + mask.GetOffset().z;
		xSize=ySize=710;
		zSize=85;
	}

	//reset offset to ours...
	mask.SetOffset(i3d::Vector3d<float>(xOff,yOff,zOff));
	//mask::Resolution is already set
	mask.MakeRoom(xSize,ySize,zSize);
	mask.GetVoxelData()=0;

	REPORT("new image at " << mask.GetOffset() << " of size " \
	       << i3d::PixelsToMicrons(mask.GetSize(),mask.GetResolution())  << " in microns");
}



//
// explicit instantiations
//
template size_t ResetBoundaryPoints(std::vector< i3d::Vector3d<float> /**/> &BPList, i3d::Vector3d<float> &BPCentre,
				i3d::Image3d<bool> const &mask, const bool ID);
template size_t ResetBoundaryPoints(std::vector< i3d::Vector3d<float> /**/> &BPList, i3d::Vector3d<float> &BPCentre,
				i3d::Image3d<i3d::GRAY8> const &mask, const i3d::GRAY8 ID);
template size_t ResetBoundaryPoints(std::vector< i3d::Vector3d<float> /**/> &BPList, i3d::Vector3d<float> &BPCentre,
				i3d::Image3d<i3d::GRAY16> const &mask, const i3d::GRAY16 ID);

template void RenderBPListIntoMask(i3d::Image3d<bool> &mask, const bool value,
			   std::vector< i3d::Vector3d<float> /**/> const &BPList,
			   i3d::Vector3d<float> const &BPCentre,const int OuterBP);
template void RenderBPListIntoMask(i3d::Image3d<i3d::GRAY8> &mask, const i3d::GRAY8 value,
			   std::vector< i3d::Vector3d<float> /**/> const &BPList,
			   i3d::Vector3d<float> const &BPCentre,const int OuterBP);
template void RenderBPListIntoMask(i3d::Image3d<i3d::GRAY16> &mask, const i3d::GRAY16 value,
			   std::vector< i3d::Vector3d<float> /**/> const &BPList,
			   i3d::Vector3d<float> const &BPCentre,const int OuterBP);

template void RenderBPListIntoMask(i3d::Image3d<i3d::GRAY8> &mask, const i3d::GRAY8 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP, FlowField<float> const &FF);
template void RenderBPListIntoMask(i3d::Image3d<i3d::GRAY16> &mask, const i3d::GRAY16 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP, FlowField<float> const &FF);

template void SoftRenderBPListIntoMask(i3d::Image3d<bool> &mask, const bool value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre, const int OuterBP);
template void SoftRenderBPListIntoMask(i3d::Image3d<i3d::GRAY8> &mask, const i3d::GRAY8 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre, const int OuterBP);
template void SoftRenderBPListIntoMask(i3d::Image3d<i3d::GRAY16> &mask, const i3d::GRAY16 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre, const int OuterBP);

template void RenderBPListIntoNewMask(i3d::Image3d<bool> &mask, const bool value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,const int OuterBP,const size_t noFrames,
			  const float cellLookDistance,
			  const float perX,const float perY,const float perZ);
template void RenderBPListIntoNewMask(i3d::Image3d<i3d::GRAY8> &mask, const i3d::GRAY8 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,const int OuterBP,const size_t noFrames,
			  const float cellLookDistance,
			  const float perX,const float perY,const float perZ);
template void RenderBPListIntoNewMask(i3d::Image3d<i3d::GRAY16> &mask, const i3d::GRAY16 value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,const int OuterBP,const size_t noFrames,
			  const float cellLookDistance,
			  const float perX,const float perY,const float perZ);

