MC(移动立方体)算法

Marching Cubes(移动立方体)

  • Marching squares 算法
  • Marching Cubes算法

写在最前面:
在学习MC算法之前先看看二维的MS算法,MC算法是二维MS算法的拓展,通过学习MS的基本思路能够更快学习MC算法,因此在这里我们首先提一下MS的基本算法流程。

Marching squares 算法

MS算法的主要流程:

  1. 遍历每个栅格,从16种固定情况中选择类别

  2. 利用线性插值,结合网格点数值找寻交点

  3. 根据线性插值的结果连线

具体的操作过程如图所示:
MC(移动立方体)算法_第1张图片

Marching Cubes算法

MC算法也被称作“等值面提取(Isosurface Extraction)”是面绘制算法中的经典算法,算法的主要精髓为:在三维离散数据场中通过线性差值来逼近等值面
在学习MC算法前需要给定一些基本概念:首先定义一个立方体单元为一个体素 ,每一个体元均由8个顶点所构成。

体素顶点有两种不同的状态量所表示:

  1. 高于或等于势值表示在物体表面的内部
  2. 低于势值表示在物体表面的外部
    因此,体素的一个顶点有两中可能的状态,则一个体素(8个顶点)就一共有2^8=256种状态。根据旋转、映射不变等特性,体素的状态可以被归纳为以下15种基本构型:

MC(移动立方体)算法_第2张图片
为了存储方便,将其记录在表中(如下所示)

int MarchingCubes::ms_edgeTable[256]={
  0x0  , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
  0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
  0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
  0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
  0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
  0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
  0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
  0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
  0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
  0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
  0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
  0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
  0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
  0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
  0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
  0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
  0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
  0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
  0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
  0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
  0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
  0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
  0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
  0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
  0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
  0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
  0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
  0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
  0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
  0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
  0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
  0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0   };

对模型包围盒空间划分体,从下到上每个体素单元均遍历一次
MC(移动立方体)算法_第3张图片
遍历完成后得到构建的模型
MC(移动立方体)算法_第4张图片
构建得到的三角网格模型如图所示
MC(移动立方体)算法_第5张图片
附上c++实现的完整代码:

// utils.h
#pragma once
#include 

#define NULL 0
#define BYTE unsigned char

template <class T> class VECTOR3 {
public:
	T x;
	T y;
	T z;

	VECTOR3(): x(0), y(0),z(0) {}
	VECTOR3(const VECTOR3<int> &other): x(other.x), y(other.y), z(other.z) {}
	VECTOR3(const T _x, const T _y, const T _z) : x(_x), y(_y), z(_z) {}

	bool operator == ( const VECTOR3<T>& other ) const {return (other.x==x && other.y==y && other.z==z); }
	bool operator != ( const VECTOR3<T>& other ) const {return (other.x!=x || other.y!=y || other.z!=z); } 

    // binary operators with scalars
	VECTOR3<T> operator + ( T scalar ) const {return VECTOR3<T>(x+scalar,y+scalar,z+scalar);}
	VECTOR3<T> operator - ( T scalar ) const {return VECTOR3<T>(x-scalar,y-scalar,z-scalar);}
	VECTOR3<T> operator * ( T scalar ) const {return VECTOR3<T>(x*scalar,y*scalar,z*scalar);}
	VECTOR3<T> operator / ( T scalar ) const {return VECTOR3<T>(x/scalar,y/scalar,z/scalar);}

	// binaray operpators  with vectors
	VECTOR3<T> operator + ( const VECTOR3<T>& other ) const {return VECTOR3<T>(x+other.x,y+other.y,z+other.z);}
	VECTOR3<T> operator - ( const VECTOR3<T>& other ) const {return VECTOR3<T>(x-other.x,y-other.y,z-other.z);}
	VECTOR3<T> operator * ( const VECTOR3<T>& other ) const {return VECTOR3<T>(x*other.x,y*other.y,z*other.z);}
	VECTOR3<T> operator / ( const VECTOR3<T>& other ) const {return VECTOR3<T>(x/other.x,y/other.y,z/other.z);}

	T maxVal() {return (x>y) ? ((x>z) ? x : z) : ((y>z) ? y : z);}
	T minVal() {return (x<y) ? ((x<z) ? x : z) : ((y<z) ? y : z);}
	T volume() {return x*y*z;}
	T length() {return sqrt(T(x*x+y*y+z*z));}
	void normalize() {T len = length(); x/=len;y/=len;;z/=len;}
	void normalize(float epsilon) {
		T len = length();
		if (len > epsilon) {
			x/=len;
			y/=len;
			z/=len;
		} else { // specify some arbitrary normal
			x = 0;
			y = 0;
			z = 1;
		}
	}
};


void loadData(char *fileName, bool bIsFloat, VECTOR3<int>* iVolSize, float **data);
void loadData(char *fileName, bool bIsFloat, VECTOR3<int> iVolSize, float **data);

