bool ManualSegmentationTools::segmentMeshWitAABox(GenericIndexedMesh* origMesh,
	GenericIndexedCloudPersist* origVertices,
	MeshCutterParams& ioParams,
	GenericProgressCallback* progressCb/*=0*/)
{
	if (!origMesh
		|| !origVertices
		|| origMesh->size() == 0
		|| origVertices->size() < 3
		|| ioParams.bbMin.x >= ioParams.bbMax.x
		|| ioParams.bbMin.y >= ioParams.bbMax.y
		|| ioParams.bbMin.z >= ioParams.bbMax.z)
	{
		//invalid input parameters
		return false;
	}

	if (origMesh->size() > c_realIndexMask)
	{
		//too many triangles!
		return false;
	}

	const double& epsilon = ioParams.epsilon;
	const CCVector3d& bbMin = ioParams.bbMin;
	const CCVector3d& bbMax = ioParams.bbMax;

	//indexes of original triangle that are not modified bt copied "as is"
	std::vector<unsigned> preservedTrianglesInside1;	//insde (1)
	std::vector<unsigned> preservedTrianglesInside2;	//insde (2)
	std::vector<unsigned> preservedTrianglesOutside;	//outside

	//inside meshes (swapped for each dimension)
	ChunkedPointCloud* insideVertices1 = new ChunkedPointCloud;
	SimpleMesh* insideMesh1 = new SimpleMesh(insideVertices1, true);
	ChunkedPointCloud* insideVertices2 = new ChunkedPointCloud;
	SimpleMesh* insideMesh2 = new SimpleMesh(insideVertices2, true);
	
	//outside mesh (output)
	ChunkedPointCloud* outsideVertices = 0;
	SimpleMesh* outsideMesh = 0;
	if (ioParams.generateOutsideMesh)
	{
		outsideVertices = new ChunkedPointCloud;
		outsideMesh = new SimpleMesh(outsideVertices, true);
	}

	//pointers on input and output structures (will change for each dimension)
	std::vector<unsigned>* preservedTrianglesInside = &preservedTrianglesInside1;
	std::vector<unsigned>* formerPreservedTriangles = &preservedTrianglesInside2;
	ChunkedPointCloud* insideVertices = insideVertices1;
	SimpleMesh* insideMesh = insideMesh1;
	GenericIndexedMesh* sourceMesh = origMesh;
	GenericIndexedCloudPersist* sourceVertices = origVertices;
	
	CCVector3d boxCenter = (ioParams.bbMin + ioParams.bbMax) / 2;
	CCVector3d boxHalfSize = (ioParams.bbMax - ioParams.bbMin) / 2;
	bool error = false;

	//for each triangle
	try
	{
		//for each plane
		for (unsigned d = 0; d < 6; ++d)
		{
			//Extract the 'plane' information corresponding to the input box faces
			//-X,+X,-Y,+Y,-Z,+Z
			unsigned char Z = static_cast<unsigned char>(d / 2); 
			double planeCoord = ((d & 1) ? bbMax : bbMin).u[Z];
			bool keepBelow = ((d & 1) ? true : false);

			assert(preservedTrianglesInside && formerPreservedTriangles);
			assert(insideVertices && insideMesh);
			assert(sourceVertices && sourceMesh);
			s_edgePoint.clear();

			std::vector<unsigned> origTriIndexesMapInsideBackup;
			if (ioParams.trackOrigIndexes)
			{
				origTriIndexesMapInsideBackup = ioParams.origTriIndexesMapInside;
				ioParams.origTriIndexesMapInside.clear();
			}

			//look for original triangles
			//(the first time they only come from the original mesh but afterwards
			// they can come from the original mesh through the 'preserved' list
			// or from the previous 'inside' mesh as we have to test those triangles
			// against the new plane)
			unsigned sourceTriCount = sourceMesh ? sourceMesh->size() : 0; //source: previous/original mesh
			unsigned formerPreservedTriCount = static_cast<unsigned>(formerPreservedTriangles->size());
			unsigned triCount = sourceTriCount + formerPreservedTriCount;
			
			for (unsigned i = 0; i < triCount; ++i)
			{
				bool triangleIsOriginal = false;
				unsigned souceTriIndex = 0;
				const VerticesIndexes* tsi = 0;
				if (i < sourceTriCount)
				{
					souceTriIndex = i;
					triangleIsOriginal = (sourceMesh == origMesh);
					tsi = sourceMesh->getTriangleVertIndexes(souceTriIndex);
				}
				else
				{
					souceTriIndex = (*formerPreservedTriangles)[i - sourceTriCount];
					triangleIsOriginal = true;
					tsi = origMesh->getTriangleVertIndexes(souceTriIndex);
				}

				//vertices indexes
				unsigned vertIndexes[3] = { tsi->i1, tsi->i2, tsi->i3 };
				if (triangleIsOriginal)
				{
					//we flag the vertices indexes as referring to the 'original' mesh
					vertIndexes[0] |= c_origIndexFlag;
					vertIndexes[1] |= c_origIndexFlag;
					vertIndexes[2] |= c_origIndexFlag;
				}
				else
				{
					//we flag the vertices indexes as referring to the 'source' mesh
					if ((vertIndexes[0] & c_origIndexFlag) == 0)
						vertIndexes[0] |= c_srcIndexFlag;
					if ((vertIndexes[1] & c_origIndexFlag) == 0)
						vertIndexes[1] |= c_srcIndexFlag;
					if ((vertIndexes[2] & c_origIndexFlag) == 0)
						vertIndexes[2] |= c_srcIndexFlag;
				}

				//get the vertices (from the right source!)
				CCVector3d V[3] = { CCVector3d::fromArray(( (vertIndexes[0] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[0] & c_realIndexMask)->u),
									CCVector3d::fromArray(( (vertIndexes[1] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[1] & c_realIndexMask)->u),
									CCVector3d::fromArray(( (vertIndexes[2] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[2] & c_realIndexMask)->u) };

				if (d == 0)
				{
					//perform a triangle-box overlap test the first time!
					if (!CCMiscTools::TriBoxOverlapd(boxCenter, boxHalfSize, V))
					{
						if (ioParams.generateOutsideMesh)
							preservedTrianglesOutside.push_back(i);
						continue;
					}
				}

				//test the position of each vertex relatively to the current plane
				//char relativePos[3] = { 1, 1, 1 };
				//bool insideXY[3] = { false, false, false };
				std::vector<unsigned char> insideLocalVertIndexes, outsideLocalVertIndexes;
				for (unsigned char j = 0; j < 3; ++j)
				{
					const CCVector3d& v = V[j];
					if (fabs(v.u[Z] - planeCoord) < epsilon)
					{
						//relativePos[j] = 0;
					}
					else
					{
						if (v.u[Z] < planeCoord)
						{
							insideLocalVertIndexes.push_back(j);
							//relativePos[j] = -1;
						}
						else
						{
							//relativePos is already equal to 1
							//relativePos[j] = 1;
							outsideLocalVertIndexes.push_back(j);
						}
					}
				}

				//depending on the number of entities on the plane
				//we'll process the triangles differently
				bool isFullyInside = false;
				bool isFullyOutside = false;
				switch (insideLocalVertIndexes.size() + outsideLocalVertIndexes.size())
				{
				case 0: //all vertices 'in' the plane
				{
					//we arbitrarily decide that the triangle is inside!
					isFullyInside = true;
				}
				break;

				case 1: //2 vertices 'in' the plane
				{
					//the triangle is either on one side or another ;)
					if (insideLocalVertIndexes.empty())
					{
						//the only vertex far from the plane is on the 'otuside'
						isFullyOutside = true;
					}
					else
					{
						//the only vertex far from the plane is on the 'inside'
						isFullyInside = true;
					}
				}
				break;

				case 2: //1 vertex 'in' the plane
				{
					//3 cases:
					if (insideLocalVertIndexes.empty())
					{
						//the two vertices far from the plane are 'outside'
						isFullyOutside = true;
					}
					else if (outsideLocalVertIndexes.empty())
					{
						//the two vertices far from the plane are 'inside'
						isFullyInside = true;
					}
					else
					{
						//the two vertices far from the plane are on both sides
						//the plane will cut through the edge connecting those two vertices
						unsigned char iInside = insideLocalVertIndexes.front();
						unsigned char iOuside = outsideLocalVertIndexes.front();

						unsigned char iCenter = 3 - iInside - iOuside;
						unsigned iCoutside, iCinside;
						//we can now create one vertex and two new triangles
						if (!ComputeEdgePoint(
							V[iInside], vertIndexes[iInside],
							V[iOuside], vertIndexes[iOuside],
							iCoutside, iCinside,
							planeCoord, Z,
							outsideVertices, insideVertices)

						|| !AddTriangle(
							vertIndexes[iCenter],
							vertIndexes[iInside],
							keepBelow ? iCinside : iCoutside,
							keepBelow ? insideMesh : outsideMesh,
							((iCenter + 1) % 3) == iInside)

						|| !AddTriangle(
							vertIndexes[iCenter],
							vertIndexes[iOuside],
							keepBelow ? iCoutside : iCinside,
							keepBelow ? outsideMesh : insideMesh,
							((iCenter + 1) % 3) == iOuside))
						{
							//early stop
							i = triCount;
							error = true;
							break;
						}

						//remember (origin) source triangle index
						if (ioParams.trackOrigIndexes)
						{
							assert(triangleIsOriginal || souceTriIndex < origTriIndexesMapInsideBackup.size());
							unsigned origTriIndex = triangleIsOriginal ? souceTriIndex : origTriIndexesMapInsideBackup[souceTriIndex];
							//the source triangle is split in two so each side get one new triangle
							ioParams.origTriIndexesMapInside.push_back(origTriIndex);
							if (ioParams.generateOutsideMesh)
								ioParams.origTriIndexesMapOutside.push_back(origTriIndex);
						}
					}
				}
				break;

				case 3: //no vertex 'in' the plane
				{
					if (insideLocalVertIndexes.empty())
					{
						//all vertices are 'outside'
						isFullyOutside = true;
					}
					else if (outsideLocalVertIndexes.empty())
					{
						//all vertices are 'inside'
						isFullyInside = true;
					}
					else
					{
						//we have one vertex on one side and two on the other side
						unsigned char iLeft, iRight1, iRight2;
						bool leftIsInside = true;
						if (insideLocalVertIndexes.size() == 1)
						{
							assert(outsideLocalVertIndexes.size() == 2);
							iLeft = insideLocalVertIndexes.front();
							iRight1 = outsideLocalVertIndexes[0];
							iRight2 = outsideLocalVertIndexes[1];
							leftIsInside = keepBelow;
						}
						else
						{
							assert(insideLocalVertIndexes.size() == 2);
							iLeft = outsideLocalVertIndexes.front();
							iRight1 = insideLocalVertIndexes[0];
							iRight2 = insideLocalVertIndexes[1];
							leftIsInside = !keepBelow;
						}

						//the plane cuts through the two edges having the 'single' vertex in common
						//we are going to create 3 triangles
						unsigned i1outside, i1inside;
						unsigned i2outside, i2inside;
						if (  !ComputeEdgePoint(	V[iRight1], vertIndexes[iRight1],
													V[iLeft], vertIndexes[iLeft],
													i1outside, i1inside,
													planeCoord, Z,
													outsideVertices, insideVertices)
							
							|| !ComputeEdgePoint(	V[iRight2], vertIndexes[iRight2],
													V[iLeft], vertIndexes[iLeft],
													i2outside, i2inside,
													planeCoord, Z,
													outsideVertices, insideVertices)

							|| !AddTriangle(	vertIndexes[iLeft],
												leftIsInside ? i1inside : i1outside,
												leftIsInside ? i2inside : i2outside,
												leftIsInside ? insideMesh : outsideMesh,
												((iLeft + 1) % 3) == iRight1)

							|| !AddTriangle(	leftIsInside ? i1outside : i1inside,
												leftIsInside ? i2outside : i2inside,
												vertIndexes[iRight1],
												leftIsInside ? outsideMesh : insideMesh,
												((iRight2 + 1) % 3) == iRight1)

							|| !AddTriangle(	vertIndexes[iRight1],
												leftIsInside ? i2outside : i2inside,
												vertIndexes[iRight2],
												leftIsInside ? outsideMesh : insideMesh,
												((iRight2 + 1) % 3) == iRight1)
							)
						{
							//early stop
							i = triCount;
							error = true;
							break;
						}

						//remember (origin) source triangle index
						if (ioParams.trackOrigIndexes)
						{
							assert(triangleIsOriginal || souceTriIndex < origTriIndexesMapInsideBackup.size());
							unsigned origTriIndex = triangleIsOriginal ? souceTriIndex : origTriIndexesMapInsideBackup[souceTriIndex];
							//each side gets at least one new triangle
							ioParams.origTriIndexesMapInside.push_back(origTriIndex);
							if (ioParams.generateOutsideMesh)
								ioParams.origTriIndexesMapOutside.push_back(origTriIndex);
							//the third triangle has been added either to the 'inside' or to the 'outside' mesh
							if (!leftIsInside)
								ioParams.origTriIndexesMapInside.push_back(origTriIndex);
							else if (ioParams.generateOutsideMesh)
								ioParams.origTriIndexesMapOutside.push_back(origTriIndex);
						}
					}
				}
				break;

				}

				if (isFullyInside || isFullyOutside)
				{
					//inverted selection?
					if (!keepBelow)
						std::swap(isFullyInside, isFullyOutside);
					
					if (triangleIsOriginal)
					{
						if (isFullyInside)
							preservedTrianglesInside->push_back(souceTriIndex);
						else if (ioParams.generateOutsideMesh)
							preservedTrianglesOutside.push_back(souceTriIndex);
					}
					else
					{
						//we import the former triangle
						if (!AddTriangle(vertIndexes[0], vertIndexes[1], vertIndexes[2], isFullyInside ? insideMesh : outsideMesh, true))
						{
							//early stop
							error = true;
							break;
						}
						if (ioParams.trackOrigIndexes)
						{
							assert(souceTriIndex < origTriIndexesMapInsideBackup.size());
							unsigned origTriIndex = origTriIndexesMapInsideBackup[souceTriIndex];
							if (isFullyInside)
								ioParams.origTriIndexesMapInside.push_back(origTriIndex);
							else if (ioParams.generateOutsideMesh)
								ioParams.origTriIndexesMapOutside.push_back(origTriIndex);
						}
					}
				}

			}
			//end for each triangle

			if (   !ImportSourceVertices(sourceVertices, insideMesh, insideVertices)
				|| (ioParams.generateOutsideMesh && !ImportSourceVertices(sourceVertices, outsideMesh, outsideVertices))
				)
			{
				//early stop
				error = true;
				break;
			}

			if (insideMesh->size() == 0 && preservedTrianglesInside->empty())
			{
				//no triangle inside!
				break;
			}

			if (d < 5)
			{
				//clear the source mesh and swap the buffers
				if (insideMesh == insideMesh1)
				{
					assert(sourceMesh == insideMesh2 || sourceMesh == origMesh);
					insideMesh2->clear(false);
					insideVertices2->clear();
					sourceMesh = insideMesh1;
					sourceVertices = insideVertices1;
					insideMesh = insideMesh2;
					insideVertices = insideVertices2;
					preservedTrianglesInside2.clear();
					preservedTrianglesInside = &preservedTrianglesInside2;
					formerPreservedTriangles = &preservedTrianglesInside1;
				}
				else
				{
					assert(sourceMesh == insideMesh1 || sourceMesh == origMesh);
					insideMesh1->clear(false);
					insideVertices1->clear();
					sourceMesh = insideMesh2;
					sourceVertices = insideVertices2;
					insideMesh = insideMesh1;
					insideVertices = insideVertices1;
					preservedTrianglesInside1.clear();
					preservedTrianglesInside = &preservedTrianglesInside1;
					formerPreservedTriangles = &preservedTrianglesInside2;
				}
			}
		}
		//end for each plane

		//now add the remaining triangles
	}
	catch (const std::bad_alloc&)
	{
		//not enough memory
		error = true;
	}

	//free some memory
	s_edgePoint.clear();
	formerPreservedTriangles->clear();

	if (!error)
	{
		//import the 'preserved' (original) triangles 
		if (	!MergeOldTriangles(	origMesh, origVertices,
									insideMesh, insideVertices,
									*preservedTrianglesInside,
									ioParams.trackOrigIndexes ? &ioParams.origTriIndexesMapInside : 0)
			||	(	ioParams.generateOutsideMesh
				&&	!MergeOldTriangles(	origMesh, origVertices,
										outsideMesh, outsideVertices,
										preservedTrianglesOutside,
										ioParams.trackOrigIndexes ? &ioParams.origTriIndexesMapOutside : 0))
			)
		{
			error = true;
		}
	}

	if (insideMesh == insideMesh1)
	{
		delete insideMesh2;
		insideMesh2 = 0;
		insideVertices2 = 0;
	}
	else
	{
		delete insideMesh1;
		insideMesh1 = 0;
		insideVertices1 = 0;
	}

	if (error)
	{
		delete insideMesh;
		if (outsideMesh)
			delete outsideMesh;
		return false;
	}

	if (insideMesh)
	{
		insideMesh->resize(insideMesh->size());
	}
	if (outsideMesh)
	{
		outsideMesh->resize(outsideMesh->size());
	}

	ioParams.insideMesh = insideMesh;
	ioParams.outsideMesh = outsideMesh;
	return true;
}
GenericIndexedMesh* ManualSegmentationTools::segmentMesh(GenericIndexedMesh* theMesh, ReferenceCloud* pointIndexes, bool pointsWillBeInside, GenericProgressCallback* progressCb, GenericIndexedCloud* destCloud, unsigned indexShift)
{
	if (!theMesh || !pointIndexes || !pointIndexes->getAssociatedCloud())
		return 0;

	//by default we try a fast process (but with a higher memory consumption)
	unsigned numberOfPoints = pointIndexes->getAssociatedCloud()->size();
	unsigned numberOfIndexes = pointIndexes->size();

	//we determine for each point if it is used in the output mesh or not
	//(and we compute its new index by the way: 0 means that the point is not used, otherwise its index will be newPointIndexes-1)
	std::vector<unsigned> newPointIndexes;
	{
		try
		{
			newPointIndexes.resize(numberOfPoints,0);
		}
		catch (const std::bad_alloc&)
		{
			return 0; //not enough memory
		}

		for (unsigned i=0; i<numberOfIndexes; ++i)
		{
			assert(pointIndexes->getPointGlobalIndex(i) < numberOfPoints);
			newPointIndexes[pointIndexes->getPointGlobalIndex(i)] = i+1;
		}
	}

	//negative array for the case where input points are "outside"
	if (!pointsWillBeInside)
	{
		unsigned newIndex = 0;
		for (unsigned i=0;i<numberOfPoints;++i)
			newPointIndexes[i] = (newPointIndexes[i] == 0 ? ++newIndex : 0);
	}

	//create resulting mesh
	SimpleMesh* newMesh = 0;
	{
		unsigned numberOfTriangles = theMesh->size();

		//progress notification
		NormalizedProgress* nprogress = 0;
		if (progressCb)
		{
			progressCb->reset();
			progressCb->setMethodTitle("Extract mesh");
			char buffer[256];
			sprintf(buffer,"New vertex number: %u",numberOfIndexes);
			nprogress = new NormalizedProgress(progressCb,numberOfTriangles);
			progressCb->setInfo(buffer);
			progressCb->start();
		}

		newMesh = new SimpleMesh(destCloud ? destCloud : pointIndexes->getAssociatedCloud());
		unsigned count = 0;

		theMesh->placeIteratorAtBegining();
		for (unsigned i=0; i<numberOfTriangles; ++i)
		{
			bool triangleIsOnTheRightSide = true;

			const VerticesIndexes* tsi = theMesh->getNextTriangleVertIndexes(); //DGM: getNextTriangleVertIndexes is faster for mesh groups!
			int newVertexIndexes[3];

			//VERSION: WE KEEP THE TRIANGLE ONLY IF ITS 3 VERTICES ARE INSIDE
			for (uchar j=0;j <3; ++j)
			{
				const unsigned& currentVertexFlag = newPointIndexes[tsi->i[j]];

				//if the vertex is rejected, we discard this triangle
				if (currentVertexFlag == 0)
				{
					triangleIsOnTheRightSide = false;
					break;
				}
				newVertexIndexes[j] = currentVertexFlag-1;
			}

			//if we keep the triangle
			if (triangleIsOnTheRightSide)
			{
				if (count == newMesh->size() && !newMesh->reserve(newMesh->size() + 1000)) //auto expand mesh size
				{
					//stop process
					delete newMesh;
					newMesh = 0;
					break;
				}
				++count;

				newMesh->addTriangle(	indexShift + newVertexIndexes[0],
										indexShift + newVertexIndexes[1],
										indexShift + newVertexIndexes[2] );
			}

			if (nprogress && !nprogress->oneStep())
			{
				//cancel process
				break;
			}
		}

		if (nprogress)
		{
			delete nprogress;
			nprogress = 0;
		}

		if (newMesh)
		{
			if (newMesh->size() == 0)
			{
				delete newMesh;
				newMesh = 0;
			}
			else if (count < newMesh->size())
			{
				newMesh->resize(count); //should always be ok as count<maxNumberOfTriangles
			}
		}
	}

	return newMesh;
}