Antkeeper  0.0.1
simplex.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
21 #define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
22 
23 #include <engine/math/vector.hpp>
25 #include <engine/math/hash/pcg.hpp>
26 #include <algorithm>
27 #include <utility>
28 
29 namespace math {
30 namespace noise {
31 
37 template <std::size_t N>
38 constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max<std::size_t>(0, N - 1);
39 
45 template <std::size_t N>
46 constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count<N - 1> : 2;
47 
53 template <class T, std::size_t N, std::size_t... I>
54 [[nodiscard]] constexpr vector<T, N> make_simplex_corner(std::size_t i, std::index_sequence<I...>)
55 {
56  return {((i >> I) % 2) * T{2} - T{1}...};
57 }
58 
64 template <class T, std::size_t N, std::size_t... I>
65 [[nodiscard]] constexpr std::array<vector<T, N>, simplex_corner_count<N>> make_simplex_corners(std::index_sequence<I...>)
66 {
67  return {make_simplex_corner<T, N>(I, std::make_index_sequence<N>{})...};
68 }
69 
75 template <class T, std::size_t N>
76 constexpr auto simplex_corners = make_simplex_corners<T, N>(std::make_index_sequence<simplex_corner_count<N>>{});
77 
83 template <class T, std::size_t N, std::size_t... I>
84 [[nodiscard]] constexpr vector<T, N> make_simplex_edge(std::size_t i, std::index_sequence<I...>)
85 {
86  std::size_t j = i / (simplex_edge_count<N> / N);
87 
88  return
89  {
90  I < j ?
91  simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I]
92  :
93  I > j ?
94  simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I - 1]
95  :
96  T{0}
97  ...
98  };
99 }
100 
106 template <class T, std::size_t N, std::size_t... I>
107 [[nodiscard]] constexpr std::array<vector<T, N>, simplex_edge_count<N>> make_simplex_edges(std::index_sequence<I...>)
108 {
109  if constexpr (N == 1)
110  return std::array<vector<T, N>, simplex_edge_count<N>>{vector<T, N>{T{1}}, vector<T, N>{T{-1}}};
111  else
112  return {make_simplex_edge<T, N>(I, std::make_index_sequence<N>{})...};
113 }
114 
120 template <class T, std::size_t N>
121 constexpr auto simplex_edges = make_simplex_edges<T, N>(std::make_index_sequence<simplex_edge_count<N>>{});
122 
140 template <class T, std::size_t N>
141 [[nodiscard]] T simplex
142 (
143  const vector<T, N>& position,
144  vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
145 )
146 {
147  // Skewing (F) and unskewing (G) factors
148  static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N);
149  static const T g = f / (T{1} + f * static_cast<T>(N));
150 
151  // Kernel radius set to the height of the equilateral triangle, `sqrt(0.5)`
152  constexpr T sqr_kernel_radius = T{0.5};
153 
161  auto falloff = [sqr_kernel_radius](T sqr_distance) constexpr
162  {
163  sqr_distance = sqr_kernel_radius - sqr_distance;
165  };
166 
167  // Normalization factor when using corner gradient vectors
168  // @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116
169  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))));
170 
171  // Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors
172  static const T edge_normalization = corner_normalization * (std::sqrt(static_cast<T>(N)) / length(simplex_edges<T, N>[0]));
173 
174  // Skew input position to get the origin vertex of the unit hypercube cell to which they belong
175  const vector<T, N> origin_vertex = floor(position + sum(position) * f);
176 
177  // Displacement vector from origin vertex position to input position
178  const vector<T, N> dx = position - origin_vertex + sum(origin_vertex) * g;
179 
180  // Find axis traversal order
181  vector<std::size_t, N> axis_order;
182  for (std::size_t i = 0; i < N; ++i)
183  axis_order[i] = i;
184  std::sort
185  (
186  axis_order.begin(),
187  axis_order.end(),
188  [&dx](std::size_t lhs, std::size_t rhs)
189  {
190  return dx[lhs] > dx[rhs];
191  }
192  );
193 
194  T n = T{0};
195  vector<T, N> current_vertex = origin_vertex;
196  for (std::size_t i = 0; i <= N; ++i)
197  {
198  if (i)
199  ++current_vertex[axis_order[i - 1]];
200 
201  // Calculate displacement vector from current vertex to input position
202  const vector<T, N> d = dx - (current_vertex - origin_vertex) + g * static_cast<T>(i);
203 
204  // Calculate falloff
205  T t = falloff(sqr_length(d));
206  if (t > T{0})
207  {
208  const hash::make_uint_t<T> gradient_index = hash(current_vertex)[0] % simplex_edges<T, N>.size();
209  const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index];
210 
211  n += dot(d, gradient) * t;
212  }
213  }
214 
215  // Normalize value
216  return n * edge_normalization;
217 }
218 
219 } // namespace noise
220 } // namespace math
221 
222 #endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
Hash functions.
Definition: fnv1a.hpp:29
typename make_uint< T >::type make_uint_t
Helper type for make_uint.
Definition: make-uint.hpp:63
T simplex(const vector< T, N > &position, vector< hash::make_uint_t< T >, N >(*hash)(const vector< T, N > &)=&hash::pcg< T, N >)
n-dimensional simplex noise.
Definition: simplex.hpp:142
Mathematical functions and data types.
Definition: angles.hpp:26
constexpr T sqr_distance(const vector< T, N > &p0, const vector< T, N > &p1) noexcept
Calculates the square distance between two points.
Definition: vector.hpp:1514
constexpr vector< T, N > floor(const vector< T, N > &x)
Performs a element-wise floor operation.
Definition: vector.hpp:1184
T length(const quaternion< T > &q)
Calculates the length of a quaternion.
Definition: quaternion.hpp:602
constexpr T sqr_length(const quaternion< T > &q) noexcept
Calculates the square length of a quaternion.
Definition: quaternion.hpp:744
vector< T, N > sqrt(const vector< T, N > &x)
Takes the square root of each element.
constexpr T dot(const quaternion< T > &a, const quaternion< T > &b) noexcept
Calculates the dot product of two quaternions.
Definition: quaternion.hpp:572
constexpr T sum(const vector< T, N > &x) noexcept
Calculates the sum of all elements in a vector.
Definition: vector.hpp:1585
n-dimensional vector.
Definition: vector.hpp:44
constexpr element_type * begin() noexcept
Returns an iterator to the first element.
Definition: vector.hpp:217
constexpr element_type * end() noexcept
Returns an iterator to the element following the last element.
Definition: vector.hpp:235