//utils.cpp
#include "utils.h"
#include 
#include 
#include 


void loadData(char *fileName, bool bIsFloat, VECTOR3<int>* iVolSize, float **data) {
	printf("\nTrying to load %s Dataset...\n",fileName);

	FILE *stream;	
	if((stream = fopen(fileName, "rb"))==NULL) {
		printf("Error while loading data from file %s.\n",fileName);
		exit(0);
	} else {
		fread(iVolSize , sizeof(VECTOR3<int>), 1, stream);
		*data = new float[iVolSize->volume()];
		if (bIsFloat) {
			fread(*data, sizeof(float), iVolSize->volume(), stream);
		} else {
			BYTE* temp_data = new BYTE[iVolSize->volume()];
			fread(temp_data, sizeof(BYTE), iVolSize->volume(), stream);
			for (int i = 0;i<iVolSize->volume();i++) (*data)[i] = float(temp_data[i])/255.0f;
			delete [] temp_data;
		}

		fclose( stream );
		printf("\nData loaded\n");
	}
}

void loadData(char *fileName, bool bIsFloat, VECTOR3<int> iVolSize, float **data) {
	printf("\nTrying to load %s Dataset...\n",fileName);

	FILE *stream;	
	if((stream = fopen(fileName, "rb"))==NULL) {
		printf("Error while loading data from file %s.\n",fileName);
		exit(0);
	} else {
		*data = new float[iVolSize.volume()];
		if (bIsFloat) {
			fread(*data, sizeof(float), iVolSize.volume(), stream);
		} else {
			BYTE* temp_data = new BYTE[iVolSize.volume()];
			fread(temp_data, sizeof(BYTE), iVolSize.volume(), stream);
			for (int i = 0;i<iVolSize.volume();i++) (*data)[i] = float(temp_data[i])/255.0f;
			delete [] temp_data;
		}

		fclose( stream );
		printf("\nData loaded\n");
	}
}


//MarchingCube.h
#pragma once

#include 
#include "utils.h"

#define EPSILON 0.000001f
#define DATA_INDEX(I, J, K, IDIM, JDIM) ((I) + ((IDIM) * (J)) + ((IDIM * JDIM) * (K)))
#define EDGE_INDEX(E, I, J, IDIM) ((E) + (12 * (I)) + ((12 * (IDIM)) * (J)))
#define NO_EDGE -1

class LayerTempData 
{
private:
	VECTOR3<int> m_vVolSize;
public:
	LayerTempData(VECTOR3<int> vVolSize, float* pfVolume) 
	{
		m_vVolSize = vVolSize;
		
		pfBotData	= pfVolume;
		pfTopData	= pfVolume+DATA_INDEX(0, 0, 1, vVolSize.x, vVolSize.y);
		piEdges		= new int[(vVolSize.x-1) * (vVolSize.y-1) * 12];  // allocate storage to hold the indexing tags for edges in the layer
		
		for (int i = 0; i < (vVolSize.x-1) * (vVolSize.y-1) * 12; i++)	piEdges[i] = NO_EDGE;  // init edge list
	}
	
	virtual ~LayerTempData() 
	{
		delete [] piEdges;
		delete [] pfTopData;
		delete [] pfBotData;
	}
	
	void nextIteration() 
	{
		// update the layer for this iteration
		pfBotData = pfTopData;
		// now topData points to next layer of scalar data
		pfTopData += DATA_INDEX(0, 0, 1, m_vVolSize.x, m_vVolSize.y);
		// percolate the last layer's top edges to this layer's bottom edges
		for (int iY = 0; iY < m_vVolSize.y-1; iY++) 
		{
			for (int iX = 0; iX < m_vVolSize.x-1; iX++) 
			{
				for (int iEdges = 0; iEdges < 4; iEdges++) 
				{
					piEdges[EDGE_INDEX(iEdges, iX, iY, m_vVolSize.x-1)] =  piEdges[EDGE_INDEX(iEdges+4, iX, iY, m_vVolSize.x-1)];
				}
				// reinitialize all of the remaining edges
				for (int  iEdges = 4; iEdges < 12; iEdges++) 
				{
					piEdges[EDGE_INDEX(iEdges, iX, iY, m_vVolSize.x-1)] = NO_EDGE;
				}
			}
		}
	}
	
	float*	pfBotData;
	float*	pfTopData;
	int*	piEdges;  // tag indexing into vertex list
};

class Isosurface{
public:
	VECTOR3<float>*	vfVertices;
	VECTOR3<float>*	vfNormals;
	VECTOR3<int>*	viTriangles;
	int				iVertices;
	int				iTriangles;

	Isosurface() {
	    vfVertices  = NULL;
	    vfNormals   = NULL;
	    viTriangles = NULL;
	    iVertices   = 0;
	    iTriangles  = 0;
	}

