// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause /** * @class vtkMathPrivate * @brief Internal toolkit used in some vtkMath methods. * * vtkMatrixUtilities provides matrix indexing / wrapping tools. One can use * this utility to wrap a 1D array into a matrix shape, index it at compile * time. * @sa * vtkMath * vtkMathPrivate */ #ifndef vtkMatrixUtilities_h #define vtkMatrixUtilities_h #include "vtkABINamespace.h" #include // for type traits #include // for std::forward namespace vtkMatrixUtilities { VTK_ABI_NAMESPACE_BEGIN //============================================================================= /** * This struct determines a prior transform to input matrices, changing the * way they are indexed */ struct Layout { /** * Input matrix is unchanged, i.e. sorted row-wise ordered */ struct Identity; /* * Input matrix is transposed, i.e. sorted in column-wise ordered. */ struct Transpose; /** * Input matrix is considered diagonal, and value at index idx points * to component of coordinates (idx, idx) in the diagonal matrix. */ struct Diag; }; namespace detail { // Extracting for STL-like containers template struct ScalarTypeExtractor { typedef typename std::decay::type::value_type>::type value_type; static_assert(std::is_integral::value || std::is_floating_point::value, "value_type is not a numeric type"); }; // Extracting for C++ arrays template struct ScalarTypeExtractor<1, ContainerT> { private: typedef typename std::remove_reference::type DerefContainer; public: typedef typename std::decay::type>::type>::type>::type value_type; static_assert(std::is_integral::value || std::is_floating_point::value, "value_type is not a numeric type"); }; //============================================================================= template struct ArrayOrPointerDepth { static constexpr int Value = Depth; }; //============================================================================= template struct ArrayOrPointerDepth { static constexpr int Value = ArrayOrPointerDepth::Value; }; //============================================================================= template struct ArrayOrPointerDepth { static constexpr int Value = ArrayOrPointerDepth::Value; }; //============================================================================= template struct ArrayOrPointerDepth { static constexpr int Value = ArrayOrPointerDepth::Value; }; //============================================================================= template struct ArrayOrPointerDepth { static constexpr int Value = ArrayOrPointerDepth::Value; }; } // namespace detail //----------------------------------------------------------------------------- /** * At compile time, returns `true` if the templated parameter is a 2D array * (`double[3][3]` for instance), false otherwise. */ template static constexpr bool MatrixIs2DArray() { return detail::ArrayOrPointerDepth::Value == 2; } namespace detail { //============================================================================= template struct ExtractRawComponentType; //============================================================================= template struct ExtractRawComponentType()>::type> { using Type = decltype(std::declval()[0][0]); }; //============================================================================= template struct ExtractRawComponentType()>::type> { using Type = decltype(std::declval()[0]); }; } // namespace detail //============================================================================= /** * This class extract the underlying value type of containers. It works on * multi-dimensional C++ arrays as well as with STL container like that * have a value_type typedef. * * One can access the value type by fetching * ScalarTypeExtractor::value_type. */ template struct ScalarTypeExtractor { private: typedef typename std::remove_reference::type DerefContainer; public: /** * `value_type` is the underlying arithmetic type held in `ContainerT` */ using value_type = typename detail::ScalarTypeExtractor< // This parameter equals 0 or 1 std::is_array::value || std::is_pointer::value, ContainerT>::value_type; static_assert(std::is_integral::value || std::is_floating_point::value, "value_type is not a numeric type"); /** * `RawComponentType` is the type given by `operator[]` for 1D containers, or `operator[][]` for * 2D containers. */ using RawComponentType = typename detail::ExtractRawComponentType::Type; /** * ComponentType is the RawComponentType dereferenced if it was an rvalue reference. */ using ComponentType = typename std::conditional::value, typename std::remove_reference::type, RawComponentType>::type; }; //----------------------------------------------------------------------------- /** * At compile time, returns `true` if the templated parameter is a pointer to * pointer (`double**` for instance), false otherwise. */ template static constexpr bool MatrixIsPointerToPointer() { typedef typename std::remove_pointer::type Row; typedef typename std::remove_pointer::type Value; return std::is_pointer::value && std::is_pointer::value && !std::is_pointer::value; } //----------------------------------------------------------------------------- /** * At compile time, returns `true` if the templated parameter layout is 2D, * i.e. elements can be accessed using the operator `[][]`. It returns false otherwise. */ template static constexpr bool MatrixLayoutIs2D() { return MatrixIs2DArray(); } namespace detail { // Class actually implementing matrix mapping. template struct Mapper; // Specialization of the matrix mapper for when the layout is the identity template struct Mapper { template static constexpr int GetIndex() { static_assert(RowT >= 0 && RowT < RowsT, "RowT out of bounds"); static_assert(ColT >= 0 && ColT < ColsT, "ColT out of bounds"); return ColsT * RowT + ColT; } }; template struct Mapper { template static constexpr int GetIndex() { static_assert(RowT >= 0 && RowT < RowsT, "RowT out of bounds"); static_assert(ColT >= 0 && ColT < ColsT, "ColT out of bounds"); return RowsT * ColT + RowT; } }; } // namespace detail //============================================================================= /** * This class is a helper class to compute at compile time the index of a matrix * stored as a 1D array from its 2D coordinates. This class maps matrices of * dimension RowsT x ColsT. The LayoutT template parameter permits to switch to * the indexing of the transpose of the matrix. LayoutT can be set to * Layout::Identity for a row-wise ordering, or to Layout::Transpose for a * column-wise ordering * * @warning This mapper does not work with matrices stored as 2D arrays, or with * diagonal matrices. */ template struct Mapper { template static constexpr int GetIndex() { return detail::Mapper::template GetIndex(); } }; namespace detail { // Class implementing matrix wrapping. template class Wrapper; // Specializaion of matrix wrapping for matrices stored as 1D arrays // in row-wise order template class Wrapper { private: using ComponentType = typename vtkMatrixUtilities::ScalarTypeExtractor::ComponentType; public: template static ComponentType Get(MatrixTT&& M) { return M[Mapper::template GetIndex()]; } }; // Specialization for matrices stored as 2D arrays with an unchanged layout template class Wrapper { private: using ComponentType = typename vtkMatrixUtilities::ScalarTypeExtractor::ComponentType; public: template static ComponentType Get(MatrixTT&& M) { return M[RowT][ColT]; } }; // Specialization for matrices stored as 2D arrays read as its transposed self. template class Wrapper { private: using ComponentType = typename vtkMatrixUtilities::ScalarTypeExtractor::ComponentType; public: template static ComponentType Get(MatrixTT&& M) { return M[ColT][RowT]; } }; // Specialization for diagonal matrices. // Note: a diagonal matrix has to be stored in a 1D array. template class Wrapper { private: using Scalar = typename vtkMatrixUtilities::ScalarTypeExtractor::value_type; using ComponentType = typename vtkMatrixUtilities::ScalarTypeExtractor::ComponentType; template struct Helper { static constexpr Scalar ZERO = Scalar(0); template static ComponentType Get(MatrixTT&&) { return ZERO; } }; template struct Helper { template static ComponentType Get(MatrixTT&& M) { return M[RowT]; } }; public: template ComponentType Get(MatrixTT& M) { return Helper::Get(std::forward(M)); } }; } // namespace detail //============================================================================= /** * Matrix wrapping class. This class implements a getter templated on the * coordinates of the wanted element. A matrix can be a 2D C++ array, a 1D C++ * array row-wise ordered, or any STL-like container implementing operator[] and * having a value_type typedef. * * This class wraps a RowsT x ColsT matrix stored in the container MatrixT. The * LayoutT template parameter permits to reindex at compile-time the matrix. If * it is set to Layout::Identity, the matrix is assumed to be row-wised ordered. * If it is set to Layout::Transpose, the matrix is assumed to be column-wise ordered. * One can also convert a 1D input array into a diagonal matrix by setting * LayoutT to Layout::Diag. In this particular case, method Get will return a * read-only zero on elements outside of the diagonal. */ template class Wrapper { private: using Scalar = typename ScalarTypeExtractor::value_type; using ComponentType = typename vtkMatrixUtilities::ScalarTypeExtractor::ComponentType; static_assert(!MatrixLayoutIs2D() || !std::is_same::value, "A diagonal matrix cannot be a 2D array"); public: template static ComponentType Get(MatrixTT&& M) { return detail::Wrapper()>::template Get(std::forward(M)); } }; VTK_ABI_NAMESPACE_END } // namespace vtkMatrixUtilities #endif // VTK-HeaderTest-Exclude: vtkMatrixUtilities.h