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.
 
 

2266 lines
79 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.9.2021.04.22
#pragma once
#include <Mathematics/Logger.h>
#include <Mathematics/ConvexHull3.h>
#include <Mathematics/MinimumAreaBox2.h>
#include <Mathematics/VETManifoldMesh.h>
#include <Mathematics/AlignedBox.h>
#include <Mathematics/UniqueVerticesSimplices.h>
#include <cstring>
// Compute a minimum-volume oriented box containing the specified points. The
// algorithm is really about computing the minimum-volume box containing the
// convex hull of the points, so you must compute the convex hull or pass an
// an already built hull to the code. The convex hull is, of course, a convex
// polyhedron.
//
// According to
// J.O'Rourke, "Finding minimal enclosing boxes",
// Internat. J. Comput. Inform. Sci., 14:183-199, 1985.
// the minimum-volume oriented box must have at least two adjacent faces
// flush with edges of the convex polyhedron. The implementation processes
// all pairs of edges, determining for each pair the relevant box-face
// normals that are candidates for the minimum-volume box. I use an approach
// different from that of the details of proof in the paper; see
// https://www.geometrictools.com/Documentation/MinimumVolumeBox.pdf
// The computations involve an iterative minimizer, so you cannot expect
// to obtain the exact minimum-volume box, but you will obtain a good
// approximation to it based on how many samples the minimizer uses in its
// search. You can also derive from a class and override the virtual
// functions that are used for minimization in order to provided your own
// minimizer algorithm.
namespace gte
{
// The InputType is 'float' or 'double'. The ComputeType is 'double' when
// computeDouble is 'true' or it is the appropriate fixed-precision
// BSNumber<> class when computeDouble is 'false'. If you use rational
// arithmetic for the computations, you must increase the default program
// stack size significantly. In Microsoft Visual Studio, I set the Stack
// Reserve Size to 1 GB (which is 1073741824 bytes and is probably much
// more than required.).
template <typename InputType, bool computeDouble>
class MinimumVolumeBox3
{
public:
// Supporting constants and types for numerical computing.
static int constexpr NumWords = std::is_same<InputType, float>::value ? 342 : 2561;
using UIntegerType = UIntegerFP32<NumWords>;
using ComputeType = typename std::conditional<computeDouble, double, BSNumber<UIntegerType>>::type;
using RationalType = typename std::conditional<computeDouble, double, BSRational<UIntegerType>>::type;
using VCompute3 = Vector3<ComputeType>;
using RVCompute3 = Vector3<RationalType>;
// Supporting constants types for a compact vertex-edge-triangle mesh
// that represents the convex polyhedron input.
static size_t constexpr invalidIndex = std::numeric_limits<size_t>::max();
struct Edge
{
Edge()
:
V{ invalidIndex, invalidIndex },
T{ invalidIndex, invalidIndex }
{
}
std::array<size_t, 2> V;
std::array<size_t, 2> T;
};
struct Triangle
{
Triangle()
:
V{ invalidIndex, invalidIndex },
E{ invalidIndex, invalidIndex },
T{ invalidIndex, invalidIndex }
{
}
std::array<size_t, 3> V;
std::array<size_t, 3> E;
std::array<size_t, 3> T;
};
// Information about candidates for the minimum-volume box and about
// that box itself.
struct Candidate
{
Candidate()
:
edgeIndex{ invalidIndex, invalidIndex },
edge{},
N{ VCompute3::Zero(), VCompute3::Zero() },
M{ VCompute3::Zero(), VCompute3::Zero() },
f00(static_cast<ComputeType>(0)),
f10(static_cast<ComputeType>(0)),
f01(static_cast<ComputeType>(0)),
f11(static_cast<ComputeType>(0)),
levelCurveProcessorIndex(invalidIndex),
axis{ VCompute3::Unit(0), VCompute3::Unit(1), VCompute3::Unit(2) },
minSupportIndex{ invalidIndex, invalidIndex, invalidIndex },
maxSupportIndex{ invalidIndex, invalidIndex, invalidIndex },
volume(static_cast<ComputeType>(0))
{
}
// Set by ProcessEdgePair.
std::array<size_t, 2> edgeIndex;
std::array<Edge, 2> edge;
std::array<VCompute3, 2> N, M;
ComputeType f00, f10, f01, f11;
size_t levelCurveProcessorIndex;
// Set by Pair, MinimizerConstantT, MinimizerConstantS,
// MinimizerVariableS and MinimizerVariableT. The axis[0] and
// axis[1] are set by the aforementioned functions. The axis[2]
// is computed by ComputeVolume.
std::array<VCompute3, 3> axis;
// Set by ComputeVolume.
std::array<size_t, 3> minSupportIndex;
std::array<size_t, 3> maxSupportIndex;
RationalType volume;
};
// The rational representation of the minimum-volume box. The axis[]
// vectors are generally not unit length. To obtain a unit-length
// vector, use axis[i]/std::sqrt(sqrLengthAxis[i]).
struct RBox
{
RBox()
:
center(RVCompute3::Zero()),
axis{ RVCompute3::Zero(), RVCompute3::Zero(), RVCompute3::Zero() },
sqrLengthAxis(RVCompute3::Zero()),
volume(static_cast<RationalType>(0))
{
}
RVCompute3 center;
std::array<RVCompute3, 3> axis;
RVCompute3 sqrLengthAxis;
RVCompute3 scaledExtent;
RationalType volume;
};
public:
// Construction and destruction. To execute in the main thread, set
// numThreads to 0. To run multithreaded on the CPU, set numThreads
// to a positive number.
MinimumVolumeBox3(size_t numThreads = 0)
:
mNumThreads(numThreads),
mDomainIndex{},
mZero(static_cast<ComputeType>(0)),
mOne(static_cast<ComputeType>(1)),
mHalf(static_cast<ComputeType>(0.5)),
mNumVertices(0),
mNumTriangles(0),
mAdjacentPool{},
mVertexAdjacent{},
mEdges{},
mTriangles{},
mEdgeIndices{},
mOrigin{},
mVertices{},
mNormals{},
mAlignedCandidate{},
mMinimumVolumeObject{},
mLevelCurveProcessor{}
{
static_assert(std::is_floating_point<InputType>::value,
"The input type must be 'float' or 'double'.");
InitializeLevelCurveProcessors();
}
virtual ~MinimumVolumeBox3() = default;
// The class is a wrapper for operator()(*), so there is no need for
// copy semantics.
MinimumVolumeBox3(MinimumVolumeBox3 const&) = delete;
MinimumVolumeBox3& operator=(MinimumVolumeBox3 const&) = delete;
// The convex hull of the input points is computed. The output
// box is determined by the dimension of the hull.
// 0D: The hull is a single point. The box has center at that
// point, the axes are the standard Euclidean basis, and the
// extents are all 0.
// 1D: The hull is a line segment. The box has center at the
// midpoint of the segment, axis[0] is the segment direction,
// axis[1] and axis[2] are chosen so that the three axes form
// a right-handed orthonormal set, extent[0] is the half-length
// of the segment, and extent[1] and extent[2] are 0.
// 2D: The hull is a planar polygon. The box is the minimum-area
// box containing that polygon. The axis[0] and axis[1] are
// the axis directions for the planar box, axis[2] is normal
// to the plane of the polygon, extent[0] and extent[1] are
// the extents of the planar box, and extent[2] is 0.
// 3D: The hull is a convex polyhedron. The other operator()(*)
// function is called to compute the minimum-volume box of the
// polyhedron.
// If the dimension is 0, 1 or 2, the objects returned by the
// GetMinimumVolumeObject() and GetRationalBox() are invalid for this
// operator()(*).
int operator()(
int numPoints,
Vector3<InputType> const* points,
size_t lgMaxSample,
OrientedBox3<InputType>& box,
InputType& volume)
{
LogAssert(numPoints > 0 && points != nullptr && lgMaxSample >= 2,
"Invalid argument.");
InputType const zero = static_cast<InputType>(0);
InputType const one = static_cast<InputType>(1);
InputType const half = static_cast<InputType>(0.5);
ConvexHull3<InputType> ch3;
ch3(static_cast<size_t>(numPoints), points, 0);
size_t dimension = ch3.GetDimension();
auto const& hull = ch3.GetHull();
if (dimension == 0)
{
// The points are all the same.
box.center = points[hull[0]];
box.axis[0] = { one, zero, zero };
box.axis[1] = { zero, one, zero };
box.axis[2] = { zero, zero, one };
box.extent[0] = zero;
box.extent[1] = zero;
box.extent[2] = zero;
volume = zero;
return 0;
}
if (dimension == 1)
{
// The points lie on a line.
Vector3<InputType> direction = points[hull[1]] - points[hull[0]];
box.center = half * (points[hull[0]] + points[hull[1]]);
box.extent[0] = half * Normalize(direction);
box.extent[1] = zero;
box.extent[2] = zero;
box.axis[0] = direction;
ComputeOrthogonalComplement(1, &box.axis[0]);
volume = zero;
return 1;
}
if (dimension == 2)
{
// The points line on a plane. Get a coordinate system
// relative to the plane of the points. Choose the origin
// to be any of the input points.
Vector3<InputType> origin = points[hull[0]];
Vector3<InputType> normal = Vector3<InputType>::Zero();
size_t numHull = hull.size();
for (size_t i0 = numHull - 1, i1 = 1; i1 < numHull; i0 = i1++)
{
auto const& P0 = points[hull[i0]];
auto const& P1 = points[hull[i1]];
normal += Cross(P0, P1);
}
Vector3<InputType> basis[3];
basis[0] = normal;
ComputeOrthogonalComplement(1, basis);
// Project the input points onto the plane.
std::vector<Vector2<InputType>> projection(numPoints);
for (int i = 0; i < numPoints; ++i)
{
Vector3<InputType> diff = points[i] - origin;
projection[i][0] = Dot(basis[1], diff);
projection[i][1] = Dot(basis[2], diff);
}
// Compute the minimum area box in 2D.
MinimumAreaBox2<InputType, BSRational<UIntegerAP32>> mab2;
OrientedBox2<InputType> rectangle = mab2(numPoints, &projection[0]);
// Lift the values into 3D.
box.center = origin + rectangle.center[0] * basis[1] + rectangle.center[1] * basis[2];
box.axis[0] = rectangle.axis[0][0] * basis[1] + rectangle.axis[0][1] * basis[2];
box.axis[1] = rectangle.axis[1][0] * basis[1] + rectangle.axis[1][1] * basis[2];
box.axis[2] = basis[0];
box.extent[0] = rectangle.extent[0];
box.extent[1] = rectangle.extent[1];
box.extent[2] = zero;
volume = zero;
return 2;
}
// Remove duplicated vertices and reindex them for the convex
// polyhedron.
std::vector<Vector3<InputType>> inVertices(numPoints);
std::memcpy(inVertices.data(), points, inVertices.size() * sizeof(Vector3<InputType>));
auto const& triangles = ch3.GetHull();
std::vector<int> inIndices(triangles.size());
size_t current = 0;
for (auto index : triangles)
{
inIndices[current++] = static_cast<int>(index);
}
UniqueVerticesSimplices<Vector3<InputType>, int, 3> uvt;
std::vector<Vector3<InputType>> outVertices;
std::vector<int> outIndices;
uvt.RemoveDuplicateAndUnusedVertices(inVertices, inIndices,
outVertices, outIndices);
operator()(static_cast<int>(outVertices.size()), outVertices.data(),
static_cast<int>(outIndices.size()), outIndices.data(),
lgMaxSample, box, volume);
return 3;
}
// The points form a nondegenerate convex polyhedron. The inputs
// 'vertices' and 'indices' must be nonempty and the 'vertices' must
// have no duplicates. The triangle faces are triples of the indices;
// there are indices.size()/3 triangles. Also, 0 <= indices[i] <
// vertices.size() for all 0 <= i < indices.size(). The logarithm base
// 2 of maximum sample index must satisfy the condition
// lgMaxSample >= 2, so there are at least 4 samples. Do not choose it
// to be too large when using rational computation because the
// computational costs are excessive. You can override the minimizer
// functions to use your own minimization algorithm; see the comments
// before MinimizerConstantT.
void operator()(
int numVertices,
Vector3<InputType> const* inVertices,
int numIndices,
int const* inIndices,
size_t lgMaxSample,
OrientedBox3<InputType>& box,
InputType& volume)
{
LogAssert(
numVertices > 0 && inVertices != nullptr &&
numIndices > 0 && inIndices != nullptr &&
(numIndices % 3) == 0 && lgMaxSample >= 2,
"Invalid argument.");
for (int i = 0; i < numIndices; ++i)
{
LogAssert(0 <= inIndices[i] && inIndices[i] < numVertices,
"Invalid index.");
}
std::vector<Vector3<InputType>> vertices(numVertices);
std::memcpy(vertices.data(), inVertices,
vertices.size() * sizeof(Vector3<InputType>));
std::vector<int> indices(numIndices);
std::memcpy(indices.data(), inIndices,
indices.size() * sizeof(int));
GenerateSubdivision(lgMaxSample);
CreateCompactMesh(vertices, indices);
PrepareVerticesAndNormals(vertices);
ComputeAlignedCandidate();
GetMinimumVolumeCandidate();
GetMinimumVolumeBox(box, volume);
}
// For more information about the minimum-volume box, access the
// candidate that stores it.
inline Candidate const& GetMinimumVolumeObject() const
{
return mMinimumVolumeObject;
}
// The operator()(*) function returns a floating-point box and volume.
// If you computed using rational arithmetic, the rational box is
// accessed by this member function.
inline void GetRationalBox(RBox& rbox) const
{
rbox = mRBox;
}
protected:
void CreateDomainIndex(size_t& current, size_t end0, size_t end1)
{
size_t mid = (end0 + end1) / 2;
if (mid != end0 && mid != end1)
{
mDomainIndex[current++] = { mid, end0, end1 };
CreateDomainIndex(current, end0, mid);
CreateDomainIndex(current, mid, end1);
}
}
void GenerateSubdivision(size_t lgMaxSample)
{
mMaxSample = (static_cast<size_t>(1) << lgMaxSample);
mDomainIndex.resize(mMaxSample - 1);
size_t current = 0;
CreateDomainIndex(current, 0, mMaxSample);
}
// The vertices are stored in a vertex-edge-triangle manifold mesh.
// Each vertex as a set of adjacent vertices, a set of adjacent
// edges and a set of adjacent triangles. The adjacent vertices are
// repackaged into mVertexAdjacent[] and mAdjacentPool[]. For
// vertex v with n adjacent vertices, mVertexAdjacent[v] is the
// index into mAdjacentPool[] whre the n adjacent vertices are
// stored. If the adjacent vertices are a[0] through a[n-1], then
// mAdjacentPool[mVertexAdjacent[v] + i] is a[i].
void CreateCompactMesh(
std::vector<Vector3<InputType>> const& vertices,
std::vector<int> const& indices)
{
mNumVertices = vertices.size();
mNumTriangles = indices.size() / 3;
VETManifoldMesh mesh;
int const* current = indices.data();
for (size_t t = 0; t < mNumTriangles; ++t)
{
int v0 = *current++;
int v1 = *current++;
int v2 = *current++;
mesh.Insert(v0, v1, v2);
}
// It is implicit in the construction of mVertexAdjacent that
// (1) the vertex indices v satisfy 0 <= v < N for a mesh of
// N vertices and
// (2) the vertex map itself is ordered as <0,vertex0>,
// <1,vertex1>, ..., <N-1,vertexNm1>.
// Condition (1) is guaranteed because the input to the MVB3
// constructor uses the contiguous indices of the position array.
// Condition (2) is not guaranteed because VETManifoldMesh::VMap
// is a std::unordered_map. The vertices must be sorted here to
// satisfy condition2.
auto const& vmap = mesh.GetVertices();
std::map<int, VETManifoldMesh::Vertex*> sortedVMap;
for (auto const& element : vmap)
{
sortedVMap.emplace(element.first, element.second.get());
}
size_t numAdjacentPool = 0;
for (auto const& element : sortedVMap)
{
numAdjacentPool += element.second->VAdjacent.size() + 1;
}
mAdjacentPool.resize(numAdjacentPool);
mVertexAdjacent.resize(sortedVMap.size());
size_t apIndex = 0, vaIndex = 0;
for (auto const& element : sortedVMap)
{
auto const& adjacent = element.second->VAdjacent;
mVertexAdjacent[vaIndex++] = apIndex;
mAdjacentPool[apIndex++] = adjacent.size();
for (auto v : adjacent)
{
mAdjacentPool[apIndex++] = static_cast<size_t>(v);
}
}
auto const& emap = mesh.GetEdges();
auto const& tmap = mesh.GetTriangles();
mEdges.resize(emap.size());
mTriangles.resize(tmap.size());
std::map<ETManifoldMesh::Edge*, size_t> edgeIndexMap;
size_t index = 0;
for (auto const& element : emap)
{
edgeIndexMap.emplace(element.second.get(), index);
for (size_t j = 0; j < 2; ++j)
{
mEdges[index].V[j] = (size_t)element.second->V[j];
}
++index;
}
std::map<ETManifoldMesh::Triangle*, size_t> triangleIndexMap;
index = 0;
for (auto const& element : tmap)
{
triangleIndexMap.emplace(element.second.get(), index);
for (size_t j = 0; j < 3; ++j)
{
mTriangles[index].V[j] = (size_t)element.second->V[j];
}
++index;
}
index = 0;
for (auto const& element : emap)
{
for (size_t j = 0; j < 2; ++j)
{
auto tri = element.second->T[j];
auto titer = triangleIndexMap.find(tri);
mEdges[index].T[j] = titer->second;
}
++index;
}
index = 0;
for (auto const& element : tmap)
{
for (size_t j = 0; j < 3; ++j)
{
auto edg = element.second->E[j];
auto eiter = edgeIndexMap.find(edg);
mTriangles[index].E[j] = eiter->second;
}
for (size_t j = 0; j < 3; ++j)
{
auto tri = element.second->T[j];
auto titer = triangleIndexMap.find(tri);
mTriangles[index].T[j] = titer->second;
}
++index;
}
size_t const numEdges = mEdges.size();
mEdgeIndices.reserve(mEdges.size() * mEdges.size());
for (size_t e0 = 0; e0 < numEdges; ++e0)
{
for (size_t e1 = e0 + 1; e1 < numEdges; ++e1)
{
mEdgeIndices.push_back({ e0, e1 });
}
}
}
template <bool useDouble = computeDouble>
typename std::enable_if<useDouble, void>::type
ComputeNormal(VCompute3 const& edge0, VCompute3 const& edge1, VCompute3& normal)
{
normal = UnitCross(edge0, edge1);
}
template <bool useDouble = computeDouble>
typename std::enable_if<!useDouble, void>::type
ComputeNormal(VCompute3 const& edge0, VCompute3 const& edge1, VCompute3& normal)
{
normal = Cross(edge0, edge1);
}
void PrepareVerticesAndNormals(std::vector<Vector3<InputType>> const& vertices)
{
// Convert from floating-point type to the compute type (double or
// rational). The origin is considered to be inVertices[0]).
mVertices.resize(mNumVertices);
for (int32_t j = 0; j < 3; ++j)
{
mOrigin[j] = static_cast<ComputeType>(vertices[0][j]);
mVertices[0][j] = static_cast<ComputeType>(0);
}
for (size_t i = 1; i < mNumVertices; ++i)
{
for (int32_t j = 0; j < 3; ++j)
{
mVertices[i][j] = static_cast<ComputeType>(vertices[i][j]) - mOrigin[j];
}
}
// Compute inner-pointing normals that are not required to be
// unit length.
mNormals.resize(mNumTriangles);
for (size_t i = 0; i < mNumTriangles; ++i)
{
auto const& tri = mTriangles[i];
size_t v0 = tri.V[0], v1 = tri.V[1], v2 = tri.V[2];
VCompute3 edge10 = mVertices[v1] - mVertices[v0];
VCompute3 edge20 = mVertices[v2] - mVertices[v0];
ComputeNormal(edge20, edge10, mNormals[i]);
}
}
void ComputeAlignedCandidate()
{
VCompute3 pmin, pmax;
for (int32_t j = 0; j < 3; ++j)
{
mAlignedCandidate.maxSupportIndex[j] =
GetExtreme(mAlignedCandidate.axis[j], pmax[j]);
mAlignedCandidate.minSupportIndex[j] =
GetExtreme(-mAlignedCandidate.axis[j], pmin[j]);
pmin[j] = -pmin[j];
}
VCompute3 diff = pmax - pmin;
mAlignedCandidate.volume = diff[0] * diff[1] * diff[2];
}
size_t GetExtreme(VCompute3 const& direction, ComputeType& dMax)
{
size_t vMax = 0;
dMax = Dot(direction, mVertices[vMax]);
for (size_t i = 0; i < mNumVertices; ++i)
{
size_t vLocalMax = vMax;
ComputeType dLocalMax = dMax;
size_t const* adjacent = &mAdjacentPool[mVertexAdjacent[vMax]];
size_t numAdjacent = *adjacent++;
for (size_t j = 1; j <= numAdjacent; ++j)
{
size_t vCandidate = *adjacent++;
ComputeType dCandidate = Dot(direction, mVertices[vCandidate]);
if (dCandidate > dLocalMax)
{
vLocalMax = vCandidate;
dLocalMax = dCandidate;
}
}
if (vMax != vLocalMax)
{
vMax = vLocalMax;
dMax = dLocalMax;
}
else
{
break;
}
}
return vMax;
}
void ComputeVolume(Candidate& candidate)
{
// The last axis is needed only when computing the volume for
// comparison to the current candidate volume, so compute this
// axis now.
candidate.axis[2] = Cross(candidate.axis[0], candidate.axis[1]);
VCompute3 pmin, pmax;
candidate.minSupportIndex[0] = mEdges[candidate.edgeIndex[0]].V[0];
pmin[0] = Dot(candidate.axis[0], mVertices[candidate.minSupportIndex[0]]);
candidate.maxSupportIndex[0] = GetExtreme(candidate.axis[0], pmax[0]);
candidate.minSupportIndex[1] = mEdges[candidate.edgeIndex[1]].V[0];
pmin[1] = Dot(candidate.axis[1], mVertices[candidate.minSupportIndex[1]]);
candidate.maxSupportIndex[1] = GetExtreme(candidate.axis[1], pmax[1]);
candidate.axis[2] = Cross(candidate.axis[0], candidate.axis[1]);
candidate.minSupportIndex[2] = GetExtreme(-candidate.axis[2], pmin[2]);
pmin[2] = -pmin[2];
candidate.maxSupportIndex[2] = GetExtreme(candidate.axis[2], pmax[2]);
VCompute3 diff = pmax - pmin;
candidate.volume =
static_cast<RationalType>(diff[0] * diff[1] * diff[2]) /
static_cast<RationalType>(Dot(candidate.axis[2], candidate.axis[2]));
}
void ProcessEdgePair(std::array<size_t, 2> const& edgeIndex, Candidate& mvCandidate)
{
// Examine the zero-valued level curves for
// F(s,t)
// = Dot((1-s)*edge0.N0 + s*edge0.N1, (1-t)*edge1.N0 + t*edge1.N1)
// = (1-s)*(1-t)*Dot(edge0.N0,edge1.N0)
// + (1-s)*t*Dot(edge0.N0,edge1.N1)
// + s*(1-t)*Dot(edge0.N1,edge1.N0)
// + s*t*Dot(edge0.N1,edge1.N1)
// = (1-s)*(1-t)*f00 + (1-s)*t*f01 + s*(1-t)*f10 + s*t*f11
// = a00 + a10*s + a01*t + a11*s*t
// = [(a00*a11 - a01*a10) + (a01 + a11*s)*(a10 + a11*t)]/a11
// where a00 = f00, a10 = f10-f00, a01 = f01-f00 and
// a11 = f00-f01-f10+f11. Let d = a00*a11 - a01*a10 =
// f00*f11 - f01*f10. If d = 0, then the level curves are
// s = -a01/a11 and t = -a10/a11. If d != 0, then the level curves
// are hyperbolic curves with asymptotes s = -a01/a11 and
// t = -a10/a11.
Candidate candidate = mAlignedCandidate;
candidate.edgeIndex = edgeIndex;
Edge const& edge0 = mEdges[candidate.edgeIndex[0]];
Edge const& edge1 = mEdges[candidate.edgeIndex[1]];
candidate.edge[0] = edge0;
candidate.edge[1] = edge1;
candidate.N[0] = mNormals[edge0.T[0]];
candidate.N[1] = mNormals[edge0.T[1]];
candidate.M[0] = mNormals[edge1.T[0]];
candidate.M[1] = mNormals[edge1.T[1]];
candidate.f00 = Dot(candidate.N[0], candidate.M[0]);
candidate.f10 = Dot(candidate.N[1], candidate.M[0]);
candidate.f01 = Dot(candidate.N[0], candidate.M[1]);
candidate.f11 = Dot(candidate.N[1], candidate.M[1]);
uint32_t bits00 = (candidate.f00 > mZero ? 1 : (candidate.f00 < mZero ? 2 : 0));
uint32_t bits10 = (candidate.f10 > mZero ? 1 : (candidate.f10 < mZero ? 2 : 0));
uint32_t bits01 = (candidate.f01 > mZero ? 1 : (candidate.f01 < mZero ? 2 : 0));
uint32_t bits11 = (candidate.f11 > mZero ? 1 : (candidate.f11 < mZero ? 2 : 0));
uint32_t index = bits00 | (bits10 << 2) | (bits01 << 4) | (bits11 << 6);
if (index != 0x55 && index != 0xaa)
{
candidate.levelCurveProcessorIndex = index;
(this->*mLevelCurveProcessor[candidate.levelCurveProcessorIndex])(candidate, mvCandidate);
}
}
void GetMinimumVolumeCandidate()
{
mMinimumVolumeObject = mAlignedCandidate;
if (mNumThreads > 0)
{
size_t const numPairsPerThread = mEdgeIndices.size() / mNumThreads;
std::vector<size_t> imin(mNumThreads), imax(mNumThreads);
for (size_t t = 0; t < mNumThreads; ++t)
{
imin[t] = t * numPairsPerThread;
imax[t] = (t + 1) * numPairsPerThread;
}
imax.back() = mEdgeIndices.size();
std::vector<Candidate> candidates(mNumThreads);
std::vector<std::thread> process(mNumThreads);
for (size_t t = 0; t < mNumThreads; ++t)
{
process[t] = std::thread(
[this, t, &imin, &imax, &candidates]()
{
candidates[t] = mAlignedCandidate;
for (size_t i = imin[t]; i < imax[t]; ++i)
{
ProcessEdgePair(mEdgeIndices[i], candidates[t]);
}
});
}
for (size_t t = 0; t < mNumThreads; ++t)
{
process[t].join();
if (candidates[t].volume < mMinimumVolumeObject.volume)
{
mMinimumVolumeObject = candidates[t];
}
}
}
else
{
for (auto const& edgeIndex : mEdgeIndices)
{
ProcessEdgePair(edgeIndex, mMinimumVolumeObject);
}
}
}
void GetMinimumVolumeBox(OrientedBox3<InputType>& box, InputType& volume)
{
Candidate const& mvc = mMinimumVolumeObject;
// Compute the rational-valued box and volume.
Vector3<RationalType> pmin, pmax;
for (int32_t i = 0; i < 3; ++i)
{
mRBox.center[i] = mOrigin[i];
for (int32_t j = 0; j < 3; ++j)
{
mRBox.axis[i][j] = mvc.axis[i][j];
}
mRBox.sqrLengthAxis[i] = Dot(mRBox.axis[i], mRBox.axis[i]);
pmin[i] = static_cast<RationalType>(Dot(mvc.axis[i], mVertices[mvc.minSupportIndex[i]]));
pmax[i] = static_cast<RationalType>(Dot(mvc.axis[i], mVertices[mvc.maxSupportIndex[i]]));
}
RationalType const half(0.5);
Vector3<RationalType> average = half * (pmax + pmin);
for (int32_t i = 0; i < 3; ++i)
{
for (int32_t j = 0; j < 3; ++j)
{
mRBox.center[j] += (average[i] / mRBox.sqrLengthAxis[i]) * mRBox.axis[i][j];
}
}
Vector3<RationalType> difference = pmax - pmin;
mRBox.scaledExtent = half * difference;
mRBox.volume = difference[0] * difference[1] * difference[2] / mRBox.sqrLengthAxis[2];
// Compute the floating-point-valued box and volume.
for (int32_t i = 0; i < 3; ++i)
{
box.center[i] = static_cast<InputType>(mRBox.center[i]);
InputType length = static_cast<InputType>(std::sqrt(mRBox.sqrLengthAxis[i]));
for (int32_t j = 0; j < 3; ++j)
{
box.axis[i][j] = static_cast<InputType>(mRBox.axis[i][j]) / length;
}
box.extent[i] = static_cast<InputType>(mRBox.scaledExtent[i]) / length;
}
volume = static_cast<InputType>(mRBox.volume);
}
// The number of threads to use for computing. If 0, the main thread
// is used. If positive, std::thread objects are used.
size_t mNumThreads;
// The maximum sample index used to search each level curve for
// non-face-supporting boxes (mMaxSample + 1 values). The samples are
// visited using subdivision of the domain of the level curve. The
// subdivision/ information is stored in mDomainIndex(mNumSamples-1).
size_t mMaxSample;
std::vector<std::array<size_t, 3>> mDomainIndex;
// Convenient members to allow construction once when the ComputeType
// is BSNumber<*>.
ComputeType const mZero, mOne, mHalf;
// A mesh representation of the convex polyhedron. The mesh is
// generated dynamically from the inputs to operator() but then is
// converted to a pointerless representation. The mAdjacentPool and
// mVertexAdjacent member are used for fast lookup of adjacent
// vertices in GetExtreme(*).
size_t mNumVertices, mNumTriangles;
std::vector<size_t> mAdjacentPool;
std::vector<size_t> mVertexAdjacent;
std::vector<Edge> mEdges;
std::vector<Triangle> mTriangles;
std::vector<std::array<size_t, 2>> mEdgeIndices;
// Storage for translated vertices and normal vectors. If the
// compute type is 'double', the normals must be normalized to
// unit length (within floating-point rounding error).
VCompute3 mOrigin;
std::vector<VCompute3> mVertices;
std::vector<VCompute3> mNormals;
// The axis-aligned bounding box of the vertices is used as the
// initial candidate for the minimum-volume box.
Candidate mAlignedCandidate;
// The information for the minimum-volume bounding box of the
// vertices.
Candidate mMinimumVolumeObject;
RBox mRBox;
// Each member function A00B10C01D11(*) corresponds to a bilinear
// function on the domain [0,1]^2. Each corner of the domain has a
// bilinear function value that is positive, negative or zero,
// leading to 3^4 = 81 possibilities. The 'A', 'B', 'C' and 'D' are
// in {'P', 'M', 'Z'} [for Plus, Minus, Zero].
typedef void (MinimumVolumeBox3::* LevelCurveProcessor)(Candidate&, Candidate&);
std::array<LevelCurveProcessor, 256> mLevelCurveProcessor;
protected:
// Support for the level-curve processing functions.
void InitializeLevelCurveProcessors()
{
// Generate the initialization code for mLevelCurveProcessor.
// To compile the code, include <fstream>, <strstream> and
// <iomanip.
//
// std::ofstream output("LevelCurveProcessor.txt");
// std::array<char, 3> signchar = { 'Z', 'P', 'M' };
// for (uint32_t index = 0; index < 256u; ++index)
// {
// if ((index & 0x00000003u) != 0x00000003u &&
// (index & 0x0000000Cu) != 0x0000000Cu &&
// (index & 0x00000030u) != 0x00000030u &&
// (index & 0x000000C0u) != 0x000000C0u)
// {
// char s00 = signchar[index & 0x00000003u];
// char s10 = signchar[(index & 0x0000000Cu) >> 2];
// char s01 = signchar[(index & 0x00000030u) >> 4];
// char s11 = signchar[(index & 0x000000C0u) >> 6];
// std::strstream ostream;
// ostream << std::hex << std::setfill('0') << std::setw(2);
// ostream
// << " mLevelCurveProcessor[0x0"
// << std::hex << std::setfill('0') << std::setw(2)
// << index
// << "] = &MinimumVolumeBox3::"
// << s00 << "00"
// << s10 << "10"
// << s01 << "01"
// << s11 << "11;"
// << std::ends;
// output << ostream.str() << std::endl;
// }
// }
// output.close();
mLevelCurveProcessor.fill(nullptr);
mLevelCurveProcessor[0x00] = &MinimumVolumeBox3::Z00Z10Z01Z11;
mLevelCurveProcessor[0x01] = &MinimumVolumeBox3::P00Z10Z01Z11;
mLevelCurveProcessor[0x02] = &MinimumVolumeBox3::M00Z10Z01Z11;
mLevelCurveProcessor[0x04] = &MinimumVolumeBox3::Z00P10Z01Z11;
mLevelCurveProcessor[0x05] = &MinimumVolumeBox3::P00P10Z01Z11;
mLevelCurveProcessor[0x06] = &MinimumVolumeBox3::M00P10Z01Z11;
mLevelCurveProcessor[0x08] = &MinimumVolumeBox3::Z00M10Z01Z11;
mLevelCurveProcessor[0x09] = &MinimumVolumeBox3::P00M10Z01Z11;
mLevelCurveProcessor[0x0a] = &MinimumVolumeBox3::M00M10Z01Z11;
mLevelCurveProcessor[0x10] = &MinimumVolumeBox3::Z00Z10P01Z11;
mLevelCurveProcessor[0x11] = &MinimumVolumeBox3::P00Z10P01Z11;
mLevelCurveProcessor[0x12] = &MinimumVolumeBox3::M00Z10P01Z11;
mLevelCurveProcessor[0x14] = &MinimumVolumeBox3::Z00P10P01Z11;
mLevelCurveProcessor[0x15] = &MinimumVolumeBox3::P00P10P01Z11;
mLevelCurveProcessor[0x16] = &MinimumVolumeBox3::M00P10P01Z11;
mLevelCurveProcessor[0x18] = &MinimumVolumeBox3::Z00M10P01Z11;
mLevelCurveProcessor[0x19] = &MinimumVolumeBox3::P00M10P01Z11;
mLevelCurveProcessor[0x1a] = &MinimumVolumeBox3::M00M10P01Z11;
mLevelCurveProcessor[0x20] = &MinimumVolumeBox3::Z00Z10M01Z11;
mLevelCurveProcessor[0x21] = &MinimumVolumeBox3::P00Z10M01Z11;
mLevelCurveProcessor[0x22] = &MinimumVolumeBox3::M00Z10M01Z11;
mLevelCurveProcessor[0x24] = &MinimumVolumeBox3::Z00P10M01Z11;
mLevelCurveProcessor[0x25] = &MinimumVolumeBox3::P00P10M01Z11;
mLevelCurveProcessor[0x26] = &MinimumVolumeBox3::M00P10M01Z11;
mLevelCurveProcessor[0x28] = &MinimumVolumeBox3::Z00M10M01Z11;
mLevelCurveProcessor[0x29] = &MinimumVolumeBox3::P00M10M01Z11;
mLevelCurveProcessor[0x2a] = &MinimumVolumeBox3::M00M10M01Z11;
mLevelCurveProcessor[0x40] = &MinimumVolumeBox3::Z00Z10Z01P11;
mLevelCurveProcessor[0x41] = &MinimumVolumeBox3::P00Z10Z01P11;
mLevelCurveProcessor[0x42] = &MinimumVolumeBox3::M00Z10Z01P11;
mLevelCurveProcessor[0x44] = &MinimumVolumeBox3::Z00P10Z01P11;
mLevelCurveProcessor[0x45] = &MinimumVolumeBox3::P00P10Z01P11;
mLevelCurveProcessor[0x46] = &MinimumVolumeBox3::M00P10Z01P11;
mLevelCurveProcessor[0x48] = &MinimumVolumeBox3::Z00M10Z01P11;
mLevelCurveProcessor[0x49] = &MinimumVolumeBox3::P00M10Z01P11;
mLevelCurveProcessor[0x4a] = &MinimumVolumeBox3::M00M10Z01P11;
mLevelCurveProcessor[0x50] = &MinimumVolumeBox3::Z00Z10P01P11;
mLevelCurveProcessor[0x51] = &MinimumVolumeBox3::P00Z10P01P11;
mLevelCurveProcessor[0x52] = &MinimumVolumeBox3::M00Z10P01P11;
mLevelCurveProcessor[0x54] = &MinimumVolumeBox3::Z00P10P01P11;
mLevelCurveProcessor[0x55] = &MinimumVolumeBox3::P00P10P01P11;
mLevelCurveProcessor[0x56] = &MinimumVolumeBox3::M00P10P01P11;
mLevelCurveProcessor[0x58] = &MinimumVolumeBox3::Z00M10P01P11;
mLevelCurveProcessor[0x59] = &MinimumVolumeBox3::P00M10P01P11;
mLevelCurveProcessor[0x5a] = &MinimumVolumeBox3::M00M10P01P11;
mLevelCurveProcessor[0x60] = &MinimumVolumeBox3::Z00Z10M01P11;
mLevelCurveProcessor[0x61] = &MinimumVolumeBox3::P00Z10M01P11;
mLevelCurveProcessor[0x62] = &MinimumVolumeBox3::M00Z10M01P11;
mLevelCurveProcessor[0x64] = &MinimumVolumeBox3::Z00P10M01P11;
mLevelCurveProcessor[0x65] = &MinimumVolumeBox3::P00P10M01P11;
mLevelCurveProcessor[0x66] = &MinimumVolumeBox3::M00P10M01P11;
mLevelCurveProcessor[0x68] = &MinimumVolumeBox3::Z00M10M01P11;
mLevelCurveProcessor[0x69] = &MinimumVolumeBox3::P00M10M01P11;
mLevelCurveProcessor[0x6a] = &MinimumVolumeBox3::M00M10M01P11;
mLevelCurveProcessor[0x80] = &MinimumVolumeBox3::Z00Z10Z01M11;
mLevelCurveProcessor[0x81] = &MinimumVolumeBox3::P00Z10Z01M11;
mLevelCurveProcessor[0x82] = &MinimumVolumeBox3::M00Z10Z01M11;
mLevelCurveProcessor[0x84] = &MinimumVolumeBox3::Z00P10Z01M11;
mLevelCurveProcessor[0x85] = &MinimumVolumeBox3::P00P10Z01M11;
mLevelCurveProcessor[0x86] = &MinimumVolumeBox3::M00P10Z01M11;
mLevelCurveProcessor[0x88] = &MinimumVolumeBox3::Z00M10Z01M11;
mLevelCurveProcessor[0x89] = &MinimumVolumeBox3::P00M10Z01M11;
mLevelCurveProcessor[0x8a] = &MinimumVolumeBox3::M00M10Z01M11;
mLevelCurveProcessor[0x90] = &MinimumVolumeBox3::Z00Z10P01M11;
mLevelCurveProcessor[0x91] = &MinimumVolumeBox3::P00Z10P01M11;
mLevelCurveProcessor[0x92] = &MinimumVolumeBox3::M00Z10P01M11;
mLevelCurveProcessor[0x94] = &MinimumVolumeBox3::Z00P10P01M11;
mLevelCurveProcessor[0x95] = &MinimumVolumeBox3::P00P10P01M11;
mLevelCurveProcessor[0x96] = &MinimumVolumeBox3::M00P10P01M11;
mLevelCurveProcessor[0x98] = &MinimumVolumeBox3::Z00M10P01M11;
mLevelCurveProcessor[0x99] = &MinimumVolumeBox3::P00M10P01M11;
mLevelCurveProcessor[0x9a] = &MinimumVolumeBox3::M00M10P01M11;
mLevelCurveProcessor[0xa0] = &MinimumVolumeBox3::Z00Z10M01M11;
mLevelCurveProcessor[0xa1] = &MinimumVolumeBox3::P00Z10M01M11;
mLevelCurveProcessor[0xa2] = &MinimumVolumeBox3::M00Z10M01M11;
mLevelCurveProcessor[0xa4] = &MinimumVolumeBox3::Z00P10M01M11;
mLevelCurveProcessor[0xa5] = &MinimumVolumeBox3::P00P10M01M11;
mLevelCurveProcessor[0xa6] = &MinimumVolumeBox3::M00P10M01M11;
mLevelCurveProcessor[0xa8] = &MinimumVolumeBox3::Z00M10M01M11;
mLevelCurveProcessor[0xa9] = &MinimumVolumeBox3::P00M10M01M11;
mLevelCurveProcessor[0xaa] = &MinimumVolumeBox3::M00M10M01M11;
}
// The subdivision-based sampling functions.
template <bool useDouble = computeDouble>
typename std::enable_if<useDouble, void>::type
Adjust(VCompute3& normal)
{
Normalize(normal);
}
template <bool useDouble = computeDouble>
typename std::enable_if<!useDouble, void>::type
Adjust(VCompute3&)
{
// Nothing to do when the compute type is rational.
}
void Pair(Candidate& c, Candidate& mvc)
{
ComputeVolume(c);
if (c.volume < mvc.volume)
{
mvc = c;
}
}
// The minimizers for the operator()(maxSample, *) function. The
// default behavior of MinimumVolumeBox3D is to use the built-in
// minimizers that sample the level curves as a simple search for a
// minimum volume. However, you can override the minimizers and
// provide a more sophisticated algorithm.
virtual void MinimizerConstantS(Candidate& c, Candidate& mvc)
{
std::vector<ComputeType> t(mMaxSample + 1);
t[0] = mZero;
t[mMaxSample] = mOne;
for (auto const& item : mDomainIndex)
{
t[item[0]] = mHalf * (t[item[1]] + t[item[2]]);
}
Adjust(c.axis[0]);
for (size_t i = 0, j = mMaxSample; i <= mMaxSample; ++i, --j)
{
c.axis[1] = t[j] * c.M[0] + t[i] * c.M[1];
Adjust(c.axis[1]);
ComputeVolume(c);
if (c.volume < mvc.volume)
{
mvc = c;
}
}
}
virtual void MinimizerConstantT(Candidate& c, Candidate& mvc)
{
std::vector<ComputeType> s(mMaxSample + 1);
s[0] = mZero;
s[mMaxSample] = mOne;
for (auto const& item : mDomainIndex)
{
s[item[0]] = mHalf * (s[item[1]] + s[item[2]]);
}
Adjust(c.axis[1]);
for (size_t i = 0, j = mMaxSample; i <= mMaxSample; ++i, --j)
{
c.axis[0] = s[j] * c.N[0] + s[i] * c.N[1];
Adjust(c.axis[0]);
ComputeVolume(c);
if (c.volume < mvc.volume)
{
mvc = c;
}
}
}
virtual void MinimizerVariableS(ComputeType const& sminNumer,
ComputeType const& smaxNumer, ComputeType const& sDenom,
Candidate& c, Candidate& mvc)
{
std::vector<ComputeType> s(mMaxSample + 1), oms(mMaxSample + 1);
s[0] = sminNumer;
oms[0] = sDenom - sminNumer;
s[mMaxSample] = smaxNumer;
oms[mMaxSample] = sDenom - smaxNumer;
for (auto const& item : mDomainIndex)
{
s[item[0]] = mHalf * (s[item[1]] + s[item[2]]);
oms[item[0]] = mHalf * (oms[item[1]] + oms[item[2]]);
}
for (size_t i = 0; i <= mMaxSample; ++i)
{
c.axis[0] = oms[i] * c.N[0] + s[i] * c.N[1];
Adjust(c.axis[0]);
ComputeType q0 = oms[i] * c.f00 + s[i] * c.f10;
ComputeType q1 = oms[i] * c.f01 + s[i] * c.f11;
if (q0 > q1)
{
c.axis[1] = q0 * c.M[1] - q1 * c.M[0];
}
else
{
c.axis[1] = q1 * c.M[0] - q0 * c.M[1];
}
Adjust(c.axis[1]);
ComputeVolume(c);
if (c.volume < mvc.volume)
{
mvc = c;
}
}
}
virtual void MinimizerVariableT(ComputeType const& tminNumer,
ComputeType const& tmaxNumer, ComputeType const& tDenom,
Candidate& c, Candidate& mvc)
{
std::vector<ComputeType> t(mMaxSample + 1), omt(mMaxSample + 1);
t[0] = tminNumer;
omt[0] = tDenom - tminNumer;
t[mMaxSample] = tmaxNumer;
omt[mMaxSample] = tDenom - tmaxNumer;
for (auto const& item : mDomainIndex)
{
t[item[0]] = mHalf * (t[item[1]] + t[item[2]]);
omt[item[0]] = mHalf * (omt[item[1]] + omt[item[2]]);
}
for (size_t i = 0; i <= mMaxSample; ++i)
{
ComputeType p0 = omt[i] * c.f00 + t[i] * c.f01;
ComputeType p1 = omt[i] * c.f10 + t[i] * c.f11;
if (p0 > p1)
{
c.axis[0] = p0 * c.N[1] - p1 * c.N[0];
}
else
{
c.axis[0] = p1 * c.N[0] - p0 * c.N[1];
}
Adjust(c.axis[0]);
c.axis[1] = omt[i] * c.M[0] + t[i] * c.M[1];
Adjust(c.axis[1]);
ComputeVolume(c);
if (c.volume < mvc.volume)
{
mvc = c;
}
}
}
void Z00Z10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x00
// 0 0
// 0 0
//
// This case occurs when each edge is shared by two coplanar
// faces, so we have only two different normals. The normals
// are perpendicular.
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void P00Z10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x01
// 0 0
// + 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void M00Z10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x02
// 0 0
// - 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void Z00P10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x04
// 0 0
// 0 +
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void P00P10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x05
// 0 0
// + +
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void M00P10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x06
// 0 0
// - +
// tmin = 0, tmax = 1
// s = -f00 / (f10 - f00), (+)/(+)
// 1-s = f10 / (f10 - f00), (+)/(+)
// N = (1-s) * N0 + s * N1, omit denominator
c.axis[0] = c.f10 * c.N[0] - c.f00 * c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void Z00M10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x08
// 0 0
// 0 -
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void P00M10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x09
// 0 0
// + -
// tmin = 0, tmax = 1
// s = f00 / (f00 - f10), (+)/(+)
// 1-s = -f10 / (f00 - f10), (+)/(+)
// N = s * N1 + (1-s) * N0, omit denominator
c.axis[0] = c.f00 * c.N[1] - c.f10 * c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 0, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void M00M10Z01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x0a
// 0 0
// - -
// smin = 0, smax = 1, t = 1
c.axis[1] = c.M[1];
MinimizerConstantT(c, mvc);
}
void Z00Z10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x10
// + 0
// 0 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x11
// + 0
// + 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
}
void M00Z10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x12
// + 0
// - 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1
// t = -f00 / (f01 - f00), (+)/(+)
// 1-t = f01 / (f01 - f00), (+)/(+)
// M = (1-t) * M0 + t * M1, omit denominator
c.axis[1] = c.f01 * c.M[0] - c.f00 * c.M[1];
MinimizerConstantT(c, mvc);
}
void Z00P10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x14
// + 0
// 0 +
// It is not possible for a level curve to connect the corners.
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void P00P10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x15
// + 0
// + +
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void M00P10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x16
// + 0
// - +
// smin = 0
// smax = -f00 / (f10 - f00), (+)/(+)
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(mZero, -c.f00, f10mf00, c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void Z00M10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x18
// + 0
// 0 -
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void P00M10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x19
// + 0
// + -
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void M00M10P01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x1a
// + 0
// - -
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void Z00Z10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x20
// - 0
// 0 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x21
// - 0
// + 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1
// t = f00 / (f00 - f01), (+)/(+)
// 1-t = -f01 / (f00 - f01), (+)/(+)
// M = t * M1 + (1-t) * M0, omit denominator
c.axis[1] = c.f00 * c.M[1] - c.f01 * c.M[0];
MinimizerConstantT(c, mvc);
}
void M00Z10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x22
// - 0
// - 0
// tmin = 0, tmax = 1, s = 1
c.axis[0] = c.N[1];
MinimizerConstantS(c, mvc);
}
void Z00P10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x24
// - 0
// 0 +
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void P00P10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x25
// - 0
// + +
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void M00P10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x26
// - 0
// - +
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void Z00M10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x28
// - 0
// 0 -
// It is not possible for a level curve to connect the corners.
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void P00M10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x29
// - 0
// + -
// smin = 0
// smax = f00 / (f00 - f10), (+)/(+)
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(mZero, c.f00, f00mf10, c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void M00M10M01Z11(Candidate& c, Candidate& mvc)
{
// index = 0x2a
// - 0
// - -
c.axis[0] = c.N[1];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void Z00Z10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x40
// 0 +
// 0 0
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smimn = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x41
// 0 +
// + 0
// It is not possible for a level curve to connect the corners.
c.axis[0] = c.N[0];
c.axis[1] = c.M[1];
Pair(c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void M00Z10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x42
// 0 +
// - 0
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void Z00P10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x44
// 0 +
// 0 +
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
}
void P00P10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x45
// 0 +
// + +
c.axis[0] = c.N[0];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void M00P10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x46
// 0 +
// - +
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void Z00M10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x48
// 0 +
// 0 -
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1
// t = -f10 / (f11 - f10), (+)/(+)
// 1-t = f11 / (f11 - f10), (+)/(+)
// M = (1-t) * M0 + t * M1, omit denominator
c.axis[1] = c.f11 * c.M[0] - c.f10 * c.M[1];
MinimizerConstantT(c, mvc);
}
void P00M10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x49
// 0 +
// + -
// smin = f00 / (f00 - f10), (+)/(+)
// smax = 1
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(c.f00, f00mf10, f00mf10, c, mvc);
c.axis[0] = c.N[0];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void M00M10Z01P11(Candidate& c, Candidate& mvc)
{
// index = 0x4a
// 0 +
// - -
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void Z00Z10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x50
// + +
// 0 0
// smin = 0, smax = 1, t = 0s
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x51
// + +
// + 0
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void M00Z10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x52
// + +
// - 0
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void Z00P10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x54
// + +
// 0 +
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void P00P10P01P11(Candidate&, Candidate&)
{
// index = 0x55
// + +
// + +
// Nothing to do.
}
void M00P10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x56
// + +
// - +
// smin = 0
// smax = -f00 / (f10 - f00), (+)/(+)
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(mZero, -c.f00, f10mf00, c, mvc);
}
void Z00M10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x58
// + +
// 0 -
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void P00M10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x59
// + +
// + -
// smin = f00 / (f00 - f10), (+)/(+)
// smax = 1
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(c.f00, f00mf10, f00mf10, c, mvc);
}
void M00M10P01P11(Candidate& c, Candidate& mvc)
{
// index = 0x5a
// + +
// - -
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void Z00Z10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x60
// - +
// 0 0
// tmin = 0, tmax = 1
// s = -f01 / (f11 - f01), (+)/(+)
// 1-s = f11 / (f11 - f01), (+)/(+)
// N = (1-s) * N0 + s * N1, omit denominator
c.axis[0] = c.f11 * c.N[0] - c.f01 * c.N[1];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x61
// - +
// + 0
// smin = 0
// smax = -f01 / (f11 - f01), (+)/(+)
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(mZero, -c.f01, f11mf01, c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void M00Z10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x62
// - +
// - 0
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void Z00P10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x64
// - +
// 0 +
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void P00P10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x65
// - +
// + +
// smin = 0
// smax = -f01 / (f11 - f01), (+)/(+)
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(mZero, -c.f01, f11mf01, c, mvc);
}
void M00P10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x66
// - +
// - +
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void Z00M10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x68
// - +
// 0 -
// smin = -f01 / (f11 - f01), (+)/(+)
// smax = 1
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(-c.f01, f11mf01, f11mf01, c, mvc);
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void P00M10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x69
// - +
// + -
//
// The level set F = 0 has two hyperbolic curves, each formed by a
// pair of endpoints in {(0,t0), (s0,0), (s1,1), (1,t1)}, where
// s0 = -f00 / (f10 - f00), s1 = -f01 / (f11 - f01),
// t0 = -f00 / (f01 - f00), t1 = -f10 / (f11 - f10), all quantites
// in (0,1). The two curves are on opposite sides of the
// asymptotes
// sa = (f01 - f00) / ((f01 - f00) + (f10 - f11))
// ta = (f10 - f00) / ((f10 - f00) + (f01 - f11))
// If s0 < sa, one curve has endpoints {(0,t0),(s0,0)} and the
// other curve has endpoints {(s1,1),(1,t1)}. If s0 > sa, one
// curve has endpoints {(0,t0),(s1,1)} and the other curve has
// endpoints {(s0,0),(1,t1)}. If s0 = sa, then segments of the
// asymptotes are the two curves for the level set. Define
// d = f00 * f11 - f10 * f01. It can be shown that
// s0 - sa = d / ((f10 - f00)((f10 - f00) + (f01 - f11))
// The denominator is positive, so sign(s0 - sa) = sign(d). A
// similar argument applies for the comparison between t0 and ta.
ComputeType d = c.f00 * c.f11 - c.f10 * c.f01;
if (d > mZero)
{
// endpoints (s0,0) and (1,t1)
// smin = f00 / (f00 - f10), (+)/(+)
// smax = 1
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(c.f00, f00mf10, f00mf10, c, mvc);
// endpoints (0,t0) and (s1,1)
// smin = 0
// smax = -f01 / (f11 - f01), (+)/(+)
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(mZero, -c.f01, f11mf01, c, mvc);
}
else if (d < mZero)
{
// endpoints (0,t0) and (s0,0)
// smin = 0
// smax = f00 / (f00 - f10), (+)/(+)
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(mZero, c.f00, f00mf10, c, mvc);
// endpoints (s1,1) and (1,t1)
// smin = -f01 / (f11 - f01), (+)/(+)
// smax = 1
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(-c.f01, f11mf01, f11mf01, c, mvc);
}
else
{
// endpoints (sa,0) and (sa,1)
// sa = (f00 - f01) / ((f00 - f01) + (f11 - f10)), (+)/(+)
// 1-sa = (f11 - f10) / ((f00 - f01) + (f11 - f10)), (+)/(+)
// N = (1-sa) * N0 + sa * N1, omit the denominator
c.axis[0] = (c.f11 - c.f10) * c.N[0] + (c.f00 - c.f01) * c.N[1];
MinimizerConstantS(c, mvc);
// endpoints (0,ta) and (1,ta)
// ta = (f00 - f10) / ((f00 - f10) + (f11 - f01)), (+)/(+)
// 1-ta = (f11 - f01) / ((f00 - f01) + (f11 - f01)), (+)/(+)
// M = (1-ta) * M0 + ta * M1, omit the denominator
c.axis[1] = (c.f11 - c.f01) * c.M[0] + (c.f00 - c.f10) * c.M[1];
MinimizerConstantT(c, mvc);
}
}
void M00M10M01P11(Candidate& c, Candidate& mvc)
{
// index = 0x6a
// - +
// - -
// smin = -f01 / (f11 - f01), (+)/(+)
// smax = 1
ComputeType f11mf01 = c.f11 - c.f01;
MinimizerVariableS(-c.f01, f11mf01, f11mf01, c, mvc);
}
void Z00Z10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x80
// 0 -
// 0 0
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x81
// 0 -
// + 0
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void M00Z10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x82
// 0 -
// - 0
// It is not possible for a level curve to connect the corners.
c.axis[0] = c.N[0];
c.axis[1] = c.M[1];
Pair(c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void Z00P10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x84
// 0 -
// 0 +
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1
// t = f10 / (f10 - f11), (+)/(+)
// 1-t = -f11 / (f10 - f11), (+)/(+)
// M = t * M1 + (1-t) * M0, omit the denominator
c.axis[1] = c.f10 * c.M[1] - c.f11 * c.M[0];
MinimizerConstantT(c, mvc);
}
void P00P10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x85
// 0 -
// + +
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void M00P10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x86
// 0 -
// - +
// smin = -f00 / (f10 - f00), (+)/(+)
// smax = 1
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(-c.f00, f10mf00, f10mf00, c, mvc);
}
void Z00M10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x88
// 0 -
// 0 -
// tmin = 0, tmax = 1, s = 0
c.axis[0] = c.N[0];
MinimizerConstantS(c, mvc);
}
void P00M10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x89
// 0 -
// + -
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void M00M10Z01M11(Candidate& c, Candidate& mvc)
{
// index = 0x8a
// 0 -
// - -
c.axis[0] = c.N[0];
c.axis[1] = c.M[1];
Pair(c, mvc);
}
void Z00Z10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x90
// + -
// 0 0
// tmin = 0, tmax = 1
// s = f01 / (f01 - f11), (+)/(+)
// 1-s = -f11 / (f01 - f11), (+)/(+)
// N = s * N1 + (1-s) * N0, omit the denominator
c.axis[0] = c.f01 * c.N[1] - c.f11 * c.N[0];
MinimizerConstantS(c, mvc);
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x91
// + -
// + 0
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void M00Z10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x92
// + -
// - 0
// smin = 0
// smax = f01 / (f01 - f11), (+)/(+)
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(mZero, c.f01, f01mf11, c, mvc);
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void Z00P10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x94
// + -
// 0 +
// smin = f01 / (f01 - f11), (+)/(+)
// smax = 1
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(c.f01, f01mf11, f01mf11, c, mvc);
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void P00P10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x95
// + -
// + +
// smin = f01 / (f01 - f11), (+)/(+)
// smax = 1
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(c.f01, f01mf11, f01mf11, c, mvc);
}
void M00P10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x96
// + -
// - +
//
// The level set F = 0 has two hyperbolic curves, each formed by a
// pair of endpoints in {(0,t0), (s0,0), (s1,1), (1,t1)}, where
// s0 = -f00 / (f10 - f00), s1 = -f01 / (f11 - f01),
// t0 = -f00 / (f01 - f00), t1 = -f10 / (f11 - f10), all quantites
// in (0,1). The two curves are on opposite sides of the
// asymptotes
// sa = (f01 - f00) / ((f01 - f00) + (f10 - f11))
// ta = (f10 - f00) / ((f10 - f00) + (f01 - f11))
// If s0 < sa, one curve has endpoints {(0,t0),(s0,0)} and the
// other curve has endpoints {(s1,1),(1,t1)}. If s0 > sa, one
// curve has endpoints {(0,t0),(s1,1)} and the other curve has
// endpoints {(s0,0),(1,t1)}. If s0 = sa, then segments of the
// asymptotes are the two curves for the level set. Define
// d = f00 * f11 - f10 * f01. It can be shown that
// s0 - sa = d / ((f10 - f00)((f10 - f00) + (f01 - f11))
// The denominator is positive, so sign(s0 - sa) = sign(d). A
// similar argument applies for the comparison between t0 and ta.
ComputeType d = c.f00 * c.f11 - c.f10 * c.f01;
if (d > mZero)
{
// endpoints (s0,0) and (1,t1)
// smin = -f00 / (f10 - f00), (+)/(+)
// smax = 1
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(-c.f00, f10mf00, f10mf00, c, mvc);
// endpoints (0,t0) and (s1,1)
// smin = 0
// smax = f01 / (f01 - f11)
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(mZero, c.f01, f01mf11, c, mvc);
}
else if (d < mZero)
{
// endpoints (0,t0) and (s0,0)
// smin = 0
// smax = -f00 / (f10- f00), (+)/(+)
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(mZero, -c.f00, f10mf00, c, mvc);
// endpoints (s1,1) and (1,t1)
// smin = f01 / (f01 - f11), (+)/(+)
// smax = 1
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(c.f01, f01mf11, f01mf11, c, mvc);
}
else
{
// endpoints (sa,0) and (sa,1)
// sa = (f01 - f00) / ((f01 - f00) + (f10 - f11)), (+)/(+)
// 1-sa = (f10 - f11) / ((f01 - f00) + (f10 - f11)), (+)/(+)
// N = (1-sa) * N0 + s1* N1
c.axis[0] = (c.f10 - c.f11) * c.N[0] + (c.f01 - c.f00) * c.N[1];
MinimizerConstantS(c, mvc);
// endpoints (0,ta) and (1,ta)
// ta = (f10 - f00) / ((f10 - f00) + (f01 - f11)), (+)/(+)
// 1-ta = (f01 - f11) / ((f10 - f00) + (f01 - f11)), (+)/(+)
// M = (1-ta) * M0 + ta * M1, omit the denominator
c.axis[1] = (c.f01 - c.f11) * c.M[0] + (c.f10 - c.f00) * c.M[1];
MinimizerConstantT(c, mvc);
}
}
void Z00M10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x98
// + -
// 0 -
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void P00M10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x99
// + -
// + -
// tmin = 0, tmax = 1
MinimizerVariableT(mZero, mOne, mOne, c, mvc);
}
void M00M10P01M11(Candidate& c, Candidate& mvc)
{
// index = 0x9a
// + -
// - -
// smin = 0
// smax = f01 / (f01 - f11), (+)/(+)
ComputeType f01mf11 = c.f01 - c.f11;
MinimizerVariableS(mZero, c.f01, f01mf11, c, mvc);
}
void Z00Z10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa0
// - -
// 0 0
// smin = 0, smax = 1, t = 0
c.axis[1] = c.M[0];
MinimizerConstantT(c, mvc);
}
void P00Z10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa1
// - -
// + 0
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void M00Z10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa2
// - -
// - 0
c.axis[0] = c.N[1];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void Z00P10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa4
// - -
// 0 +
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void P00P10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa5
// - -
// + +
// smin = 0, smax = 1
MinimizerVariableS(mZero, mOne, mOne, c, mvc);
}
void M00P10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa6
// - -
// - +
// smin = -f00 / (f10 - f00), (+)/(+)
// smax = 1
ComputeType f10mf00 = c.f10 - c.f00;
MinimizerVariableS(-c.f00, f10mf00, f10mf00, c, mvc);
}
void Z00M10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa8
// - -
// 0 -
c.axis[0] = c.N[0];
c.axis[1] = c.M[0];
Pair(c, mvc);
}
void P00M10M01M11(Candidate& c, Candidate& mvc)
{
// index = 0xa9
// - -
// + -
// smin = 0
// smax = f00 / (f00 - f10), (+)/(+)
ComputeType f00mf10 = c.f00 - c.f10;
MinimizerVariableS(mZero, c.f00, f00mf10, c, mvc);
}
void M00M10M01M11(Candidate&, Candidate&)
{
// index = 0xaa
// - -
// - -
// Nothing to do.
}
};
}