You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1031 lines
35 KiB
1031 lines
35 KiB
// David Eberly, Geometric Tools, Redmond WA 98052
|
|
// Copyright (c) 1998-2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
|
|
// Version: 4.0.2019.08.13
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/SurfaceExtractor.h>
|
|
|
|
// The level set extraction algorithm implemented here is described
|
|
// in Section 3.2 of the document
|
|
// https://www.geometrictools.com/Documentation/LevelSetExtraction.pdf
|
|
|
|
namespace gte
|
|
{
|
|
// The image type T must be one of the integer types: int8_t, int16_t,
|
|
// int32_t, uint8_t, uint16_t or uint32_t. Internal integer computations
|
|
// are performed using int64_t. The type Real is for extraction to
|
|
// floating-point vertices.
|
|
template <typename T, typename Real>
|
|
class SurfaceExtractorCubes : public SurfaceExtractor<T, Real>
|
|
{
|
|
public:
|
|
// Convenience type definitions.
|
|
typedef typename SurfaceExtractor<T, Real>::Vertex Vertex;
|
|
typedef typename SurfaceExtractor<T, Real>::Triangle Triangle;
|
|
|
|
// The input is a 3D image with lexicographically ordered voxels
|
|
// (x,y,z) stored in a linear array. Voxel (x,y,z) is stored in the
|
|
// array at location index = x + xBound * (y + yBound * z). The
|
|
// inputs xBound, yBound and zBound must each be 2 or larger so that
|
|
// there is at least one image cube to process. The inputVoxels must
|
|
// be nonnull and point to contiguous storage that contains at least
|
|
// xBound * yBound * zBound elements.
|
|
SurfaceExtractorCubes(int xBound, int yBound, int zBound, T const* inputVoxels)
|
|
:
|
|
SurfaceExtractor<T, Real>(xBound, yBound, zBound, inputVoxels)
|
|
{
|
|
}
|
|
|
|
// Extract level surfaces and return rational vertices. Use the
|
|
// base-class Extract if you want real-valued vertices.
|
|
virtual void Extract(T level, std::vector<Vertex>& vertices,
|
|
std::vector<Triangle>& triangles) override
|
|
{
|
|
// Adjust the image so that the level set is F(x,y,z) = 0. The
|
|
// precondition for 'level' is that it is not exactly a voxel
|
|
// value. However, T is an integer type, so we cannot pass in
|
|
// a 'level' that has a fractional value. To circumvent this,
|
|
// the voxel values are doubled so that they are even integers.
|
|
// The level value is doubled and 1 added to obtain an odd
|
|
// integer, guaranteeing 'level' is not a voxel value.
|
|
int64_t levelI64 = 2 * static_cast<int64_t>(level) + 1;
|
|
for (size_t i = 0; i < this->mVoxels.size(); ++i)
|
|
{
|
|
int64_t inputI64 = 2 * static_cast<int64_t>(this->mInputVoxels[i]);
|
|
this->mVoxels[i] = inputI64 - levelI64;
|
|
}
|
|
|
|
vertices.clear();
|
|
triangles.clear();
|
|
for (int z = 0; z < this->mZBound - 1; ++z)
|
|
{
|
|
for (int y = 0; y < this->mYBound - 1; ++y)
|
|
{
|
|
for (int x = 0; x < this->mXBound - 1; ++x)
|
|
{
|
|
// Get vertices on edges of box (if any).
|
|
VETable table;
|
|
int type = GetVertices(x, y, z, table);
|
|
if (type != 0)
|
|
{
|
|
// Get edges on faces of box.
|
|
GetXMinEdges(x, y, z, type, table);
|
|
GetXMaxEdges(x, y, z, type, table);
|
|
GetYMinEdges(x, y, z, type, table);
|
|
GetYMaxEdges(x, y, z, type, table);
|
|
GetZMinEdges(x, y, z, type, table);
|
|
GetZMaxEdges(x, y, z, type, table);
|
|
|
|
// Ear-clip the wireframe mesh.
|
|
table.RemoveTriangles(vertices, triangles);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
enum
|
|
{
|
|
EI_XMIN_YMIN = 0,
|
|
EI_XMIN_YMAX = 1,
|
|
EI_XMAX_YMIN = 2,
|
|
EI_XMAX_YMAX = 3,
|
|
EI_XMIN_ZMIN = 4,
|
|
EI_XMIN_ZMAX = 5,
|
|
EI_XMAX_ZMIN = 6,
|
|
EI_XMAX_ZMAX = 7,
|
|
EI_YMIN_ZMIN = 8,
|
|
EI_YMIN_ZMAX = 9,
|
|
EI_YMAX_ZMIN = 10,
|
|
EI_YMAX_ZMAX = 11,
|
|
FI_XMIN = 12,
|
|
FI_XMAX = 13,
|
|
FI_YMIN = 14,
|
|
FI_YMAX = 15,
|
|
FI_ZMIN = 16,
|
|
FI_ZMAX = 17,
|
|
|
|
EB_XMIN_YMIN = 1 << EI_XMIN_YMIN,
|
|
EB_XMIN_YMAX = 1 << EI_XMIN_YMAX,
|
|
EB_XMAX_YMIN = 1 << EI_XMAX_YMIN,
|
|
EB_XMAX_YMAX = 1 << EI_XMAX_YMAX,
|
|
EB_XMIN_ZMIN = 1 << EI_XMIN_ZMIN,
|
|
EB_XMIN_ZMAX = 1 << EI_XMIN_ZMAX,
|
|
EB_XMAX_ZMIN = 1 << EI_XMAX_ZMIN,
|
|
EB_XMAX_ZMAX = 1 << EI_XMAX_ZMAX,
|
|
EB_YMIN_ZMIN = 1 << EI_YMIN_ZMIN,
|
|
EB_YMIN_ZMAX = 1 << EI_YMIN_ZMAX,
|
|
EB_YMAX_ZMIN = 1 << EI_YMAX_ZMIN,
|
|
EB_YMAX_ZMAX = 1 << EI_YMAX_ZMAX,
|
|
FB_XMIN = 1 << FI_XMIN,
|
|
FB_XMAX = 1 << FI_XMAX,
|
|
FB_YMIN = 1 << FI_YMIN,
|
|
FB_YMAX = 1 << FI_YMAX,
|
|
FB_ZMIN = 1 << FI_ZMIN,
|
|
FB_ZMAX = 1 << FI_ZMAX
|
|
};
|
|
|
|
// Vertex-edge-triangle table to support mesh topology.
|
|
class VETable
|
|
{
|
|
public:
|
|
VETable() = default;
|
|
|
|
inline bool IsValidVertex(int i) const
|
|
{
|
|
return mVertex[i].valid;
|
|
}
|
|
|
|
inline int64_t GetXN(int i) const
|
|
{
|
|
return mVertex[i].pos.xNumer;
|
|
}
|
|
|
|
inline int64_t GetXD(int i) const
|
|
{
|
|
return mVertex[i].pos.xDenom;
|
|
}
|
|
|
|
inline int64_t GetYN(int i) const
|
|
{
|
|
return mVertex[i].pos.yNumer;
|
|
}
|
|
|
|
inline int64_t GetYD(int i) const
|
|
{
|
|
return mVertex[i].pos.yDenom;
|
|
}
|
|
|
|
inline int64_t GetZN(int i) const
|
|
{
|
|
return mVertex[i].pos.zNumer;
|
|
}
|
|
|
|
inline int64_t GetZD(int i) const
|
|
{
|
|
return mVertex[i].pos.zDenom;
|
|
}
|
|
|
|
void Insert(int i, Vertex const& pos)
|
|
{
|
|
TVertex& vertex = mVertex[i];
|
|
vertex.pos = pos;
|
|
vertex.valid = true;
|
|
}
|
|
|
|
void Insert(int i0, int i1)
|
|
{
|
|
TVertex& vertex0 = mVertex[i0];
|
|
TVertex& vertex1 = mVertex[i1];
|
|
vertex0.adj[vertex0.numAdjacents++] = i1;
|
|
vertex1.adj[vertex1.numAdjacents++] = i0;
|
|
}
|
|
|
|
void RemoveTriangles(std::vector<Vertex>& vertices, std::vector<Triangle>& triangles)
|
|
{
|
|
// Ear-clip the wireframe to get the triangles.
|
|
Triangle triangle;
|
|
while (Remove(triangle))
|
|
{
|
|
int v0 = static_cast<int>(vertices.size());
|
|
int v1 = v0 + 1;
|
|
int v2 = v1 + 1;
|
|
triangles.push_back(Triangle(v0, v1, v2));
|
|
vertices.push_back(mVertex[triangle.v[0]].pos);
|
|
vertices.push_back(mVertex[triangle.v[1]].pos);
|
|
vertices.push_back(mVertex[triangle.v[2]].pos);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void RemoveVertex(int i)
|
|
{
|
|
TVertex& vertex0 = mVertex[i];
|
|
// assert: vertex0.numAdjacents == 2
|
|
int a0 = vertex0.adj[0];
|
|
int a1 = vertex0.adj[1];
|
|
TVertex& adjVertex0 = mVertex[a0];
|
|
TVertex& adjVertex1 = mVertex[a1];
|
|
|
|
int j;
|
|
for (j = 0; j < adjVertex0.numAdjacents; ++j)
|
|
{
|
|
if (adjVertex0.adj[j] == i)
|
|
{
|
|
adjVertex0.adj[j] = a1;
|
|
break;
|
|
}
|
|
}
|
|
// assert: j != adjVertex0.numAdjacents
|
|
|
|
for (j = 0; j < adjVertex1.numAdjacents; j++)
|
|
{
|
|
if (adjVertex1.adj[j] == i)
|
|
{
|
|
adjVertex1.adj[j] = a0;
|
|
break;
|
|
}
|
|
}
|
|
// assert: j != adjVertex1.numAdjacents
|
|
|
|
vertex0.valid = false;
|
|
|
|
if (adjVertex0.numAdjacents == 2)
|
|
{
|
|
if (adjVertex0.adj[0] == adjVertex0.adj[1])
|
|
{
|
|
adjVertex0.valid = false;
|
|
}
|
|
}
|
|
|
|
if (adjVertex1.numAdjacents == 2)
|
|
{
|
|
if (adjVertex1.adj[0] == adjVertex1.adj[1])
|
|
{
|
|
adjVertex1.valid = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Remove(Triangle& triangle)
|
|
{
|
|
for (int i = 0; i < 18; ++i)
|
|
{
|
|
TVertex& vertex = mVertex[i];
|
|
if (vertex.valid && vertex.numAdjacents == 2)
|
|
{
|
|
triangle.v[0] = i;
|
|
triangle.v[1] = vertex.adj[0];
|
|
triangle.v[2] = vertex.adj[1];
|
|
RemoveVertex(i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
class TVertex
|
|
{
|
|
public:
|
|
TVertex()
|
|
:
|
|
numAdjacents(0),
|
|
valid(false)
|
|
{
|
|
}
|
|
|
|
Vertex pos;
|
|
int numAdjacents;
|
|
std::array<int, 4> adj;
|
|
bool valid;
|
|
};
|
|
|
|
std::array<TVertex, 18> mVertex;
|
|
};
|
|
|
|
virtual std::array<Real, 3> GetGradient(std::array<Real, 3> const& pos) override
|
|
{
|
|
std::array<Real, 3> const zero{ (Real)0, (Real)0, (Real)0 };
|
|
|
|
int x = static_cast<int>(pos[0]);
|
|
if (x < 0 || x + 1 >= this->mXBound)
|
|
{
|
|
return zero;
|
|
}
|
|
|
|
int y = static_cast<int>(pos[1]);
|
|
if (y < 0 || y + 1 >= this->mYBound)
|
|
{
|
|
return zero;
|
|
}
|
|
|
|
int z = static_cast<int>(pos[2]);
|
|
if (z < 0 || z + 1 >= this->mZBound)
|
|
{
|
|
return zero;
|
|
}
|
|
|
|
// Get image values at corners of voxel.
|
|
int i000 = x + this->mXBound * (y + this->mYBound * z);
|
|
int i100 = i000 + 1;
|
|
int i010 = i000 + this->mXBound;
|
|
int i110 = i010 + 1;
|
|
int i001 = i000 + this->mXYBound;
|
|
int i101 = i001 + 1;
|
|
int i011 = i001 + this->mXBound;
|
|
int i111 = i011 + 1;
|
|
Real f000 = static_cast<Real>(this->mVoxels[i000]);
|
|
Real f100 = static_cast<Real>(this->mVoxels[i100]);
|
|
Real f010 = static_cast<Real>(this->mVoxels[i010]);
|
|
Real f110 = static_cast<Real>(this->mVoxels[i110]);
|
|
Real f001 = static_cast<Real>(this->mVoxels[i001]);
|
|
Real f101 = static_cast<Real>(this->mVoxels[i101]);
|
|
Real f011 = static_cast<Real>(this->mVoxels[i011]);
|
|
Real f111 = static_cast<Real>(this->mVoxels[i111]);
|
|
|
|
Real dx = pos[0] - static_cast<Real>(x);
|
|
Real dy = pos[1] - static_cast<Real>(y);
|
|
Real dz = pos[2] - static_cast<Real>(z);
|
|
Real oneMX = 1.0f - dx;
|
|
Real oneMY = 1.0f - dy;
|
|
Real oneMZ = 1.0f - dz;
|
|
|
|
std::array<Real, 3> grad;
|
|
|
|
Real tmp0 = oneMY * (f100 - f000) + dy * (f110 - f010);
|
|
Real tmp1 = oneMY * (f101 - f001) + dy * (f111 - f011);
|
|
grad[0] = oneMZ * tmp0 + dz * tmp1;
|
|
|
|
tmp0 = oneMX * (f010 - f000) + dx * (f110 - f100);
|
|
tmp1 = oneMX * (f011 - f001) + dx * (f111 - f101);
|
|
grad[1] = oneMZ * tmp0 + dz * tmp1;
|
|
|
|
tmp0 = oneMX * (f001 - f000) + dx * (f101 - f100);
|
|
tmp1 = oneMX * (f011 - f010) + dx * (f111 - f110);
|
|
grad[2] = oneMY * tmp0 + dy * tmp1;
|
|
|
|
return grad;
|
|
}
|
|
|
|
int GetVertices(int x, int y, int z, VETable& table)
|
|
{
|
|
int type = 0;
|
|
|
|
// Get the image values at the corners of the voxel.
|
|
int i000 = x + this->mXBound * (y + this->mYBound * z);
|
|
int i100 = i000 + 1;
|
|
int i010 = i000 + this->mXBound;
|
|
int i110 = i010 + 1;
|
|
int i001 = i000 + this->mXYBound;
|
|
int i101 = i001 + 1;
|
|
int i011 = i001 + this->mXBound;
|
|
int i111 = i011 + 1;
|
|
int64_t f000 = this->mVoxels[i000];
|
|
int64_t f100 = this->mVoxels[i100];
|
|
int64_t f010 = this->mVoxels[i010];
|
|
int64_t f110 = this->mVoxels[i110];
|
|
int64_t f001 = this->mVoxels[i001];
|
|
int64_t f101 = this->mVoxels[i101];
|
|
int64_t f011 = this->mVoxels[i011];
|
|
int64_t f111 = this->mVoxels[i111];
|
|
|
|
int64_t x0 = x, x1 = x + 1, y0 = y, y1 = y + 1, z0 = z, z1 = z + 1;
|
|
int64_t d;
|
|
|
|
// xmin-ymin edge
|
|
if (f000 * f001 < 0)
|
|
{
|
|
type |= EB_XMIN_YMIN;
|
|
d = f001 - f000;
|
|
table.Insert(EI_XMIN_YMIN, Vertex(x0, 1, y0, 1, z0 * d - f000, d));
|
|
}
|
|
|
|
// xmin-ymax edge
|
|
if (f010 * f011 < 0)
|
|
{
|
|
type |= EB_XMIN_YMAX;
|
|
d = f011 - f010;
|
|
table.Insert(EI_XMIN_YMAX, Vertex(x0, 1, y1, 1, z0 * d - f010, d));
|
|
}
|
|
|
|
// xmax-ymin edge
|
|
if (f100 * f101 < 0)
|
|
{
|
|
type |= EB_XMAX_YMIN;
|
|
d = f101 - f100;
|
|
table.Insert(EI_XMAX_YMIN, Vertex(x1, 1, y0, 1, z0 * d - f100, d));
|
|
}
|
|
|
|
// xmax-ymax edge
|
|
if (f110 * f111 < 0)
|
|
{
|
|
type |= EB_XMAX_YMAX;
|
|
d = f111 - f110;
|
|
table.Insert(EI_XMAX_YMAX, Vertex(x1, 1, y1, 1, z0 * d - f110, d));
|
|
}
|
|
|
|
// xmin-zmin edge
|
|
if (f000 * f010 < 0)
|
|
{
|
|
type |= EB_XMIN_ZMIN;
|
|
d = f010 - f000;
|
|
table.Insert(EI_XMIN_ZMIN, Vertex(x0, 1, y0 * d - f000, d, z0, 1));
|
|
}
|
|
|
|
// xmin-zmax edge
|
|
if (f001 * f011 < 0)
|
|
{
|
|
type |= EB_XMIN_ZMAX;
|
|
d = f011 - f001;
|
|
table.Insert(EI_XMIN_ZMAX, Vertex(x0, 1, y0 * d - f001, d, z1, 1));
|
|
}
|
|
|
|
// xmax-zmin edge
|
|
if (f100 * f110 < 0)
|
|
{
|
|
type |= EB_XMAX_ZMIN;
|
|
d = f110 - f100;
|
|
table.Insert(EI_XMAX_ZMIN, Vertex(x1, 1, y0 * d - f100, d, z0, 1));
|
|
}
|
|
|
|
// xmax-zmax edge
|
|
if (f101 * f111 < 0)
|
|
{
|
|
type |= EB_XMAX_ZMAX;
|
|
d = f111 - f101;
|
|
table.Insert(EI_XMAX_ZMAX, Vertex(x1, 1, y0 * d - f101, d, z1, 1));
|
|
}
|
|
|
|
// ymin-zmin edge
|
|
if (f000 * f100 < 0)
|
|
{
|
|
type |= EB_YMIN_ZMIN;
|
|
d = f100 - f000;
|
|
table.Insert(EI_YMIN_ZMIN, Vertex(x0 * d - f000, d, y0, 1, z0, 1));
|
|
}
|
|
|
|
// ymin-zmax edge
|
|
if (f001 * f101 < 0)
|
|
{
|
|
type |= EB_YMIN_ZMAX;
|
|
d = f101 - f001;
|
|
table.Insert(EI_YMIN_ZMAX, Vertex(x0 * d - f001, d, y0, 1, z1, 1));
|
|
}
|
|
|
|
// ymax-zmin edge
|
|
if (f010 * f110 < 0)
|
|
{
|
|
type |= EB_YMAX_ZMIN;
|
|
d = f110 - f010;
|
|
table.Insert(EI_YMAX_ZMIN, Vertex(x0 * d - f010, d, y1, 1, z0, 1));
|
|
}
|
|
|
|
// ymax-zmax edge
|
|
if (f011 * f111 < 0)
|
|
{
|
|
type |= EB_YMAX_ZMAX;
|
|
d = f111 - f011;
|
|
table.Insert(EI_YMAX_ZMAX, Vertex(x0 * d - f011, d, y1, 1, z1, 1));
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
void GetXMinEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMIN_YMIN)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMIN_YMAX)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_XMIN_ZMIN)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_XMIN_ZMAX)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMIN_YMIN, EI_XMIN_YMAX);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMIN_YMIN, EI_XMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMIN_YMAX, EI_XMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMIN_YMIN, EI_XMIN_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMIN_YMAX, EI_XMIN_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_XMIN_ZMIN, EI_XMIN_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = x + this->mXBound * (y + this->mYBound * z);
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
i += this->mXBound;
|
|
// F(x,y+1,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXYBound;
|
|
// F(x,y+1,z+1)
|
|
int64_t f11 = this->mVoxels[i];
|
|
i -= this->mXBound;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMIN_YMIN, EI_XMIN_ZMIN);
|
|
table.Insert(EI_XMIN_YMAX, EI_XMIN_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMIN_YMIN, EI_XMIN_ZMAX);
|
|
table.Insert(EI_XMIN_YMAX, EI_XMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_XMIN, Vertex(
|
|
table.GetXN(EI_XMIN_ZMIN), table.GetXD(EI_XMIN_ZMIN),
|
|
table.GetYN(EI_XMIN_ZMIN), table.GetYD(EI_XMIN_ZMIN),
|
|
table.GetZN(EI_XMIN_YMIN), table.GetZD(EI_XMIN_YMIN)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMIN_YMIN, FI_XMIN);
|
|
table.Insert(EI_XMIN_YMAX, FI_XMIN);
|
|
table.Insert(EI_XMIN_ZMIN, FI_XMIN);
|
|
table.Insert(EI_XMIN_ZMAX, FI_XMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
|
|
void GetXMaxEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMAX_YMIN)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMAX_YMAX)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_XMAX_ZMIN)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_XMAX_ZMAX)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMAX_YMIN, EI_XMAX_YMAX);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMAX_YMIN, EI_XMAX_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMAX_YMAX, EI_XMAX_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMAX_YMIN, EI_XMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMAX_YMAX, EI_XMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_XMAX_ZMIN, EI_XMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = (x + 1) + this->mXBound * (y + this->mYBound * z);
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
i += this->mXBound;
|
|
// F(x,y+1,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXYBound;
|
|
// F(x,y+1,z+1)
|
|
int64_t f11 = this->mVoxels[i];
|
|
i -= this->mXBound;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMAX_YMIN, EI_XMAX_ZMIN);
|
|
table.Insert(EI_XMAX_YMAX, EI_XMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMAX_YMIN, EI_XMAX_ZMAX);
|
|
table.Insert(EI_XMAX_YMAX, EI_XMAX_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_XMAX, Vertex(
|
|
table.GetXN(EI_XMAX_ZMIN), table.GetXD(EI_XMAX_ZMIN),
|
|
table.GetYN(EI_XMAX_ZMIN), table.GetYD(EI_XMAX_ZMIN),
|
|
table.GetZN(EI_XMAX_YMIN), table.GetZD(EI_XMAX_YMIN)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMAX_YMIN, FI_XMAX);
|
|
table.Insert(EI_XMAX_YMAX, FI_XMAX);
|
|
table.Insert(EI_XMAX_ZMIN, FI_XMAX);
|
|
table.Insert(EI_XMAX_ZMAX, FI_XMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
|
|
void GetYMinEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMIN_YMIN)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMAX_YMIN)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_YMIN_ZMIN)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_YMIN_ZMAX)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMIN_YMIN, EI_XMAX_YMIN);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMIN_YMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMAX_YMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMIN_YMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMAX_YMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_YMIN_ZMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = x + this->mXBound * (y + this->mYBound * z);
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXYBound;
|
|
// F(x+1,y,z+1)
|
|
int64_t f11 = this->mVoxels[i];
|
|
--i;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMIN_YMIN, EI_YMIN_ZMIN);
|
|
table.Insert(EI_XMAX_YMIN, EI_YMIN_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMIN_YMIN, EI_YMIN_ZMAX);
|
|
table.Insert(EI_XMAX_YMIN, EI_YMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_YMIN, Vertex(
|
|
table.GetXN(EI_YMIN_ZMIN), table.GetXD(EI_YMIN_ZMIN),
|
|
table.GetYN(EI_XMIN_YMIN), table.GetYD(EI_XMIN_YMIN),
|
|
table.GetZN(EI_XMIN_YMIN), table.GetZD(EI_XMIN_YMIN)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMIN_YMIN, FI_YMIN);
|
|
table.Insert(EI_XMAX_YMIN, FI_YMIN);
|
|
table.Insert(EI_YMIN_ZMIN, FI_YMIN);
|
|
table.Insert(EI_YMIN_ZMAX, FI_YMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
|
|
void GetYMaxEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMIN_YMAX)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMAX_YMAX)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_YMAX_ZMIN)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_YMAX_ZMAX)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMIN_YMAX, EI_XMAX_YMAX);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMIN_YMAX, EI_YMAX_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMAX_YMAX, EI_YMAX_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMIN_YMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMAX_YMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_YMAX_ZMIN, EI_YMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = x + this->mXBound * ((y + 1) + this->mYBound * z);
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXYBound;
|
|
// F(x+1,y,z+1)
|
|
int64_t f11 = this->mVoxels[i];
|
|
--i;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMIN_YMAX, EI_YMAX_ZMIN);
|
|
table.Insert(EI_XMAX_YMAX, EI_YMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMIN_YMAX, EI_YMAX_ZMAX);
|
|
table.Insert(EI_XMAX_YMAX, EI_YMAX_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_YMAX, Vertex(
|
|
table.GetXN(EI_YMAX_ZMIN), table.GetXD(EI_YMAX_ZMIN),
|
|
table.GetYN(EI_XMIN_YMAX), table.GetYD(EI_XMIN_YMAX),
|
|
table.GetZN(EI_XMIN_YMAX), table.GetZD(EI_XMIN_YMAX)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMIN_YMAX, FI_YMAX);
|
|
table.Insert(EI_XMAX_YMAX, FI_YMAX);
|
|
table.Insert(EI_YMAX_ZMIN, FI_YMAX);
|
|
table.Insert(EI_YMAX_ZMAX, FI_YMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
|
|
void GetZMinEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMIN_ZMIN)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMAX_ZMIN)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_YMIN_ZMIN)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_YMAX_ZMIN)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMIN_ZMIN, EI_XMAX_ZMIN);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMIN_ZMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMAX_ZMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMIN_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMAX_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_YMIN_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = x + this->mXBound * (y + this->mYBound * z);
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXBound;
|
|
// F(x+1,y+1,z)
|
|
int64_t f11 = this->mVoxels[i];
|
|
--i;
|
|
// F(x,y+1,z)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMIN_ZMIN, EI_YMIN_ZMIN);
|
|
table.Insert(EI_XMAX_ZMIN, EI_YMAX_ZMIN);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMIN_ZMIN, EI_YMAX_ZMIN);
|
|
table.Insert(EI_XMAX_ZMIN, EI_YMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_ZMIN, Vertex(
|
|
table.GetXN(EI_YMIN_ZMIN), table.GetXD(EI_YMIN_ZMIN),
|
|
table.GetYN(EI_XMIN_ZMIN), table.GetYD(EI_XMIN_ZMIN),
|
|
table.GetZN(EI_XMIN_ZMIN), table.GetZD(EI_XMIN_ZMIN)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMIN_ZMIN, FI_ZMIN);
|
|
table.Insert(EI_XMAX_ZMIN, FI_ZMIN);
|
|
table.Insert(EI_YMIN_ZMIN, FI_ZMIN);
|
|
table.Insert(EI_YMAX_ZMIN, FI_ZMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
|
|
void GetZMaxEdges(int x, int y, int z, int type, VETable& table)
|
|
{
|
|
int faceType = 0;
|
|
if (type & EB_XMIN_ZMAX)
|
|
{
|
|
faceType |= 0x01;
|
|
}
|
|
if (type & EB_XMAX_ZMAX)
|
|
{
|
|
faceType |= 0x02;
|
|
}
|
|
if (type & EB_YMIN_ZMAX)
|
|
{
|
|
faceType |= 0x04;
|
|
}
|
|
if (type & EB_YMAX_ZMAX)
|
|
{
|
|
faceType |= 0x08;
|
|
}
|
|
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
table.Insert(EI_XMIN_ZMAX, EI_XMAX_ZMAX);
|
|
break;
|
|
case 5:
|
|
table.Insert(EI_XMIN_ZMAX, EI_YMIN_ZMAX);
|
|
break;
|
|
case 6:
|
|
table.Insert(EI_XMAX_ZMAX, EI_YMIN_ZMAX);
|
|
break;
|
|
case 9:
|
|
table.Insert(EI_XMIN_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.Insert(EI_XMAX_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.Insert(EI_YMIN_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = x + this->mXBound * (y + this->mYBound * (z + 1));
|
|
// F(x,y,z)
|
|
int64_t f00 = this->mVoxels[i];
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = this->mVoxels[i];
|
|
i += this->mXBound;
|
|
// F(x+1,y+1,z)
|
|
int64_t f11 = this->mVoxels[i];
|
|
--i;
|
|
// F(x,y+1,z)
|
|
int64_t f01 = this->mVoxels[i];
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.Insert(EI_XMIN_ZMAX, EI_YMIN_ZMAX);
|
|
table.Insert(EI_XMAX_ZMAX, EI_YMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.Insert(EI_XMIN_ZMAX, EI_YMAX_ZMAX);
|
|
table.Insert(EI_XMAX_ZMAX, EI_YMIN_ZMAX);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to tessellation.
|
|
table.Insert(FI_ZMAX, Vertex(
|
|
table.GetXN(EI_YMIN_ZMAX), table.GetXD(EI_YMIN_ZMAX),
|
|
table.GetYN(EI_XMIN_ZMAX), table.GetYD(EI_XMIN_ZMAX),
|
|
table.GetZN(EI_XMIN_ZMAX), table.GetZD(EI_XMIN_ZMAX)));
|
|
|
|
// Add edges sharing the branch point.
|
|
table.Insert(EI_XMIN_ZMAX, FI_ZMAX);
|
|
table.Insert(EI_XMAX_ZMAX, FI_ZMAX);
|
|
table.Insert(EI_YMIN_ZMAX, FI_ZMAX);
|
|
table.Insert(EI_YMAX_ZMAX, FI_ZMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("Unexpected condition.");
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|