	Isosurface(int iMaxVertices, int iMaxTris) 
	{
	    vfVertices  = new VECTOR3<float>[iMaxVertices];
	    vfNormals   = new VECTOR3<float>[iMaxVertices];
	    viTriangles = new VECTOR3<int>[iMaxTris];
	    iVertices   = 0;
	    iTriangles  = 0;
	}

	virtual ~Isosurface() 
	{
		delete [] vfVertices;
		delete [] vfNormals;
		delete [] viTriangles;
	}

	int AddTriangle(int a, int b, int c) 
	{
		viTriangles[iTriangles++] = VECTOR3<int>(a,b,c);
		return iTriangles-1;
	}

	int AddVertex(VECTOR3<float> v, VECTOR3<float> n) 
	{
		vfVertices[iVertices] = v;
		vfNormals[iVertices++] = n; 
		return iVertices-1;
	}

	void AppendData(const Isosurface* other) 
	{
		// if verts in other, expand the storage this surface
		if (other->iVertices > 0) 
		{

			// create new mem
			VECTOR3<float>*	temp_Vertices  = new VECTOR3<float>[iVertices  + other->iVertices];
			VECTOR3<float>*	temp_Normals   = new VECTOR3<float>[iVertices  + other->iVertices];
			VECTOR3<int>*	temp_Triangles = new VECTOR3<int>[iTriangles + other->iTriangles];

			// copy "old" data
			memcpy(temp_Vertices, vfVertices, sizeof(VECTOR3<float>)*iVertices);
			memcpy(temp_Normals, vfNormals, sizeof(VECTOR3<float>)*iVertices);
			memcpy(temp_Triangles, viTriangles, sizeof(VECTOR3<int>)*iTriangles);

			// append new data
			memcpy(temp_Vertices+iVertices, other->vfVertices, sizeof(VECTOR3<float>)*other->iVertices);
			memcpy(temp_Normals+iVertices, other->vfNormals, sizeof(VECTOR3<float>)*other->iVertices);
			memcpy(temp_Triangles+iTriangles, other->viTriangles, sizeof(VECTOR3<int>)*other->iTriangles);

			// delete "old" data
			delete [] vfVertices;
			delete [] vfNormals;
			delete [] viTriangles;

			// rename
			vfVertices  = temp_Vertices;
			vfNormals   = temp_Normals;
			viTriangles = temp_Triangles;
		}

		// update this list's counters
		iVertices  += other->iVertices;
		iTriangles += other->iTriangles;
	}
};


class MarchingCubes
{
protected:
	static int		ms_edgeTable[256];
	static int		ms_triTable[256][16];

	float			m_fFlipFact;
	float			m_bFlipSurface;
	VECTOR3<int>	m_vVolSize;
	float*			m_pfVolume;
	float			m_fIsoValue;

	virtual void MarchLayer(LayerTempData *layer, int iLayer);
	virtual int MakeVertex(int whichEdge, int i, int j, int k, Isosurface* sliceIso);
	virtual VECTOR3<float> InterpolateNormal(float fValueAtPos, VECTOR3<int> vPosition);

public:
	Isosurface*		m_Isosurface;
	VECTOR3<float>  BoxMin;     //网格顶点中最左下点坐标
	float           spacing;    //网格间距,这里假定三个方向的间距一致

	MarchingCubes(void);
	virtual ~MarchingCubes(void);

	virtual void SetVolume(int iSizeX, int iSizeY, int iSizeZ, float* pfVolume);
	virtual void Process(float fIsoValue, bool bFlipSurface=false);
};

//MarchingCube.cpp
#include "marchingcubes.h"
int MarchingCubes::ms_edgeTable[256]={
  0x0  , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
  0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
  0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
  0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
  0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
  0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
  0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
  0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
  0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
  0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
  0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
  0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
  0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
  0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
  0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
  0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
  0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
  0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
  0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
  0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
  0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
  0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
  0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
  0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
  0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
  0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
  0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
  0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
  0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
  0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
  0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
  0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0   };


