Signed distance function¶
In EBGeometry we have encapsulated the concept of a signed distance function in an abstract class
/* EBGeometry
* Copyright © 2022 Robert Marskar
* Please refer to Copyright.txt and LICENSE in the EBGeometry root directory.
*/
/*!
@file EBGeometry_SignedDistanceFunction.hpp
@brief Abstract base class for representing a signed distance function.
@author Robert Marskar
*/
#ifndef EBGeometry_SignedDistanceFunction
#define EBGeometry_SignedDistanceFunction
#include <memory>
#include <deque>
// Our includes
#include "EBGeometry_ImplicitFunction.hpp"
#include "EBGeometry_NamespaceHeader.hpp"
/*!
@brief Abstract representation of a signed distance function.
@details Users can put whatever they like in here, e.g. analytic functions,
DCEL meshes, or DCEL meshes stored in full or compact BVH trees. The
signedDistance function must be implemented by the user. When computing it,
the user can apply transformation operators (rotations, scaling, translations)
by calling transformPoint on the input coordinate.
*/
template <class T>
class SignedDistanceFunction : public ImplicitFunction<T>
{
public:
/*!
@brief Disallowed, use the full constructor
*/
SignedDistanceFunction() = default;
/*!
@brief Destructor (does nothing)
*/
virtual ~SignedDistanceFunction() = default;
/*!
@brief Implementation of ImplicitFunction::value
@param[in] a_point 3D point.
*/
virtual T
value(const Vec3T<T>& a_point) const noexcept override final;
/*!
@brief Signed distance function.
@param[in] a_point 3D point.
*/
virtual T
signedDistance(const Vec3T<T>& a_point) const noexcept = 0;
/*!
@brief Signed distance normal vector.
@details Computed using finite differences with step a_delta
@param[in] a_point 3D point
@param[in] a_delta Finite difference step
*/
inline virtual Vec3T<T>
normal(const Vec3T<T>& a_point, const T& a_delta) const noexcept;
};
#include "EBGeometry_NamespaceFooter.hpp"
#include "EBGeometry_SignedDistanceFunctionImplem.hpp"
#endif
We point out that the BVH and DCEL classes are fundamentally also signed distance functions, and they also inherit from SignedDistanceFunction
.
The SignedDistanceFunction
class also exists so that we have a common entry point for performing distance field manipulations like rotations and translations.
When implementing the signedDistance
function, one can transform the input point by first calling transformPoint
.
The functions translate
and rotate
will translate or rotate the object.
It is also possible to scale an object, but this is not simply a coordinate transform so it is implemented as a separate signed distance function.
For example, in order to rotate a DCEL mesh (without using the BVH accelerator) we can implement the following signed distance function:
template <class T>
class MySignedDistanceFunction : public SignedDistanceFunction<T> {
public:
T signedDistance(const Vec3T<T>& a_point) const noexcept override {
return m_mesh->signedDistance(this->transformPoint(a_point));
}
protected:
// DCEL mesh object, must be constructed externally and
// supplied to MyDistanceFunction (e.g. through the constructor).
std::shared_ptr<EBGeometry::Dcel::MeshT<T> > m_mesh;
};
Alternatively, using a BVH structure:
template <class T, class P, class BV, int K>
class MySignedDistanceFunction : public SignedDistanceFunction<T> {
public:
T signedDistance(const Vec3T<T>& a_point) const noexcept override {
return m_bvh->signedDistance(this->transformPoint(a_point));
}
protected:
// BVH object, must be constructed externally
// and supplied to MyDistanceFunction (e.g. through the constructor).
std::shared_ptr<EBGeometry::BVH::LinearBVH<T, P, BV, K> > m_bvh;
};
Normal vector¶
The normal vector of EBGeometry::SignedDistanceFunction<T>
is computed using centered finite differences:
where \(i\) is a coordinate direction and \(\Delta > 0\). This is done for each component, and the normalized vector is then returned.
Transformations¶
The following transformations are possible:
Translation, which defines the operation \(\mathbf{x}^\prime = \mathbf{x} - \mathbf{t}\) where \(\mathbf{t}\) is a translation vector.
Rotation, which defines the operation \(\mathbf{x}^\prime = R\left(\mathbf{x}, \theta, a\right)\) where \(\mathbf{x}\) is rotated an angle \(\theta\) around the coordinate axis \(a\).
Transformations are applied sequentially. The APIs are as follows:
void translate(const Vec3T<T>& a_translation) noexcept; // a_translation are Cartesian translations vector
void rotate(const T a_angle, const int a_axis) noexcept; // a_angle in degrees, and a_axis being the Cartesian axis
E.g. the following code will first translate, then 90 degrees about the \(x\)-axis.
MySignedDistanceFunction<float> sdf;
sdf.translate({1,0,0});
sdf.rotate(90, 0);
Note that if the transformations are to be applied, the implementation of signedDistance(...)
must transform the input point, as shown in the examples above.
Rounding¶
Distance functions can be rounded by displacing the SDF by a specified distance. For example, given a distance functions \(S\left(\mathbf{x}\right)\), the rounded distance functions is
where \(r\) is some rounding radius. Note that the rounding does not preserve the volume of the original SDF, so subsequent scaling of the object is usually necessary.
The rounded SDF is implemented in Source/EBGeometry_AnalyticDistanceFunctions.hpp
template <class T>
class RoundedSDF : public SignedDistanceFunction<T>
{
public:
/*!
@brief Disallowed weak construction
*/
RoundedSDF() = delete;
/*!
@brief Rounded SDF. Rounds the input SDF
@param[in] a_sdf Input signed distance function.
@param[in] a_curv Rounding radius.
*/
RoundedSDF(const std::shared_ptr<SignedDistanceFunction<T>> a_sdf, const T a_curv)
{
m_sdf = a_sdf;
m_curv = a_curv;
}
/*!
@brief Destructor
*/
virtual ~RoundedSDF()
{}
/*!
@brief Signed distance field.
*/
virtual T
signedDistance(const Vec3T<T>& a_point) const noexcept override
{
return m_sdf->signedDistance(a_point) - m_curv;
}
protected:
/*!
@brief Original signed distance function
*/
std::shared_ptr<const SignedDistanceFunction<T>> m_sdf;
/*!
@brief Rounding radius
*/
T m_curv;
To use it, simply pass an SDF into the constructor and use the new distance function.
Scaling¶
Scaling of distance functions are possible through the transformation
where \(c\) is a scaling factor. We point out that anisotropic stretching does not preserve the distance field.
The rounded SDF is implemented in Source/EBGeometry_AnalyticDistanceFunctions.hpp
@brief Scaled signed distance function.
*/
template <class T>
class ScaledSDF : public SignedDistanceFunction<T>
{
public:
/*!
@brief Disallowed weak construction
*/
ScaledSDF() = delete;
/*!
@brief Scaled SDF.
@param[in] a_sdf Input signed distance function.
@param[in] a_scale Scaling factor.
*/
ScaledSDF(const std::shared_ptr<SignedDistanceFunction<T>> a_sdf, const T a_scale)
{
m_sdf = a_sdf;
m_scale = a_scale;
}
/*!
@brief Destructor
*/
virtual ~ScaledSDF()
{}
/*!
@brief Signed distance field.
@param[in] a_point Input point.
*/
virtual T
signedDistance(const Vec3T<T>& a_point) const noexcept override
{
return (m_sdf->signedDistance(a_point / m_scale)) * m_scale;
}
protected:
/*!
@brief Original signed distance function
*/
std::shared_ptr<const SignedDistanceFunction<T>> m_sdf;
/*!
@brief Scaling factor.
Analytic functions¶
Above, we have shown how users can supply a DCEL or BVH structure to implement SignedDistanceFunction
.
In addition, the file Source/EBGeometry_AnalyticSignedDistanceFunctions.hpp
defines various other analytic shapes such as:
Sphere
template <class T> class SphereSDF : public SignedDistanceFunction<T>
Box
template <class T> class BoxSDF : public SignedDistanceFunction<T>
Torus
template <class T> class TorusSDF : public SignedDistanceFunction<T>
Capped cylinder
template <class T> class CylinderSDF : public SignedDistanceFunction<T>
Infinite cylinder
template <class T> class InfiniteCylinderSDF : public SignedDistanceFunction<T>
Capsule/rounded cylinder
template <class T> class CapsuleSDF : public SignedDistanceFunction<T>
Infinite cone
template <class T> class InfiniteConeSDF : public SignedDistanceFunction<T>
Cone
template <class T> class ConeSDF : public SignedDistanceFunction<T>