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.
2892 lines
97 KiB
2892 lines
97 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.2020.09.14
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/Logger.h>
|
|
#include <Mathematics/Array2.h>
|
|
#include <Mathematics/Math.h>
|
|
#include <Mathematics/TriangleKey.h>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <ostream>
|
|
|
|
// Extract level surfaces using an adaptive approach to reduce the triangle
|
|
// count. The implementation is for the algorithm described in the paper
|
|
// Multiresolution Isosurface Extraction with Adaptive Skeleton Climbing
|
|
// Tim Poston, Tien-Tsin Wong and Pheng-Ann Heng
|
|
// Computer Graphics forum, volume 17, issue 3, September 1998
|
|
// pages 137-147
|
|
// https://onlinelibrary.wiley.com/doi/abs/10.1111/1467-8659.00261
|
|
|
|
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 AdaptiveSkeletonClimbing3
|
|
{
|
|
public:
|
|
// Construction and destruction. The input image is assumed to
|
|
// contain (2^N+1)-by-(2^N+1)-by-(2^N+1) elements where N >= 0.
|
|
// The organization is lexicographic order for (x,y,z). When
|
|
// 'fixBoundary' is set to 'true', image boundary voxels are not
|
|
// allowed to merge with any other voxels. This forces highest
|
|
// level of detail on the boundary. The idea is that an image too
|
|
// large to process by itself can be partitioned into smaller
|
|
// subimages and the adaptive skeleton climbing applied to each
|
|
// subimage. By forcing highest resolution on the boundary,
|
|
// adjacent subimages will not have any cracking problems.
|
|
AdaptiveSkeletonClimbing3(int N, T const* inputVoxels,
|
|
bool fixBoundary = false)
|
|
:
|
|
mTwoPowerN(1 << N),
|
|
mSize(mTwoPowerN + 1),
|
|
mSizeSqr(mSize * mSize),
|
|
mInputVoxels(inputVoxels),
|
|
mLevel((Real)0),
|
|
mFixBoundary(fixBoundary),
|
|
mXMerge(mSize, mSize),
|
|
mYMerge(mSize, mSize),
|
|
mZMerge(mSize, mSize)
|
|
{
|
|
static_assert(std::is_integral<T>::value && sizeof(T) <= 4,
|
|
"Type T must be int{8,16,32}_t or uint{8,16,32}_t.");
|
|
if (N <= 0 || mInputVoxels == nullptr)
|
|
{
|
|
LogError("Invalid input.");
|
|
}
|
|
|
|
for (int i = 0; i < mSize; ++i)
|
|
{
|
|
for (int j = 0; j < mSize; ++j)
|
|
{
|
|
mXMerge[i][j] = std::make_shared<LinearMergeTree>(N);
|
|
mYMerge[i][j] = std::make_shared<LinearMergeTree>(N);
|
|
mZMerge[i][j] = std::make_shared<LinearMergeTree>(N);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Refactor this class to have base class SurfaceExtractor.
|
|
typedef std::array<Real, 3> Vertex;
|
|
typedef std::array<int, 2> Edge;
|
|
typedef TriangleKey<true> Triangle;
|
|
|
|
void Extract(Real level, int depth,
|
|
std::vector<Vertex>& vertices, std::vector<Triangle>& triangles)
|
|
{
|
|
std::vector<Vertex> localVertices;
|
|
std::vector<Triangle> localTriangles;
|
|
mBoxes.clear();
|
|
|
|
mLevel = level;
|
|
Merge(depth);
|
|
Tessellate(localVertices, localTriangles);
|
|
|
|
vertices = std::move(localVertices);
|
|
triangles = std::move(localTriangles);
|
|
}
|
|
|
|
void MakeUnique(std::vector<Vertex>& vertices,
|
|
std::vector<Triangle>& triangles)
|
|
{
|
|
size_t numVertices = vertices.size();
|
|
size_t numTriangles = triangles.size();
|
|
if (numVertices == 0 || numTriangles == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Compute the map of unique vertices and assign to them new and
|
|
// unique indices.
|
|
std::map<Vertex, int> vmap;
|
|
int nextVertex = 0;
|
|
for (size_t v = 0; v < numVertices; ++v)
|
|
{
|
|
// Keep only unique vertices.
|
|
auto result = vmap.insert(std::make_pair(vertices[v], nextVertex));
|
|
if (result.second)
|
|
{
|
|
++nextVertex;
|
|
}
|
|
}
|
|
|
|
// Compute the map of unique triangles and assign to them new and
|
|
// unique indices.
|
|
std::map<Triangle, int> tmap;
|
|
int nextTriangle = 0;
|
|
for (size_t t = 0; t < numTriangles; ++t)
|
|
{
|
|
Triangle& triangle = triangles[t];
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
auto iter = vmap.find(vertices[triangle.V[i]]);
|
|
LogAssert(iter != vmap.end(), "Expecting the vertex to be in the vmap.");
|
|
triangle.V[i] = iter->second;
|
|
}
|
|
|
|
// Keep only unique triangles.
|
|
auto result = tmap.insert(std::make_pair(triangle, nextTriangle));
|
|
if (result.second)
|
|
{
|
|
++nextTriangle;
|
|
}
|
|
}
|
|
|
|
// Pack the vertices into an array.
|
|
vertices.resize(vmap.size());
|
|
for (auto const& element : vmap)
|
|
{
|
|
vertices[element.second] = element.first;
|
|
}
|
|
|
|
// Pack the triangles into an array.
|
|
triangles.resize(tmap.size());
|
|
for (auto const& element : tmap)
|
|
{
|
|
triangles[element.second] = element.first;
|
|
}
|
|
}
|
|
|
|
void OrientTriangles(std::vector<Vertex>& vertices,
|
|
std::vector<Triangle>& triangles, bool sameDir)
|
|
{
|
|
for (auto& triangle : triangles)
|
|
{
|
|
// Get the triangle vertices.
|
|
std::array<Real, 3> v0 = vertices[triangle.V[0]];
|
|
std::array<Real, 3> v1 = vertices[triangle.V[1]];
|
|
std::array<Real, 3> v2 = vertices[triangle.V[2]];
|
|
|
|
// Construct the triangle normal based on the current
|
|
// orientation.
|
|
std::array<Real, 3> edge1, edge2, normal;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
edge1[i] = v1[i] - v0[i];
|
|
edge2[i] = v2[i] - v0[i];
|
|
}
|
|
normal[0] = edge1[1] * edge2[2] - edge1[2] * edge2[1];
|
|
normal[1] = edge1[2] * edge2[0] - edge1[0] * edge2[2];
|
|
normal[2] = edge1[0] * edge2[1] - edge1[1] * edge2[0];
|
|
|
|
// Get the image gradient at the vertices.
|
|
std::array<Real, 3> grad0 = GetGradient(v0);
|
|
std::array<Real, 3> grad1 = GetGradient(v1);
|
|
std::array<Real, 3> grad2 = GetGradient(v2);
|
|
|
|
// Compute the average gradient.
|
|
std::array<Real, 3> gradAvr;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
gradAvr[i] = (grad0[i] + grad1[i] + grad2[i]) / (Real)3;
|
|
}
|
|
|
|
// Compute the dot product of normal and average gradient.
|
|
Real dot = gradAvr[0] * normal[0] + gradAvr[1] * normal[1] + gradAvr[2] * normal[2];
|
|
|
|
// Choose triangle orientation based on gradient direction.
|
|
if (sameDir)
|
|
{
|
|
if (dot < (Real)0)
|
|
{
|
|
// Wrong orientation, reorder it.
|
|
std::swap(triangle.V[1], triangle.V[2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dot > (Real)0)
|
|
{
|
|
// Wrong orientation, reorder it.
|
|
std::swap(triangle.V[1], triangle.V[2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ComputeNormals(std::vector<Vertex> const& vertices,
|
|
std::vector<Triangle> const& triangles,
|
|
std::vector<std::array<Real, 3>>& normals)
|
|
{
|
|
// Compute a vertex normal to be area-weighted sums of the normals
|
|
// to the triangles that share that vertex.
|
|
std::array<Real, 3> const zero{ (Real)0, (Real)0, (Real)0 };
|
|
normals.resize(vertices.size());
|
|
std::fill(normals.begin(), normals.end(), zero);
|
|
|
|
for (auto const& triangle : triangles)
|
|
{
|
|
// Get the triangle vertices.
|
|
std::array<Real, 3> v0 = vertices[triangle.V[0]];
|
|
std::array<Real, 3> v1 = vertices[triangle.V[1]];
|
|
std::array<Real, 3> v2 = vertices[triangle.V[2]];
|
|
|
|
// Construct the triangle normal.
|
|
std::array<Real, 3> edge1, edge2, normal;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
edge1[i] = v1[i] - v0[i];
|
|
edge2[i] = v2[i] - v0[i];
|
|
}
|
|
normal[0] = edge1[1] * edge2[2] - edge1[2] * edge2[1];
|
|
normal[1] = edge1[2] * edge2[0] - edge1[0] * edge2[2];
|
|
normal[2] = edge1[0] * edge2[1] - edge1[1] * edge2[0];
|
|
|
|
// Maintain the sum of normals at each vertex.
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
normals[triangle.V[i]][j] += normal[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// The normal vector storage was used to accumulate the sum of
|
|
// triangle normals. Now these vectors must be rescaled to be
|
|
// unit length.
|
|
for (auto& normal : normals)
|
|
{
|
|
Real sqrLength = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2];
|
|
Real length = std::sqrt(sqrLength);
|
|
if (length > (Real)0)
|
|
{
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
normal[i] /= length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
normal[i] = (Real)0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Support for debugging.
|
|
void PrintBoxes(std::ostream& output)
|
|
{
|
|
output << mBoxes.size() << std::endl;
|
|
for (size_t i = 0; i < mBoxes.size(); ++i)
|
|
{
|
|
OctBox const& box = mBoxes[i];
|
|
output << "box " << i << ": ";
|
|
output << "x0 = " << box.x0 << ", ";
|
|
output << "y0 = " << box.y0 << ", ";
|
|
output << "z0 = " << box.z0 << ", ";
|
|
output << "dx = " << box.dx << ", ";
|
|
output << "dy = " << box.dy << ", ";
|
|
output << "dz = " << box.dz << std::endl;
|
|
}
|
|
}
|
|
|
|
private:
|
|
// Helper classes for the skeleton climbing.
|
|
struct OctBox
|
|
{
|
|
OctBox(int inX0, int inY0, int inZ0, int inDX, int inDY, int inDZ,
|
|
int inLX, int inLY, int inLZ)
|
|
:
|
|
x0(inX0), y0(inY0), z0(inZ0),
|
|
x1(inX0 + inDX), y1(inY0 + inDY), z1(inZ0 + inDZ),
|
|
dx(inDX), dy(inDY), dz(inDZ),
|
|
LX(inLX), LY(inLY), LZ(inLZ)
|
|
{
|
|
}
|
|
|
|
int x0, y0, z0, x1, y1, z1, dx, dy, dz, LX, LY, LZ;
|
|
};
|
|
|
|
struct MergeBox
|
|
{
|
|
MergeBox(int stride)
|
|
:
|
|
xStride(stride), yStride(stride), zStride(stride),
|
|
valid(true)
|
|
{
|
|
}
|
|
|
|
int xStride, yStride, zStride;
|
|
bool valid;
|
|
};
|
|
|
|
class LinearMergeTree
|
|
{
|
|
public:
|
|
LinearMergeTree(int N)
|
|
:
|
|
mTwoPowerN(1 << N),
|
|
mNodes(2 * mTwoPowerN - 1),
|
|
mZeroBases(2 * mTwoPowerN - 1)
|
|
{
|
|
}
|
|
|
|
enum
|
|
{
|
|
CFG_NONE = 0,
|
|
CFG_INCR = 1,
|
|
CFG_DECR = 2,
|
|
CFG_MULT = 3,
|
|
CFG_ROOT_MASK = 3,
|
|
CFG_EDGE = 4,
|
|
CFG_ZERO_SUBEDGE = 8
|
|
};
|
|
|
|
bool IsNone(int i) const
|
|
{
|
|
return (mNodes[i] & CFG_ROOT_MASK) == CFG_NONE;
|
|
}
|
|
|
|
int GetRootType(int i) const
|
|
{
|
|
return mNodes[i] & CFG_ROOT_MASK;
|
|
}
|
|
|
|
int GetZeroBase(int i) const
|
|
{
|
|
return mZeroBases[i];
|
|
}
|
|
|
|
void SetEdge(int i)
|
|
{
|
|
mNodes[i] |= CFG_EDGE;
|
|
|
|
// Inform all predecessors whether they have a zero subedge.
|
|
if (mZeroBases[i] >= 0)
|
|
{
|
|
while (i > 0)
|
|
{
|
|
i = (i - 1) / 2;
|
|
mNodes[i] |= CFG_ZERO_SUBEDGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsZeroEdge(int i) const
|
|
{
|
|
return mNodes[i] == (CFG_EDGE | CFG_INCR)
|
|
|| mNodes[i] == (CFG_EDGE | CFG_DECR);
|
|
}
|
|
|
|
bool HasZeroSubedge(int i) const
|
|
{
|
|
return (mNodes[i] & CFG_ZERO_SUBEDGE) != 0;
|
|
}
|
|
|
|
void SetLevel(Real level, T const* data, int offset, int stride)
|
|
{
|
|
// Assert: The 'level' is not an image value. Because T is
|
|
// an integer type, choose 'level' to be a Real-valued number
|
|
// that does not represent an integer.
|
|
|
|
// Determine the sign changes between pairs of consecutive
|
|
// samples.
|
|
int firstLeaf = mTwoPowerN - 1;
|
|
for (int i = 0, leaf = firstLeaf; i < mTwoPowerN; ++i, ++leaf)
|
|
{
|
|
int base = offset + stride * i;
|
|
Real value0 = static_cast<Real>(data[base]);
|
|
Real value1 = static_cast<Real>(data[base + stride]);
|
|
|
|
if (value0 > level)
|
|
{
|
|
if (value1 > level)
|
|
{
|
|
mNodes[leaf] = CFG_NONE;
|
|
mZeroBases[leaf] = -1;
|
|
}
|
|
else
|
|
{
|
|
mNodes[leaf] = CFG_DECR;
|
|
mZeroBases[leaf] = i;
|
|
}
|
|
}
|
|
else // value0 < level
|
|
{
|
|
if (value1 > level)
|
|
{
|
|
mNodes[leaf] = CFG_INCR;
|
|
mZeroBases[leaf] = i;
|
|
}
|
|
else
|
|
{
|
|
mNodes[leaf] = CFG_NONE;
|
|
mZeroBases[leaf] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Propagate the sign change information up the binary tree.
|
|
for (int i = firstLeaf - 1; i >= 0; --i)
|
|
{
|
|
int twoIp1 = 2 * i + 1, twoIp2 = twoIp1 + 1;
|
|
int value0 = mNodes[twoIp1];
|
|
int value1 = mNodes[twoIp2];
|
|
int combine = (value0 | value1);
|
|
mNodes[i] = combine;
|
|
if (combine == CFG_INCR)
|
|
{
|
|
if (value0 == CFG_INCR)
|
|
{
|
|
mZeroBases[i] = mZeroBases[twoIp1];
|
|
}
|
|
else
|
|
{
|
|
mZeroBases[i] = mZeroBases[twoIp2];
|
|
}
|
|
}
|
|
else if (combine == CFG_DECR)
|
|
{
|
|
if (value0 == CFG_DECR)
|
|
{
|
|
mZeroBases[i] = mZeroBases[twoIp1];
|
|
}
|
|
else
|
|
{
|
|
mZeroBases[i] = mZeroBases[twoIp2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mZeroBases[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
int mTwoPowerN;
|
|
std::vector<int> mNodes;
|
|
std::vector<int> mZeroBases;
|
|
};
|
|
|
|
class VETable
|
|
{
|
|
public:
|
|
VETable()
|
|
:
|
|
mVertices(18)
|
|
{
|
|
}
|
|
|
|
bool IsValidVertex(int i) const
|
|
{
|
|
return mVertices[i].valid;
|
|
}
|
|
|
|
int GetNumVertices() const
|
|
{
|
|
return static_cast<int>(mVertices.size());
|
|
}
|
|
|
|
Vertex const& GetVertex(int i) const
|
|
{
|
|
return mVertices[i].position;
|
|
}
|
|
|
|
void Insert(int i, Real x, Real y, Real z)
|
|
{
|
|
TVertex& vertex = mVertices[i];
|
|
vertex.position = Vertex{ x, y, z };
|
|
vertex.valid = true;
|
|
}
|
|
|
|
void Insert(Vertex const& position)
|
|
{
|
|
mVertices.push_back(TVertex(position));
|
|
}
|
|
|
|
void InsertEdge(int v0, int v1)
|
|
{
|
|
TVertex& vertex0 = mVertices[v0];
|
|
TVertex& vertex1 = mVertices[v1];
|
|
vertex0.adjacent[vertex0.adjQuantity] = v1;
|
|
++vertex0.adjQuantity;
|
|
vertex1.adjacent[vertex1.adjQuantity] = v0;
|
|
++vertex1.adjQuantity;
|
|
}
|
|
|
|
void RemoveTrianglesEC(std::vector<Vertex>& positions,
|
|
std::vector<Triangle>& triangles)
|
|
{
|
|
// Ear-clip the wireframe to get the triangles.
|
|
Triangle triangle;
|
|
while (RemoveEC(triangle))
|
|
{
|
|
int v0 = static_cast<int>(positions.size());
|
|
int v1 = v0 + 1;
|
|
int v2 = v1 + 1;
|
|
// Bypassing the constructor to avoid a warning in the
|
|
// release build by gcc 7.5.0 on Ubuntu 18.04.5 LTS:
|
|
// "assuming signed overflow does not occur when assuming
|
|
// that (X + c) >= X is always true." The correct
|
|
// constructor case is used because v0 < v1 < v2.
|
|
Triangle tkey;
|
|
tkey.V[0] = v0;
|
|
tkey.V[1] = v1;
|
|
tkey.V[2] = v2;
|
|
triangles.push_back(tkey);
|
|
positions.push_back(mVertices[triangle.V[0]].position);
|
|
positions.push_back(mVertices[triangle.V[1]].position);
|
|
positions.push_back(mVertices[triangle.V[2]].position);
|
|
}
|
|
}
|
|
|
|
void RemoveTrianglesSE(std::vector<Vertex>& positions,
|
|
std::vector<Triangle>& triangles)
|
|
{
|
|
// Compute centroid of vertices.
|
|
Vertex centroid = { (Real)0, (Real)0, (Real)0 };
|
|
int const vmax = static_cast<int>(mVertices.size());
|
|
int i, j, quantity = 0;
|
|
for (i = 0; i < vmax; i++)
|
|
{
|
|
TVertex const& vertex = mVertices[i];
|
|
if (vertex.valid)
|
|
{
|
|
for (j = 0; j < 3; ++j)
|
|
{
|
|
centroid[j] += vertex.position[j];
|
|
}
|
|
++quantity;
|
|
}
|
|
}
|
|
for (j = 0; j < 3; ++j)
|
|
{
|
|
centroid[j] /= static_cast<Real>(quantity);
|
|
}
|
|
|
|
int v0 = static_cast<int>(positions.size());
|
|
positions.push_back(centroid);
|
|
|
|
int i1 = 18;
|
|
int v1 = v0 + 1;
|
|
positions.push_back(mVertices[i1].position);
|
|
|
|
int i2 = mVertices[i1].adjacent[1], v2;
|
|
for (i = 0; i < quantity - 1; ++i)
|
|
{
|
|
v2 = v1 + 1;
|
|
positions.push_back(mVertices[i2].position);
|
|
// Bypassing the constructor to avoid a warning in the
|
|
// release build by gcc 7.5.0 on Ubuntu 18.04.5 LTS:
|
|
// "assuming signed overflow does not occur when assuming
|
|
// that (X + c) >= X is always true." The correct
|
|
// constructor case is used because v0 < v1 < v2.
|
|
Triangle tkey;
|
|
tkey.V[0] = v0;
|
|
tkey.V[1] = v1;
|
|
tkey.V[2] = v2;
|
|
triangles.push_back(tkey);
|
|
if (mVertices[i2].adjacent[1] != i1)
|
|
{
|
|
i1 = i2;
|
|
i2 = mVertices[i2].adjacent[1];
|
|
}
|
|
else
|
|
{
|
|
i1 = i2;
|
|
i2 = mVertices[i2].adjacent[0];
|
|
}
|
|
v1 = v2;
|
|
}
|
|
|
|
v2 = v0 + 1;
|
|
// Unlike the previous two constructor calls, it is not
|
|
// guaranteed that v0 < v1.
|
|
triangles.push_back(Triangle(v0, v1, v2));
|
|
}
|
|
|
|
protected:
|
|
void RemoveVertex(int i)
|
|
{
|
|
TVertex& vertex0 = mVertices[i];
|
|
int a0 = vertex0.adjacent[0];
|
|
int a1 = vertex0.adjacent[1];
|
|
TVertex& adjVertex0 = mVertices[a0];
|
|
TVertex& adjVertex1 = mVertices[a1];
|
|
|
|
int j;
|
|
for (j = 0; j < adjVertex0.adjQuantity; j++)
|
|
{
|
|
if (adjVertex0.adjacent[j] == i)
|
|
{
|
|
adjVertex0.adjacent[j] = a1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < adjVertex1.adjQuantity; j++)
|
|
{
|
|
if (adjVertex1.adjacent[j] == i)
|
|
{
|
|
adjVertex1.adjacent[j] = a0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
vertex0.valid = false;
|
|
|
|
if (adjVertex0.adjQuantity == 2)
|
|
{
|
|
if (adjVertex0.adjacent[0] == adjVertex0.adjacent[1])
|
|
{
|
|
adjVertex0.valid = false;
|
|
}
|
|
}
|
|
|
|
if (adjVertex1.adjQuantity == 2)
|
|
{
|
|
if (adjVertex1.adjacent[0] == adjVertex1.adjacent[1])
|
|
{
|
|
adjVertex1.valid = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ear clipping
|
|
bool RemoveEC(Triangle& triangle)
|
|
{
|
|
int numVertices = static_cast<int>(mVertices.size());
|
|
for (int i = 0; i < numVertices; ++i)
|
|
{
|
|
TVertex const& vertex = mVertices[i];
|
|
if (vertex.valid && vertex.adjQuantity == 2)
|
|
{
|
|
triangle.V[0] = i;
|
|
triangle.V[1] = vertex.adjacent[0];
|
|
triangle.V[2] = vertex.adjacent[1];
|
|
RemoveVertex(i);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
class TVertex
|
|
{
|
|
public:
|
|
TVertex()
|
|
:
|
|
adjQuantity(0),
|
|
valid(false)
|
|
{
|
|
}
|
|
|
|
TVertex(Vertex const& inPosition)
|
|
:
|
|
position(inPosition),
|
|
adjQuantity(0),
|
|
valid(true)
|
|
{
|
|
|
|
}
|
|
|
|
Vertex position;
|
|
int adjQuantity;
|
|
std::array<int, 4> adjacent;
|
|
bool valid;
|
|
};
|
|
|
|
std::vector<TVertex> mVertices;
|
|
};
|
|
|
|
private:
|
|
// Support for merging monoboxes.
|
|
void Merge(int depth)
|
|
{
|
|
int x, y, z, offset, stride;
|
|
|
|
for (y = 0; y < mSize; ++y)
|
|
{
|
|
for (z = 0; z < mSize; ++z)
|
|
{
|
|
offset = mSize * (y + mSize * z);
|
|
stride = 1;
|
|
mXMerge[y][z]->SetLevel(mLevel, mInputVoxels, offset, stride);
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < mSize; ++x)
|
|
{
|
|
for (z = 0; z < mSize; ++z)
|
|
{
|
|
offset = x + mSizeSqr * z;
|
|
stride = mSize;
|
|
mYMerge[x][z]->SetLevel(mLevel, mInputVoxels, offset, stride);
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < mSize; ++x)
|
|
{
|
|
for (y = 0; y < mSize; ++y)
|
|
{
|
|
offset = x + mSize * y;
|
|
stride = mSizeSqr;
|
|
mZMerge[x][y]->SetLevel(mLevel, mInputVoxels, offset, stride);
|
|
}
|
|
}
|
|
|
|
Merge(0, 0, 0, 0, 0, 0, 0, mTwoPowerN, depth);
|
|
}
|
|
|
|
bool Merge(int v, int LX, int LY, int LZ, int x0, int y0, int z0, int stride, int depth)
|
|
{
|
|
if (stride > 1) // internal nodes
|
|
{
|
|
int hStride = stride / 2;
|
|
int vBase = 8 * v;
|
|
int v000 = vBase + 1;
|
|
int v100 = vBase + 2;
|
|
int v010 = vBase + 3;
|
|
int v110 = vBase + 4;
|
|
int v001 = vBase + 5;
|
|
int v101 = vBase + 6;
|
|
int v011 = vBase + 7;
|
|
int v111 = vBase + 8;
|
|
int LX0 = 2 * LX + 1, LX1 = LX0 + 1;
|
|
int LY0 = 2 * LY + 1, LY1 = LY0 + 1;
|
|
int LZ0 = 2 * LZ + 1, LZ1 = LZ0 + 1;
|
|
int x1 = x0 + hStride, y1 = y0 + hStride, z1 = z0 + hStride;
|
|
|
|
int dm1 = depth - 1;
|
|
bool m000 = Merge(v000, LX0, LY0, LZ0, x0, y0, z0, hStride, dm1);
|
|
bool m100 = Merge(v100, LX1, LY0, LZ0, x1, y0, z0, hStride, dm1);
|
|
bool m010 = Merge(v010, LX0, LY1, LZ0, x0, y1, z0, hStride, dm1);
|
|
bool m110 = Merge(v110, LX1, LY1, LZ0, x1, y1, z0, hStride, dm1);
|
|
bool m001 = Merge(v001, LX0, LY0, LZ1, x0, y0, z1, hStride, dm1);
|
|
bool m101 = Merge(v101, LX1, LY0, LZ1, x1, y0, z1, hStride, dm1);
|
|
bool m011 = Merge(v011, LX0, LY1, LZ1, x0, y1, z1, hStride, dm1);
|
|
bool m111 = Merge(v111, LX1, LY1, LZ1, x1, y1, z1, hStride, dm1);
|
|
|
|
MergeBox r000(hStride), r100(hStride), r010(hStride), r110(hStride);
|
|
MergeBox r001(hStride), r101(hStride), r011(hStride), r111(hStride);
|
|
|
|
if (depth <= 0)
|
|
{
|
|
if (m000 && m001)
|
|
{
|
|
DoZMerge(r000, r001, x0, y0, LZ);
|
|
}
|
|
|
|
if (m100 && m101)
|
|
{
|
|
DoZMerge(r100, r101, x1, y0, LZ);
|
|
}
|
|
|
|
if (m010 && m011)
|
|
{
|
|
DoZMerge(r010, r011, x0, y1, LZ);
|
|
}
|
|
|
|
if (m110 && m111)
|
|
{
|
|
DoZMerge(r110, r111, x1, y1, LZ);
|
|
}
|
|
|
|
if (m000 && m010)
|
|
{
|
|
DoYMerge(r000, r010, x0, LY, z0);
|
|
}
|
|
|
|
if (m100 && m110)
|
|
{
|
|
DoYMerge(r100, r110, x1, LY, z0);
|
|
}
|
|
|
|
if (m001 && m011)
|
|
{
|
|
DoYMerge(r001, r011, x0, LY, z1);
|
|
}
|
|
|
|
if (m101 && m111)
|
|
{
|
|
DoYMerge(r101, r111, x1, LY, z1);
|
|
}
|
|
|
|
if (m000 && m100)
|
|
{
|
|
DoXMerge(r000, r100, LX, y0, z0);
|
|
}
|
|
|
|
if (m010 && m110)
|
|
{
|
|
DoXMerge(r010, r110, LX, y1, z0);
|
|
}
|
|
|
|
if (m001 && m101)
|
|
{
|
|
DoXMerge(r001, r101, LX, y0, z1);
|
|
}
|
|
|
|
if (m011 && m111)
|
|
{
|
|
DoXMerge(r011, r111, LX, y1, z1);
|
|
}
|
|
}
|
|
|
|
if (depth <= 1)
|
|
{
|
|
if (r000.valid)
|
|
{
|
|
if (r000.xStride == stride)
|
|
{
|
|
if (r000.yStride == stride)
|
|
{
|
|
if (r000.zStride == stride)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
AddBox(x0, y0, z0, stride, stride, hStride, LX, LY, LZ0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r000.zStride == stride)
|
|
{
|
|
AddBox(x0, y0, z0, stride, hStride, stride, LX, LY0, LZ);
|
|
}
|
|
else
|
|
{
|
|
AddBox(x0, y0, z0, stride, hStride, hStride, LX, LY0, LZ0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r000.yStride == stride)
|
|
{
|
|
if (r000.zStride == stride)
|
|
{
|
|
AddBox(x0, y0, z0, hStride, stride, stride, LX0, LY, LZ);
|
|
}
|
|
else
|
|
{
|
|
AddBox(x0, y0, z0, hStride, stride, hStride, LX0, LY, LZ0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r000.zStride == stride)
|
|
{
|
|
AddBox(x0, y0, z0, hStride, hStride, stride, LX0, LY0, LZ);
|
|
}
|
|
else if (m000)
|
|
{
|
|
AddBox(x0, y0, z0, hStride, hStride, hStride, LX0, LY0, LZ0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r100.valid)
|
|
{
|
|
if (r100.yStride == stride)
|
|
{
|
|
if (r100.zStride == stride)
|
|
{
|
|
AddBox(x1, y0, z0, hStride, stride, stride, LX1, LY, LZ);
|
|
}
|
|
else
|
|
{
|
|
AddBox(x1, y0, z0, hStride, stride, hStride, LX1, LY, LZ0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r100.zStride == stride)
|
|
{
|
|
AddBox(x1, y0, z0, hStride, hStride, stride, LX1, LY0, LZ);
|
|
}
|
|
else if (m100)
|
|
{
|
|
AddBox(x1, y0, z0, hStride, hStride, hStride, LX1, LY0, LZ0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r010.valid)
|
|
{
|
|
if (r010.xStride == stride)
|
|
{
|
|
if (r010.zStride == stride)
|
|
{
|
|
AddBox(x0, y1, z0, stride, hStride, stride, LX, LY1, LZ);
|
|
}
|
|
else
|
|
{
|
|
AddBox(x0, y1, z0, stride, hStride, hStride, LX, LY1, LZ0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r010.zStride == stride)
|
|
{
|
|
AddBox(x0, y1, z0, hStride, hStride, stride, LX0, LY1, LZ);
|
|
}
|
|
else if (m010)
|
|
{
|
|
AddBox(x0, y1, z0, hStride, hStride, hStride, LX0, LY1, LZ0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r001.valid)
|
|
{
|
|
if (r001.xStride == stride)
|
|
{
|
|
if (r001.yStride == stride)
|
|
{
|
|
AddBox(x0, y0, z1, stride, stride, hStride, LX, LY, LZ1);
|
|
}
|
|
else
|
|
{
|
|
AddBox(x0, y0, z1, stride, hStride, hStride, LX, LY0, LZ1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r001.yStride == stride)
|
|
{
|
|
AddBox(x0, y0, z1, hStride, stride, hStride, LX0, LY, LZ1);
|
|
}
|
|
else if (m001)
|
|
{
|
|
AddBox(x0, y0, z1, hStride, hStride, hStride, LX0, LY0, LZ1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r110.valid)
|
|
{
|
|
if (r110.zStride == stride)
|
|
{
|
|
AddBox(x1, y1, z0, hStride, hStride, stride, LX1, LY1, LZ);
|
|
}
|
|
else if (m110)
|
|
{
|
|
AddBox(x1, y1, z0, hStride, hStride, hStride, LX1, LY1, LZ0);
|
|
}
|
|
}
|
|
|
|
if (r101.valid)
|
|
{
|
|
if (r101.yStride == stride)
|
|
{
|
|
AddBox(x1, y0, z1, hStride, stride, hStride, LX1, LY, LZ1);
|
|
}
|
|
else if (m101)
|
|
{
|
|
AddBox(x1, y0, z1, hStride, hStride, hStride, LX1, LY0, LZ1);
|
|
}
|
|
}
|
|
|
|
if (r011.valid)
|
|
{
|
|
if (r011.xStride == stride)
|
|
{
|
|
AddBox(x0, y1, z1, stride, hStride, hStride, LX, LY1, LZ1);
|
|
}
|
|
else if (m011)
|
|
{
|
|
AddBox(x0, y1, z1, hStride, hStride, hStride, LX0, LY1, LZ1);
|
|
}
|
|
}
|
|
|
|
if (r111.valid && m111)
|
|
{
|
|
AddBox(x1, y1, z1, hStride, hStride, hStride, LX1, LY1, LZ1);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else // leaf nodes
|
|
{
|
|
if (mFixBoundary)
|
|
{
|
|
// Do not allow boundary voxels to merge with any other
|
|
// voxels.
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// A leaf box is mergeable with neighbors as long as all its
|
|
// faces have 0 or 2 sign changes on the edges. That is, a
|
|
// face may not have sign changes on all four edges. If it
|
|
// does, the resulting box for tessellating is 1x1x1 and is
|
|
// handled separately from boxes of larger dimensions.
|
|
|
|
// xmin face
|
|
int z1 = z0 + 1;
|
|
int rt0 = mYMerge[x0][z0]->GetRootType(LY);
|
|
int rt1 = mYMerge[x0][z1]->GetRootType(LY);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// xmax face
|
|
int x1 = x0 + 1;
|
|
rt0 = mYMerge[x1][z0]->GetRootType(LY);
|
|
rt1 = mYMerge[x1][z1]->GetRootType(LY);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// ymin face
|
|
rt0 = mZMerge[x0][y0]->GetRootType(LZ);
|
|
rt1 = mZMerge[x1][y0]->GetRootType(LZ);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// ymax face
|
|
int y1 = y0 + 1;
|
|
rt0 = mZMerge[x0][y1]->GetRootType(LZ);
|
|
rt1 = mZMerge[x1][y1]->GetRootType(LZ);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// zmin face
|
|
rt0 = mXMerge[y0][z0]->GetRootType(LX);
|
|
rt1 = mXMerge[y1][z0]->GetRootType(LX);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
// zmax face
|
|
rt0 = mXMerge[y0][z1]->GetRootType(LX);
|
|
rt1 = mXMerge[y1][z1]->GetRootType(LX);
|
|
if ((rt0 | rt1) == LinearMergeTree::CFG_MULT)
|
|
{
|
|
AddBox(x0, y0, z0, 1, 1, 1, LX, LY, LZ);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool DoXMerge(MergeBox& r0, MergeBox& r1, int LX, int y0, int z0)
|
|
{
|
|
if (!r0.valid || !r1.valid || r0.yStride != r1.yStride || r0.zStride != r1.zStride)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Boxes are potentially x-mergeable.
|
|
int y1 = y0 + r0.yStride, z1 = z0 + r0.zStride;
|
|
int incr = 0, decr = 0;
|
|
for (int y = y0; y <= y1; ++y)
|
|
{
|
|
for (int z = z0; z <= z1; ++z)
|
|
{
|
|
switch (mXMerge[y][z]->GetRootType(LX))
|
|
{
|
|
case LinearMergeTree::CFG_MULT:
|
|
return false;
|
|
case LinearMergeTree::CFG_INCR:
|
|
++incr;
|
|
break;
|
|
case LinearMergeTree::CFG_DECR:
|
|
++decr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (incr != 0 && decr != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Strongly mono, x-merge the boxes.
|
|
r0.xStride *= 2;
|
|
r1.valid = false;
|
|
return true;
|
|
}
|
|
|
|
bool DoYMerge(MergeBox& r0, MergeBox& r1, int x0, int LY, int z0)
|
|
{
|
|
if (!r0.valid || !r1.valid || r0.xStride != r1.xStride || r0.zStride != r1.zStride)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Boxes are potentially y-mergeable.
|
|
int x1 = x0 + r0.xStride, z1 = z0 + r0.zStride;
|
|
int incr = 0, decr = 0;
|
|
for (int x = x0; x <= x1; ++x)
|
|
{
|
|
for (int z = z0; z <= z1; ++z)
|
|
{
|
|
switch (mYMerge[x][z]->GetRootType(LY))
|
|
{
|
|
case LinearMergeTree::CFG_MULT:
|
|
return false;
|
|
case LinearMergeTree::CFG_INCR:
|
|
++incr;
|
|
break;
|
|
case LinearMergeTree::CFG_DECR:
|
|
++decr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (incr != 0 && decr != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Strongly mono, y-merge the boxes.
|
|
r0.yStride *= 2;
|
|
r1.valid = false;
|
|
return true;
|
|
}
|
|
|
|
bool DoZMerge(MergeBox& r0, MergeBox& r1, int x0, int y0, int LZ)
|
|
{
|
|
if (!r0.valid || !r1.valid || r0.xStride != r1.xStride || r0.yStride != r1.yStride)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Boxes are potentially z-mergeable.
|
|
int x1 = x0 + r0.xStride, y1 = y0 + r0.yStride;
|
|
int incr = 0, decr = 0;
|
|
for (int x = x0; x <= x1; ++x)
|
|
{
|
|
for (int y = y0; y <= y1; ++y)
|
|
{
|
|
switch (mZMerge[x][y]->GetRootType(LZ))
|
|
{
|
|
case LinearMergeTree::CFG_MULT:
|
|
return false;
|
|
case LinearMergeTree::CFG_INCR:
|
|
++incr;
|
|
break;
|
|
case LinearMergeTree::CFG_DECR:
|
|
++decr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (incr != 0 && decr != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Strongly mono, z-merge the boxes.
|
|
r0.zStride *= 2;
|
|
r1.valid = false;
|
|
return true;
|
|
}
|
|
|
|
void AddBox(int x0, int y0, int z0, int dx, int dy, int dz, int LX, int LY, int LZ)
|
|
{
|
|
OctBox box(x0, y0, z0, dx, dy, dz, LX, LY, LZ);
|
|
mBoxes.push_back(box);
|
|
|
|
// Mark box edges in linear merge trees. This information will be
|
|
// used later for extraction.
|
|
mXMerge[box.y0][box.z0]->SetEdge(box.LX);
|
|
mXMerge[box.y0][box.z1]->SetEdge(box.LX);
|
|
mXMerge[box.y1][box.z0]->SetEdge(box.LX);
|
|
mXMerge[box.y1][box.z1]->SetEdge(box.LX);
|
|
mYMerge[box.x0][box.z0]->SetEdge(box.LY);
|
|
mYMerge[box.x0][box.z1]->SetEdge(box.LY);
|
|
mYMerge[box.x1][box.z0]->SetEdge(box.LY);
|
|
mYMerge[box.x1][box.z1]->SetEdge(box.LY);
|
|
mZMerge[box.x0][box.y0]->SetEdge(box.LZ);
|
|
mZMerge[box.x0][box.y1]->SetEdge(box.LZ);
|
|
mZMerge[box.x1][box.y0]->SetEdge(box.LZ);
|
|
mZMerge[box.x1][box.y1]->SetEdge(box.LZ);
|
|
}
|
|
|
|
// Support for tessellating monoboxes.
|
|
void Tessellate(std::vector<Vertex>& positions, std::vector<Triangle>& triangles)
|
|
{
|
|
for (size_t i = 0; i < mBoxes.size(); ++i)
|
|
{
|
|
OctBox const& box = mBoxes[i];
|
|
|
|
// Get vertices on edges of box.
|
|
VETable table;
|
|
unsigned int type;
|
|
GetVertices(box, type, table);
|
|
if (type == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Add wireframe edges to table, add face-vertices if
|
|
// necessary.
|
|
if (box.dx > 1 || box.dy > 1 || box.dz > 1)
|
|
{
|
|
// Box is larger than voxel, each face has at most one
|
|
// edge.
|
|
GetXMinEdgesM(box, type, table);
|
|
GetXMaxEdgesM(box, type, table);
|
|
GetYMinEdgesM(box, type, table);
|
|
GetYMaxEdgesM(box, type, table);
|
|
GetZMinEdgesM(box, type, table);
|
|
GetZMaxEdgesM(box, type, table);
|
|
|
|
if (table.GetNumVertices() > 18)
|
|
{
|
|
table.RemoveTrianglesSE(positions, triangles);
|
|
}
|
|
else
|
|
{
|
|
table.RemoveTrianglesEC(positions, triangles);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The box is a 1x1x1 voxel. Do the full edge analysis
|
|
// but no splitting is required.
|
|
GetXMinEdgesS(box, type, table);
|
|
GetXMaxEdgesS(box, type, table);
|
|
GetYMinEdgesS(box, type, table);
|
|
GetYMaxEdgesS(box, type, table);
|
|
GetZMinEdgesS(box, type, table);
|
|
GetZMaxEdgesS(box, type, table);
|
|
table.RemoveTrianglesEC(positions, triangles);
|
|
}
|
|
}
|
|
}
|
|
|
|
Real GetXInterp(int x, int y, int z) const
|
|
{
|
|
int index = x + mSize * (y + mSize * z);
|
|
Real f0 = static_cast<Real>(mInputVoxels[index]);
|
|
++index;
|
|
Real f1 = static_cast<Real>(mInputVoxels[index]);
|
|
return static_cast<Real>(x) + (mLevel - f0) / (f1 - f0);
|
|
}
|
|
|
|
Real GetYInterp(int x, int y, int z) const
|
|
{
|
|
int index = x + mSize * (y + mSize * z);
|
|
Real f0 = static_cast<Real>(mInputVoxels[index]);
|
|
index += mSize;
|
|
Real f1 = static_cast<Real>(mInputVoxels[index]);
|
|
return static_cast<Real>(y) + (mLevel - f0) / (f1 - f0);
|
|
}
|
|
|
|
Real GetZInterp(int x, int y, int z) const
|
|
{
|
|
int index = x + mSize * (y + mSize * z);
|
|
Real f0 = static_cast<Real>(mInputVoxels[index]);
|
|
index += mSizeSqr;
|
|
Real f1 = static_cast<Real>(mInputVoxels[index]);
|
|
return static_cast<Real>(z) + (mLevel - f0) / (f1 - f0);
|
|
}
|
|
|
|
void GetVertices(OctBox const& box, unsigned int& type, VETable& table)
|
|
{
|
|
int root;
|
|
type = 0;
|
|
|
|
// xmin-ymin edge
|
|
root = mZMerge[box.x0][box.y0]->GetZeroBase(box.LZ);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMIN_YMIN;
|
|
table.Insert(EI_XMIN_YMIN,
|
|
static_cast<float>(box.x0),
|
|
static_cast<float>(box.y0),
|
|
GetZInterp(box.x0, box.y0, root));
|
|
}
|
|
|
|
// xmin-ymax edge
|
|
root = mZMerge[box.x0][box.y1]->GetZeroBase(box.LZ);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMIN_YMAX;
|
|
table.Insert(EI_XMIN_YMAX,
|
|
static_cast<float>(box.x0),
|
|
static_cast<float>(box.y1),
|
|
GetZInterp(box.x0, box.y1, root));
|
|
}
|
|
|
|
// xmax-ymin edge
|
|
root = mZMerge[box.x1][box.y0]->GetZeroBase(box.LZ);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMAX_YMIN;
|
|
table.Insert(EI_XMAX_YMIN,
|
|
static_cast<float>(box.x1),
|
|
static_cast<float>(box.y0),
|
|
GetZInterp(box.x1, box.y0, root));
|
|
}
|
|
|
|
// xmax-ymax edge
|
|
root = mZMerge[box.x1][box.y1]->GetZeroBase(box.LZ);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMAX_YMAX;
|
|
table.Insert(EI_XMAX_YMAX,
|
|
static_cast<float>(box.x1),
|
|
static_cast<float>(box.y1),
|
|
GetZInterp(box.x1, box.y1, root));
|
|
}
|
|
|
|
// xmin-zmin edge
|
|
root = mYMerge[box.x0][box.z0]->GetZeroBase(box.LY);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMIN_ZMIN;
|
|
table.Insert(EI_XMIN_ZMIN,
|
|
static_cast<float>(box.x0),
|
|
GetYInterp(box.x0, root, box.z0),
|
|
static_cast<float>(box.z0));
|
|
}
|
|
|
|
// xmin-zmax edge
|
|
root = mYMerge[box.x0][box.z1]->GetZeroBase(box.LY);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMIN_ZMAX;
|
|
table.Insert(EI_XMIN_ZMAX,
|
|
static_cast<float>(box.x0),
|
|
GetYInterp(box.x0, root, box.z1),
|
|
static_cast<float>(box.z1));
|
|
}
|
|
|
|
// xmax-zmin edge
|
|
root = mYMerge[box.x1][box.z0]->GetZeroBase(box.LY);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMAX_ZMIN;
|
|
table.Insert(EI_XMAX_ZMIN,
|
|
static_cast<float>(box.x1),
|
|
GetYInterp(box.x1, root, box.z0),
|
|
static_cast<float>(box.z0));
|
|
}
|
|
|
|
// xmax-zmax edge
|
|
root = mYMerge[box.x1][box.z1]->GetZeroBase(box.LY);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_XMAX_ZMAX;
|
|
table.Insert(EI_XMAX_ZMAX,
|
|
static_cast<float>(box.x1),
|
|
GetYInterp(box.x1, root, box.z1),
|
|
static_cast<float>(box.z1));
|
|
}
|
|
|
|
// ymin-zmin edge
|
|
root = mXMerge[box.y0][box.z0]->GetZeroBase(box.LX);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_YMIN_ZMIN;
|
|
table.Insert(EI_YMIN_ZMIN,
|
|
GetXInterp(root, box.y0, box.z0),
|
|
static_cast<float>(box.y0),
|
|
static_cast<float>(box.z0));
|
|
}
|
|
|
|
// ymin-zmax edge
|
|
root = mXMerge[box.y0][box.z1]->GetZeroBase(box.LX);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_YMIN_ZMAX;
|
|
table.Insert(EI_YMIN_ZMAX,
|
|
GetXInterp(root, box.y0, box.z1),
|
|
static_cast<float>(box.y0),
|
|
static_cast<float>(box.z1));
|
|
}
|
|
|
|
// ymax-zmin edge
|
|
root = mXMerge[box.y1][box.z0]->GetZeroBase(box.LX);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_YMAX_ZMIN;
|
|
table.Insert(EI_YMAX_ZMIN,
|
|
GetXInterp(root, box.y1, box.z0),
|
|
static_cast<float>(box.y1),
|
|
static_cast<float>(box.z0));
|
|
}
|
|
|
|
// ymax-zmax edge
|
|
root = mXMerge[box.y1][box.z1]->GetZeroBase(box.LX);
|
|
if (root != -1)
|
|
{
|
|
type |= EB_YMAX_ZMAX;
|
|
table.Insert(EI_YMAX_ZMAX,
|
|
GetXInterp(root, box.y1, box.z1),
|
|
static_cast<float>(box.y1),
|
|
static_cast<float>(box.z1));
|
|
}
|
|
}
|
|
|
|
// Edge extraction for single boxes (1x1x1).
|
|
void GetXMinEdgesS(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMIN_YMAX);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_XMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMIN_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_XMIN_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_XMIN_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x0 + mSize * (box.y0 + mSize * box.z0);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSize;
|
|
// F(x,y+1,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSizeSqr;
|
|
// F(x,y+1,z+1)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i -= mSize;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMIN_ZMIN);
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_XMIN_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMIN_ZMAX);
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_XMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_XMIN,
|
|
table.GetVertex(EI_XMIN_ZMIN)[0],
|
|
table.GetVertex(EI_XMIN_ZMIN)[1],
|
|
table.GetVertex(EI_XMIN_YMIN)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMIN_YMIN, FI_XMIN);
|
|
table.InsertEdge(EI_XMIN_YMAX, FI_XMIN);
|
|
table.InsertEdge(EI_XMIN_ZMIN, FI_XMIN);
|
|
table.InsertEdge(EI_XMIN_ZMAX, FI_XMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
void GetXMaxEdgesS(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_XMAX_YMAX);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_XMAX_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_XMAX_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_XMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_XMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_XMAX_ZMIN, EI_XMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x1 + mSize * (box.y0 + mSize * box.z0);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSize;
|
|
// F(x,y+1,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSizeSqr;
|
|
// F(x,y+1,z+1)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i -= mSize;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_XMAX_ZMIN);
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_XMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_XMAX_ZMAX);
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_XMAX_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_XMAX,
|
|
table.GetVertex(EI_XMAX_ZMIN)[0],
|
|
table.GetVertex(EI_XMAX_ZMIN)[1],
|
|
table.GetVertex(EI_XMAX_YMIN)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMAX_YMIN, FI_XMAX);
|
|
table.InsertEdge(EI_XMAX_YMAX, FI_XMAX);
|
|
table.InsertEdge(EI_XMAX_ZMIN, FI_XMAX);
|
|
table.InsertEdge(EI_XMAX_ZMAX, FI_XMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
void GetYMinEdgesS(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_XMAX_YMIN);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_YMIN_ZMIN, EI_YMIN_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x0 + mSize * (box.y0 + mSize * box.z0);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSizeSqr;
|
|
// F(x+1,y,z+1)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
--i;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_YMIN_ZMIN);
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_YMIN_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMIN_YMIN, EI_YMIN_ZMAX);
|
|
table.InsertEdge(EI_XMAX_YMIN, EI_YMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_YMIN,
|
|
table.GetVertex(EI_YMIN_ZMIN)[0],
|
|
table.GetVertex(EI_XMIN_YMIN)[1],
|
|
table.GetVertex(EI_XMIN_YMIN)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMIN_YMIN, FI_YMIN);
|
|
table.InsertEdge(EI_XMAX_YMIN, FI_YMIN);
|
|
table.InsertEdge(EI_YMIN_ZMIN, FI_YMIN);
|
|
table.InsertEdge(EI_YMIN_ZMAX, FI_YMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
void GetYMaxEdgesS(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_XMAX_YMAX);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_YMAX_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_YMAX_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_YMAX_ZMIN, EI_YMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x0 + mSize * (box.y1 + mSize * box.z0);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSizeSqr;
|
|
// F(x+1,y,z+1)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
--i;
|
|
// F(x,y,z+1)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_YMAX_ZMIN);
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_YMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMIN_YMAX, EI_YMAX_ZMAX);
|
|
table.InsertEdge(EI_XMAX_YMAX, EI_YMAX_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_YMAX,
|
|
table.GetVertex(EI_YMAX_ZMIN)[0],
|
|
table.GetVertex(EI_XMIN_YMAX)[1],
|
|
table.GetVertex(EI_XMIN_YMAX)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMIN_YMAX, FI_YMAX);
|
|
table.InsertEdge(EI_XMAX_YMAX, FI_YMAX);
|
|
table.InsertEdge(EI_YMAX_ZMIN, FI_YMAX);
|
|
table.InsertEdge(EI_YMAX_ZMAX, FI_YMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
void GetZMinEdgesS(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_XMAX_ZMIN);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMAX_ZMIN, EI_YMIN_ZMIN);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMAX_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_YMIN_ZMIN, EI_YMAX_ZMIN);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x0 + mSize * (box.y0 + mSize * box.z0);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
++i;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSize;
|
|
// F(x+1,y+1,z)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
--i;
|
|
// F(x,y+1,z)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_YMIN_ZMIN);
|
|
table.InsertEdge(EI_XMAX_ZMIN, EI_YMAX_ZMIN);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMIN_ZMIN, EI_YMAX_ZMIN);
|
|
table.InsertEdge(EI_XMAX_ZMIN, EI_YMIN_ZMIN);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_ZMIN,
|
|
table.GetVertex(EI_YMIN_ZMIN)[0],
|
|
table.GetVertex(EI_XMIN_ZMIN)[1],
|
|
table.GetVertex(EI_XMIN_ZMIN)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMIN_ZMIN, FI_ZMIN);
|
|
table.InsertEdge(EI_XMAX_ZMIN, FI_ZMIN);
|
|
table.InsertEdge(EI_YMIN_ZMIN, FI_ZMIN);
|
|
table.InsertEdge(EI_YMAX_ZMIN, FI_ZMIN);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
void GetZMaxEdgesS(const OctBox& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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:
|
|
return;
|
|
case 3:
|
|
table.InsertEdge(EI_XMIN_ZMAX, EI_XMAX_ZMAX);
|
|
break;
|
|
case 5:
|
|
table.InsertEdge(EI_XMIN_ZMAX, EI_YMIN_ZMAX);
|
|
break;
|
|
case 6:
|
|
table.InsertEdge(EI_XMAX_ZMAX, EI_YMIN_ZMAX);
|
|
break;
|
|
case 9:
|
|
table.InsertEdge(EI_XMIN_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 10:
|
|
table.InsertEdge(EI_XMAX_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 12:
|
|
table.InsertEdge(EI_YMIN_ZMAX, EI_YMAX_ZMAX);
|
|
break;
|
|
case 15:
|
|
{
|
|
// Four vertices, one per edge, need to disambiguate.
|
|
int i = box.x0 + mSize * (box.y0 + mSize * box.z1);
|
|
// F(x,y,z)
|
|
int64_t f00 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i++;
|
|
// F(x+1,y,z)
|
|
int64_t f10 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i += mSize;
|
|
// F(x+1,y+1,z)
|
|
int64_t f11 = static_cast<int64_t>(mInputVoxels[i]);
|
|
i--;
|
|
// F(x,y+1,z)
|
|
int64_t f01 = static_cast<int64_t>(mInputVoxels[i]);
|
|
int64_t det = f00 * f11 - f01 * f10;
|
|
|
|
if (det > 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P2>, <P1,P3>.
|
|
table.InsertEdge(EI_XMIN_ZMAX, EI_YMIN_ZMAX);
|
|
table.InsertEdge(EI_XMAX_ZMAX, EI_YMAX_ZMAX);
|
|
}
|
|
else if (det < 0)
|
|
{
|
|
// Disjoint hyperbolic segments, pair <P0,P3>, <P1,P2>.
|
|
table.InsertEdge(EI_XMIN_ZMAX, EI_YMAX_ZMAX);
|
|
table.InsertEdge(EI_XMAX_ZMAX, EI_YMIN_ZMAX);
|
|
}
|
|
else
|
|
{
|
|
// Plus-sign configuration, add branch point to
|
|
// tessellation.
|
|
table.Insert(FI_ZMAX,
|
|
table.GetVertex(EI_YMIN_ZMAX)[0],
|
|
table.GetVertex(EI_XMIN_ZMAX)[1],
|
|
table.GetVertex(EI_XMIN_ZMAX)[2]);
|
|
|
|
// Add edges sharing the branch point.
|
|
table.InsertEdge(EI_XMIN_ZMAX, FI_ZMAX);
|
|
table.InsertEdge(EI_XMAX_ZMAX, FI_ZMAX);
|
|
table.InsertEdge(EI_YMIN_ZMAX, FI_ZMAX);
|
|
table.InsertEdge(EI_YMAX_ZMAX, FI_ZMAX);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
}
|
|
|
|
// Edge extraction for merged boxes.
|
|
class Sort0
|
|
{
|
|
public:
|
|
bool operator()(Vertex const& arg0, Vertex const& arg1) const
|
|
{
|
|
if (arg0[0] < arg1[0])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[0] > arg1[0])
|
|
{
|
|
return false;
|
|
}
|
|
if (arg0[1] < arg1[1])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[1] > arg1[1])
|
|
{
|
|
return false;
|
|
}
|
|
return arg0[2] < arg1[2];
|
|
}
|
|
};
|
|
|
|
class Sort1
|
|
{
|
|
public:
|
|
bool operator()(Vertex const& arg0, Vertex const& arg1) const
|
|
{
|
|
if (arg0[2] < arg1[2])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[2] > arg1[2])
|
|
{
|
|
return false;
|
|
}
|
|
if (arg0[0] < arg1[0])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[0] > arg1[0])
|
|
{
|
|
return false;
|
|
}
|
|
return arg0[1] < arg1[1];
|
|
}
|
|
};
|
|
|
|
class Sort2
|
|
{
|
|
public:
|
|
bool operator()(Vertex const& arg0, Vertex const& arg1) const
|
|
{
|
|
if (arg0[1] < arg1[1])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[1] > arg1[1])
|
|
{
|
|
return false;
|
|
}
|
|
if (arg0[2] < arg1[2])
|
|
{
|
|
return true;
|
|
}
|
|
if (arg0[2] > arg1[2])
|
|
{
|
|
return false;
|
|
}
|
|
return arg0[0] < arg1[0];
|
|
}
|
|
};
|
|
|
|
void GetZMinEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMIN_ZMIN;
|
|
end1 = EI_XMAX_ZMIN;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMIN_ZMIN;
|
|
end1 = EI_YMIN_ZMIN;
|
|
break;
|
|
case 6:
|
|
end0 = EI_YMIN_ZMIN;
|
|
end1 = EI_XMAX_ZMIN;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMIN_ZMIN;
|
|
end1 = EI_YMAX_ZMIN;
|
|
break;
|
|
case 10:
|
|
end0 = EI_YMAX_ZMIN;
|
|
end1 = EI_XMAX_ZMIN;
|
|
break;
|
|
case 12:
|
|
end0 = EI_YMIN_ZMIN;
|
|
end1 = EI_YMAX_ZMIN;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort0> vSet;
|
|
|
|
for (int x = box.x0 + 1; x < box.x1; ++x)
|
|
{
|
|
auto const& merge = mYMerge[x][box.z0];
|
|
if (merge->IsZeroEdge(box.LY) || merge->HasZeroSubedge(box.LY))
|
|
{
|
|
int root = merge->GetZeroBase(box.LY);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(x),
|
|
GetYInterp(x, root, box.z0),
|
|
static_cast<Real>(box.z0) });
|
|
}
|
|
}
|
|
|
|
for (int y = box.y0 + 1; y < box.y1; ++y)
|
|
{
|
|
auto const& merge = mXMerge[y][box.z0];
|
|
if (merge->IsZeroEdge(box.LX) || merge->HasZeroSubedge(box.LX))
|
|
{
|
|
int root = merge->GetZeroBase(box.LX);
|
|
vSet.insert(Vertex{
|
|
GetXInterp(root, y, box.z0),
|
|
static_cast<Real>(y),
|
|
static_cast<Real>(box.z0) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0x = std::floor((*vSet.begin())[0]);
|
|
Real v1x = std::floor((*vSet.rbegin())[0]);
|
|
Real e0x = std::floor(table.GetVertex(end0)[0]);
|
|
Real e1x = std::floor(table.GetVertex(end1)[0]);
|
|
if (e1x <= v0x && v1x <= e0x)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
void GetZMaxEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMIN_ZMAX;
|
|
end1 = EI_XMAX_ZMAX;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMIN_ZMAX;
|
|
end1 = EI_YMIN_ZMAX;
|
|
break;
|
|
case 6:
|
|
end0 = EI_YMIN_ZMAX;
|
|
end1 = EI_XMAX_ZMAX;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMIN_ZMAX;
|
|
end1 = EI_YMAX_ZMAX;
|
|
break;
|
|
case 10:
|
|
end0 = EI_YMAX_ZMAX;
|
|
end1 = EI_XMAX_ZMAX;
|
|
break;
|
|
case 12:
|
|
end0 = EI_YMIN_ZMAX;
|
|
end1 = EI_YMAX_ZMAX;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort0> vSet;
|
|
|
|
for (int x = box.x0 + 1; x < box.x1; ++x)
|
|
{
|
|
auto const& merge = mYMerge[x][box.z1];
|
|
if (merge->IsZeroEdge(box.LY) || merge->HasZeroSubedge(box.LY))
|
|
{
|
|
int root = merge->GetZeroBase(box.LY);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(x),
|
|
GetYInterp(x, root, box.z1),
|
|
static_cast<Real>(box.z1) });
|
|
}
|
|
}
|
|
|
|
for (int y = box.y0 + 1; y < box.y1; ++y)
|
|
{
|
|
auto const& merge = mXMerge[y][box.z1];
|
|
if (merge->IsZeroEdge(box.LX) || merge->HasZeroSubedge(box.LX))
|
|
{
|
|
int root = merge->GetZeroBase(box.LX);
|
|
vSet.insert(Vertex{
|
|
GetXInterp(root, y, box.z1),
|
|
static_cast<Real>(y),
|
|
static_cast<Real>(box.z1) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0x = std::floor((*vSet.begin())[0]);
|
|
Real v1x = std::floor((*vSet.rbegin())[0]);
|
|
Real e0x = std::floor(table.GetVertex(end0)[0]);
|
|
Real e1x = std::floor(table.GetVertex(end1)[0]);
|
|
if (e1x <= v0x && v1x <= e0x)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
void GetYMinEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_XMAX_YMIN;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_YMIN_ZMIN;
|
|
break;
|
|
case 6:
|
|
end0 = EI_YMIN_ZMIN;
|
|
end1 = EI_XMAX_YMIN;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_YMIN_ZMAX;
|
|
break;
|
|
case 10:
|
|
end0 = EI_YMIN_ZMAX;
|
|
end1 = EI_XMAX_YMIN;
|
|
break;
|
|
case 12:
|
|
end0 = EI_YMIN_ZMIN;
|
|
end1 = EI_YMIN_ZMAX;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort1> vSet;
|
|
|
|
for (int x = box.x0 + 1; x < box.x1; ++x)
|
|
{
|
|
auto const& merge = mZMerge[x][box.y0];
|
|
if (merge->IsZeroEdge(box.LZ) || merge->HasZeroSubedge(box.LZ))
|
|
{
|
|
int root = merge->GetZeroBase(box.LZ);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(x),
|
|
static_cast<Real>(box.y0),
|
|
GetZInterp(x, box.y0, root) });
|
|
}
|
|
}
|
|
|
|
for (int z = box.z0 + 1; z < box.z1; ++z)
|
|
{
|
|
auto const& merge = mXMerge[box.y0][z];
|
|
if (merge->IsZeroEdge(box.LX) || merge->HasZeroSubedge(box.LX))
|
|
{
|
|
int root = merge->GetZeroBase(box.LX);
|
|
vSet.insert(Vertex{
|
|
GetXInterp(root, box.y0, z),
|
|
static_cast<Real>(box.y0),
|
|
static_cast<Real>(z) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0z = std::floor((*vSet.begin())[2]);
|
|
Real v1z = std::floor((*vSet.rbegin())[2]);
|
|
Real e0z = std::floor(table.GetVertex(end0)[2]);
|
|
Real e1z = std::floor(table.GetVertex(end1)[2]);
|
|
if (e1z <= v0z && v1z <= e0z)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
void GetYMaxEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMIN_YMAX;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMIN_YMAX;
|
|
end1 = EI_YMAX_ZMIN;
|
|
break;
|
|
case 6:
|
|
end0 = EI_YMAX_ZMIN;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMIN_YMAX;
|
|
end1 = EI_YMAX_ZMAX;
|
|
break;
|
|
case 10:
|
|
end0 = EI_YMAX_ZMAX;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 12:
|
|
end0 = EI_YMAX_ZMIN;
|
|
end1 = EI_YMAX_ZMAX;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort1> vSet;
|
|
|
|
for (int x = box.x0 + 1; x < box.x1; ++x)
|
|
{
|
|
auto const& merge = mZMerge[x][box.y1];
|
|
if (merge->IsZeroEdge(box.LZ) || merge->HasZeroSubedge(box.LZ))
|
|
{
|
|
int root = merge->GetZeroBase(box.LZ);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(x),
|
|
static_cast<Real>(box.y1),
|
|
GetZInterp(x, box.y1, root) });
|
|
}
|
|
}
|
|
|
|
for (int z = box.z0 + 1; z < box.z1; ++z)
|
|
{
|
|
auto const& merge = mXMerge[box.y1][z];
|
|
if (merge->IsZeroEdge(box.LX) || merge->HasZeroSubedge(box.LX))
|
|
{
|
|
int root = merge->GetZeroBase(box.LX);
|
|
vSet.insert(Vertex{
|
|
GetXInterp(root, box.y1, z),
|
|
static_cast<Real>(box.y1),
|
|
static_cast<Real>(z) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0z = std::floor((*vSet.begin())[2]);
|
|
Real v1z = std::floor((*vSet.rbegin())[2]);
|
|
Real e0z = std::floor(table.GetVertex(end0)[2]);
|
|
Real e1z = std::floor(table.GetVertex(end1)[2]);
|
|
if (e1z <= v0z && v1z <= e0z)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
void GetXMinEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_XMIN_YMAX;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_XMIN_ZMIN;
|
|
break;
|
|
case 6:
|
|
end0 = EI_XMIN_ZMIN;
|
|
end1 = EI_XMIN_YMAX;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMIN_YMIN;
|
|
end1 = EI_XMIN_ZMAX;
|
|
break;
|
|
case 10:
|
|
end0 = EI_XMIN_ZMAX;
|
|
end1 = EI_XMIN_YMAX;
|
|
break;
|
|
case 12:
|
|
end0 = EI_XMIN_ZMIN;
|
|
end1 = EI_XMIN_ZMAX;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort2> vSet;
|
|
|
|
for (int z = box.z0 + 1; z < box.z1; ++z)
|
|
{
|
|
auto const& merge = mYMerge[box.x0][z];
|
|
if (merge->IsZeroEdge(box.LY) || merge->HasZeroSubedge(box.LY))
|
|
{
|
|
int root = merge->GetZeroBase(box.LY);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(box.x0),
|
|
GetYInterp(box.x0, root, z),
|
|
static_cast<Real>(z) });
|
|
}
|
|
}
|
|
|
|
for (int y = box.y0 + 1; y < box.y1; ++y)
|
|
{
|
|
auto const& merge = mZMerge[box.x0][y];
|
|
if (merge->IsZeroEdge(box.LZ) || merge->HasZeroSubedge(box.LZ))
|
|
{
|
|
int root = merge->GetZeroBase(box.LZ);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(box.x0),
|
|
static_cast<Real>(y),
|
|
GetZInterp(box.x0, y, root) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0y = std::floor((*vSet.begin())[1]);
|
|
Real v1y = std::floor((*vSet.rbegin())[1]);
|
|
Real e0y = std::floor(table.GetVertex(end0)[1]);
|
|
Real e1y = std::floor(table.GetVertex(end1)[1]);
|
|
if (e1y <= v0y && v1y <= e0y)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
void GetXMaxEdgesM(OctBox const& box, unsigned int type, VETable& table)
|
|
{
|
|
unsigned 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;
|
|
}
|
|
|
|
int end0 = 0, end1 = 0;
|
|
switch (faceType)
|
|
{
|
|
case 0:
|
|
return;
|
|
case 3:
|
|
end0 = EI_XMAX_YMIN;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 5:
|
|
end0 = EI_XMAX_YMIN;
|
|
end1 = EI_XMAX_ZMIN;
|
|
break;
|
|
case 6:
|
|
end0 = EI_XMAX_ZMIN;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 9:
|
|
end0 = EI_XMAX_YMIN;
|
|
end1 = EI_XMAX_ZMAX;
|
|
break;
|
|
case 10:
|
|
end0 = EI_XMAX_ZMAX;
|
|
end1 = EI_XMAX_YMAX;
|
|
break;
|
|
case 12:
|
|
end0 = EI_XMAX_ZMIN;
|
|
end1 = EI_XMAX_ZMAX;
|
|
break;
|
|
default:
|
|
LogError("The level value cannot be an exact integer.");
|
|
}
|
|
|
|
std::set<Vertex, Sort2> vSet;
|
|
|
|
for (int z = box.z0 + 1; z < box.z1; ++z)
|
|
{
|
|
auto const& merge = mYMerge[box.x1][z];
|
|
if (merge->IsZeroEdge(box.LY) || merge->HasZeroSubedge(box.LY))
|
|
{
|
|
int root = merge->GetZeroBase(box.LY);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(box.x1),
|
|
GetYInterp(box.x1, root, z),
|
|
static_cast<Real>(z) });
|
|
}
|
|
}
|
|
|
|
for (int y = box.y0 + 1; y < box.y1; y++)
|
|
{
|
|
auto const& merge = mZMerge[box.x1][y];
|
|
if (merge->IsZeroEdge(box.LZ) || merge->HasZeroSubedge(box.LZ))
|
|
{
|
|
int root = merge->GetZeroBase(box.LZ);
|
|
vSet.insert(Vertex{
|
|
static_cast<Real>(box.x1),
|
|
static_cast<Real>(y),
|
|
GetZInterp(box.x1, y, root) });
|
|
}
|
|
}
|
|
|
|
// Add subdivision.
|
|
if (vSet.size() == 0)
|
|
{
|
|
table.InsertEdge(end0, end1);
|
|
return;
|
|
}
|
|
|
|
Real v0y = std::floor((*vSet.begin())[1]);
|
|
Real v1y = std::floor((*vSet.rbegin())[1]);
|
|
Real e0y = std::floor(table.GetVertex(end0)[1]);
|
|
Real e1y = std::floor(table.GetVertex(end1)[1]);
|
|
if (e1y <= v0y && v1y <= e0y)
|
|
{
|
|
std::swap(end0, end1);
|
|
}
|
|
|
|
// Add vertices.
|
|
int v0 = table.GetNumVertices(), v1 = v0;
|
|
for (auto const& position : vSet)
|
|
{
|
|
table.Insert(position);
|
|
}
|
|
|
|
// Add edges.
|
|
table.InsertEdge(end0, v1);
|
|
++v1;
|
|
int const imax = static_cast<int>(vSet.size());
|
|
for (int i = 1; i < imax; ++i, ++v0, ++v1)
|
|
{
|
|
table.InsertEdge(v0, v1);
|
|
}
|
|
table.InsertEdge(v0, end1);
|
|
}
|
|
|
|
// Support for normal vector calculations.
|
|
Vertex GetGradient(Vertex const& position) const
|
|
{
|
|
Vertex vzero = { (Real)0, (Real)0, (Real)0 };
|
|
int x = static_cast<int>(position[0]);
|
|
if (x < 0 || x >= mTwoPowerN)
|
|
{
|
|
return vzero;
|
|
}
|
|
|
|
int y = static_cast<int>(position[1]);
|
|
if (y < 0 || y >= mTwoPowerN)
|
|
{
|
|
return vzero;
|
|
}
|
|
|
|
int z = static_cast<int>(position[2]);
|
|
if (z < 0 || z >= mTwoPowerN)
|
|
{
|
|
return vzero;
|
|
}
|
|
|
|
int i000 = x + mSize * (y + mSize * z);
|
|
int i100 = i000 + 1;
|
|
int i010 = i000 + mSize;
|
|
int i110 = i100 + mSize;
|
|
int i001 = i000 + mSizeSqr;
|
|
int i101 = i100 + mSizeSqr;
|
|
int i011 = i010 + mSizeSqr;
|
|
int i111 = i110 + mSizeSqr;
|
|
Real f000 = static_cast<Real>(mInputVoxels[i000]);
|
|
Real f100 = static_cast<Real>(mInputVoxels[i100]);
|
|
Real f010 = static_cast<Real>(mInputVoxels[i010]);
|
|
Real f110 = static_cast<Real>(mInputVoxels[i110]);
|
|
Real f001 = static_cast<Real>(mInputVoxels[i001]);
|
|
Real f101 = static_cast<Real>(mInputVoxels[i101]);
|
|
Real f011 = static_cast<Real>(mInputVoxels[i011]);
|
|
Real f111 = static_cast<Real>(mInputVoxels[i111]);
|
|
|
|
Real fx = position[0] - static_cast<Real>(x);
|
|
Real fy = position[1] - static_cast<Real>(y);
|
|
Real fz = position[2] - static_cast<Real>(z);
|
|
Real oneMinusX = (Real)1 - fx;
|
|
Real oneMinusY = (Real)1 - fy;
|
|
Real oneMinusZ = (Real)1 - fz;
|
|
Real tmp0, tmp1;
|
|
Vertex gradient;
|
|
|
|
tmp0 = oneMinusY * (f100 - f000) + fy * (f110 - f010);
|
|
tmp1 = oneMinusY * (f101 - f001) + fy * (f111 - f011);
|
|
gradient[0] = oneMinusZ * tmp0 + fz * tmp1;
|
|
|
|
tmp0 = oneMinusX * (f010 - f000) + fx * (f110 - f100);
|
|
tmp1 = oneMinusX * (f011 - f001) + fx * (f111 - f101);
|
|
gradient[1] = oneMinusZ * tmp0 + fz * tmp1;
|
|
|
|
tmp0 = oneMinusX * (f001 - f000) + fx * (f101 - f100);
|
|
tmp1 = oneMinusX * (f011 - f010) + fx * (f111 - f110);
|
|
gradient[2] = oneMinusY * tmp0 + oneMinusY * tmp1;
|
|
|
|
return gradient;
|
|
}
|
|
|
|
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,
|
|
I_QUANTITY = 18,
|
|
|
|
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
|
|
};
|
|
|
|
// image data
|
|
int mTwoPowerN, mSize, mSizeSqr;
|
|
T const* mInputVoxels;
|
|
Real mLevel;
|
|
|
|
bool mFixBoundary;
|
|
|
|
// Trees for linear merging.
|
|
Array2<std::shared_ptr<LinearMergeTree>> mXMerge, mYMerge, mZMerge;
|
|
|
|
// monoboxes
|
|
std::vector<OctBox> mBoxes;
|
|
};
|
|
}
|
|
|