// Copyright (C) 2017, Dominik Wodniok // This software may be modified and distributed under the terms // of the BSD 3-Clause license. // See the LICENSE.txt file for details. #ifndef DUALMC_H_INCLUDED #define DUALMC_H_INCLUDED /// \file dualmc.h /// \author Dominik Wodniok /// \date 2009 // c includes #include // stl includes #include #include namespace dualmc { typedef float VertexComponentsType; typedef uint32_t PropertyType; typedef uint32_t QuadIndexType; typedef uint32_t TriIndexType; /// vertex structure for dual points struct Vertex { /// non-initializing constructor Vertex(); /// initializing constructor Vertex(VertexComponentsType x, VertexComponentsType y, VertexComponentsType z, PropertyType w); /// initializing constructor Vertex(Vertex const & v); // components VertexComponentsType x; VertexComponentsType y; VertexComponentsType z; // texture PropertyType w; }; /// tri indices structure struct Tri { /// non-initializing constructor Tri(); /// initializing constructor Tri(TriIndexType i0, TriIndexType i1, TriIndexType i2); // tri indices TriIndexType i0; TriIndexType i1; TriIndexType i2; }; /// \class DualMC /// \author Dominik Wodniok /// \date 2009 /// Class which implements the dual marching cubes algorithm from Gregory M. Nielson. /// Faces and vertices of the standard marching cubes algorithm correspond to /// vertices and faces in the dual algorithm. As a vertex in standard marching cubes /// usually is shared by 4 faces, the dual mesh is entirely made from quadrangles. /// Unfortunately, under rare circumstances the original algorithm can create /// non-manifold meshes. See the remarks of the original paper on this. /// The class optionally can guarantee manifold meshes by taking the Manifold /// Dual Marching Cubes approach from Rephael Wenger as described in /// chapter 3.3.5 of his book "Isosurfaces: Geometry, Topology, and Algorithms". template class DualMC { public: // typedefs typedef T VolumeDataType; /// point structure for input points struct Point { /// non-initializing constructor Point(); /// initializing constructor Point(VolumeDataType x, PropertyType w); /// initializing constructor Point(Point const &v); // components VolumeDataType x; // material PropertyType w; inline bool operator<(const VolumeDataType& b) const { return x < b; } inline bool operator>=(const VolumeDataType& b) const { return x >= b; } inline std::pair pair() const { return std::make_pair(w, x); } }; /// Extracts the iso surface for a given volume and iso value. /// Output is a list of vertices and a list of indices, which connect /// vertices to tris. void buildTris( Point const *data, int32_t const dimX, int32_t const dimY, int32_t const dimZ, VolumeDataType const iso, size_t const *textures_map, float const *roughness, bool const generateManifold, std::vector &vertices, std::vector &tris); private: /// Extract tris mesh with shared vertex indices. void buildSharedVerticesTris( VolumeDataType const iso, std::vector & vertices, std::vector & tris ); protected: /// enum with edge codes for a 12-bit voxel edge mask to indicate /// grid edges which intersect the ISO surface of classic marching cubes enum DMCEdgeCode { EDGE0 = 1, EDGE1 = 1 << 1, EDGE2 = 1 << 2, EDGE3 = 1 << 3, EDGE4 = 1 << 4, EDGE5 = 1 << 5, EDGE6 = 1 << 6, EDGE7 = 1 << 7, EDGE8 = 1 << 8, EDGE9 = 1 << 9, EDGE10 = 1 << 10, EDGE11 = 1 << 11, FORCE_32BIT = 0xffffffff }; /// get the 8-bit in-out mask for the voxel corners of the cell cube at (cx,cy,cz) /// and the given iso value int getCellCode(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso) const; /// Get the 12-bit dual point code mask, which encodes the traditional /// marching cube vertices of the traditional marching cubes face which /// corresponds to the dual point. /// This is also where the manifold dual marching cubes algorithm is /// implemented. int getDualPointCode(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, DMCEdgeCode const edge) const; /// Given a dual point code and iso value, compute the dual point. void calculateDualPoint(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, int const pointCode, Vertex &v) const; /// Get the shared index of a dual point which is uniquly identified by its /// cell cube index and a cube edge. The dual point is computed, /// if it has not been computed before. QuadIndexType getSharedDualPointIndex(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, DMCEdgeCode const edge, std::vector & vertices); /// Compute a linearized cell cube index. int32_t gA(int32_t const x, int32_t const y, int32_t const z) const; /// Update texture if max. void maxProp(int32_t const x, int32_t const y, int32_t const z, Vertex &vertex, VolumeDataType& max) const; protected: // static lookup tables needed for (manifold) dual marching cubes /// Dual Marching Cubes table /// Encodes the edge vertices for the 256 marching cubes cases. /// A marching cube case produces up to four faces and ,thus, up to four /// dual points. static int32_t const dualPointsList[256][4]; /// Table which encodes the ambiguous face of cube configurations, which /// can cause non-manifold meshes. /// Needed for manifold dual marching cubes. static uint8_t const problematicConfigs[256]; protected: /// convenience volume extent array for x-,y-, and z-dimension int32_t dims[3]; /// convenience volume data point Point const *data; /// point to vertex property table size_t const *textures_map; /// property roughness table float const *roughness; /// store whether the manifold dual marching cubes algorithm should be /// applied. bool generateManifold; /// Dual point key structure for hashing of shared vertices struct DualPointKey { // a dual point can be uniquely identified by ite linearized volume cell // id and point code int32_t linearizedCellID; int pointCode; /// Equal operator for unordered map bool operator==(DualPointKey const & other) const; }; /// Functor for dual point key hash generation struct DualPointKeyHash { size_t operator()(DualPointKey const & k) const { return size_t(k.linearizedCellID) | (size_t(k.pointCode) << 32u); } }; /// Hash map for shared vertex index computations robin_hood::unordered_map pointToIndex; }; // inline function definitions //------------------------------------------------------------------------------ inline Vertex::Vertex(){} //------------------------------------------------------------------------------ inline Vertex::Vertex( VertexComponentsType x, VertexComponentsType y, VertexComponentsType z, PropertyType w ) : x(x), y(y), z(z), w(w) {} //------------------------------------------------------------------------------ inline Vertex::Vertex(Vertex const & v) : x(v.x), y(v.y), z(v.z), w(v.w) {} //------------------------------------------------------------------------------ inline Tri::Tri() {} //------------------------------------------------------------------------------ inline Tri::Tri( TriIndexType i0, TriIndexType i1, TriIndexType i2 ) : i0(i0), i1(i1), i2(i2) {} //------------------------------------------------------------------------------ template inline DualMC::Point::Point() {} //------------------------------------------------------------------------------ template inline DualMC::Point::Point(VolumeDataType x, PropertyType w): x(x), w(w) { } //------------------------------------------------------------------------------ template inline DualMC::Point::Point(Point const &v): x(v.x), w(v.w) {} //------------------------------------------------------------------------------ template inline int32_t DualMC::gA(int32_t const x, int32_t const y, int32_t const z) const { return x + dims[0] * (y + dims[1] * z); } //------------------------------------------------------------------------------ template inline void DualMC::maxProp(int32_t const x, int32_t const y, int32_t const z, Vertex &vertex, VolumeDataType &max) const { if(data[gA(x, y, z)].x > max) { max = data[gA(x, y, z)].x; vertex.w = textures_map[data[gA(x, y, z)].w]; } } //------------------------------------------------------------------------------ template inline bool DualMC::DualPointKey::operator==(typename DualMC::DualPointKey const & other) const { return linearizedCellID == other.linearizedCellID && pointCode == other.pointCode; } // Copyright (C) 2017, Dominik Wodniok // This software may be modified and distributed under the terms // of the BSD 3-Clause license. See the LICENSE.txt file for details. /// \file dualmc.tpp /// \author Dominik Wodniok /// \date 2009 //------------------------------------------------------------------------------ template inline int DualMC::getCellCode(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso) const { // determine for each cube corner if it is outside or inside int code = 0; if (data[gA(cx, cy, cz)] >= iso) code |= 1; if (data[gA(cx + 1, cy, cz)] >= iso) code |= 2; if (data[gA(cx, cy + 1, cz)] >= iso) code |= 4; if (data[gA(cx + 1, cy + 1, cz)] >= iso) code |= 8; if (data[gA(cx, cy, cz + 1)] >= iso) code |= 16; if (data[gA(cx + 1, cy, cz + 1)] >= iso) code |= 32; if (data[gA(cx, cy + 1, cz + 1)] >= iso) code |= 64; if (data[gA(cx + 1, cy + 1, cz + 1)] >= iso) code |= 128; return code; } //------------------------------------------------------------------------------ template inline int DualMC::getDualPointCode(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, DMCEdgeCode const edge) const { int cubeCode = getCellCode(cx, cy, cz, iso); // is manifold dual marching cubes desired? if (generateManifold) { // The Manifold Dual Marching Cubes approach from Rephael Wenger as described in // chapter 3.3.5 of his book "Isosurfaces: Geometry, Topology, and Algorithms" // is implemente here. // If a problematic C16 or C19 configuration shares the ambiguous face // with another C16 or C19 configuration we simply invert the cube code // before looking up dual points. Doing this for these pairs ensures // manifold meshes. // But this removes the dualism to marching cubes. // check if we have a potentially problematic configuration uint8_t const direction = problematicConfigs[uint8_t(cubeCode)]; // If the direction code is in {0,...,5} we have a C16 or C19 configuration. if (direction != 255) { // We have to check the neighboring cube, which shares the ambiguous // face. For this we decode the direction. This could also be done // with another lookup table. // copy current cube coordinates into an array. int32_t neighborCoords[] = { cx,cy,cz }; // get the dimension of the non-zero coordinate axis unsigned int const component = direction >> 1; // get the sign of the direction int32_t delta = (direction & 1) == 1 ? 1 : -1; // modify the correspong cube coordinate neighborCoords[component] += delta; // have we left the volume in this direction? if (neighborCoords[component] >= 0 && neighborCoords[component] < (dims[component] - 1)) { // get the cube configuration of the relevant neighbor int neighborCubeCode = getCellCode(neighborCoords[0], neighborCoords[1], neighborCoords[2], iso); // Look up the neighbor configuration ambiguous face direction. // If the direction is valid we have a C16 or C19 neighbor. // As C16 and C19 have exactly one ambiguous face this face is // guaranteed to be shared for the pair. if (problematicConfigs[uint8_t(neighborCubeCode)] != 255) { // replace the cube configuration with its inverse. cubeCode ^= 0xff; } } } } for (int i = 0; i < 4; ++i) if (dualPointsList[cubeCode][i] & edge) { return dualPointsList[cubeCode][i]; } return 0; } //------------------------------------------------------------------------------ template inline void DualMC::calculateDualPoint(int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, int const pointCode, Vertex & v) const { // initialize the point with lower voxel coordinates v.x = static_cast(cx); v.y = static_cast(cy); v.z = static_cast(cz); // compute the dual point as the mean of the face vertices belonging to the // original marching cubes face Vertex p(0, 0, 0, 0); int points = 0; T max = 0; // sum edge intersection vertices using the point code if (pointCode & EDGE0) { p.x += ((float)iso - data[gA(cx, cy, cz)].x) / (data[gA(cx + 1, cy, cz)].x - data[gA(cx, cy, cz)].x); points++; maxProp(cx, cy, cz, v, max); maxProp(cx + 1, cy, cz, v, max); } if (pointCode & EDGE1) { p.x += 1.0f; p.z += ((float)iso - data[gA(cx + 1, cy, cz)].x) / (data[gA(cx + 1, cy, cz + 1)].x - data[gA(cx + 1, cy, cz)].x); points++; maxProp(cx + 1, cy, cz, v, max); maxProp(cx + 1, cy, cz + 1, v, max); } if (pointCode & EDGE2) { p.x += ((float)iso - data[gA(cx, cy, cz + 1)].x) / (data[gA(cx + 1, cy, cz + 1)].x - data[gA(cx, cy, cz + 1)].x); p.z += 1.0f; points++; maxProp(cx, cy, cz + 1, v, max); maxProp(cx + 1, cy, cz + 1, v, max); } if (pointCode & EDGE3) { p.z += ((float)iso - data[gA(cx, cy, cz)].x) / (data[gA(cx, cy, cz + 1)].x - data[gA(cx, cy, cz)].x); points++; maxProp(cx, cy, cz, v, max); maxProp(cx, cy, cz + 1, v, max); } if (pointCode & EDGE4) { p.x += ((float)iso - data[gA(cx, cy + 1, cz)].x) / (data[gA(cx + 1, cy + 1, cz)].x - data[gA(cx, cy + 1, cz)].x); p.y += 1.0f; points++; maxProp(cx, cy+1, cz, v, max); maxProp(cx + 1, cy+1, cz, v, max); } if (pointCode & EDGE5) { p.x += 1.0f; p.z += ((float)iso - data[gA(cx + 1, cy + 1, cz)].x) / (data[gA(cx + 1, cy + 1, cz + 1)].x - data[gA(cx + 1, cy + 1, cz)].x); p.y += 1.0f; points++; maxProp(cx+1, cy+1, cz, v, max); maxProp(cx + 1, cy+1, cz+1, v, max); } if (pointCode & EDGE6) { p.x += ((float)iso - data[gA(cx, cy + 1, cz + 1)].x) / (data[gA(cx + 1, cy + 1, cz + 1)].x - data[gA(cx, cy + 1, cz + 1)].x); p.z += 1.0f; p.y += 1.0f; points++; maxProp(cx, cy+1, cz+1, v, max); maxProp(cx + 1, cy+1, cz+1, v, max); } if (pointCode & EDGE7) { p.z += ((float)iso - data[gA(cx, cy + 1, cz)].x) / (data[gA(cx, cy + 1, cz + 1)].x - data[gA(cx, cy + 1, cz)].x); p.y += 1.0f; points++; maxProp(cx, cy+1, cz, v, max); maxProp(cx, cy+1, cz+1, v, max); } if (pointCode & EDGE8) { p.y += ((float)iso - data[gA(cx, cy, cz)].x) / (data[gA(cx, cy + 1, cz)].x - data[gA(cx, cy, cz)].x); points++; maxProp(cx, cy, cz, v, max); maxProp(cx, cy+1, cz, v, max); } if (pointCode & EDGE9) { p.x += 1.0f; p.y += ((float)iso - data[gA(cx + 1, cy, cz)].x) / (data[gA(cx + 1, cy + 1, cz)].x - data[gA(cx + 1, cy, cz)].x); points++; maxProp(cx+1, cy, cz, v, max); maxProp(cx + 1, cy+1, cz, v, max); } if (pointCode & EDGE10) { p.x += 1.0f; p.y += ((float)iso - data[gA(cx + 1, cy, cz + 1)].x) / (data[gA(cx + 1, cy + 1, cz + 1)].x - data[gA(cx + 1, cy, cz + 1)].x); p.z += 1.0f; points++; maxProp(cx+1, cy, cz+1, v, max); maxProp(cx + 1, cy+1, cz+1, v, max); } if (pointCode & EDGE11) { p.z += 1.0f; p.y += ((float)iso - data[gA(cx, cy, cz + 1)].x) / (data[gA(cx, cy + 1, cz + 1)].x - data[gA(cx, cy, cz + 1)].x); points++; maxProp(cx, cy, cz+1, v, max); maxProp(cx, cy+1, cz+1, v, max); } // divide by number of accumulated points float invPoints = (1.f - roughness[v.w]) / (float)points; p.x *= invPoints; p.y *= invPoints; p.z *= invPoints; // offset point by voxel coordinates v.x += p.x; v.y += p.y; v.z += p.z; } //------------------------------------------------------------------------------ template inline QuadIndexType DualMC::getSharedDualPointIndex( int32_t const cx, int32_t const cy, int32_t const cz, VolumeDataType const iso, DMCEdgeCode const edge, std::vector & vertices ) { // create a key for the dual point from its linearized cell ID and point code DualPointKey key; key.linearizedCellID = gA(cx, cy, cz); key.pointCode = getDualPointCode(cx, cy, cz, iso, edge); // have we already computed the dual point? auto iterator = pointToIndex.find(key); if (iterator != pointToIndex.end()) { // just return the dual point index return iterator->second; } else { // create new vertex and vertex id QuadIndexType newVertexId = static_cast(vertices.size()); vertices.emplace_back(); calculateDualPoint(cx, cy, cz, iso, key.pointCode, vertices.back()); // insert vertex ID into map and also return it pointToIndex[key] = newVertexId; return newVertexId; } } //------------------------------------------------------------------------------ template inline void DualMC::buildTris( Point const * data, int32_t const dimX, int32_t const dimY, int32_t const dimZ, VolumeDataType const iso, size_t const * textures_map, float const * roughness, bool const generateManifold, std::vector & vertices, std::vector & tris ) { // set members this->dims[0] = dimX; this->dims[1] = dimY; this->dims[2] = dimZ; this->data = data; this->textures_map = textures_map; this->roughness = roughness; this->generateManifold = generateManifold; // clear vertices and quad indices vertices.clear(); tris.clear(); buildSharedVerticesTris(iso, vertices, tris); } template inline void DualMC::buildSharedVerticesTris( VolumeDataType const iso, std::vector & vertices, std::vector & tris ) { int32_t const reducedX = dims[0] - 2; int32_t const reducedY = dims[1] - 2; int32_t const reducedZ = dims[2] - 2; TriIndexType i0, i1, i2, i3; pointToIndex.clear(); // iterate voxels for (int32_t z = 0; z < reducedZ; ++z) for (int32_t y = 0; y < reducedY; ++y) for (int32_t x = 0; x < reducedX; ++x) { // construct quads for x edge if (z > 0 && y > 0) { bool const entering = data[gA(x, y, z)] < iso && data[gA(x + 1, y, z)] >= iso; bool const exiting = data[gA(x, y, z)] >= iso && data[gA(x + 1, y, z)] < iso; if (entering || exiting) { // get quad i0 = getSharedDualPointIndex(x, y, z, iso, EDGE0, vertices); i1 = getSharedDualPointIndex(x, y, z - 1, iso, EDGE2, vertices); i2 = getSharedDualPointIndex(x, y - 1, z - 1, iso, EDGE6, vertices); i3 = getSharedDualPointIndex(x, y - 1, z, iso, EDGE4, vertices); if (entering) { tris.emplace_back(i0, i1, i2); tris.emplace_back(i2, i3, i0); } else { tris.emplace_back(i2, i1, i0); tris.emplace_back(i0, i3, i2); } } } // construct quads for y edge if (z > 0 && x > 0) { bool const entering = data[gA(x, y, z)] < iso && data[gA(x, y + 1, z)] >= iso; bool const exiting = data[gA(x, y, z)] >= iso && data[gA(x, y + 1, z)] < iso; if (entering || exiting) { // generate quad i0 = getSharedDualPointIndex(x, y, z, iso, EDGE8, vertices); i1 = getSharedDualPointIndex(x, y, z - 1, iso, EDGE11, vertices); i2 = getSharedDualPointIndex(x - 1, y, z - 1, iso, EDGE10, vertices); i3 = getSharedDualPointIndex(x - 1, y, z, iso, EDGE9, vertices); if (exiting) { tris.emplace_back(i0, i1, i2); tris.emplace_back(i2, i3, i0); } else { tris.emplace_back(i2, i1, i0); tris.emplace_back(i0, i3, i2); } } } // construct quads for z edge if (x > 0 && y > 0) { bool const entering = data[gA(x, y, z)] < iso && data[gA(x, y, z + 1)] >= iso; bool const exiting = data[gA(x, y, z)] >= iso && data[gA(x, y, z + 1)] < iso; if (entering || exiting) { // generate quad i0 = getSharedDualPointIndex(x, y, z, iso, EDGE3, vertices); i1 = getSharedDualPointIndex(x - 1, y, z, iso, EDGE1, vertices); i2 = getSharedDualPointIndex(x - 1, y - 1, z, iso, EDGE5, vertices); i3 = getSharedDualPointIndex(x, y - 1, z, iso, EDGE7, vertices); if (exiting) { tris.emplace_back(i0, i1, i2); tris.emplace_back(i2, i3, i0); } else { tris.emplace_back(i2, i1, i0); tris.emplace_back(i0, i3, i2); } } } } } // Copyright (C) 2018, Dominik Wodniok // This software may be modified and distributed under the terms // of the BSD 3-Clause license. // See the LICENSE.txt file for details. /// \file dualmc_table.tpp /// \author Dominik Wodniok /// \date 2009 // TODO: invert y and z // Coordinate system // // y // | // | // | // 0-----x // / // / // z // // Cell Corners // (Corners are voxels. Number correspond to Morton codes of corner coordinates) // // 2-------------------3 // /| /| // / | / | // / | / | // 6-------------------7 | // | | | | // | | | | // | | | | // | | | | // | 0---------------|---1 // | / | / // | / | / // |/ |/ // 4-------------------5 // // Cell Edges // // o--------4----------o // /| /| // 7 | 5 | // / | / | // o--------6----------o | // | 8 | 9 // | | | | // | | | | // 11 | 10 | // | o--------0------|---o // | / | / // | 3 | 1 // |/ |/ // o--------2----------o // // Encodes the edge vertices for the 256 marching cubes cases. // A marching cube case produces up to four faces and ,thus, up to four // dual points. template int32_t const DualMC::dualPointsList[256][4] = { {0, 0, 0, 0}, // 0 {EDGE0 | EDGE3 | EDGE8, 0, 0, 0}, // 1 {EDGE0 | EDGE1 | EDGE9, 0, 0, 0}, // 2 {EDGE1 | EDGE3 | EDGE8 | EDGE9, 0, 0, 0}, // 3 {EDGE4 | EDGE7 | EDGE8, 0, 0, 0}, // 4 {EDGE0 | EDGE3 | EDGE4 | EDGE7, 0, 0, 0}, // 5 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE7 | EDGE8, 0, 0}, // 6 {EDGE1 | EDGE3 | EDGE4 | EDGE7 | EDGE9, 0, 0, 0}, // 7 {EDGE4 | EDGE5 | EDGE9, 0, 0, 0}, // 8 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE5 | EDGE9, 0, 0}, // 9 {EDGE0 | EDGE1 | EDGE4 | EDGE5, 0, 0, 0}, // 10 {EDGE1 | EDGE3 | EDGE4 | EDGE5 | EDGE8, 0, 0, 0}, // 11 {EDGE5 | EDGE7 | EDGE8 | EDGE9, 0, 0, 0}, // 12 {EDGE0 | EDGE3 | EDGE5 | EDGE7 | EDGE9, 0, 0, 0}, // 13 {EDGE0 | EDGE1 | EDGE5 | EDGE7 | EDGE8, 0, 0, 0}, // 14 {EDGE1 | EDGE3 | EDGE5 | EDGE7, 0, 0, 0}, // 15 {EDGE2 | EDGE3 | EDGE11, 0, 0, 0}, // 16 {EDGE0 | EDGE2 | EDGE8 | EDGE11, 0, 0, 0}, // 17 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 18 {EDGE1 | EDGE2 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 19 {EDGE4 | EDGE7 | EDGE8, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 20 {EDGE0 | EDGE2 | EDGE4 | EDGE7 | EDGE11, 0, 0, 0}, // 21 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE7 | EDGE8, EDGE2 | EDGE3 | EDGE11, 0}, // 22 {EDGE1 | EDGE2 | EDGE4 | EDGE7 | EDGE9 | EDGE11, 0, 0, 0}, // 23 {EDGE4 | EDGE5 | EDGE9, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 24 {EDGE0 | EDGE2 | EDGE8 | EDGE11, EDGE4 | EDGE5 | EDGE9, 0, 0}, // 25 {EDGE0 | EDGE1 | EDGE4 | EDGE5, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 26 {EDGE1 | EDGE2 | EDGE4 | EDGE5 | EDGE8 | EDGE11, 0, 0, 0}, // 27 {EDGE5 | EDGE7 | EDGE8 | EDGE9, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 28 {EDGE0 | EDGE2 | EDGE5 | EDGE7 | EDGE9 | EDGE11, 0, 0, 0}, // 29 {EDGE0 | EDGE1 | EDGE5 | EDGE7 | EDGE8, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 30 {EDGE1 | EDGE2 | EDGE5 | EDGE7 | EDGE11, 0, 0, 0}, // 31 {EDGE1 | EDGE2 | EDGE10, 0, 0, 0}, // 32 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 33 {EDGE0 | EDGE2 | EDGE9 | EDGE10, 0, 0, 0}, // 34 {EDGE2 | EDGE3 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 35 {EDGE4 | EDGE7 | EDGE8, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 36 {EDGE0 | EDGE3 | EDGE4 | EDGE7, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 37 {EDGE0 | EDGE2 | EDGE9 | EDGE10, EDGE4 | EDGE7 | EDGE8, 0, 0}, // 38 {EDGE2 | EDGE3 | EDGE4 | EDGE7 | EDGE9 | EDGE10, 0, 0, 0}, // 39 {EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 40 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE2 | EDGE10, 0}, // 41 {EDGE0 | EDGE2 | EDGE4 | EDGE5 | EDGE10, 0, 0, 0}, // 42 {EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE8 | EDGE10, 0, 0, 0}, // 43 {EDGE5 | EDGE7 | EDGE8 | EDGE9, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 44 {EDGE0 | EDGE3 | EDGE5 | EDGE7 | EDGE9, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 45 {EDGE0 | EDGE2 | EDGE5 | EDGE7 | EDGE8 | EDGE10, 0, 0, 0}, // 46 {EDGE2 | EDGE3 | EDGE5 | EDGE7 | EDGE10, 0, 0, 0}, // 47 {EDGE1 | EDGE3 | EDGE10 | EDGE11, 0, 0, 0}, // 48 {EDGE0 | EDGE1 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 49 {EDGE0 | EDGE3 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 50 {EDGE8 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 51 {EDGE4 | EDGE7 | EDGE8, EDGE1 | EDGE3 | EDGE10 | EDGE11, 0, 0}, // 52 {EDGE0 | EDGE1 | EDGE4 | EDGE7 | EDGE10 | EDGE11, 0, 0, 0}, // 53 {EDGE0 | EDGE3 | EDGE9 | EDGE10 | EDGE11, EDGE4 | EDGE7 | EDGE8, 0, 0}, // 54 {EDGE4 | EDGE7 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 55 {EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE3 | EDGE10 | EDGE11, 0, 0}, // 56 {EDGE0 | EDGE1 | EDGE8 | EDGE10 | EDGE11, EDGE4 | EDGE5 | EDGE9, 0, 0}, // 57 {EDGE0 | EDGE3 | EDGE4 | EDGE5 | EDGE10 | EDGE11, 0, 0, 0}, // 58 {EDGE4 | EDGE5 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 59 {EDGE5 | EDGE7 | EDGE8 | EDGE9, EDGE1 | EDGE3 | EDGE10 | EDGE11, 0, 0}, // 60 {EDGE0 | EDGE1 | EDGE5 | EDGE7 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 61 {EDGE0 | EDGE3 | EDGE5 | EDGE7 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 62 {EDGE5 | EDGE7 | EDGE10 | EDGE11, 0, 0, 0}, // 63 {EDGE6 | EDGE7 | EDGE11, 0, 0, 0}, // 64 {EDGE0 | EDGE3 | EDGE8, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 65 {EDGE0 | EDGE1 | EDGE9, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 66 {EDGE1 | EDGE3 | EDGE8 | EDGE9, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 67 {EDGE4 | EDGE6 | EDGE8 | EDGE11, 0, 0, 0}, // 68 {EDGE0 | EDGE3 | EDGE4 | EDGE6 | EDGE11, 0, 0, 0}, // 69 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE6 | EDGE8 | EDGE11, 0, 0}, // 70 {EDGE1 | EDGE3 | EDGE4 | EDGE6 | EDGE9 | EDGE11, 0, 0, 0}, // 71 {EDGE4 | EDGE5 | EDGE9, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 72 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE5 | EDGE9, EDGE6 | EDGE7 | EDGE11, 0}, // 73 {EDGE0 | EDGE1 | EDGE4 | EDGE5, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 74 {EDGE1 | EDGE3 | EDGE4 | EDGE5 | EDGE8, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 75 {EDGE5 | EDGE6 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 76 {EDGE0 | EDGE3 | EDGE5 | EDGE6 | EDGE9 | EDGE11, 0, 0, 0}, // 77 {EDGE0 | EDGE1 | EDGE5 | EDGE6 | EDGE8 | EDGE11, 0, 0, 0}, // 78 {EDGE1 | EDGE3 | EDGE5 | EDGE6 | EDGE11, 0, 0, 0}, // 79 {EDGE2 | EDGE3 | EDGE6 | EDGE7, 0, 0, 0}, // 80 {EDGE0 | EDGE2 | EDGE6 | EDGE7 | EDGE8, 0, 0, 0}, // 81 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE6 | EDGE7, 0, 0}, // 82 {EDGE1 | EDGE2 | EDGE6 | EDGE7 | EDGE8 | EDGE9, 0, 0, 0}, // 83 {EDGE2 | EDGE3 | EDGE4 | EDGE6 | EDGE8, 0, 0, 0}, // 84 {EDGE0 | EDGE2 | EDGE4 | EDGE6, 0, 0, 0}, // 85 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE4 | EDGE6 | EDGE8, 0, 0}, // 86 {EDGE1 | EDGE2 | EDGE4 | EDGE6 | EDGE9, 0, 0, 0}, // 87 {EDGE4 | EDGE5 | EDGE9, EDGE2 | EDGE3 | EDGE6 | EDGE7, 0, 0}, // 88 {EDGE0 | EDGE2 | EDGE6 | EDGE7 | EDGE8, EDGE4 | EDGE5 | EDGE9, 0, 0}, // 89 {EDGE0 | EDGE1 | EDGE4 | EDGE5, EDGE2 | EDGE3 | EDGE6 | EDGE7, 0, 0}, // 90 {EDGE1 | EDGE2 | EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE8, 0, 0, 0}, // 91 {EDGE2 | EDGE3 | EDGE5 | EDGE6 | EDGE8 | EDGE9, 0, 0, 0}, // 92 {EDGE0 | EDGE2 | EDGE5 | EDGE6 | EDGE9, 0, 0, 0}, // 93 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE5 | EDGE6 | EDGE8, 0, 0, 0}, // 94 {EDGE1 | EDGE2 | EDGE5 | EDGE6, 0, 0, 0}, // 95 {EDGE1 | EDGE2 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 96 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0}, // 97 {EDGE0 | EDGE2 | EDGE9 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 98 {EDGE2 | EDGE3 | EDGE8 | EDGE9 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 99 {EDGE4 | EDGE6 | EDGE8 | EDGE11, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 100 {EDGE0 | EDGE3 | EDGE4 | EDGE6 | EDGE11, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 101 {EDGE0 | EDGE2 | EDGE9 | EDGE10, EDGE4 | EDGE6 | EDGE8 | EDGE11, 0, 0}, // 102 {EDGE2 | EDGE3 | EDGE4 | EDGE6 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 103 {EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE2 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0}, // 104 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE2 | EDGE10, EDGE6 | EDGE7 | EDGE11}, // 105 {EDGE0 | EDGE2 | EDGE4 | EDGE5 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 106 {EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE8 | EDGE10, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 107 {EDGE5 | EDGE6 | EDGE8 | EDGE9 | EDGE11, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 108 {EDGE0 | EDGE3 | EDGE5 | EDGE6 | EDGE9 | EDGE11, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 109 {EDGE0 | EDGE2 | EDGE5 | EDGE6 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 110 {EDGE2 | EDGE3 | EDGE5 | EDGE6 | EDGE10 | EDGE11, 0, 0, 0}, // 111 {EDGE1 | EDGE3 | EDGE6 | EDGE7 | EDGE10, 0, 0, 0}, // 112 {EDGE0 | EDGE1 | EDGE6 | EDGE7 | EDGE8 | EDGE10, 0, 0, 0}, // 113 {EDGE0 | EDGE3 | EDGE6 | EDGE7 | EDGE9 | EDGE10, 0, 0, 0}, // 114 {EDGE6 | EDGE7 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 115 {EDGE1 | EDGE3 | EDGE4 | EDGE6 | EDGE8 | EDGE10, 0, 0, 0}, // 116 {EDGE0 | EDGE1 | EDGE4 | EDGE6 | EDGE10, 0, 0, 0}, // 117 {EDGE0 | EDGE3 | EDGE4 | EDGE6 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 118 {EDGE4 | EDGE6 | EDGE9 | EDGE10, 0, 0, 0}, // 119 {EDGE4 | EDGE5 | EDGE9, EDGE1 | EDGE3 | EDGE6 | EDGE7 | EDGE10, 0, 0}, // 120 {EDGE0 | EDGE1 | EDGE6 | EDGE7 | EDGE8 | EDGE10, EDGE4 | EDGE5 | EDGE9, 0, 0}, // 121 {EDGE0 | EDGE3 | EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE10, 0, 0, 0}, // 122 {EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE8 | EDGE10, 0, 0, 0}, // 123 {EDGE1 | EDGE3 | EDGE5 | EDGE6 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 124 {EDGE0 | EDGE1 | EDGE5 | EDGE6 | EDGE9 | EDGE10, 0, 0, 0}, // 125 {EDGE0 | EDGE3 | EDGE8, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 126 {EDGE5 | EDGE6 | EDGE10, 0, 0, 0}, // 127 {EDGE5 | EDGE6 | EDGE10, 0, 0, 0}, // 128 {EDGE0 | EDGE3 | EDGE8, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 129 {EDGE0 | EDGE1 | EDGE9, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 130 {EDGE1 | EDGE3 | EDGE8 | EDGE9, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 131 {EDGE4 | EDGE7 | EDGE8, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 132 {EDGE0 | EDGE3 | EDGE4 | EDGE7, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 133 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE7 | EDGE8, EDGE5 | EDGE6 | EDGE10, 0}, // 134 {EDGE1 | EDGE3 | EDGE4 | EDGE7 | EDGE9, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 135 {EDGE4 | EDGE6 | EDGE9 | EDGE10, 0, 0, 0}, // 136 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE6 | EDGE9 | EDGE10, 0, 0}, // 137 {EDGE0 | EDGE1 | EDGE4 | EDGE6 | EDGE10, 0, 0, 0}, // 138 {EDGE1 | EDGE3 | EDGE4 | EDGE6 | EDGE8 | EDGE10, 0, 0, 0}, // 139 {EDGE6 | EDGE7 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 140 {EDGE0 | EDGE3 | EDGE6 | EDGE7 | EDGE9 | EDGE10, 0, 0, 0}, // 141 {EDGE0 | EDGE1 | EDGE6 | EDGE7 | EDGE8 | EDGE10, 0, 0, 0}, // 142 {EDGE1 | EDGE3 | EDGE6 | EDGE7 | EDGE10, 0, 0, 0}, // 143 {EDGE2 | EDGE3 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 144 {EDGE0 | EDGE2 | EDGE8 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 145 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0}, // 146 {EDGE1 | EDGE2 | EDGE8 | EDGE9 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 147 {EDGE4 | EDGE7 | EDGE8, EDGE2 | EDGE3 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0}, // 148 {EDGE0 | EDGE2 | EDGE4 | EDGE7 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 149 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE7 | EDGE8, EDGE2 | EDGE3 | EDGE11, EDGE5 | EDGE6 | EDGE10}, // 150 {EDGE1 | EDGE2 | EDGE4 | EDGE7 | EDGE9 | EDGE11, EDGE5 | EDGE6 | EDGE10, 0, 0}, // 151 {EDGE4 | EDGE6 | EDGE9 | EDGE10, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 152 {EDGE0 | EDGE2 | EDGE8 | EDGE11, EDGE4 | EDGE6 | EDGE9 | EDGE10, 0, 0}, // 153 {EDGE0 | EDGE1 | EDGE4 | EDGE6 | EDGE10, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 154 {EDGE1 | EDGE2 | EDGE4 | EDGE6 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 155 {EDGE6 | EDGE7 | EDGE8 | EDGE9 | EDGE10, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 156 {EDGE0 | EDGE2 | EDGE6 | EDGE7 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 157 {EDGE0 | EDGE1 | EDGE6 | EDGE7 | EDGE8 | EDGE10, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 158 {EDGE1 | EDGE2 | EDGE6 | EDGE7 | EDGE10 | EDGE11, 0, 0, 0}, // 159 {EDGE1 | EDGE2 | EDGE5 | EDGE6, 0, 0, 0}, // 160 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE5 | EDGE6, 0, 0}, // 161 {EDGE0 | EDGE2 | EDGE5 | EDGE6 | EDGE9, 0, 0, 0}, // 162 {EDGE2 | EDGE3 | EDGE5 | EDGE6 | EDGE8 | EDGE9, 0, 0, 0}, // 163 {EDGE4 | EDGE7 | EDGE8, EDGE1 | EDGE2 | EDGE5 | EDGE6, 0, 0}, // 164 {EDGE0 | EDGE3 | EDGE4 | EDGE7, EDGE1 | EDGE2 | EDGE5 | EDGE6, 0, 0}, // 165 {EDGE0 | EDGE2 | EDGE5 | EDGE6 | EDGE9, EDGE4 | EDGE7 | EDGE8, 0, 0}, // 166 {EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE9, 0, 0, 0}, // 167 {EDGE1 | EDGE2 | EDGE4 | EDGE6 | EDGE9, 0, 0, 0}, // 168 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE4 | EDGE6 | EDGE9, 0, 0}, // 169 {EDGE0 | EDGE2 | EDGE4 | EDGE6, 0, 0, 0}, // 170 {EDGE2 | EDGE3 | EDGE4 | EDGE6 | EDGE8, 0, 0, 0}, // 171 {EDGE1 | EDGE2 | EDGE6 | EDGE7 | EDGE8 | EDGE9, 0, 0, 0}, // 172 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE6 | EDGE7 | EDGE9, 0, 0, 0}, // 173 {EDGE0 | EDGE2 | EDGE6 | EDGE7 | EDGE8, 0, 0, 0}, // 174 {EDGE2 | EDGE3 | EDGE6 | EDGE7, 0, 0, 0}, // 175 {EDGE1 | EDGE3 | EDGE5 | EDGE6 | EDGE11, 0, 0, 0}, // 176 {EDGE0 | EDGE1 | EDGE5 | EDGE6 | EDGE8 | EDGE11, 0, 0, 0}, // 177 {EDGE0 | EDGE3 | EDGE5 | EDGE6 | EDGE9 | EDGE11, 0, 0, 0}, // 178 {EDGE5 | EDGE6 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 179 {EDGE4 | EDGE7 | EDGE8, EDGE1 | EDGE3 | EDGE5 | EDGE6 | EDGE11, 0, 0}, // 180 {EDGE0 | EDGE1 | EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE11, 0, 0, 0}, // 181 {EDGE0 | EDGE3 | EDGE5 | EDGE6 | EDGE9 | EDGE11, EDGE4 | EDGE7 | EDGE8, 0, 0}, // 182 {EDGE4 | EDGE5 | EDGE6 | EDGE7 | EDGE9 | EDGE11, 0, 0, 0}, // 183 {EDGE1 | EDGE3 | EDGE4 | EDGE6 | EDGE9 | EDGE11, 0, 0, 0}, // 184 {EDGE0 | EDGE1 | EDGE4 | EDGE6 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 185 {EDGE0 | EDGE3 | EDGE4 | EDGE6 | EDGE11, 0, 0, 0}, // 186 {EDGE4 | EDGE6 | EDGE8 | EDGE11, 0, 0, 0}, // 187 {EDGE1 | EDGE3 | EDGE6 | EDGE7 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 188 {EDGE0 | EDGE1 | EDGE9, EDGE6 | EDGE7 | EDGE11, 0, 0}, // 189 {EDGE0 | EDGE3 | EDGE6 | EDGE7 | EDGE8 | EDGE11, 0, 0, 0}, // 190 {EDGE6 | EDGE7 | EDGE11, 0, 0, 0}, // 191 {EDGE5 | EDGE7 | EDGE10 | EDGE11, 0, 0, 0}, // 192 {EDGE0 | EDGE3 | EDGE8, EDGE5 | EDGE7 | EDGE10 | EDGE11, 0, 0}, // 193 {EDGE0 | EDGE1 | EDGE9, EDGE5 | EDGE7 | EDGE10 | EDGE11, 0, 0}, // 194 {EDGE1 | EDGE3 | EDGE8 | EDGE9, EDGE5 | EDGE7 | EDGE10 | EDGE11, 0, 0}, // 195 {EDGE4 | EDGE5 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 196 {EDGE0 | EDGE3 | EDGE4 | EDGE5 | EDGE10 | EDGE11, 0, 0, 0}, // 197 {EDGE0 | EDGE1 | EDGE9, EDGE4 | EDGE5 | EDGE8 | EDGE10 | EDGE11, 0, 0}, // 198 {EDGE1 | EDGE3 | EDGE4 | EDGE5 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 199 {EDGE4 | EDGE7 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 200 {EDGE0 | EDGE3 | EDGE8, EDGE4 | EDGE7 | EDGE9 | EDGE10 | EDGE11, 0, 0}, // 201 {EDGE0 | EDGE1 | EDGE4 | EDGE7 | EDGE10 | EDGE11, 0, 0, 0}, // 202 {EDGE1 | EDGE3 | EDGE4 | EDGE7 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 203 {EDGE8 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 204 {EDGE0 | EDGE3 | EDGE9 | EDGE10 | EDGE11, 0, 0, 0}, // 205 {EDGE0 | EDGE1 | EDGE8 | EDGE10 | EDGE11, 0, 0, 0}, // 206 {EDGE1 | EDGE3 | EDGE10 | EDGE11, 0, 0, 0}, // 207 {EDGE2 | EDGE3 | EDGE5 | EDGE7 | EDGE10, 0, 0, 0}, // 208 {EDGE0 | EDGE2 | EDGE5 | EDGE7 | EDGE8 | EDGE10, 0, 0, 0}, // 209 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE5 | EDGE7 | EDGE10, 0, 0}, // 210 {EDGE1 | EDGE2 | EDGE5 | EDGE7 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 211 {EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE8 | EDGE10, 0, 0, 0}, // 212 {EDGE0 | EDGE2 | EDGE4 | EDGE5 | EDGE10, 0, 0, 0}, // 213 {EDGE0 | EDGE1 | EDGE9, EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE8 | EDGE10, 0, 0}, // 214 {EDGE1 | EDGE2 | EDGE4 | EDGE5 | EDGE9 | EDGE10, 0, 0, 0}, // 215 {EDGE2 | EDGE3 | EDGE4 | EDGE7 | EDGE9 | EDGE10, 0, 0, 0}, // 216 {EDGE0 | EDGE2 | EDGE4 | EDGE7 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 217 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE4 | EDGE7 | EDGE10, 0, 0, 0}, // 218 {EDGE4 | EDGE7 | EDGE8, EDGE1 | EDGE2 | EDGE10, 0, 0}, // 219 {EDGE2 | EDGE3 | EDGE8 | EDGE9 | EDGE10, 0, 0, 0}, // 220 {EDGE0 | EDGE2 | EDGE9 | EDGE10, 0, 0, 0}, // 221 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE8 | EDGE10, 0, 0, 0}, // 222 {EDGE1 | EDGE2 | EDGE10, 0, 0, 0}, // 223 {EDGE1 | EDGE2 | EDGE5 | EDGE7 | EDGE11, 0, 0, 0}, // 224 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE5 | EDGE7 | EDGE11, 0, 0}, // 225 {EDGE0 | EDGE2 | EDGE5 | EDGE7 | EDGE9 | EDGE11, 0, 0, 0}, // 226 {EDGE2 | EDGE3 | EDGE5 | EDGE7 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 227 {EDGE1 | EDGE2 | EDGE4 | EDGE5 | EDGE8 | EDGE11, 0, 0, 0}, // 228 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE4 | EDGE5 | EDGE11, 0, 0, 0}, // 229 {EDGE0 | EDGE2 | EDGE4 | EDGE5 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 230 {EDGE4 | EDGE5 | EDGE9, EDGE2 | EDGE3 | EDGE11, 0, 0}, // 231 {EDGE1 | EDGE2 | EDGE4 | EDGE7 | EDGE9 | EDGE11, 0, 0, 0}, // 232 {EDGE0 | EDGE3 | EDGE8, EDGE1 | EDGE2 | EDGE4 | EDGE7 | EDGE9 | EDGE11, 0, 0}, // 233 {EDGE0 | EDGE2 | EDGE4 | EDGE7 | EDGE11, 0, 0, 0}, // 234 {EDGE2 | EDGE3 | EDGE4 | EDGE7 | EDGE8 | EDGE11, 0, 0, 0}, // 235 {EDGE1 | EDGE2 | EDGE8 | EDGE9 | EDGE11, 0, 0, 0}, // 236 {EDGE0 | EDGE1 | EDGE2 | EDGE3 | EDGE9 | EDGE11, 0, 0, 0}, // 237 {EDGE0 | EDGE2 | EDGE8 | EDGE11, 0, 0, 0}, // 238 {EDGE2 | EDGE3 | EDGE11, 0, 0, 0}, // 239 {EDGE1 | EDGE3 | EDGE5 | EDGE7, 0, 0, 0}, // 240 {EDGE0 | EDGE1 | EDGE5 | EDGE7 | EDGE8, 0, 0, 0}, // 241 {EDGE0 | EDGE3 | EDGE5 | EDGE7 | EDGE9, 0, 0, 0}, // 242 {EDGE5 | EDGE7 | EDGE8 | EDGE9, 0, 0, 0}, // 243 {EDGE1 | EDGE3 | EDGE4 | EDGE5 | EDGE8, 0, 0, 0}, // 244 {EDGE0 | EDGE1 | EDGE4 | EDGE5, 0, 0, 0}, // 245 {EDGE0 | EDGE3 | EDGE4 | EDGE5 | EDGE8 | EDGE9, 0, 0, 0}, // 246 {EDGE4 | EDGE5 | EDGE9, 0, 0, 0}, // 247 {EDGE1 | EDGE3 | EDGE4 | EDGE7 | EDGE9, 0, 0, 0}, // 248 {EDGE0 | EDGE1 | EDGE4 | EDGE7 | EDGE8 | EDGE9, 0, 0, 0}, // 249 {EDGE0 | EDGE3 | EDGE4 | EDGE7, 0, 0, 0}, // 250 {EDGE4 | EDGE7 | EDGE8, 0, 0, 0}, // 251 {EDGE1 | EDGE3 | EDGE8 | EDGE9, 0, 0, 0}, // 252 {EDGE0 | EDGE1 | EDGE9, 0, 0, 0}, // 253 {EDGE0 | EDGE3 | EDGE8, 0, 0, 0}, // 254 {0, 0, 0, 0} // 255 }; /// Encodes the ambiguous face of cube configurations, which /// can cause non-manifold meshes. /// Non-problematic configurations have a value of 255. /// The first bit of each value actually encodes a positive or negative /// direction while the second and third bit enumerate the axis. template uint8_t const DualMC::problematicConfigs[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,1,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,3,255,255,2,255, 255,255,255,255,255,255,255,5,255,255,255,255,255,255,5,5, 255,255,255,255,255,255,4,255,255,255,3,3,1,1,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,5,255,5,255,5, 255,255,255,255,255,255,255,3,255,255,255,255,255,2,255,255, 255,255,255,255,255,3,255,3,255,4,255,255,0,255,0,255, 255,255,255,255,255,255,255,1,255,255,255,0,255,255,255,255, 255,255,255,1,255,255,255,1,255,4,2,255,255,255,2,255, 255,255,255,0,255,2,4,255,255,255,255,0,255,2,255,255, 255,255,255,255,255,255,4,255,255,4,255,255,255,255,255,255 }; } // END: namespace dualmc #endif // DUALMC_H_INCLUDED