From: Daniel Karbach Date: Mon, 13 Nov 2017 21:28:14 +0000 (+0100) Subject: rand lib from blank X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=644b3fb597ec32a6238f7d0b13a3271bce9fb9c5;p=blobs.git rand lib from blank --- diff --git a/src/rand/GaloisLFSR.hpp b/src/rand/GaloisLFSR.hpp new file mode 100644 index 0000000..83dc0e5 --- /dev/null +++ b/src/rand/GaloisLFSR.hpp @@ -0,0 +1,87 @@ +#ifndef BLOBS_RAND_GALOISLFSR_HPP_ +#define BLOBS_RAND_GALOISLFSR_HPP_ + +#include +#include +#include + + +namespace blobs { +namespace rand { + +class GaloisLFSR { + +public: + // seed should be non-zero + explicit GaloisLFSR(std::uint64_t seed) noexcept + : state(seed) { + if (state == 0) { + state = 1; + } + } + + // get the next bit + bool operator ()() noexcept { + bool result = state & 1; + state >>= 1; + if (result) { + state |= 0x8000000000000000; + state ^= mask; + } else { + state &= 0x7FFFFFFFFFFFFFFF; + } + return result; + } + + template + T operator ()(T &out) noexcept { + constexpr int num_bits = + std::numeric_limits::digits + + std::numeric_limits::is_signed; + for (int i = 0; i < num_bits; ++i) { + operator ()(); + } + return out = static_cast(state); + } + + /// special case for randrom(boolean), since static_cast(0b10) == true + bool operator ()(bool &out) noexcept { + return out = operator ()(); + } + + template + T Next() noexcept { + T next; + return (*this)(next); + } + + float SNorm() noexcept { + return float(Next()) * (1.0f / 2147483647.5f) - 1.0f; + } + + float UNorm() noexcept { + return float(Next()) * (1.0f / 4294967295.0f); + } + + template + typename Container::reference From(Container &c) { + assert(c.size() > 0); + return c[Next() % c.size()]; + } + template + typename Container::const_reference From(const Container &c) { + assert(c.size() > 0); + return c[Next() % c.size()]; + } + +private: + std::uint64_t state; + // bits 64, 63, 61, and 60 set to 1 (counting from 1 lo to hi) + static constexpr std::uint64_t mask = 0xD800000000000000; + +}; + +} +} + +#endif diff --git a/src/rand/OctaveNoise.hpp b/src/rand/OctaveNoise.hpp new file mode 100644 index 0000000..6a91c85 --- /dev/null +++ b/src/rand/OctaveNoise.hpp @@ -0,0 +1,35 @@ +#ifndef BLOBS_RAND_OCTAVENOISE_HPP_ +#define BLOBS_RAND_OCTAVENOISE_HPP_ + +#include "../graphics/glm.hpp" + + +namespace blobs { +namespace rand { + +template +float OctaveNoise( + const Noise &noise, + const glm::vec3 &in, + int num, + float persistence, + float frequency = 1.0f, + float amplitude = 1.0f, + float growth = 2.0f +) { + float total = 0.0f; + float max = 0.0f; + for (int i = 0; i < num; ++i) { + total += noise(in * frequency) * amplitude; + max += amplitude; + amplitude *= persistence; + frequency *= growth; + } + + return total / max; +} + +} +} + +#endif diff --git a/src/rand/SimplexNoise.hpp b/src/rand/SimplexNoise.hpp new file mode 100644 index 0000000..9a72e64 --- /dev/null +++ b/src/rand/SimplexNoise.hpp @@ -0,0 +1,38 @@ +#ifndef BLOBS_RAND_SIMPLEXNOISE_HPP_ +#define BLOBS_RAND_SIMPLEXNOISE_HPP_ + +#include "../graphics/glm.hpp" + +#include + + +namespace blobs { +namespace rand { + +class SimplexNoise { + +public: + explicit SimplexNoise(std::uint64_t seed) noexcept; + + float operator ()(const glm::vec3 &) const noexcept; + +private: + int Perm(int idx) const noexcept; + int Perm12(int idx) const noexcept; + const glm::vec3 &Grad(int idx) const noexcept; + +private: + int perm[512]; + int perm12[512]; + glm::vec3 grad[12]; + glm::ivec3 second_ints[8]; + glm::ivec3 third_ints[8]; + glm::vec3 second_floats[8]; + glm::vec3 third_floats[8]; + +}; + +} +} + +#endif diff --git a/src/rand/WorleyNoise.hpp b/src/rand/WorleyNoise.hpp new file mode 100644 index 0000000..34c4871 --- /dev/null +++ b/src/rand/WorleyNoise.hpp @@ -0,0 +1,27 @@ +#ifndef BLOBS_RAND_WORLEYNOISE_HPP_ +#define BLOBS_RAND_WORLEYNOISE_HPP_ + +#include "../graphics/glm.hpp" + + +namespace blobs { +namespace rand { + +/// implementation of Worley noise (aka Cell or Voroni noise) +class WorleyNoise { + +public: + explicit WorleyNoise(unsigned int seed) noexcept; + + float operator ()(const glm::vec3 &) const noexcept; + +private: + const unsigned int seed; + const int num_points; + +}; + +} +} + +#endif diff --git a/src/rand/noise.cpp b/src/rand/noise.cpp new file mode 100644 index 0000000..07ab817 --- /dev/null +++ b/src/rand/noise.cpp @@ -0,0 +1,212 @@ +#include "GaloisLFSR.hpp" +#include "SimplexNoise.hpp" +#include "WorleyNoise.hpp" + +#include +#include + + +namespace { + +constexpr float one_third = 1.0f/3.0f; +constexpr float one_sixth = 1.0f/6.0f; + +} + +namespace blobs { +namespace rand { + +SimplexNoise::SimplexNoise(std::uint64_t seed) noexcept +: grad({ + { 1.0f, 1.0f, 0.0f }, + { -1.0f, 1.0f, 0.0f }, + { 1.0f, -1.0f, 0.0f }, + { -1.0f, -1.0f, 0.0f }, + { 1.0f, 0.0f, 1.0f }, + { -1.0f, 0.0f, 1.0f }, + { 1.0f, 0.0f, -1.0f }, + { -1.0f, 0.0f, -1.0f }, + { 0.0f, 1.0f, 1.0f }, + { 0.0f, -1.0f, 1.0f }, + { 0.0f, 1.0f, -1.0f }, + { 0.0f, -1.0f, -1.0f }, +}) +, second_ints({ + // x>y x>z y>z + { 0, 0, 1 }, // 0 0 0 ZYX + { 0, 1, 0 }, // 0 0 1 YZX + { 0, 0, 1 }, // 0 1 0 illogical, but ZYX + { 0, 1, 0 }, // 0 1 1 YXZ + { 0, 0, 1 }, // 1 0 0 ZXY + { 1, 0, 0 }, // 1 0 1 illogical, but XYZ + { 1, 0, 0 }, // 1 1 0 XZY + { 1, 0, 0 }, // 1 1 1 XYZ +}) +, third_ints({ + // x>y x>z y>z + { 0, 1, 1 }, // 0 0 0 ZYX + { 0, 1, 1 }, // 0 0 1 YZX + { 0, 1, 1 }, // 0 1 0 illogical, but ZYX + { 1, 1, 0 }, // 0 1 1 YXZ + { 1, 0, 1 }, // 1 0 0 ZXY + { 1, 1, 0 }, // 1 0 1 illogical, but XYZ + { 1, 0, 1 }, // 1 1 0 XZY + { 1, 1, 0 }, // 1 1 1 XYZ +}) +, second_floats({ + // x>y x>z y>z + { 0.0f, 0.0f, 1.0f }, // 0 0 0 ZYX + { 0.0f, 1.0f, 0.0f }, // 0 0 1 YZX + { 0.0f, 0.0f, 1.0f }, // 0 1 0 illogical, but ZYX + { 0.0f, 1.0f, 0.0f }, // 0 1 1 YXZ + { 0.0f, 0.0f, 1.0f }, // 1 0 0 ZXY + { 1.0f, 0.0f, 0.0f }, // 1 0 1 illogical, but XYZ + { 1.0f, 0.0f, 0.0f }, // 1 1 0 XZY + { 1.0f, 0.0f, 0.0f }, // 1 1 1 XYZ +}) +, third_floats({ + // x>y x>z y>z + { 0.0f, 1.0f, 1.0f }, // 0 0 0 ZYX + { 0.0f, 1.0f, 1.0f }, // 0 0 1 YZX + { 0.0f, 1.0f, 1.0f }, // 0 1 0 illogical, but ZYX + { 1.0f, 1.0f, 0.0f }, // 0 1 1 YXZ + { 1.0f, 0.0f, 1.0f }, // 1 0 0 ZXY + { 1.0f, 1.0f, 0.0f }, // 1 0 1 illogical, but XYZ + { 1.0f, 0.0f, 1.0f }, // 1 1 0 XZY + { 1.0f, 1.0f, 0.0f }, // 1 1 1 XYZ +}) { + GaloisLFSR random(seed ^ 0x0123456789ACBDEF); + unsigned char value; + for (size_t i = 0; i < 256; ++i) { + perm[i] = random(value); + perm[i] &= 0xFF; + perm[i + 256] = perm[i]; + perm12[i] = perm[i] % 12; + perm12[i + 256] = perm12[i]; + } +} + + +float SimplexNoise::operator ()(const glm::vec3 &in) const noexcept { + float skew = (in.x + in.y + in.z) * one_third; + + glm::vec3 skewed(glm::floor(in + skew)); + float tr = (skewed.x + skewed.y + skewed.z) * one_sixth; + + glm::vec3 unskewed(skewed - tr); + glm::vec3 relative(in - unskewed); + + bool x_ge_y = relative.x >= relative.y; + bool x_ge_z = relative.x >= relative.z; + bool y_ge_z = relative.y >= relative.z; + unsigned int st = (x_ge_y << 2) | (x_ge_z << 1) | y_ge_z; + + glm::ivec3 second_int(second_ints[st]); + glm::ivec3 third_int(third_ints[st]); + glm::vec3 second_float(second_floats[st]); + glm::vec3 third_float(third_floats[st]); + + glm::vec3 offset[4] = { + in - unskewed, + relative - second_float + one_sixth, + relative - third_float + one_third, + relative - 0.5f, + }; + + int index[3] = { + (int)(skewed.x) & 0xFF, + (int)(skewed.y) & 0xFF, + (int)(skewed.z) & 0xFF, + }; + + float n = 0.0f; + + // I know 0.6 is wrong, but for some reason it looks better than 0.5 + + // 0 + float t = glm::clamp(0.6f - glm::length2(offset[0]), 0.0f, 1.0f); + t *= t; + int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2]))); + n += t * t * glm::dot(Grad(corner), offset[0]); + + // 1 + t = glm::clamp(0.6f - glm::length2(offset[1]), 0.0f, 1.0f); + t *= t; + corner = Perm12(index[0] + second_int.x + Perm(index[1] + second_int.y + Perm(index[2] + second_int.z))); + n += t * t * glm::dot(Grad(corner), offset[1]); + + // 2 + t = glm::clamp(0.6f - glm::length2(offset[2]), 0.0f, 1.0f); + t *= t; + corner = Perm12(index[0] + third_int.x + Perm(index[1] + third_int.y + Perm(index[2] + third_int.z))); + n += t * t * glm::dot(Grad(corner), offset[2]); + + // 3 + t = glm::clamp(0.6f - glm::length2(offset[3]), 0.0f, 1.0f); + t *= t; + corner = Perm12(index[0] + 1 + Perm(index[1] + 1 + Perm(index[2] + 1))); + n += t * t * glm::dot(Grad(corner), offset[3]); + + return 32.0f * n; +} + + +int SimplexNoise::Perm(int idx) const noexcept { + return perm[idx]; +} + +int SimplexNoise::Perm12(int idx) const noexcept { + return perm12[idx]; +} + +const glm::vec3 &SimplexNoise::Grad(int idx) const noexcept { + return grad[idx]; +} + + +WorleyNoise::WorleyNoise(unsigned int seed) noexcept +: seed(seed) +, num_points(8) { + +} + +float WorleyNoise::operator ()(const glm::vec3 &in) const noexcept { + glm::vec3 center = glm::floor(in); + + float closest = 1.0f; // cannot be farther away than 1.0 + + for (int z = -1; z <= 1; ++z) { + for (int y = -1; y <= 1; ++y) { + for (int x = -1; x <= 1; ++x) { + glm::vec3 cube(center.x + x, center.y + y, center.z + z); + unsigned int cube_rand = + (unsigned(cube.x) * 130223) ^ + (unsigned(cube.y) * 159899) ^ + (unsigned(cube.z) * 190717) ^ + seed; + + for (int i = 0; i < num_points; ++i) { + glm::vec3 point(cube); + cube_rand = 190667 * cube_rand + 109807; + point.x += float(cube_rand % 262144) / 262144.0f; + cube_rand = 135899 * cube_rand + 189169; + point.y += float(cube_rand % 262144) / 262144.0f; + cube_rand = 159739 * cube_rand + 112139; + point.z += float(cube_rand % 262144) / 262144.0f; + + float distance = glm::distance(in, point); + if (distance < closest) { + closest = distance; + } + } + } + } + } + + // closest ranges (0, 1), so normalizing to (-1,1) is trivial + // though heavily biased towards lower numbers + return 2.0f * closest - 1.0f; +} + +} +} diff --git a/tst/rand/GaloisLFSRTest.cpp b/tst/rand/GaloisLFSRTest.cpp new file mode 100644 index 0000000..0078207 --- /dev/null +++ b/tst/rand/GaloisLFSRTest.cpp @@ -0,0 +1,86 @@ +#include "GaloisLFSRTest.hpp" + +#include "rand/GaloisLFSR.hpp" + +#include +#include + +CPPUNIT_TEST_SUITE_REGISTRATION(blobs::rand::test::GaloisLFSRTest); + +using namespace std; + + +namespace blobs { +namespace rand { +namespace test { + +void GaloisLFSRTest::setUp() { + +} + +void GaloisLFSRTest::tearDown() { + +} + +void GaloisLFSRTest::testFloatNorm() { + GaloisLFSR random(4); + for (int i = 0; i < 64; ++i) { + float value = random.SNorm(); + AssertBetween( + "random signed normal float", + -1.0f, 1.0f, value); + } + for (int i = 0; i < 64; ++i) { + float value = random.UNorm(); + AssertBetween( + "random unsigned normal float", + 0.0f, 1.0f, value); + } +} + +void GaloisLFSRTest::testFromContainer() { + GaloisLFSR random(5); + const vector container({ 1, 2, 3, 4, 5 }); + for (int i = 0; i < 64; ++i) { + int element = random.From(container); + AssertContains( + "random element from container", + container, element); + } +} + +void GaloisLFSRTest::AssertBetween( + string message, + float minimum, + float maximum, + float actual +) { + stringstream msg; + msg << message << ": " << actual << " not in [" + << minimum << ',' << maximum << ']'; + CPPUNIT_ASSERT_MESSAGE( + msg.str(), + minimum <= actual && actual <= maximum); + +} + +void GaloisLFSRTest::AssertContains( + string message, + const vector &container, + int element +) { + stringstream msg; + msg << message << ": " << element << " not in { "; + for (int i : container) { + msg << i << ' '; + } + msg << '}'; + CPPUNIT_ASSERT_MESSAGE( + msg.str(), + find(container.begin(), container.end(), element) != container.end()); + +} + +} +} +} diff --git a/tst/rand/GaloisLFSRTest.hpp b/tst/rand/GaloisLFSRTest.hpp new file mode 100644 index 0000000..512bb99 --- /dev/null +++ b/tst/rand/GaloisLFSRTest.hpp @@ -0,0 +1,49 @@ +#ifndef BLOBS_TEST_RAND_GALOISLFSRTEST_HPP +#define BLOBS_TEST_RAND_GALOISLFSRTEST_HPP + +#include + +#include +#include + + +namespace blobs { +namespace rand { +namespace test { + +class GaloisLFSRTest +: public CppUnit::TestFixture { + +CPPUNIT_TEST_SUITE(GaloisLFSRTest); + +CPPUNIT_TEST(testFloatNorm); +CPPUNIT_TEST(testFromContainer); + +CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testFloatNorm(); + void testFromContainer(); + + /// check if value is in range [minimum,maximum] + static void AssertBetween( + std::string message, + float minimum, + float maximum, + float actual); + + static void AssertContains( + std::string message, + const std::vector &container, + int element); + +}; + +} +} +} + +#endif diff --git a/tst/rand/StabilityTest.cpp b/tst/rand/StabilityTest.cpp new file mode 100644 index 0000000..c6c6339 --- /dev/null +++ b/tst/rand/StabilityTest.cpp @@ -0,0 +1,305 @@ +#include "StabilityTest.hpp" + +#include "rand/GaloisLFSR.hpp" +#include "rand/SimplexNoise.hpp" +#include "rand/WorleyNoise.hpp" + +#include +#include +#include +#include + +CPPUNIT_TEST_SUITE_REGISTRATION(blobs::rand::test::StabilityTest); + +using namespace std; + + +namespace blobs { +namespace rand { +namespace test { + +void StabilityTest::setUp() { + +} + +void StabilityTest::tearDown() { + +} + + +void StabilityTest::testRNG() { + GaloisLFSR random(0); + uint16_t value; + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #1 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #2 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #3 from RNG", + uint16_t(0xB000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #4 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #5 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #6 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #7 from RNG", + uint16_t(0x4500), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #8 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #9 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #10 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #11 from RNG", + uint16_t(0x2E70), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #12 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #13 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #14 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #15 from RNG", + uint16_t(0x1011), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #16 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #17 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #18 from RNG", + uint16_t(0xB000), value + ); + value = random.Next(); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #19 from RNG (using Next())", + uint16_t(0x0B0B), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #20 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #21 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #22 from RNG", + uint16_t(0x1500), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #23 from RNG", + uint16_t(0x0454), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #24 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #25 from RNG", + uint16_t(0x0000), value + ); + value = random.Next(); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #26 from RNG (using Next())", + uint16_t(0xC970), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #27 from RNG", + uint16_t(0x02E5), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #28 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #29 from RNG", + uint16_t(0x0000), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #30 from RNG", + uint16_t(0x0101), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #31 from RNG", + uint16_t(0x0100), value + ); + random(value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected value #32 from RNG", + uint16_t(0x0000), value + ); + + GaloisLFSR random1(1); + uint16_t value1; + for (int i = 0; i < 32; ++i) { + random1(value1); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "RNG with seeds 0 and 1 differ", + value, value1 + ); + + GaloisLFSR random_bool(0); + bool value_bool; + for (int i = 0; i < (16 * 32); ++i) { + random_bool(value_bool); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected result for bool", + false, value_bool + ); + + GaloisLFSR random8(0); + uint8_t value8; + for (int i = 0; i < 31; ++i) { + random8(value8); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected result for uint8", + uint8_t(0x10), value8 + ); + + GaloisLFSR random32(0); + uint32_t value32; + for (int i = 0; i < 16; ++i) { + random32(value32); + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected result for uint32", + uint32_t(0xB0000000), value32 + ); +} + +void StabilityTest::testSimplex() { + SimplexNoise noise(0); + + Assert(noise, glm::vec3(0.0f, 0.0f, 0.0f), 0.0f); + Assert(noise, glm::vec3(0.0f, 0.0f, 1.0f), 0.652221322059631f); + Assert(noise, glm::vec3(0.0f, 1.0f, 0.0f), 0.867977976799011f); + Assert(noise, glm::vec3(0.0f, 1.0f, 1.0f), -0.107878111302853f); + Assert(noise, glm::vec3(1.0f, 0.0f, 0.0f), -0.107878260314465f); + Assert(noise, glm::vec3(1.0f, 0.0f, 1.0f), -6.31356940061778e-08f); + Assert(noise, glm::vec3(1.0f, 1.0f, 0.0f), -0.107878245413303f); + Assert(noise, glm::vec3(1.0f, 1.0f, 1.0f), 0.0f); + + Assert(noise, glm::vec3( 0.0f, 0.0f, -1.0f), -0.107878483831882f); + Assert(noise, glm::vec3( 0.0f, -1.0f, 0.0f), -0.760099768638611f); + Assert(noise, glm::vec3( 0.0f, -1.0f, -1.0f), 0.0f); + Assert(noise, glm::vec3(-1.0f, 0.0f, 0.0f), 0.760099768638611f); + Assert(noise, glm::vec3(-1.0f, 0.0f, -1.0f), 0.0f); + Assert(noise, glm::vec3(-1.0f, -1.0f, 0.0f), -0.107878118753433f); + Assert(noise, glm::vec3(-1.0f, -1.0f, -1.0f), 0.0f); +} + +void StabilityTest::testWorley() { + WorleyNoise noise(0); + + Assert(noise, glm::vec3(0.0f, 0.0f, 0.0f), -0.117765009403229f); + Assert(noise, glm::vec3(0.0f, 0.0f, 1.0f), -0.209876894950867f); + Assert(noise, glm::vec3(0.0f, 1.0f, 0.0f), -0.290086328983307f); + Assert(noise, glm::vec3(0.0f, 1.0f, 1.0f), -0.332393705844879f); + Assert(noise, glm::vec3(1.0f, 0.0f, 0.0f), -0.621925830841064f); + Assert(noise, glm::vec3(1.0f, 0.0f, 1.0f), -0.338455379009247f); + Assert(noise, glm::vec3(1.0f, 1.0f, 0.0f), -0.386664032936096f); + Assert(noise, glm::vec3(1.0f, 1.0f, 1.0f), -0.533940434455872f); + + Assert(noise, glm::vec3( 0.0f, 0.0f, -1.0f), -0.425480604171753f); + Assert(noise, glm::vec3( 0.0f, -1.0f, 0.0f), -0.189745843410492f); + Assert(noise, glm::vec3( 0.0f, -1.0f, -1.0f), -0.30408102273941f); + Assert(noise, glm::vec3(-1.0f, 0.0f, 0.0f), -0.618566155433655f); + Assert(noise, glm::vec3(-1.0f, 0.0f, -1.0f), -0.060045599937439f); + Assert(noise, glm::vec3(-1.0f, -1.0f, 0.0f), -0.366827547550201f); + Assert(noise, glm::vec3(-1.0f, -1.0f, -1.0f), -0.575981974601746f); +} + +void StabilityTest::Assert( + const SimplexNoise &noise, + const glm::vec3 &position, + float expected +) { + stringstream msg; + msg << "unexpected simplex noise value at " << position; + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( + msg.str(), + expected, noise(position), numeric_limits::epsilon() + ); +} + +void StabilityTest::Assert( + const WorleyNoise &noise, + const glm::vec3 &position, + float expected +) { + stringstream msg; + msg << "unexpected worley noise value at " << position; + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( + msg.str(), + expected, noise(position), numeric_limits::epsilon() + ); +} + +} +} +} diff --git a/tst/rand/StabilityTest.hpp b/tst/rand/StabilityTest.hpp new file mode 100644 index 0000000..8f38114 --- /dev/null +++ b/tst/rand/StabilityTest.hpp @@ -0,0 +1,53 @@ +#ifndef BLOBS_TEST_RAND_STABILITYTEST_HPP +#define BLOBS_TEST_RAND_STABILITYTEST_HPP + +#include "graphics/glm.hpp" + +#include + + + +namespace blobs { +namespace rand { + +class SimplexNoise; +class WorleyNoise; + +namespace test { + +class StabilityTest +: public CppUnit::TestFixture { + +CPPUNIT_TEST_SUITE(StabilityTest); + +CPPUNIT_TEST(testRNG); +CPPUNIT_TEST(testSimplex); +CPPUNIT_TEST(testWorley); + +CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testRNG(); + void testSimplex(); + void testWorley(); + + static void Assert( + const SimplexNoise &noise, + const glm::vec3 &position, + float expected); + + static void Assert( + const WorleyNoise &noise, + const glm::vec3 &position, + float expected); + +}; + +} +} +} + +#endif