winmr-api/Utils/Perlin2D.cs
2026-01-08 21:35:06 +03:00

93 lines
3.0 KiB
C#

namespace WebmrAPI.Utils
{
public class Perlin2D : BaseRandom
{
byte[] _mTable = new byte[1024];
public Perlin2D(int seed = 0)
: base(seed)
{
Generate(_mTable);
}
private double[] GetPseudoRandomGradientVector(int x, int y)
{
int v = (int)(((x * 1836311903) ^ (y * 2971215073) + 4807526976) & 1023);
v = _mTable[v] & 3;
switch (v)
{
case 0: return new double[] { 1, 0 };
case 1: return new double[] { -1, 0 };
case 2: return new double[] { 0, 1 };
default: return new double[] { 0, -1 };
}
}
static double QunticCurve(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
static double Lerp(double a, double b, double t)
{
return a + (b - a) * t;
}
static double Dot(double[] a, double[] b)
{
return a[0] * b[0] + a[1] * b[1];
}
public double Noise(double fx, double fy)
{
int left = (int)System.Math.Floor(fx);
int top = (int)System.Math.Floor(fy);
double pointInQuadX = fx - left;
double pointInQuadY = fy - top;
double[] topLeftGradient = GetPseudoRandomGradientVector(left, top);
double[] topRightGradient = GetPseudoRandomGradientVector(left + 1, top);
double[] bottomLeftGradient = GetPseudoRandomGradientVector(left, top + 1);
double[] bottomRightGradient = GetPseudoRandomGradientVector(left + 1, top + 1);
double[] distanceToTopLeft = { pointInQuadX, pointInQuadY };
double[] distanceToTopRight = { pointInQuadX - 1, pointInQuadY };
double[] distanceToBottomLeft = { pointInQuadX, pointInQuadY - 1 };
double[] distanceToBottomRight = { pointInQuadX - 1, pointInQuadY - 1 };
double tx1 = Dot(distanceToTopLeft, topLeftGradient);
double tx2 = Dot(distanceToTopRight, topRightGradient);
double bx1 = Dot(distanceToBottomLeft, bottomLeftGradient);
double bx2 = Dot(distanceToBottomRight, bottomRightGradient);
pointInQuadX = QunticCurve(pointInQuadX);
pointInQuadY = QunticCurve(pointInQuadY);
double tx = Lerp(tx1, tx2, pointInQuadX);
double bx = Lerp(bx1, bx2, pointInQuadX);
double tb = Lerp(tx, bx, pointInQuadY);
return tb;
}
public double Noise(double fx, double fy, int octaves, double persistence = 0.5)
{
double amplitude = 1;
double max = 0;
double result = 0;
while (octaves-- > 0)
{
max += amplitude;
result += Noise(fx, fy) * amplitude;
amplitude *= persistence;
fx *= 2;
fy *= 2;
}
return result / max;
}
}
}