// ReSharper disable InconsistentNaming using System; namespace RGB.NET.Core.Tests.Helper; // Simplex Noise for C# // Copyright © Benjamin Ward 2019 // See LICENSE (https://github.com/WardBenjamin/SimplexNoise/blob/2afa9a63483562cc4c0a95bbfa6b183fc256a790/LICENSE.txt) // Simplex Noise implementation offering 1D, 2D, and 3D forms w/ values in the range of 0 to 255. // Based on work by Heikki Törmälä (2012) and Stefan Gustavson (2006). /// /// Implementation of the Perlin simplex noise, an improved Perlin noise algorithm. /// Based loosely on SimplexNoise1234 by Stefan Gustavson: http://staffwww.itn.liu.se/~stegu/aqsis/aqsis-newnoise/ /// public static class SimplexNoise { public static float[] Calc1D(int width, float scale) { float[] values = new float[width]; for (int i = 0; i < width; i++) values[i] = Generate(i * scale); return values; } public static float[,] Calc2D(int width, int height, float scale) { float[,] values = new float[width, height]; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) values[i, j] = Generate(i * scale, j * scale); return values; } public static float[,,] Calc3D(int width, int height, int length, float scale) { float[,,] values = new float[width, height, length]; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) for (int k = 0; k < length; k++) values[i, j, k] = Generate(i * scale, j * scale, k * scale); return values; } public static float CalcPixel1D(int x, float scale) => Generate(x * scale); public static float CalcPixel2D(int x, int y, float scale) => Generate(x * scale, y * scale); public static float CalcPixel3D(int x, int y, int z, float scale) => Generate(x * scale, y * scale, z * scale); static SimplexNoise() { _perm = new byte[PermOriginal.Length]; PermOriginal.CopyTo(_perm, 0); } public static int Seed { get => _seed; set { if (value == 0) { _perm = new byte[PermOriginal.Length]; PermOriginal.CopyTo(_perm, 0); } else { _perm = new byte[512]; Random random = new(value); random.NextBytes(_perm); } _seed = value; } } private static int _seed; /// /// 1D simplex noise /// /// /// private static float Generate(float x) { int i0 = FastFloor(x); int i1 = i0 + 1; float x0 = x - i0; float x1 = x0 - 1.0f; float t0 = 1.0f - (x0 * x0); t0 *= t0; float n0 = t0 * t0 * Grad(_perm[i0 & 0xff], x0); float t1 = 1.0f - (x1 * x1); t1 *= t1; float n1 = t1 * t1 * Grad(_perm[i1 & 0xff], x1); // The maximum value of this noise is 8*(3/4)^4 = 2.53125 // A factor of 0.395 scales to fit exactly within [-1,1] return 0.395f * (n0 + n1); } /// /// 2D simplex noise /// /// /// /// private static float Generate(float x, float y) { const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 float n0, n1, n2; // Noise contributions from the three corners // Skew the input space to determine which simplex cell we're in float s = (x + y) * F2; // Hairy factor for 2D float xs = x + s; float ys = y + s; int i = FastFloor(xs); int j = FastFloor(ys); float t = (i + j) * G2; float X0 = i - t; // Unskew the cell origin back to (x,y) space float Y0 = j - t; float x0 = x - X0; // The x,y distances from the cell origin float y0 = y - Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords if (x0 > y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) else { i1 = 0; j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 float x1 = (x0 - i1) + G2; // Offsets for middle corner in (x,y) unskewed coords float y1 = (y0 - j1) + G2; float x2 = (x0 - 1.0f) + (2.0f * G2); // Offsets for last corner in (x,y) unskewed coords float y2 = (y0 - 1.0f) + (2.0f * G2); // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = Mod(i, 256); int jj = Mod(j, 256); // Calculate the contribution from the three corners float t0 = 0.5f - (x0 * x0) - (y0 * y0); if (t0 < 0.0f) n0 = 0.0f; else { t0 *= t0; n0 = t0 * t0 * Grad(_perm[ii + _perm[jj]], x0, y0); } float t1 = 0.5f - (x1 * x1) - (y1 * y1); if (t1 < 0.0f) n1 = 0.0f; else { t1 *= t1; n1 = t1 * t1 * Grad(_perm[ii + i1 + _perm[jj + j1]], x1, y1); } float t2 = 0.5f - (x2 * x2) - (y2 * y2); if (t2 < 0.0f) n2 = 0.0f; else { t2 *= t2; n2 = t2 * t2 * Grad(_perm[ii + 1 + _perm[jj + 1]], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 40.0f * (n0 + n1 + n2); } private static float Generate(float x, float y, float z) { // Simple skewing factors for the 3D case const float F3 = 0.333333333f; const float G3 = 0.166666667f; float n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in float s = (x + y + z) * F3; // Very nice and simple skew factor for 3D float xs = x + s; float ys = y + s; float zs = z + s; int i = FastFloor(xs); int j = FastFloor(ys); int k = FastFloor(zs); float t = (i + j + k) * G3; float X0 = i - t; // Unskew the cell origin back to (x,y,z) space float Y0 = j - t; float Z0 = k - t; float x0 = x - X0; // The x,y,z distances from the cell origin float y0 = y - Y0; float z0 = z - Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords /* This code would benefit from a backport from the GLSL version! */ if (x0 >= y0) { if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order } else { // x0 0) ? ((int)x) : (((int)x) - 1); } private static int Mod(int x, int m) { int a = x % m; return a < 0 ? a + m : a; } private static float Grad(int hash, float x) { int h = hash & 15; float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 if ((h & 8) != 0) grad = -grad; // Set a random sign for the gradient return (grad * x); // Multiply the gradient with the distance } private static float Grad(int hash, float x, float y) { int h = hash & 7; // Convert low 3 bits of hash code float u = h < 4 ? x : y; // into 8 simple gradient directions, float v = h < 4 ? y : x; // and compute the dot product with (x,y). return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -2.0f * v : 2.0f * v); } private static float Grad(int hash, float x, float y, float z) { int h = hash & 15; // Convert low 4 bits of hash code into 12 simple float u = h < 8 ? x : y; // gradient directions, and compute dot product. float v = h < 4 ? y : (h == 12) || (h == 14) ? x : z; // Fix repeats at h = 12 to 15 return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v); } }