| @@ -18,15 +18,17 @@ | |||||
| using namespace lol; | using namespace lol; | ||||
| /* Constants to tweak */ | /* 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 const octaves = 1; | ||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||
| { | { | ||||
| UNUSED(argc, argv); | UNUSED(argc, argv); | ||||
| srand(time(nullptr)); | |||||
| /* Create an image */ | /* Create an image */ | ||||
| ivec2 const size(1280, 720); | |||||
| Image img(size); | Image img(size); | ||||
| array2d<vec4> &data = img.Lock2D<PixelFormat::RGBA_F32>(); | 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 x = (float)i, y = (float)j; | ||||
| float sum = 0.f, coeff = 0.f; | float sum = 0.f, coeff = 0.f; | ||||
| int const max_k = 1 << octaves; | 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) | for (int k = 1; k < max_k; k *= 2) | ||||
| { | { | ||||
| @@ -60,36 +63,52 @@ int main(int argc, char **argv) | |||||
| switch (cell) | switch (cell) | ||||
| { | { | ||||
| case 0: | case 0: | ||||
| t = s2.Interp(zoom * k * vec2(x, y)); | |||||
| t = s2.eval(zoom * k * vec2(x, y)); | |||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| t = s3.Interp(zoom * k * vec3(x, 1.f, y)); | |||||
| t = s3.eval(zoom * k * vec3(x, 1.f, y)); | |||||
| break; | break; | ||||
| case 2: | 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; | break; | ||||
| case 3: | 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; | break; | ||||
| case 4: | 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; | break; | ||||
| case 5: | 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; | break; | ||||
| default: | default: | ||||
| break; | 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; | sum += 1.f / k * t; | ||||
| coeff += 1.f / k; | 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 | #if 0 | ||||
| @@ -108,7 +127,7 @@ int main(int argc, char **argv) | |||||
| ivec2 coord = ivec2(i / zoom * dx + j / zoom * dy); | 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) | for (int t = 0; t < 40; ++t) | ||||
| putpixel(coord + (ivec2)(g * (t / 2.f)), vec4(0.f, 1.f, 0.f, 1.f)); | putpixel(coord + (ivec2)(g * (t / 2.f)), vec4(0.f, 1.f, 0.f, 1.f)); | ||||
| @@ -50,8 +50,8 @@ public: | |||||
| #endif | #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 | // Retrieve the containing hypercube origin and associated decimals | ||||
| vec_t<int, N> origin; | vec_t<int, N> origin; | ||||
| @@ -61,8 +61,9 @@ public: | |||||
| return get_noise(origin, pos); | 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<int, N> origin; | ||||
| vec_t<float, N> pos; | vec_t<float, N> pos; | ||||
| @@ -83,7 +84,8 @@ protected: | |||||
| for (int i = 0; i < N; ++i) | for (int i = 0; i < N; ++i) | ||||
| traversal_order[i] = 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 i = 0; i < N; ++i) | ||||
| for (int j = i + 1; j < N; ++j) | for (int j = i + 1; j < N; ++j) | ||||
| if (pos[traversal_order[i]] < pos[traversal_order[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 | /* “corner” will traverse the simplex along its edges in world | ||||
| * coordinates. */ | * coordinates. */ | ||||
| vec_t<float, N> world_corner(0.f); | 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) | for (int i = 0; i < N + 1; ++i) | ||||
| { | { | ||||
| #if 1 | #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 | #else | ||||
| // DEBUG: this is the linear contribution of each vertex | // DEBUG: this is the linear contribution of each vertex | ||||
| // in the skewed simplex. Unfortunately it creates artifacts. | // in the skewed simplex. Unfortunately it creates artifacts. | ||||
| @@ -116,6 +125,16 @@ protected: | |||||
| - ((i == N) ? 0.f : pos[traversal_order[i]]); | - ((i == N) ? 0.f : pos[traversal_order[i]]); | ||||
| #endif | #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) | if (d > 0) | ||||
| { | { | ||||
| // Perlin uses 8d⁴ whereas Gustavson uses d⁴ and a final | // 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 | 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) | for (int i = 0; i < N + 1; ++i) | ||||
| vertices[i] = unskew(vertices[i]); | vertices[i] = unskew(vertices[i]); | ||||
| /* Output information for each vertex */ | |||||
| for (int i = 0; i < N + 1; ++i) | for (int i = 0; i < N + 1; ++i) | ||||
| { | { | ||||
| printf(" - vertex %d\n", i); | printf(" - vertex %d\n", i); | ||||
| #if 0 | |||||
| /* Coordinates for debugging purposes */ | /* Coordinates for debugging purposes */ | ||||
| #if 0 | |||||
| printf(" · ["); | printf(" · ["); | ||||
| for (int k = 0; k < N; ++k) | for (int k = 0; k < N; ++k) | ||||
| printf(" %f", vertices[i][k]); | printf(" %f", vertices[i][k]); | ||||
| @@ -285,6 +319,7 @@ private: | |||||
| #endif | #endif | ||||
| /* Analyze edge lengths from that vertex */ | /* Analyze edge lengths from that vertex */ | ||||
| #if 0 | |||||
| float minlength = 1.0f; | float minlength = 1.0f; | ||||
| float maxlength = 0.0f; | float maxlength = 0.0f; | ||||
| for (int j = 0; j < N + 1; ++j) | for (int j = 0; j < N + 1; ++j) | ||||
| @@ -298,12 +333,13 @@ private: | |||||
| } | } | ||||
| printf(" · edge lengths between %f and %f\n", | printf(" · edge lengths between %f and %f\n", | ||||
| minlength, maxlength); | minlength, maxlength); | ||||
| #endif | |||||
| #if 0 | |||||
| /* Experimental calculation of the distance to the opposite | /* Experimental calculation of the distance to the opposite | ||||
| * hyperplane, by picking random points. Works reasonably | * hyperplane, by picking random points. Works reasonably | ||||
| * well up to dimension 6. After that, we’d need something | * well up to dimension 6. After that, we’d need something | ||||
| * better such as gradient walk. */ | * better such as gradient walk. */ | ||||
| #if 0 | |||||
| float mindist = 1.0f; | float mindist = 1.0f; | ||||
| for (int run = 0; run < 10000000; ++run) | for (int run = 0; run < 10000000; ++run) | ||||
| { | { | ||||
| @@ -322,7 +358,6 @@ private: | |||||
| printf(" · approx. dist. to opposite hyperplane: %f\n", mindist); | printf(" · approx. dist. to opposite hyperplane: %f\n", mindist); | ||||
| #endif | #endif | ||||
| #if 0 | |||||
| /* Find a normal vector to the opposite hyperplane. First, pick | /* Find a normal vector to the opposite hyperplane. First, pick | ||||
| * any point p(i0) on the hyperplane. We just need i0 != i. Then, | * 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. | * 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. So we build a vector | ||||
| * full of zeroes except at position i, and multiply it by the | * full of zeroes except at position i, and multiply it by the | ||||
| * matrix inverse. */ | * matrix inverse. */ | ||||
| #if 0 | |||||
| int i0 = (i == 0) ? 1 : 0; | int i0 = (i == 0) ? 1 : 0; | ||||
| mat_t<float, N, N> m; | mat_t<float, N, N> m; | ||||
| for (int j = 0; j < N; ++j) | for (int j = 0; j < N; ++j) | ||||
| @@ -349,6 +385,68 @@ private: | |||||
| printf(" · distance to opposite hyperplane: %f\n", dist); | printf(" · distance to opposite hyperplane: %f\n", dist); | ||||
| #endif | #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"); | printf("\n"); | ||||
| } | } | ||||
| @@ -19,354 +19,11 @@ | |||||
| namespace lol | 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 SetUp() {} | ||||
| void TearDown() {} | 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 | |||||
| }; | }; | ||||
| } | } | ||||