188 lines
7.4 KiB
C#
188 lines
7.4 KiB
C#
|
|
using System.Drawing;
|
|||
|
|
using System.Numerics;
|
|||
|
|
|
|||
|
|
namespace AutoAgent.Domain.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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public abstract class PerlinGenerator<T> : BaseGenerator<T>
|
|||
|
|
{
|
|||
|
|
private Perlin2D _perlin;
|
|||
|
|
private double _amplitude = 100.0;
|
|||
|
|
public double Amplitude { get => _amplitude; protected set => _amplitude = Math.Abs(value); }
|
|||
|
|
public double Persistance { get; protected set; } = 0.5;
|
|||
|
|
public double Frequency { get; protected set; } = 5.0;
|
|||
|
|
public int Octaves { get; protected set; } = 4;
|
|||
|
|
public double Seed { get; private set; }
|
|||
|
|
public override double Step { get; protected set; }
|
|||
|
|
protected double Noise { get => _perlin.Noise(Progress * Frequency, Seed, Octaves, Persistance); }
|
|||
|
|
public PerlinGenerator(T min, T max, double seed)
|
|||
|
|
: base(min, max)
|
|||
|
|
{
|
|||
|
|
Seed = seed;
|
|||
|
|
_perlin = new Perlin2D((int)(Seed * 0x000fffff));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public sealed class PerlinNumberGenerator<T> : PerlinGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
|||
|
|
{
|
|||
|
|
public INumberGenerator<T> Self { get => this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetOctaves(int octaves) { Octaves = octaves; return this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetPersistence(double peristance) { Persistance = peristance; return this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetFrequency(double frequency) { Frequency = frequency; return this; }
|
|||
|
|
public PerlinNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
|||
|
|
protected override T Calc()
|
|||
|
|
{
|
|||
|
|
var offs = Noise * Scale * ((Self.DMax - Self.DMin) / 2.0);
|
|||
|
|
|
|||
|
|
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
|
|||
|
|
}
|
|||
|
|
private static double GetSeed(T min, T max, double seed)
|
|||
|
|
{
|
|||
|
|
if (Double.IsNormal(seed)) return seed;
|
|||
|
|
return (Double.CreateChecked(min) + Double.CreateChecked(max)) / 1000.0;
|
|||
|
|
}
|
|||
|
|
public PerlinNumberGenerator(T min, T max, int count = 10, double seed = Double.NaN) : base(min, max, GetSeed(min, max, seed))
|
|||
|
|
{
|
|||
|
|
Step = CalcStepByCount(count);
|
|||
|
|
Amplitude = 1.0; // Set default
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public sealed class PerlinPointGenerator : PerlinGenerator<Point>, IPointGenerator
|
|||
|
|
{
|
|||
|
|
private readonly double _pAngle;
|
|||
|
|
public IPointGenerator Self { get => this; }
|
|||
|
|
public PerlinPointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
|||
|
|
public PerlinPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
|||
|
|
public PerlinPointGenerator SetOctaves(int octaves) { Octaves = octaves; return this; }
|
|||
|
|
public PerlinPointGenerator SetPersistence(double peristance) { Persistance = peristance; return this; }
|
|||
|
|
public PerlinPointGenerator SetFrequency(double frequency) { Frequency = frequency; return this; }
|
|||
|
|
public PerlinPointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
|||
|
|
protected override Point Calc()
|
|||
|
|
{
|
|||
|
|
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Noise * Amplitude * Scale, _pAngle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static double GetSeed(Point start, Point end, double seed)
|
|||
|
|
{
|
|||
|
|
if (Double.IsNormal(seed)) return seed;
|
|||
|
|
return (start.X + end.Y) / 1000.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static PerlinPointGenerator CreateByCount(Point start, Point end, int count, double seed = Double.NaN)
|
|||
|
|
{
|
|||
|
|
return new PerlinPointGenerator(start, end, seed)
|
|||
|
|
{
|
|||
|
|
Step = CalcStepByCount(count)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
public static PerlinPointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
|
|||
|
|
{
|
|||
|
|
return new PerlinPointGenerator(start, end, pxPerStep, seed);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public PerlinPointGenerator(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
|
|||
|
|
: this(start, end, seed)
|
|||
|
|
{
|
|||
|
|
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private PerlinPointGenerator(Point start, Point end, double seed)
|
|||
|
|
: base(start, end, GetSeed(start, end, seed))
|
|||
|
|
{
|
|||
|
|
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|