@@ -18,15 +18,17 @@ | |||
using namespace lol; | |||
/* Constants to tweak */ | |||
float const zoom = 0.03f; | |||
ivec2 const size(1280 * 1, 720 * 1); | |||
float const zoom = 0.03f / 1; | |||
int const octaves = 1; | |||
int main(int argc, char **argv) | |||
{ | |||
UNUSED(argc, argv); | |||
srand(time(nullptr)); | |||
/* Create an image */ | |||
ivec2 const size(1280, 720); | |||
Image img(size); | |||
array2d<vec4> &data = img.Lock2D<PixelFormat::RGBA_F32>(); | |||
@@ -52,6 +54,7 @@ int main(int argc, char **argv) | |||
float x = (float)i, y = (float)j; | |||
float sum = 0.f, coeff = 0.f; | |||
int const max_k = 1 << octaves; | |||
bool b_centre = false, b_sphere1 = false, b_sphere2 = false; | |||
for (int k = 1; k < max_k; k *= 2) | |||
{ | |||
@@ -60,36 +63,52 @@ int main(int argc, char **argv) | |||
switch (cell) | |||
{ | |||
case 0: | |||
t = s2.Interp(zoom * k * vec2(x, y)); | |||
t = s2.eval(zoom * k * vec2(x, y)); | |||
break; | |||
case 1: | |||
t = s3.Interp(zoom * k * vec3(x, 1.f, y)); | |||
t = s3.eval(zoom * k * vec3(x, 1.f, y)); | |||
break; | |||
case 2: | |||
t = s4.Interp(zoom * k * vec4(x, 1.f, y, 2.f)); | |||
t = s4.eval(zoom * k * vec4(x, 1.f, y, 2.f)); | |||
break; | |||
case 3: | |||
t = s6.Interp(zoom * k * vec6(x, 1.f, 2.f, y, 3.f, 4.f)); | |||
t = s6.eval(zoom * k * vec6(x, 1.f, 2.f, y, 3.f, 4.f)); | |||
break; | |||
case 4: | |||
t = s8.Interp(zoom * k * vec8(x, 1.f, 2.f, 3.f, y, 4.f, | |||
5.f, 6.f)); | |||
t = s8.eval(zoom * k * vec8(x, 1.f, 2.f, 3.f, | |||
y, 4.f, 5.f, 6.f)); | |||
break; | |||
case 5: | |||
t = s12.Interp(zoom * k * vec12(x, 1.f, 2.f, 3.f, 4.f, 5.f, | |||
y, 6.f, 7.f, 8.f, 9.f, 10.f)); | |||
//t = s12.eval(zoom * k * vec12(x / 2, -x / 2, y / 2, -y / 2, | |||
// -x / 2, x / 2, -y / 2, y / 2, | |||
// 7.f, 8.f, 9.f, 10.f)); | |||
t = s12.eval(zoom * k * vec12(x, 1.f, 2.f, 3.f, 4.f, 5.f, | |||
y, 6.f, 7.f, 8.f, 9.f, 10.f)); | |||
break; | |||
default: | |||
break; | |||
} | |||
if (t == -2.f) b_centre = true; | |||
if (t == -3.f) b_sphere1 = true; | |||
if (t == -4.f) b_sphere2 = true; | |||
sum += 1.f / k * t; | |||
coeff += 1.f / k; | |||
} | |||
float c = saturate(0.5f + 0.5f * sum / coeff); | |||
data[i][j] = vec4(c, c, c, 1.f); | |||
//data[i][j] = Color::HSVToRGB(vec4(c, 1.0f, 0.5f, 1.f)); | |||
if (b_centre) | |||
data[i][j] = vec4(1.f, 0.f, 1.f, 1.f); | |||
else if (b_sphere1) | |||
data[i][j] = vec4(0.f, 1.f, 0.f, 1.f); | |||
else if (b_sphere2) | |||
data[i][j] = vec4(0.f, 0.f, 1.f, 1.f); | |||
else | |||
{ | |||
float c = saturate(0.5f + 0.5f * sum / coeff); | |||
data[i][j] = vec4(c, c, c, 1.f); | |||
//data[i][j] = Color::HSVToRGB(vec4(c, 1.0f, 0.5f, 1.f)); | |||
} | |||
} | |||
#if 0 | |||
@@ -108,7 +127,7 @@ int main(int argc, char **argv) | |||
ivec2 coord = ivec2(i / zoom * dx + j / zoom * dy); | |||
vec2 g = s2.GetGradient((i + 0.1f) * dx + (j + 0.1f) * dy); | |||
vec2 g = s2.gradient((i + 0.1f) * dx + (j + 0.1f) * dy); | |||
for (int t = 0; t < 40; ++t) | |||
putpixel(coord + (ivec2)(g * (t / 2.f)), vec4(0.f, 1.f, 0.f, 1.f)); | |||
@@ -50,8 +50,8 @@ public: | |||
#endif | |||
} | |||
/* Single interpolation */ | |||
inline float Interp(vec_t<float, N> position) const | |||
/* Evaluate noise at a given point */ | |||
inline float eval(vec_t<float, N> position) const | |||
{ | |||
// Retrieve the containing hypercube origin and associated decimals | |||
vec_t<int, N> origin; | |||
@@ -61,8 +61,9 @@ public: | |||
return get_noise(origin, pos); | |||
} | |||
/* Only for debug purposes: return the gradient vector */ | |||
inline vec_t<float, N> GetGradient(vec_t<float, N> position) const | |||
/* Only for debug purposes: return the gradient vector of the given | |||
* point’s simplex origin. */ | |||
inline vec_t<float, N> gradient(vec_t<float, N> position) const | |||
{ | |||
vec_t<int, N> origin; | |||
vec_t<float, N> pos; | |||
@@ -83,7 +84,8 @@ protected: | |||
for (int i = 0; i < N; ++i) | |||
traversal_order[i] = i; | |||
/* Naïve bubble sort — enough for now */ | |||
/* Naïve bubble sort — enough for now since the general complexity | |||
* of our algorithm is O(N²). Sorting in O(N²) will not hurt more! */ | |||
for (int i = 0; i < N; ++i) | |||
for (int j = i + 1; j < N; ++j) | |||
if (pos[traversal_order[i]] < pos[traversal_order[j]]) | |||
@@ -96,19 +98,26 @@ protected: | |||
/* “corner” will traverse the simplex along its edges in world | |||
* coordinates. */ | |||
vec_t<float, N> world_corner(0.f); | |||
float result = 0.f, sum = 0.f; | |||
float result = 0.f, sum = 0.f, special = 0.f; | |||
UNUSED(sum, special); | |||
for (int i = 0; i < N + 1; ++i) | |||
{ | |||
#if 1 | |||
// In “Noise Hardware” (2-17) Perlin uses 0.6 but Gustavson uses | |||
// 0.5 instead, saying “else the noise is not continuous at | |||
// simplex boundaries”. | |||
// And indeed, the distance between any given simplex vertex and | |||
// the opposite hyperplane is 1/sqrt(2), so the contribution of | |||
// that vertex should never be > 0 for points further than | |||
// 1/sqrt(2). Hence 0.5 - d². | |||
float d = 0.5f - sqlength(world_pos - world_corner); | |||
/* In “Noise Hardware” (2-17) Perlin uses 0.6 - d². | |||
* | |||
* In an errata to “Simplex noise demystified”, Gustavson uses | |||
* 0.5 - d² instead, saying “else the noise is not continuous at | |||
* simplex boundaries”. | |||
* And indeed, the distance between any given simplex vertex and | |||
* the opposite hyperplane is 1/sqrt(2), so the contribution of | |||
* that vertex should never be > 0 for points further than | |||
* 1/sqrt(2). Hence 0.5 - d². | |||
* | |||
* I prefer to use 1 - 2d² and compensate for the d⁴ below by | |||
* dividing the final result by 2⁴, because manipulating values | |||
* between 0 and 1 is more convenient. */ | |||
float d = 1.0f - 2.f * sqlength(world_pos - world_corner); | |||
#else | |||
// DEBUG: this is the linear contribution of each vertex | |||
// in the skewed simplex. Unfortunately it creates artifacts. | |||
@@ -116,6 +125,16 @@ protected: | |||
- ((i == N) ? 0.f : pos[traversal_order[i]]); | |||
#endif | |||
#if 0 | |||
// DEBUG: identify simplex features: | |||
// -4.f: centre (-2.f), | |||
// -3.f: r=0.38 sphere of influence (contribution = 1/4) | |||
// -2.f: r=0.52 sphere of influence (contribution = 1/24) | |||
if (d > 0.99f) special = min(special, -4.f); | |||
if (d > 0.7f && d < 0.72f) special = min(special, -3.f); | |||
if (d > 0.44f && d < 0.46f) special = min(special, -2.f); | |||
#endif | |||
if (d > 0) | |||
{ | |||
// Perlin uses 8d⁴ whereas Gustavson uses d⁴ and a final | |||
@@ -140,16 +159,30 @@ protected: | |||
} | |||
} | |||
// FIXME: Gustavson uses the value 70 for dimension 2, 32 for | |||
// dimension 3, and 27 for dimension 4, and uses non-unit gradients | |||
// of length sqrt(2), sqrt(2) and sqrt(3). Find out where this comes | |||
// from and maybe find a more generic formula. | |||
float const k = N == 2 ? (70.f * sqrt(2.f)) // approx. 99 | |||
: N == 3 ? (70.f * sqrt(2.f)) // approx. 45 | |||
: N == 4 ? (70.f * sqrt(3.f)) // approx. 47 | |||
: 50.f; | |||
//return k * result / sum; | |||
return k * result; | |||
#if 0 | |||
if (special < 0.f) | |||
return special; | |||
#endif | |||
return get_scale() * result; | |||
} | |||
static inline float get_scale() | |||
{ | |||
/* FIXME: Gustavson uses the value 70 for dimension 2, 32 for | |||
* dimension 3, and 27 for dimension 4, and uses non-unit gradients | |||
* of length sqrt(2), sqrt(2) and sqrt(3), saying “The result is | |||
* scaled to stay just inside [-1,1]” which honestly is just not | |||
* true. | |||
* Experiments show that the scaling factor is remarkably close | |||
* to 6.7958 for all high dimensions (measured up to 12). */ | |||
return N == 2 ? 6.2003f | |||
: N == 3 ? 6.7297f | |||
: N == 4 ? 6.7861f | |||
: N == 5 ? 6.7950f | |||
: N == 6 ? 6.7958f | |||
: N == 7 ? 6.7958f | |||
: /* 7+ */ 6.7958f; | |||
} | |||
inline vec_t<float, N> get_gradient(vec_t<int, N> origin) const | |||
@@ -272,12 +305,13 @@ private: | |||
for (int i = 0; i < N + 1; ++i) | |||
vertices[i] = unskew(vertices[i]); | |||
/* Output information for each vertex */ | |||
for (int i = 0; i < N + 1; ++i) | |||
{ | |||
printf(" - vertex %d\n", i); | |||
#if 0 | |||
/* Coordinates for debugging purposes */ | |||
#if 0 | |||
printf(" · ["); | |||
for (int k = 0; k < N; ++k) | |||
printf(" %f", vertices[i][k]); | |||
@@ -285,6 +319,7 @@ private: | |||
#endif | |||
/* Analyze edge lengths from that vertex */ | |||
#if 0 | |||
float minlength = 1.0f; | |||
float maxlength = 0.0f; | |||
for (int j = 0; j < N + 1; ++j) | |||
@@ -298,12 +333,13 @@ private: | |||
} | |||
printf(" · edge lengths between %f and %f\n", | |||
minlength, maxlength); | |||
#endif | |||
#if 0 | |||
/* Experimental calculation of the distance to the opposite | |||
* hyperplane, by picking random points. Works reasonably | |||
* well up to dimension 6. After that, we’d need something | |||
* better such as gradient walk. */ | |||
#if 0 | |||
float mindist = 1.0f; | |||
for (int run = 0; run < 10000000; ++run) | |||
{ | |||
@@ -322,7 +358,6 @@ private: | |||
printf(" · approx. dist. to opposite hyperplane: %f\n", mindist); | |||
#endif | |||
#if 0 | |||
/* Find a normal vector to the opposite hyperplane. First, pick | |||
* any point p(i0) on the hyperplane. We just need i0 != i. Then, | |||
* build a matrix where each row is p(i0)p(j) for all j != i0. | |||
@@ -330,6 +365,7 @@ private: | |||
* full of zeroes except at position i. So we build a vector | |||
* full of zeroes except at position i, and multiply it by the | |||
* matrix inverse. */ | |||
#if 0 | |||
int i0 = (i == 0) ? 1 : 0; | |||
mat_t<float, N, N> m; | |||
for (int j = 0; j < N; ++j) | |||
@@ -349,6 +385,68 @@ private: | |||
printf(" · distance to opposite hyperplane: %f\n", dist); | |||
#endif | |||
} | |||
/* Compute some statistics about the noise. TODO: histogram */ | |||
#if 0 | |||
vec_t<float, N> input(0.f); | |||
for (int run = 0; run < 1000000; ++run) | |||
{ | |||
float t = eval(input); | |||
input[run % N] = rand(1000.f); | |||
} | |||
#endif | |||
/* Try to find max noise value by climbing gradient */ | |||
float minval = 0.f, maxval = 0.f; | |||
array<vec_t<float, N>> deltas; | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
vec_t<float, N> v(0.f); | |||
v[i] = 1.f; | |||
deltas << v << -v; | |||
} | |||
for (int run = 0; run < 1000; ++run) | |||
{ | |||
/* Pick a random vector */ | |||
vec_t<float, N> v; | |||
for (int i = 0; i < N; ++i) | |||
v[i] = rand(-100.f, 100.f); | |||
float t = eval(v); | |||
float e = 0.1f; | |||
/* Climb up gradient in all dimensions */ | |||
while (e > 1e-6f) | |||
{ | |||
int best_delta = -1; | |||
float best_t2 = t; | |||
for (int i = 0; i < deltas.Count(); ++i) | |||
{ | |||
float t2 = eval(v + e * deltas[i]); | |||
if (abs(t2) > abs(best_t2)) | |||
{ | |||
best_delta = i; | |||
best_t2 = t2; | |||
} | |||
} | |||
if (best_delta == -1) | |||
e *= 0.5f; | |||
else | |||
{ | |||
v += e * deltas[best_delta]; | |||
t = best_t2; | |||
} | |||
} | |||
minval = min(t, minval); | |||
maxval = max(t, maxval); | |||
} | |||
printf(" - noise value min/max: %f %f\n", minval, maxval); | |||
float newscale = 1.f / max(-minval, maxval); | |||
if (newscale < 1.f) | |||
printf(" - could replace scale %f with %f\n", | |||
get_scale(), newscale * get_scale()); | |||
else | |||
printf(" - scale looks OK\n"); | |||
printf("\n"); | |||
} | |||
@@ -19,354 +19,11 @@ | |||
namespace lol | |||
{ | |||
template<int N> | |||
class test_interpolator : public simplex_interpolator<N> | |||
{ | |||
public: | |||
test_interpolator() : | |||
simplex_interpolator<N>() | |||
{ | |||
} | |||
void DumpMatrix() | |||
{ | |||
std::cout << std::endl; | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
std::cout << this->m_base[i][j] << ", "; | |||
} | |||
std::cout << ";"; | |||
} | |||
std::cout << std::endl; | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
std::cout << this->m_base_inverse[i][j] << ", "; | |||
} | |||
std::cout << ";"; | |||
} | |||
std::cout << std::endl; | |||
} | |||
void DumpMatrix(float result[N][N]) | |||
{ | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
result[i][j] = this->m_base[i][j]; | |||
} | |||
} | |||
} | |||
void DumpCheckInverse(float result[N][N]) | |||
{ | |||
for (int i = 0; i < N; ++i) | |||
for (int j = 0; j < N; ++j) | |||
result[i][j] = 0; | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
for (int k = 0; k < N; ++k) | |||
{ | |||
result[i][j] += this->m_base[i][k] * this->m_base_inverse[k][j]; | |||
} | |||
} | |||
} | |||
} | |||
vec_t<int, N> GetIndexOrder(vec_t<float, N> const & decimal_point) | |||
{ | |||
return simplex_interpolator<N>::GetIndexOrder(decimal_point); | |||
} | |||
}; | |||
lolunit_declare_fixture(SimplexInterpolatorTest) | |||
lolunit_declare_fixture(SimplexevalolatorTest) | |||
{ | |||
void SetUp() {} | |||
void TearDown() {} | |||
template<int N> | |||
void check_base_matrix() | |||
{ | |||
test_interpolator<N> s; | |||
float result[N][N]; | |||
s.DumpCheckInverse(result); | |||
// Check base matrix inverse | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
if (i == j) | |||
lolunit_assert_doubles_equal(1, result[i][j], 1e-6); | |||
else | |||
lolunit_assert_doubles_equal(0, result[i][j], 1e-6); | |||
} | |||
} | |||
s.DumpMatrix(result); | |||
// Check base vectors’ norms | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
float norm = 0; | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
norm += result[i][j] * result[i][j]; | |||
} | |||
lolunit_assert_doubles_equal(1, norm, 1e-6); | |||
} | |||
// Check result of sum of base vectors => must have norm = 1 | |||
float vec_result[N]; | |||
for (int i = 0; i < N; ++i) | |||
{ | |||
vec_result[i] = 0; | |||
for (int j = 0; j < N; ++j) | |||
{ | |||
vec_result[i] += result[i][j]; | |||
} | |||
} | |||
float norm = 0; | |||
for (int i = 0 ; i < N ; ++i) | |||
{ | |||
norm += vec_result[i] * vec_result[i]; | |||
} | |||
lolunit_assert_doubles_equal(1, norm, 1e-6); | |||
} | |||
lolunit_declare_test(CoordinateMatrixTest) | |||
{ | |||
check_base_matrix<1>(); | |||
check_base_matrix<2>(); | |||
check_base_matrix<3>(); | |||
check_base_matrix<4>(); | |||
check_base_matrix<5>(); | |||
check_base_matrix<6>(); | |||
check_base_matrix<7>(); | |||
check_base_matrix<8>(); | |||
check_base_matrix<9>(); | |||
check_base_matrix<10>(); | |||
} | |||
template<int N> | |||
void check_index_ordering() | |||
{ | |||
static int gen = 12345678; | |||
test_interpolator<N> s; | |||
vec_t<float, N> vec_ref; | |||
for (int i = 0 ; i < N ; ++i) | |||
vec_ref[i] = (gen = gen * gen + gen); | |||
vec_t<int, N> result = s.GetIndexOrder(vec_ref); | |||
for (int i = 1 ; i < N ; ++i) | |||
lolunit_assert(vec_ref[result[i]] < vec_ref[result[i-1]]); | |||
} | |||
lolunit_declare_test(IndexOrderTest) | |||
{ | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<1>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<2>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<3>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<4>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<5>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<6>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<7>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<8>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<9>(); | |||
for (int i = 1 ; i < 10 ; ++i) | |||
check_index_ordering<10>(); | |||
} | |||
lolunit_declare_test(CheckSampleOrder2) | |||
{ | |||
static int gen = 12345678; | |||
arraynd<2, vec_t<float, 2> > gradients(vec_t<ptrdiff_t, 2>({10, 10})); | |||
for (int i = 0 ; i < 10 ; ++i) | |||
{ | |||
for (int j = 0 ; j < 10 ; ++j) | |||
{ | |||
float x1 = (gen = gen * gen + gen) % 255; | |||
float x2 = (gen = gen * gen + gen) % 255; | |||
vec_t<float, 2> v = {x1, x2}; | |||
gradients[i][j] = v; | |||
} | |||
} | |||
simplex_interpolator<2> s; | |||
s.SetGradients(gradients); | |||
std::cout << std::endl; | |||
for (int i = -64 ; i < 64 ; ++i) | |||
{ | |||
for (int j = -64 ; j < 64 ; ++j) | |||
{ | |||
std::cout << s.Interp(vec_t<float, 2>({i * 0.1f, j * 0.1f})) << ", "; | |||
} | |||
std::cout << std::endl; | |||
} | |||
std::cout << std::endl; | |||
} | |||
#if 0 | |||
lolunit_declare_test(FloatGridPoints2D1x1) | |||
{ | |||
simplex_interpolator<2> s({{1.f}}); | |||
float val; | |||
val = s.Interp(GridPoint2D(-1, -1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, -1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, -1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(-1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(-1, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
} | |||
lolunit_declare_test(VectorGridPoints2D1x1) | |||
{ | |||
simplex_interpolator<2, vec3> s({{vec3(1.f, 2.f, 3.f)}}); | |||
vec3 val = s.Interp(GridPoint2D(-1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val.x, 1e-5f); | |||
lolunit_assert_doubles_equal(2.f, val.y, 1e-5f); | |||
lolunit_assert_doubles_equal(3.f, val.z, 1e-5f); | |||
} | |||
lolunit_declare_test(GridPoints2D2x2) | |||
{ | |||
simplex_interpolator<2> s({{1.f, 1.f}, {1.f, 2.f}}); | |||
float val; | |||
val = s.Interp(GridPoint2D(0, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 1)); | |||
lolunit_assert_doubles_equal(2.f, val, 1e-5f); | |||
} | |||
lolunit_declare_test(MoreGridPoints2D2x2) | |||
{ | |||
simplex_interpolator<2> s({{1.f, 1.f}, {1.f, 2.f}}); | |||
float val; | |||
val = s.Interp(GridPoint2D(-1, -1)); | |||
lolunit_assert_doubles_equal(2.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, -1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, -1)); | |||
lolunit_assert_doubles_equal(2.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(2, -1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(2, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(2, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(2, 2)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 2)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, 2)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(-1, 2)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(-1, 1)); | |||
lolunit_assert_doubles_equal(2.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(-1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
} | |||
lolunit_declare_test(GridPoints2D3x3) | |||
{ | |||
simplex_interpolator<2> s({{1, 1, 2}, {1, 2, 2}, {2, 2, 2}}); | |||
float val; | |||
val = s.Interp(GridPoint2D(0, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 0)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(0, 1)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
val = s.Interp(GridPoint2D(1, 1)); | |||
lolunit_assert_doubles_equal(2.f, val, 1e-5f); | |||
} | |||
lolunit_declare_test(OtherPoints2D3x3) | |||
{ | |||
simplex_interpolator<2> s({{1, 1, 2}, {1, 2, 2}, {2, 2, 2}}); | |||
float val; | |||
/* 1/1 triangle edge */ | |||
val = s.Interp(vec2(1.f, lol::sin(F_PI / 3))); | |||
lolunit_assert_doubles_equal(1.5f, val, 1e-5f); | |||
/* 1/1/1 triangle interior */ | |||
val = s.Interp(vec2(0.5f, 0.5f)); | |||
lolunit_assert_doubles_equal(1.f, val, 1e-5f); | |||
/* 1/1/2 triangle interior */ | |||
val = s.Interp(vec2(0.f, 0.5f)); | |||
lolunit_assert(val < 2.f); | |||
lolunit_assert(val > 1.f); | |||
/* Another 1/1/2 triangle interior */ | |||
val = s.Interp(vec2(1.f, 0.5f)); | |||
lolunit_assert(val < 2.f); | |||
lolunit_assert(val > 1.f); | |||
} | |||
private: | |||
vec2 GridPoint2D(int i, int j) | |||
{ | |||
static vec2 vi(1.f, 0.f); | |||
static vec2 vj(lol::cos(F_PI / 3), lol::sin(F_PI / 3)); | |||
return (float)i * vi + (float)j * vj; | |||
} | |||
#endif | |||
}; | |||
} |