160 lines
8.6 KiB
C#
160 lines
8.6 KiB
C#
using CultMath;
|
|
using static CultMath.math;
|
|
|
|
namespace CultMath.Tests;
|
|
|
|
/// <summary>
|
|
/// The shader's site-array functions as C# twins. A shader function cannot take a span, so
|
|
/// <c>cultmath_power_border</c>, <c>cultmath_lattice_cell_distance</c> and <c>cultmath_weighted_lattice_cells</c> take
|
|
/// <c>float4 sites[CULTMATH_MAX_SITES]</c> and a count; reflection cannot pass a <see cref="ReadOnlySpan{T}"/>, so each
|
|
/// twin takes the array and the count and calls the span function on <c>sites[..count]</c>. This table is the one
|
|
/// place that maps them, read by the mirror test and by the GLSL fixture. The draws are the cases both run.
|
|
/// </summary>
|
|
internal static class SiteArrays
|
|
{
|
|
/// <summary>The shader's default CULTMATH_MAX_SITES; the device families spell their array with it.</summary>
|
|
internal const int Capacity = 128;
|
|
|
|
internal static float4 power_border(float3 p, float4[] sites, int count) => math.power_border(p, sites.AsSpan(0, count));
|
|
|
|
internal static float lattice_cell_distance(
|
|
float2 q, float2 cell, float time, float drift, float4[] bodies, int count, float cellSize, float boost, float falloff,
|
|
float2 normal, float stretch) =>
|
|
math.lattice_cell_distance(q, cell, time, drift, bodies.AsSpan(0, count), cellSize, boost, falloff, normal, stretch);
|
|
|
|
internal static float4 weighted_lattice_cells(
|
|
float2 p, float time, float4[] bodies, int count, float cellSize, float drift, float boost, float falloff,
|
|
float smoothing, float2 stretchNormal, float stretch) =>
|
|
math.weighted_lattice_cells(p, time, bodies.AsSpan(0, count), cellSize, drift, boost, falloff, smoothing, stretchNormal, stretch);
|
|
|
|
// The array the shader receives: the sites, then filler a loop running past the count would read. The filler is
|
|
// finite and large, so a loop bound that ignores the count changes the result.
|
|
private static float4[] Padded(IReadOnlyList<float4> sites)
|
|
{
|
|
var array = new float4[Capacity];
|
|
for (var i = 0; i < Capacity; i++)
|
|
array[i] = i < sites.Count ? sites[i] : new float4(40.0f * (i + 1), -30.0f * (i + 1), 7.0f, 90.0f + i);
|
|
return array;
|
|
}
|
|
|
|
private static float Uniform(System.Random r, float lo, float hi) => lo + (hi - lo) * r.NextSingle();
|
|
|
|
// Counts 0, 1, 6, 16 and the capacity are drawn often; the rest of [0, Capacity] fills between.
|
|
private static int Count(System.Random r) => r.Next(3) == 0 ? r.Next(Capacity + 1) : new[] { 0, 1, 6, 16, Capacity }[r.Next(5)];
|
|
|
|
// Bodies on slow orbits (float4(position.xy, 0, sigma)), spread over the plane the lattice cells cover.
|
|
private static float4[] Orbit(System.Random r, int count, float time)
|
|
{
|
|
var bodies = new List<float4>();
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var angle = 0.001f * (i + 1) * time + i;
|
|
var radius = 100.0f + 2400.0f * r.NextSingle();
|
|
bodies.Add(new float4(radius * MathF.Cos(angle), radius * MathF.Sin(angle), 0.0f, Uniform(r, 20.0f, 300.0f)));
|
|
}
|
|
|
|
return Padded(bodies);
|
|
}
|
|
|
|
internal static object[] PowerBorderArguments(System.Random r)
|
|
{
|
|
var count = Count(r);
|
|
var sites = new List<float4>();
|
|
for (var i = 0; i < count; i++)
|
|
sites.Add(new float4(Uniform(r, -1500.0f, 1500.0f), Uniform(r, -1500.0f, 1500.0f), Uniform(r, -200.0f, 200.0f), Uniform(r, 10.0f, 300.0f)));
|
|
return new object[] { new float3(Uniform(r, -1500.0f, 1500.0f), Uniform(r, -1500.0f, 1500.0f), Uniform(r, -200.0f, 200.0f)), Padded(sites), count };
|
|
}
|
|
|
|
internal static object[] LatticeCellDistanceArguments(System.Random r)
|
|
{
|
|
var angle = Uniform(r, 0.0f, 6.2831855f);
|
|
var look = LatticeCellsTests.Looks[r.Next(LatticeCellsTests.Looks.Length)];
|
|
var cell = new float2(r.Next(-300, 300), r.Next(-300, 300));
|
|
var count = Count(r);
|
|
return new object[]
|
|
{
|
|
new float2(Uniform(r, -1500.0f, 1500.0f), Uniform(r, -1500.0f, 1500.0f)) / LatticeCellsTests.Cell, cell, Uniform(r, 0.0f, 200.0f), LatticeCellsTests.Drift,
|
|
Orbit(r, count, Uniform(r, 0.0f, 200.0f)), count, LatticeCellsTests.Cell, look.Boost, look.Falloff, new float2(MathF.Cos(angle), MathF.Sin(angle)), look.Stretch,
|
|
};
|
|
}
|
|
|
|
// The unit-less normals the function normalises itself: ordinary, 3-4-5, tiny, huge, and zero (stretch 1 only).
|
|
private static readonly float2[] Normals = { LatticeCellsTests.Normal, new(3.0f, 4.0f), new(3.0e-30f, -4.0e-30f), new(1.0e30f, 2.0e30f), new(0.0f, 0.0f) };
|
|
|
|
internal static readonly LatticeCellsTests.Look WidestLook = new(LatticeMaxBoost, LatticeMaxStretch, LatticeMaxSmoothing, 1.0e6f);
|
|
|
|
/// <summary>
|
|
/// Points whose ring search runs to ring 8 or past it under the widest look (boost, stretch and smoothing at their
|
|
/// maxima, falloff so wide that every cell is boosted), found once by the radius the exit rule visits, and the
|
|
/// ring-17 point of LatticeCellsTests.TheCapIsReachedAndMatchesTheFullBox first. Each is (p, time).
|
|
/// </summary>
|
|
internal static readonly IReadOnlyList<(float2 P, float Time, int Ring)> FarPoints = FindFarPoints();
|
|
|
|
private static List<(float2, float, int)> FindFarPoints()
|
|
{
|
|
var found = new List<(float2, float, int)>();
|
|
var cap = (new float2(1446.7345f, -680.7637f), 97.69269f);
|
|
found.Add((cap.Item1, cap.Item2, LatticeCellsTests.Radius(cap.Item1, cap.Item2, LatticeCellsTests.Bodies(cap.Item2), WidestLook, LatticeCellsTests.Rules[0].stop, new Dictionary<(int, int), float>())));
|
|
var r = new System.Random(0x1A77);
|
|
while (found.Count < 12)
|
|
{
|
|
var p = LatticeCellsTests.RandomPoint(r);
|
|
var time = 13.0f * r.Next(20);
|
|
var ring = LatticeCellsTests.Radius(p, time, LatticeCellsTests.Bodies(time), WidestLook, LatticeCellsTests.Rules[0].stop, new Dictionary<(int, int), float>());
|
|
if (ring >= 8)
|
|
found.Add((p, time, ring));
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Points where an exit one ring early (the rule "bound 0.5*k") returns another result than the exact search, found once by
|
|
/// running both on the six orbiting bodies under each look: the cases that pin the ring exit to the bit.
|
|
/// </summary>
|
|
internal static readonly IReadOnlyList<(float2 P, float Time, LatticeCellsTests.Look Look)> ExitBoundPoints = FindExitBoundPoints();
|
|
|
|
private static List<(float2, float, LatticeCellsTests.Look)> FindExitBoundPoints()
|
|
{
|
|
var found = new List<(float2, float, LatticeCellsTests.Look)>();
|
|
var looks = LatticeCellsTests.Looks.Append(WidestLook).ToArray();
|
|
var r = new System.Random(0xE517);
|
|
while (found.Count < 24)
|
|
{
|
|
var p = LatticeCellsTests.RandomPoint(r);
|
|
var time = 13.0f * r.Next(20);
|
|
var look = looks[r.Next(looks.Length)];
|
|
var bodies = LatticeCellsTests.Bodies(time);
|
|
var early = LatticeCellsTests.Radius(p, time, bodies, look, LatticeCellsTests.Rules[2].stop, new Dictionary<(int, int), float>());
|
|
if (early >= LatticeCellsTests.Radius(p, time, bodies, look, LatticeCellsTests.Rules[0].stop, new Dictionary<(int, int), float>()))
|
|
continue;
|
|
if (!LatticeCellsTests.BoxFold(p, time, bodies, look, Math.Max(0, early), new Dictionary<(int, int), float>()).Equals(LatticeCellsTests.Call(p, time, bodies, look)))
|
|
found.Add((p, time, look));
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
internal static object[] LatticeArguments(System.Random r)
|
|
{
|
|
switch (r.Next(4))
|
|
{
|
|
case 0:
|
|
var far = FarPoints[r.Next(FarPoints.Count)];
|
|
return Lattice(far.P, far.Time, Padded(LatticeCellsTests.Bodies(far.Time)), 6, WidestLook, LatticeCellsTests.Normal);
|
|
case 1:
|
|
var tight = ExitBoundPoints[r.Next(ExitBoundPoints.Count)];
|
|
return Lattice(tight.P, tight.Time, Padded(LatticeCellsTests.Bodies(tight.Time)), 6, tight.Look, LatticeCellsTests.Normal);
|
|
}
|
|
|
|
var time = 13.0f * r.Next(20);
|
|
var normal = Normals[r.Next(Normals.Length)];
|
|
var look = normal.x == 0.0f ? LatticeCellsTests.Looks[0] : LatticeCellsTests.Looks[r.Next(LatticeCellsTests.Looks.Length)];
|
|
var count = Count(r);
|
|
return Lattice(LatticeCellsTests.RandomPoint(r), time, Orbit(r, count, time), count, look, normal);
|
|
}
|
|
|
|
private static object[] Lattice(float2 p, float time, float4[] bodies, int count, LatticeCellsTests.Look look, float2 normal) =>
|
|
new object[] { p, time, bodies, count, LatticeCellsTests.Cell, LatticeCellsTests.Drift,
|
|
look.Boost, look.Falloff, look.Smoothing, normal, look.Stretch };
|
|
}
|