Graphing/Base/Graphables/SlopeField.cs

85 lines
2.6 KiB
C#

using Graphing.Forms;
using Graphing.Parts;
namespace Graphing.Graphables;
public class SlopeField : Graphable
{
private static int slopeFieldNum;
protected readonly SlopeFieldsDelegate equ;
protected readonly int detail;
protected readonly List<(Float2, GraphLine)> cache;
public SlopeField(int detail, SlopeFieldsDelegate equ)
{
slopeFieldNum++;
Name = $"Slope Field {slopeFieldNum}";
this.equ = equ;
this.detail = detail;
cache = [];
}
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
{
double epsilon = 1 / (detail * 2.0);
List<IGraphPart> lines = [];
for (double x = Math.Ceiling(graph.MinVisibleGraph.x - 1); x < graph.MaxVisibleGraph.x + 1; x += 1.0 / detail)
{
for (double y = Math.Ceiling(graph.MinVisibleGraph.y - 1); y < graph.MaxVisibleGraph.y + 1; y += 1.0 / detail)
{
lines.Add(GetFromCache(epsilon, x, y));
}
}
return lines;
}
protected GraphLine MakeSlopeLine(Float2 position, double slope)
{
double size = detail;
double dirX = size, dirY = slope * size;
double magnitude = Math.Sqrt(dirX * dirX + dirY * dirY);
dirX /= magnitude * size * 2;
dirY /= magnitude * size * 2;
return new(new(position.x + dirX, position.y + dirY), new(position.x - dirX, position.y - dirY));
}
protected GraphLine GetFromCache(double epsilon, double x, double y)
{
// Probably no binary search here, though maybe it could be done
// in terms of just one axis.
foreach ((Float2 p, GraphLine l) in cache)
{
double diffX = Math.Abs(p.x - x),
diffY = Math.Abs(p.y - y);
if (diffX < epsilon && diffY < epsilon) return l;
}
// Create a new value.
double slope = equ(x, y);
GraphLine result = MakeSlopeLine(new Float2(x, y), slope);
cache.Add((new Float2(x, y), result));
return result;
}
public override Graphable DeepCopy() => new SlopeField(detail, equ);
public override void EraseCache() => cache.Clear();
public override long GetCacheBytes() => cache.Count * 48;
public override bool ShouldSelectGraphable(in GraphForm graph, Float2 graphMousePos, double factor) => false;
public override Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos) => default;
public override void Preload(Float2 xRange, Float2 yRange) { }
}
public delegate double SlopeFieldsDelegate(double x, double y);