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

98 lines
4.5 KiB
C#

using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
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;
}
}
}