int MarchingCubes::ms_triTable[256][16] =
{{NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 8, 3, 9, 8, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, 1, 2, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 2, 10, 0, 2, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 8, 3, 2, 10, 8, 10, 9, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 11, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 11, 2, 8, 11, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 9, 0, 2, 3, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 11, 2, 1, 9, 11, 9, 8, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 10, 1, 11, 10, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 10, 1, 0, 8, 10, 8, 11, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 9, 0, 3, 11, 9, 11, 10, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 8, 10, 10, 8, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 7, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 3, 0, 7, 3, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, 8, 4, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 1, 9, 4, 7, 1, 7, 3, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, 8, 4, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 4, 7, 3, 0, 4, 1, 2, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 2, 10, 9, 0, 2, 8, 4, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 4, 7, 3, 11, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 4, 7, 11, 2, 4, 2, 0, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 0, 1, 8, 4, 7, 2, 3, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 10, 1, 3, 11, 10, 7, 8, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 7, 11, 4, 11, 9, 9, 11, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 4, 0, 8, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 5, 4, 1, 5, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 5, 4, 8, 3, 5, 3, 1, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, 9, 5, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 0, 8, 1, 2, 10, 4, 9, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 2, 10, 5, 4, 2, 4, 0, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 4, 2, 3, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 11, 2, 0, 8, 11, 4, 9, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 5, 4, 0, 1, 5, 2, 3, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 3, 11, 10, 1, 3, 9, 5, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 4, 8, 5, 8, 10, 10, 8, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 7, 8, 5, 7, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 3, 0, 9, 5, 3, 5, 7, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 7, 8, 0, 1, 7, 1, 5, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 5, 3, 3, 5, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 7, 8, 9, 5, 7, 10, 1, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 10, 5, 2, 5, 3, 3, 5, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 9, 5, 7, 8, 9, 3, 11, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 2, 1, 11, 1, 7, 7, 1, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, NO_EDGE},
 {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, NO_EDGE},
 {11, 10, 5, 7, 11, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 6, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, 5, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 0, 1, 5, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 8, 3, 1, 9, 8, 5, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 6, 5, 2, 6, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 6, 5, 1, 2, 6, 3, 0, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 6, 5, 9, 0, 6, 0, 2, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 3, 11, 10, 6, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 0, 8, 11, 2, 0, 10, 6, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, 2, 3, 11, 5, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 3, 11, 6, 5, 3, 5, 1, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 5, 9, 6, 9, 11, 11, 9, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 10, 6, 4, 7, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 3, 0, 4, 7, 3, 6, 5, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 9, 0, 5, 10, 6, 8, 4, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 1, 2, 6, 5, 1, 4, 7, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, NO_EDGE},
 {3, 11, 2, 7, 8, 4, 10, 6, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, NO_EDGE},
 {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, NO_EDGE},
 {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, NO_EDGE},
 {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 4, 9, 6, 4, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 10, 6, 4, 9, 10, 0, 8, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 0, 1, 10, 6, 0, 6, 4, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 4, 9, 1, 2, 4, 2, 6, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 2, 4, 4, 2, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 3, 2, 8, 2, 4, 4, 2, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 4, 9, 10, 6, 4, 11, 2, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, NO_EDGE},
 {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, NO_EDGE},
 {3, 11, 6, 3, 6, 0, 0, 6, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 4, 8, 11, 6, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 10, 6, 7, 8, 10, 8, 9, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 6, 7, 10, 7, 1, 1, 7, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, NO_EDGE},
 {7, 8, 0, 7, 0, 6, 6, 0, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 3, 2, 6, 7, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, NO_EDGE},
 {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, NO_EDGE},
 {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, NO_EDGE},
 {0, 9, 1, 11, 6, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 11, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 6, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 0, 8, 11, 7, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, 11, 7, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 1, 9, 8, 3, 1, 11, 7, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 1, 2, 6, 11, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, 3, 0, 8, 6, 11, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 9, 0, 2, 10, 9, 6, 11, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 2, 3, 6, 2, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 0, 8, 7, 6, 0, 6, 2, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 7, 6, 2, 3, 7, 0, 1, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 7, 6, 10, 1, 7, 1, 3, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 6, 10, 7, 10, 8, 8, 10, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 8, 4, 11, 8, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 6, 11, 3, 0, 6, 0, 4, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 6, 11, 8, 4, 6, 9, 0, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 8, 4, 6, 11, 8, 2, 10, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, NO_EDGE},
 {8, 2, 3, 8, 4, 2, 4, 6, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 4, 2, 4, 6, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 9, 4, 1, 4, 2, 2, 4, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 1, 0, 10, 0, 6, 6, 0, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, NO_EDGE},
 {10, 9, 4, 6, 10, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 9, 5, 7, 6, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, 4, 9, 5, 11, 7, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 0, 1, 5, 4, 0, 7, 6, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 4, 10, 1, 2, 7, 6, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, NO_EDGE},
 {7, 2, 3, 7, 6, 2, 5, 4, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, NO_EDGE},
 {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, NO_EDGE},
 {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, NO_EDGE},
 {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 9, 5, 6, 11, 9, 11, 8, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {6, 11, 3, 6, 3, 5, 5, 3, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, NO_EDGE},
 {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, NO_EDGE},
 {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 5, 6, 9, 6, 0, 0, 6, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, NO_EDGE},
 {1, 5, 6, 2, 1, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, NO_EDGE},
 {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 3, 8, 5, 6, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 5, 6, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 5, 10, 7, 5, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 5, 10, 11, 7, 5, 8, 3, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 11, 7, 5, 10, 11, 1, 9, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 1, 2, 11, 7, 1, 7, 5, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, NO_EDGE},
 {2, 5, 10, 2, 3, 5, 3, 7, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, NO_EDGE},
 {1, 3, 5, 3, 7, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 7, 0, 7, 1, 1, 7, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 0, 3, 9, 3, 5, 5, 3, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 8, 7, 5, 9, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 8, 4, 5, 10, 8, 10, 11, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, NO_EDGE},
 {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, NO_EDGE},
 {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, NO_EDGE},
 {9, 4, 5, 2, 11, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {5, 10, 2, 5, 2, 4, 4, 2, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, NO_EDGE},
 {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 4, 5, 8, 5, 3, 3, 5, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 4, 5, 1, 0, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 4, 5, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 11, 7, 4, 9, 11, 9, 10, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, NO_EDGE},
 {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, NO_EDGE},
 {11, 7, 4, 11, 4, 2, 2, 4, 0, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, NO_EDGE},
 {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, NO_EDGE},
 {1, 10, 2, 8, 7, 4, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 9, 1, 4, 1, 7, 7, 1, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 0, 3, 7, 4, 3, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {4, 8, 7, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 10, 8, 10, 11, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 0, 9, 3, 9, 11, 11, 9, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 1, 10, 0, 10, 8, 8, 10, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 1, 10, 11, 3, 10, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 2, 11, 1, 11, 9, 9, 11, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 2, 11, 8, 0, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {3, 2, 11, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 3, 8, 2, 8, 10, 10, 8, 9, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {9, 10, 2, 0, 9, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 10, 2, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {1, 3, 8, 9, 1, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 9, 1, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {0, 3, 8, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE},
 {NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE, NO_EDGE}};

MarchingCubes::MarchingCubes(void)
{
	m_vVolSize		= VECTOR3<int>(0,0,0);
	m_pfVolume		= NULL;
	m_Isosurface	= NULL;
}

MarchingCubes::~MarchingCubes(void)
{
	if (m_Isosurface)
	{
		m_Isosurface=NULL;
		delete m_Isosurface;
	}
	if (m_pfVolume)
	{
		m_pfVolume = NULL;
		delete m_pfVolume;
	}
	
}

void MarchingCubes::SetVolume(int iSizeX, int iSizeY, int iSizeZ, float* pfVolume) 
{
	m_pfVolume	= pfVolume;
	m_vVolSize	= VECTOR3<int>(iSizeX, iSizeY, iSizeZ);
	m_fIsoValue = 0;
}

void MarchingCubes::Process(float fIsoValue, bool bFlipSurface)
{
	// sometimes it makes sense to flip the surface bFlipSurface controls this
	m_fFlipFact = (bFlipSurface) ? -0.5f : 0.5f;
	m_bFlipSurface = bFlipSurface;

	// store isovalue
	m_fIsoValue = fIsoValue;

	// init isosurface data
	if(m_Isosurface)
	{
		m_Isosurface=NULL;
	}
	delete m_Isosurface;
	m_Isosurface = new Isosurface();

	// if the volume is empty we are done
	if (m_vVolSize.volume() == 0) return;

	// create a new layer - dataset
	LayerTempData* layerData = new LayerTempData(m_vVolSize,m_pfVolume);

	// march the first layer
	MarchLayer(layerData, 0);

	// now do the remaining layers
//#pragma omp parallel for
	for (int iZ = 1; iZ < m_vVolSize.z - 1; iZ++) 
	{
		// prepare the temp data to be used in the next layer
		layerData->nextIteration();
		// march the next layer
		MarchLayer(layerData, iZ);
	}

	// delete the layer dataset
	if (layerData)
	{
		layerData=NULL;
		delete layerData;
	}
}


void MarchingCubes::MarchLayer(LayerTempData *layer, int iLayer) 
{
	double UNDEF=1e+30;
	int cellVerts[12];	// the 12 possible vertices in a cell
	for (int i = 0; i < 12; i++) cellVerts[i] = NO_EDGE;
	
	// local part of the isosurface with at most 12 vertices and at most 5 triangles per cell
	Isosurface* sliceIsosurface = new Isosurface((m_vVolSize.x-1) * (m_vVolSize.y-1) * 12, 
		(m_vVolSize.x-1) * (m_vVolSize.y-1) * 5);
	
	// march all cells in the layer
	for(int  i = 0; i < m_vVolSize.x-1; i++) 
	{
		for(int j = 0; j < m_vVolSize.y-1; j++) 
		{
			
			// fetch data from the volume
			float fVolumeValues[8];
			fVolumeValues[0] = layer->pfBotData[(j+1)	* m_vVolSize.x +		i];
			fVolumeValues[1] = layer->pfBotData[(j+1)	* m_vVolSize.x +	i + 1];
			fVolumeValues[2] = layer->pfBotData[j		* m_vVolSize.x +	i + 1];
			fVolumeValues[3] = layer->pfBotData[j		* m_vVolSize.x +		i];
			fVolumeValues[4] = layer->pfTopData[(j+1)	* m_vVolSize.x +		i];
			fVolumeValues[5] = layer->pfTopData[(j+1)	* m_vVolSize.x +	i + 1];
			fVolumeValues[6] = layer->pfTopData[j		* m_vVolSize.x +	i + 1];
			fVolumeValues[7] = layer->pfTopData[j		* m_vVolSize.x +		i];
			
			bool wu;
			wu=false;
			for (int kk=0;kk<8;kk++)
			{
				if(fVolumeValues[kk]>UNDEF) 
				{
					wu=true;
					break;
				}
			}
			if(wu) continue;
			// compute the index for the table lookup
			int cellIndex = 1*(fVolumeValues[0] < m_fIsoValue)+
				2*(fVolumeValues[1] < m_fIsoValue)+
				4*(fVolumeValues[2] < m_fIsoValue)+
				8*(fVolumeValues[3] < m_fIsoValue)+
				16*(fVolumeValues[4] < m_fIsoValue)+
				32*(fVolumeValues[5] < m_fIsoValue)+
				64*(fVolumeValues[6] < m_fIsoValue)+
				128*(fVolumeValues[7] < m_fIsoValue);
			
			// get the coordinates for the vertices, compute the triangulation and interpolate the normals
			if (ms_edgeTable[cellIndex] &    1) 
			{
				if (layer->piEdges[EDGE_INDEX(0, i, j, m_vVolSize.x-1)] == NO_EDGE)
				{     
					cellVerts[0]  = m_Isosurface->iVertices + MakeVertex(0, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[0] = layer->piEdges[EDGE_INDEX(0, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    2) 
			{
				if (layer->piEdges[EDGE_INDEX(1, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[1]  = m_Isosurface->iVertices+MakeVertex(1, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[1] = layer->piEdges[EDGE_INDEX(1, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    4) 
			{
				if (layer->piEdges[EDGE_INDEX(2, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[2]  = m_Isosurface->iVertices +MakeVertex(2, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[2] = layer->piEdges[EDGE_INDEX(2, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    8) 
			{
				if (layer->piEdges[EDGE_INDEX(3, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[3]  = m_Isosurface->iVertices+MakeVertex(3, i, j, iLayer, sliceIsosurface);
				} 
				else
				{
					cellVerts[3] = layer->piEdges[EDGE_INDEX(3, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    16) {
				if (layer->piEdges[EDGE_INDEX(4, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[4]  = m_Isosurface->iVertices+MakeVertex(4, i, j, iLayer, sliceIsosurface);
				} 
				else
				{
					cellVerts[4] = layer->piEdges[EDGE_INDEX(4, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    32)
			{
				if (layer->piEdges[EDGE_INDEX(5, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[5]  = m_Isosurface->iVertices+MakeVertex(5, i, j, iLayer, sliceIsosurface);
				}
				else 
				{
					cellVerts[5] = layer->piEdges[EDGE_INDEX(5, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    64) 
			{
				if (layer->piEdges[EDGE_INDEX(6, i, j, m_vVolSize.x-1)] == NO_EDGE)
				{     
					cellVerts[6]  = m_Isosurface->iVertices +MakeVertex(6, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[6] = layer->piEdges[EDGE_INDEX(6, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    128) 
			{
				if (layer->piEdges[EDGE_INDEX(7, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[7]  = m_Isosurface->iVertices +MakeVertex(7, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[7] = layer->piEdges[EDGE_INDEX(7, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    256) 
			{
				if (layer->piEdges[EDGE_INDEX(8, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[8]  = m_Isosurface->iVertices +MakeVertex(8, i, j, iLayer, sliceIsosurface);
				}
				else 
				{
					cellVerts[8] = layer->piEdges[EDGE_INDEX(8, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    512) 
			{
				if (layer->piEdges[EDGE_INDEX(9, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[9]  = m_Isosurface->iVertices +MakeVertex(9, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[9] = layer->piEdges[EDGE_INDEX(9, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    1024) 
			{
				if (layer->piEdges[EDGE_INDEX(10, i, j, m_vVolSize.x-1)] == NO_EDGE) 
				{     
					cellVerts[10]  = m_Isosurface->iVertices +MakeVertex(10, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[10] = layer->piEdges[EDGE_INDEX(10, i, j, m_vVolSize.x-1)];
				}
			}
			if (ms_edgeTable[cellIndex] &    2048) 
			{
				if (layer->piEdges[EDGE_INDEX(11, i, j, m_vVolSize.x-1)] == NO_EDGE)
				{     
					cellVerts[11]  = m_Isosurface->iVertices +MakeVertex(11, i, j, iLayer, sliceIsosurface);
				} 
				else 
				{
					cellVerts[11] = layer->piEdges[EDGE_INDEX(11, i, j, m_vVolSize.x-1)];
				}
			}
			
			// put the cellVerts tags into this cell's layer->edges table
			for (int iEdge = 0; iEdge < 12; iEdge++) 
			{
				if (cellVerts[iEdge] != NO_EDGE) 
				{
					layer->piEdges[EDGE_INDEX(iEdge, i, j, m_vVolSize.x-1)] = cellVerts[iEdge];
				}
			}
			
			// now propagate the vertex/normal tags to the adjacent cells to 
			// the right and behind in this layer. 
			if (i < m_vVolSize.x - 2) 
			{ // we should propagate to the right
				layer->piEdges[EDGE_INDEX( 3, i+1, j, m_vVolSize.x-1)] = cellVerts[1];
				layer->piEdges[EDGE_INDEX( 7, i+1, j, m_vVolSize.x-1)] = cellVerts[5];
				layer->piEdges[EDGE_INDEX( 8, i+1, j, m_vVolSize.x-1)] = cellVerts[9];
				layer->piEdges[EDGE_INDEX(11, i+1, j, m_vVolSize.x-1)] = cellVerts[10];
			}
			
			if (j < m_vVolSize.y - 2) 
			{ // we should propagate to the rear
				layer->piEdges[EDGE_INDEX( 2, i, j+1, m_vVolSize.x-1)] = cellVerts[0];
				layer->piEdges[EDGE_INDEX( 6, i, j+1, m_vVolSize.x-1)] = cellVerts[4];
				layer->piEdges[EDGE_INDEX(11, i, j+1, m_vVolSize.x-1)] = cellVerts[8];
				layer->piEdges[EDGE_INDEX(10, i, j+1, m_vVolSize.x-1)] = cellVerts[9];      
			}      
			
			// store the vertex indices in the triangle data structure
			int iTableIndex = 0;
			if (m_bFlipSurface)
				while (ms_triTable[cellIndex][iTableIndex] != -1) 
				{
					sliceIsosurface->AddTriangle(cellVerts[ms_triTable[cellIndex][iTableIndex+2]],
						cellVerts[ms_triTable[cellIndex][iTableIndex+1]],
						cellVerts[ms_triTable[cellIndex][iTableIndex+0]]);
					iTableIndex+=3;
				}
			else
				while (ms_triTable[cellIndex][iTableIndex] != -1) 
				{
					sliceIsosurface->AddTriangle(cellVerts[ms_triTable[cellIndex][iTableIndex+0]],
						cellVerts[ms_triTable[cellIndex][iTableIndex+1]],
						cellVerts[ms_triTable[cellIndex][iTableIndex+2]]);
					iTableIndex+=3;
				}
					
		}
	}
	
	// add this layer's triangles to the global list
	m_Isosurface->AppendData(sliceIsosurface);
	
	delete sliceIsosurface;
}

int MarchingCubes::MakeVertex(int iEdgeIndex, int i, int j, int k, Isosurface* sliceIso) 
{

	VECTOR3<int>  vFrom; // first grid vertex
	VECTOR3<int>  vTo; // second grid vertex

	// on the edge index decide what the edges are
	switch (iEdgeIndex) 
	{
		case 0: vFrom	= VECTOR3<int>(i,j+1,k);	vTo	= VECTOR3<int>(i+1,j+1,k);	break;
		case 1: vFrom	= VECTOR3<int>(i+1,j+1,k);	vTo	= VECTOR3<int>(i+1,j,k);	break;
		case 2: vFrom	= VECTOR3<int>(i+1,j,k);	vTo	= VECTOR3<int>(i,j,k);		break;
		case 3: vFrom	= VECTOR3<int>(i,j,k);		vTo	= VECTOR3<int>(i,j+1,k);	break;
		case 4: vFrom	= VECTOR3<int>(i,j+1,k+1);	vTo	= VECTOR3<int>(i+1,j+1,k+1);break;
		case 5: vFrom	= VECTOR3<int>(i+1,j+1,k+1);vTo	= VECTOR3<int>(i+1,j,k+1);	break;
		case 6: vFrom	= VECTOR3<int>(i+1,j,k+1);	vTo	= VECTOR3<int>(i,j,k+1);	break;
		case 7: vFrom	= VECTOR3<int>(i,j,k+1);	vTo	= VECTOR3<int>(i,j+1,k+1);	break;
		case 8: vFrom	= VECTOR3<int>(i,j+1,k);	vTo	= VECTOR3<int>(i,j+1,k+1);	break;
		case 9: vFrom	= VECTOR3<int>(i+1,j+1,k);	vTo	= VECTOR3<int>(i+1,j+1,k+1);break;
		case 10:vFrom	= VECTOR3<int>(i+1,j,k);	vTo	= VECTOR3<int>(i+1,j,k+1);	break;
		case 11:vFrom	= VECTOR3<int>(i,j,k);		vTo	= VECTOR3<int>(i,j,k+1);	break;
	}

	float fFromValue = m_pfVolume[DATA_INDEX(vFrom.x, vFrom.y, vFrom.z, m_vVolSize.x, m_vVolSize.y)];
	float fToValue   = m_pfVolume[DATA_INDEX(vTo.x, vTo.y, vTo.z, m_vVolSize.x, m_vVolSize.y)];

	// determine the relative distance along edge vFrom->vTo that the isosurface vertex lies
	float d = ( fFromValue - m_fIsoValue) / ( fFromValue - fToValue );
	if (d < EPSILON) {	d = 0.0f;} else if (d > (1 - EPSILON)) {d = 1.0f;}

	VECTOR3<float> startP, endP;   //对应于vFrom和 vTo的绝对坐标
	startP = VECTOR3<float>(BoxMin.x+vFrom.x*spacing, BoxMin.y+vFrom.y*spacing, BoxMin.z+vFrom.z*spacing);
	endP =   VECTOR3<float>(BoxMin.x+vTo.x*spacing, BoxMin.y+vTo.y*spacing, BoxMin.z+vTo.z*spacing);

	// interqpolate the vertex
	VECTOR3<float>	vVertex = VECTOR3<float>(startP.x + d * (endP.x - startP.x), startP.y + d * (endP.y - startP.y), startP.z + d * (endP.z - startP.z));

	// now determine the gradients at the endpoints of the edge 
	// and interpolate the normal for the isosurface vertex     
	VECTOR3<float>	vNormFrom = InterpolateNormal(fFromValue,vFrom);
	VECTOR3<float>	vNormTo   = InterpolateNormal(fToValue,vTo);

	// interpolate the normal
	VECTOR3<float>	vNormal = VECTOR3<float>(float(vNormFrom.x) + d * float(vNormTo.x - vNormFrom.x),
											 float(vNormFrom.y) + d * float(vNormTo.y - vNormFrom.y),
											 float(vNormFrom.z) + d * float(vNormTo.z - vNormFrom.z));
	vNormal.normalize(EPSILON);

	// insert the vertex and normal into the isosurface structure and return the index for this vertex
	return sliceIso->AddVertex(vVertex, vNormal);
}


VECTOR3<float> MarchingCubes::InterpolateNormal(float fValueAtPos, VECTOR3<int> vPosition) {
	// the gradients are computed by central differences, except
	// on the boundaries of the dataset, where forward or backward
	// differencing is used (three point form)
	
	VECTOR3<float> result;
	
	// the x component
	if (vPosition.x == 0) 
	{							// left border -> forward diff
		result.x = m_fFlipFact * (-3.0f * fValueAtPos + 
			4.0f * m_pfVolume[DATA_INDEX(vPosition.x+1, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)] +
			-1.0f * m_pfVolume[DATA_INDEX(vPosition.x+2, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	}
	else if (vPosition.x == m_vVolSize.x - 1) 
	{	// right border -> forward diff
		result.x = m_fFlipFact * (  3.0f * fValueAtPos+
			-4.0f * m_pfVolume[DATA_INDEX(vPosition.x-1, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)] +
			1.0f * m_pfVolume[DATA_INDEX(vPosition.x-2, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	}
	else 
	{									// interior -> central diff
		result.x = m_fFlipFact * (	m_pfVolume[DATA_INDEX(vPosition.x+1, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)] -
			m_pfVolume[DATA_INDEX(vPosition.x-1, vPosition.y, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	}
	
	// the y component
	if (vPosition.y == 0)
	{							//forward diff
		result.y = m_fFlipFact * (-3.0f * fValueAtPos + 
			4.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y+1, vPosition.z, m_vVolSize.x, m_vVolSize.y)] +
			-1.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y+2, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	} 
	else if (vPosition.y == m_vVolSize.y - 1) 
	{	// forward diff
		result.y = m_fFlipFact * (  3.0f * fValueAtPos+
			-4.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y-1, vPosition.z, m_vVolSize.x, m_vVolSize.y)] +
			1.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y-2, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	}
	else 
	{									// central diff
		result.y = m_fFlipFact * (	m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y+1, vPosition.z, m_vVolSize.x, m_vVolSize.y)] -
			m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y-1, vPosition.z, m_vVolSize.x, m_vVolSize.y)]);
	}
	
	// the z componentk
	if (vPosition.z == 0) 
	{							//forward diff
		result.z = m_fFlipFact * (-3.0f * fValueAtPos + 
			4.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z+1, m_vVolSize.x, m_vVolSize.y)] +
			-1.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z+2, m_vVolSize.x, m_vVolSize.y)]);
	} 
	else if (vPosition.z == m_vVolSize.z - 1) 
	{	// forward diff
		result.z = m_fFlipFact * (  3.0f * fValueAtPos+
			-4.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z-1, m_vVolSize.x, m_vVolSize.y)] +
			1.0f * m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z-2, m_vVolSize.x, m_vVolSize.y)]);
	}
	else 
	{									// central diff
		result.z = m_fFlipFact * (	m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z+1, m_vVolSize.x, m_vVolSize.y)] -
			m_pfVolume[DATA_INDEX(vPosition.x, vPosition.y, vPosition.z-1, m_vVolSize.x, m_vVolSize.y)]);
	}
	
	return result;
}

一些模型重构的案例:
MC(移动立方体)算法_第6张图片
圆环
MC(移动立方体)算法_第7张图片
斯坦福兔子
MC(移动立方体)算法_第8张图片
小猫

你可能感兴趣的:(算法,计算机图形学,MC算法,移动立方体,网格重构)