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.
234 lines
8.4 KiB
234 lines
8.4 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.11.16
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/Delaunay2Mesh.h>
|
|
#include <Mathematics/IntpQuadraticNonuniform2.h>
|
|
#include <memory>
|
|
|
|
// Interpolation of a scalar-valued function defined on a sphere. Although
|
|
// the sphere lives in 3D, the interpolation is a 2D method whose input
|
|
// points are angles (theta,phi) from spherical coordinates. The domains of
|
|
// the angles are -pi <= theta <= pi and 0 <= phi <= pi.
|
|
|
|
namespace gte
|
|
{
|
|
template <typename T, typename...>
|
|
class IntpSphere2 {};
|
|
}
|
|
|
|
namespace gte
|
|
{
|
|
// The InputType is 'float' or 'double'. The ComputeType can be a
|
|
// floating-point type or BSNumber<*> type, because it does not require
|
|
// divisions. The RationalType requires division, so you can use
|
|
// BSRational<*>.
|
|
|
|
template <typename InputType, typename ComputeType, typename RationalType>
|
|
class // [[deprecated("Use IntpSphere2<InputType> instead.")]]
|
|
IntpSphere2<InputType, ComputeType, RationalType>
|
|
{
|
|
public:
|
|
// Construction and destruction. For complete spherical coverage,
|
|
// include the two antipodal (theta,phi) points (-pi,0,F(-pi,0)) and
|
|
// (-pi,pi,F(-pi,pi)) in the input data. These correspond to the
|
|
// sphere poles x = 0, y = 0, and |z| = 1.
|
|
~IntpSphere2() = default;
|
|
|
|
IntpSphere2(int numPoints, InputType const* theta, InputType const* phi, InputType const* F)
|
|
:
|
|
mMesh(mDelaunay)
|
|
{
|
|
// Copy the input data. The larger arrays are used to support
|
|
// wrap-around in the Delaunay triangulation for the interpolator.
|
|
int totalPoints = 3 * numPoints;
|
|
mWrapAngles.resize(totalPoints);
|
|
mWrapF.resize(totalPoints);
|
|
for (int i = 0; i < numPoints; ++i)
|
|
{
|
|
mWrapAngles[i][0] = theta[i];
|
|
mWrapAngles[i][1] = phi[i];
|
|
mWrapF[i] = F[i];
|
|
}
|
|
|
|
// Use periodicity to get wrap-around in the Delaunay
|
|
// triangulation.
|
|
int i0 = 0, i1 = numPoints, i2 = 2 * numPoints;
|
|
for (/**/; i0 < numPoints; ++i0, ++i1, ++i2)
|
|
{
|
|
mWrapAngles[i1][0] = mWrapAngles[i0][0] + (InputType)GTE_C_TWO_PI;
|
|
mWrapAngles[i2][0] = mWrapAngles[i0][0] - (InputType)GTE_C_TWO_PI;
|
|
mWrapAngles[i1][1] = mWrapAngles[i0][1];
|
|
mWrapAngles[i2][1] = mWrapAngles[i0][1];
|
|
mWrapF[i1] = mWrapF[i0];
|
|
mWrapF[i2] = mWrapF[i0];
|
|
}
|
|
|
|
mDelaunay(totalPoints, &mWrapAngles[0], (ComputeType)0);
|
|
mInterp = std::make_unique<IntpQuadraticNonuniform2<InputType, TriangleMesh>>(
|
|
mMesh, &mWrapF[0], (InputType)1);
|
|
}
|
|
|
|
// Spherical coordinates are
|
|
// x = cos(theta)*sin(phi)
|
|
// y = sin(theta)*sin(phi)
|
|
// z = cos(phi)
|
|
// for -pi <= theta <= pi, 0 <= phi <= pi. The application can use
|
|
// this function to convert unit length vectors (x,y,z) to (theta,phi).
|
|
static void GetSphericalCoordinates(InputType x, InputType y, InputType z,
|
|
InputType& theta, InputType& phi)
|
|
{
|
|
// Assumes (x,y,z) is unit length. Returns -pi <= theta <= pi and
|
|
// 0 <= phiAngle <= pi.
|
|
|
|
if (z < (InputType)1)
|
|
{
|
|
if (z > -(InputType)1)
|
|
{
|
|
theta = std::atan2(y, x);
|
|
phi = std::acos(z);
|
|
}
|
|
else
|
|
{
|
|
theta = -(InputType)GTE_C_PI;
|
|
phi = (InputType)GTE_C_PI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theta = -(InputType)GTE_C_PI;
|
|
phi = (InputType)0;
|
|
}
|
|
}
|
|
|
|
// The return value is 'true' if and only if the input point is in the
|
|
// convex hull of the input (theta,pi) array, in which case the
|
|
// interpolation is valid.
|
|
bool operator()(InputType theta, InputType phi, InputType& F) const
|
|
{
|
|
Vector2<InputType> angles{ theta, phi };
|
|
InputType thetaDeriv = static_cast<InputType>(0);
|
|
InputType phiDeriv = static_cast<InputType>(0);
|
|
return (*mInterp)(angles, F, thetaDeriv, phiDeriv);
|
|
}
|
|
|
|
private:
|
|
typedef Delaunay2Mesh<InputType, ComputeType, RationalType> TriangleMesh;
|
|
|
|
std::vector<Vector2<InputType>> mWrapAngles;
|
|
Delaunay2<InputType, ComputeType> mDelaunay;
|
|
TriangleMesh mMesh;
|
|
std::vector<InputType> mWrapF;
|
|
std::unique_ptr<IntpQuadraticNonuniform2<InputType, TriangleMesh>> mInterp;
|
|
};
|
|
}
|
|
|
|
namespace gte
|
|
{
|
|
// The input type T is 'float' or 'double'.
|
|
|
|
template <typename T>
|
|
class IntpSphere2<T>
|
|
{
|
|
public:
|
|
// Construction and destruction. For complete spherical coverage,
|
|
// include the two antipodal (theta,phi) points (-pi,0,F(-pi,0)) and
|
|
// (-pi,pi,F(-pi,pi)) in the input data. These correspond to the
|
|
// sphere poles x = 0, y = 0, and |z| = 1.
|
|
~IntpSphere2() = default;
|
|
|
|
IntpSphere2(int numPoints, T const* theta, T const* phi, T const* F)
|
|
:
|
|
mMesh(mDelaunay)
|
|
{
|
|
// Copy the input data. The larger arrays are used to support
|
|
// wrap-around in the Delaunay triangulation for the interpolator.
|
|
int totalPoints = 3 * numPoints;
|
|
mWrapAngles.resize(totalPoints);
|
|
mWrapF.resize(totalPoints);
|
|
for (int i = 0; i < numPoints; ++i)
|
|
{
|
|
mWrapAngles[i][0] = theta[i];
|
|
mWrapAngles[i][1] = phi[i];
|
|
mWrapF[i] = F[i];
|
|
}
|
|
|
|
// Use periodicity to get wrap-around in the Delaunay
|
|
// triangulation.
|
|
int i0 = 0, i1 = numPoints, i2 = 2 * numPoints;
|
|
for (/**/; i0 < numPoints; ++i0, ++i1, ++i2)
|
|
{
|
|
mWrapAngles[i1][0] = mWrapAngles[i0][0] + static_cast<T>(GTE_C_TWO_PI);
|
|
mWrapAngles[i2][0] = mWrapAngles[i0][0] - static_cast<T>(GTE_C_TWO_PI);
|
|
mWrapAngles[i1][1] = mWrapAngles[i0][1];
|
|
mWrapAngles[i2][1] = mWrapAngles[i0][1];
|
|
mWrapF[i1] = mWrapF[i0];
|
|
mWrapF[i2] = mWrapF[i0];
|
|
}
|
|
|
|
mDelaunay(mWrapAngles);
|
|
mInterp = std::make_unique<IntpQuadraticNonuniform2<T, TriangleMesh>>(
|
|
mMesh, &mWrapF[0], static_cast<T>(1));
|
|
}
|
|
|
|
// Spherical coordinates are
|
|
// x = cos(theta)*sin(phi)
|
|
// y = sin(theta)*sin(phi)
|
|
// z = cos(phi)
|
|
// for -pi <= theta <= pi, 0 <= phi <= pi. The application can use
|
|
// this function to convert unit length vectors (x,y,z) to (theta,phi).
|
|
static void GetSphericalCoordinates(T x, T y, T z,
|
|
T& theta, T& phi)
|
|
{
|
|
// Assumes (x,y,z) is unit length. Returns -pi <= theta <= pi and
|
|
// 0 <= phiAngle <= pi.
|
|
T const zero = static_cast<T>(0);
|
|
T const one = static_cast<T>(1);
|
|
T const pi = static_cast<T>(GTE_C_PI);
|
|
|
|
if (z < one)
|
|
{
|
|
if (z > -one)
|
|
{
|
|
theta = std::atan2(y, x);
|
|
phi = std::acos(z);
|
|
}
|
|
else
|
|
{
|
|
theta = -pi;
|
|
phi = pi;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theta = -pi;
|
|
phi = zero;
|
|
}
|
|
}
|
|
|
|
// The return value is 'true' if and only if the input point is in the
|
|
// convex hull of the input (theta,pi) array, in which case the
|
|
// interpolation is valid.
|
|
bool operator()(T theta, T phi, T& F) const
|
|
{
|
|
Vector2<T> angles{ theta, phi };
|
|
T thetaDeriv = static_cast<T>(0);
|
|
T phiDeriv = static_cast<T>(0);
|
|
return (*mInterp)(angles, F, thetaDeriv, phiDeriv);
|
|
}
|
|
|
|
private:
|
|
using TriangleMesh = Delaunay2Mesh<T>;
|
|
|
|
std::vector<Vector2<T>> mWrapAngles;
|
|
Delaunay2<T> mDelaunay;
|
|
TriangleMesh mMesh;
|
|
std::vector<T> mWrapF;
|
|
std::unique_ptr<IntpQuadraticNonuniform2<T, TriangleMesh>> mInterp;
|
|
};
|
|
}
|
|
|