|
|
- /*
- * Copyright (C) 2023 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
- */
-
- #ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
- #define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
-
- #include "math/vector.hpp"
- #include "math/hash/make-uint.hpp"
- #include "math/hash/pcg.hpp"
- #include <algorithm>
- #include <utility>
-
- namespace math {
- namespace noise {
-
- /**
- * Number of corners in an *n*-dimensional simplex lattice cell.
- *
- * @private
- */
- template <std::size_t N>
- constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max<std::size_t>(0, N - 1);
-
- /**
- * Number of edges in an *n*-dimensional simplex lattice cell.
- *
- * @private
- */
- template <std::size_t N>
- constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count<N - 1> : 2;
-
- /**
- * Returns the simplex lattice cell corner vector for a given dimension and index.
- *
- * @private
- */
- template <class T, std::size_t N, std::size_t... I>
- [[nodiscard]] constexpr vector<T, N> make_simplex_corner(std::size_t i, std::index_sequence<I...>)
- {
- return {((i >> I) % 2) * T{2} - T{1}...};
- }
-
- /**
- * Builds an array of simplex lattice cell corner vectors for a given dimension.
- *
- * @private
- */
- template <class T, std::size_t N, std::size_t... I>
- [[nodiscard]] constexpr std::array<vector<T, N>, simplex_corner_count<N>> make_simplex_corners(std::index_sequence<I...>)
- {
- return {make_simplex_corner<T, N>(I, std::make_index_sequence<N>{})...};
- }
-
- /**
- * Array of simplex lattice cell corner vectors for a given dimension.
- *
- * @private
- */
- template <class T, std::size_t N>
- constexpr auto simplex_corners = make_simplex_corners<T, N>(std::make_index_sequence<simplex_corner_count<N>>{});
-
- /**
- * Returns the simplex lattice cell edge vector for a given dimension and index.
- *
- * @private
- */
- template <class T, std::size_t N, std::size_t... I>
- [[nodiscard]] constexpr vector<T, N> make_simplex_edge(std::size_t i, std::index_sequence<I...>)
- {
- std::size_t j = i / (simplex_edge_count<N> / N);
-
- return
- {
- I < j ?
- simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I]
- :
- I > j ?
- simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I - 1]
- :
- T{0}
- ...
- };
- }
-
- /**
- * Builds an array of simplex lattice cell edge vectors for a given dimension.
- *
- * @private
- */
- template <class T, std::size_t N, std::size_t... I>
- [[nodiscard]] constexpr std::array<vector<T, N>, simplex_edge_count<N>> make_simplex_edges(std::index_sequence<I...>)
- {
- if constexpr (N == 1)
- return std::array<vector<T, N>, simplex_edge_count<N>>{vector<T, N>{T{1}}, vector<T, N>{T{-1}}};
- else
- return {make_simplex_edge<T, N>(I, std::make_index_sequence<N>{})...};
- }
-
- /**
- * Array of simplex lattice cell edge vectors for a given dimension.
- *
- * @private
- */
- template <class T, std::size_t N>
- constexpr auto simplex_edges = make_simplex_edges<T, N>(std::make_index_sequence<simplex_edge_count<N>>{});
-
- /**
- * *n*-dimensional simplex noise.
- *
- * @tparam T Real type.
- * @tparam N Number of dimensions.
- *
- * @param position Input position.
- * @param hash Hash function.
- *
- * @return Noise value, on `[-1, 1]`.
- *
- * @see https://en.wikipedia.org/wiki/Simplex_noise
- * @see https://catlikecoding.com/unity/tutorials/pseudorandom-noise/simplex-noise/
- * @see https://briansharpe.wordpress.com/2012/01/13/simplex-noise/
- * @see https://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/
- * @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116
- */
- template <class T, std::size_t N>
- [[nodiscard]] T simplex
- (
- const vector<T, N>& position,
- vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
- )
- {
- // Skewing (F) and unskewing (G) factors
- static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N);
- static const T g = f / (T{1} + f * static_cast<T>(N));
-
- // Kernel radius set to the height of the equilateral triangle, `sqrt(0.5)`
- constexpr T sqr_kernel_radius = T{0.5};
-
- /**
- * C2-continuous kernel falloff function.
- *
- * @param sqr_distance Squared distance from the kernel center.
- *
- * @return Kernel strength at the given distance.
- */
- auto falloff = [sqr_kernel_radius](T sqr_distance) constexpr
- {
- sqr_distance = sqr_kernel_radius - sqr_distance;
- return sqr_distance * sqr_distance * sqr_distance;
- };
-
- // Normalization factor when using corner gradient vectors
- // @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116
- static const T corner_normalization = T{1} / ((static_cast<T>(N) / std::sqrt(static_cast<T>(N + 1))) * falloff(static_cast<T>(N) / (T{4} * static_cast<T>(N + 1))));
-
- // Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors
- static const T edge_normalization = corner_normalization * (std::sqrt(static_cast<T>(N)) / length(simplex_edges<T, N>[0]));
-
- // Skew input position to get the origin vertex of the unit hypercube cell to which they belong
- const vector<T, N> origin_vertex = floor(position + sum(position) * f);
-
- // Displacement vector from origin vertex position to input position
- const vector<T, N> dx = position - origin_vertex + sum(origin_vertex) * g;
-
- // Find axis traversal order
- vector<std::size_t, N> axis_order;
- for (std::size_t i = 0; i < N; ++i)
- axis_order[i] = i;
- std::sort
- (
- axis_order.begin(),
- axis_order.end(),
- [&dx](std::size_t lhs, std::size_t rhs)
- {
- return dx[lhs] > dx[rhs];
- }
- );
-
- T n = T{0};
- vector<T, N> current_vertex = origin_vertex;
- for (std::size_t i = 0; i <= N; ++i)
- {
- if (i)
- ++current_vertex[axis_order[i - 1]];
-
- // Calculate displacement vector from current vertex to input position
- const vector<T, N> d = dx - (current_vertex - origin_vertex) + g * static_cast<T>(i);
-
- // Calculate falloff
- T t = falloff(sqr_length(d));
- if (t > T{0})
- {
- const hash::make_uint_t<T> gradient_index = hash(current_vertex)[0] % simplex_edges<T, N>.size();
- const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index];
-
- n += dot(d, gradient) * t;
- }
- }
-
- // Normalize value
- return n * edge_normalization;
- }
-
- } // namespace noise
- } // namespace math
-
- #endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
|