MS算法
的主要流程:
1. 遍历每个栅格,从16种固定情况中选择类别
2. 利用线性插值,结合网格点数值找寻交点
3. 根据线性插值的结果连线
MC算法
也被称作“等值面提取(Isosurface Extraction)”是面绘制算法中的经典算法,算法的主要精髓为:在三维离散数据场中通过线性差值来逼近等值面。
在学习MC算法前需要给定一些基本概念:首先定义一个立方体单元为一个体素 ,每一个体元均由8个顶点所构成。
体素顶点有两种不同的状态量所表示:
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 };
对模型包围盒空间划分体,从下到上每个体素单元均遍历一次
遍历完成后得到构建的模型
构建得到的三角网格模型如图所示
附上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;
}