Graphing/Base/Graphables/ParametricEquation.cs

135 lines
4.2 KiB
C#

using Graphing.Abstract;
using Graphing.Forms;
using Graphing.Parts;
using System;
using System.Collections.Generic;
namespace Graphing.Graphables;
public class ParametricEquation : Graphable, IDerivable, ITranslatableXY
{
private static int equationNum;
public double OffsetX { get; set; }
public double OffsetY { get; set; }
public double InitialT { get; set; }
public double FinalT { get; set; }
protected readonly ParametricDelegate equX, equY;
protected readonly List<(double t, Float2 point)> cache;
public ParametricEquation(double initialT, double finalT,
ParametricDelegate equX, ParametricDelegate equY)
{
equationNum++;
Name = $"Parametric Equation {equationNum}";
InitialT = initialT;
FinalT = finalT;
this.equX = equX;
this.equY = equY;
cache = [];
}
public override Graphable ShallowCopy() => new ParametricEquation(InitialT, FinalT, equX, equY);
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
{
const int step = 10;
double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x
- graph.ScreenSpaceToGraphSpace(new Int2(step, 0)).x);
List<IGraphPart> lines = [];
Float2 previousPoint = GetFromCache(InitialT, epsilon);
for (double t = InitialT; t <= FinalT; t += epsilon)
{
Float2 currentPoint = GetFromCache(t, epsilon);
if (graph.IsGraphPointVisible(currentPoint) ||
graph.IsGraphPointVisible(previousPoint))
lines.Add(new GraphLine(previousPoint, currentPoint));
previousPoint = currentPoint;
}
return lines;
}
public Graphable Derive() =>
new ParametricEquation(InitialT, FinalT, GetDerivativeAtPointX, GetDerivativeAtPointY);
public ParametricDelegate GetXDelegate() => equX;
public ParametricDelegate GetYDelegate() => equY;
public double GetDerivativeAtPointX(double t)
{
const double step = 1e-3;
return (equX(t + step) - equX(t)) / step;
}
public double GetDerivativeAtPointY(double t)
{
const double step = 1e-3;
return (equY(t + step) - equY(t)) / step;
}
public Float2 GetDerivativeAtPoint(double t) =>
new(GetDerivativeAtPointX(t), GetDerivativeAtPointY(t));
public Float2 GetPointAt(double t) => GetFromCache(t, 0);
public override void EraseCache() => cache.Clear();
protected Float2 GetFromCache(double t, double epsilon)
{
(double dist, Float2 nearest, int index) = NearestCachedPoint(t);
if (dist < epsilon) return new(nearest.x + OffsetX, nearest.y + OffsetY);
else
{
Float2 result = new(equX(t), equY(t));
cache.Insert(index + 1, (t, result));
return new(result.x + OffsetX, result.y + OffsetY);
}
}
public override long GetCacheBytes() => cache.Count * 24;
protected (double dist, Float2 point, int index) NearestCachedPoint(double t)
{
if (cache.Count <= 1) return (double.PositiveInfinity, new(double.NaN, double.NaN), -1);
else if (cache.Count == 1)
{
(double resultT, Float2 resultPoint) = cache[0];
return (Math.Abs(resultT - t), resultPoint, 0);
}
else
{
int boundA = 0, boundB = cache.Count;
do
{
int boundC = (boundA + boundB) / 2;
(double thisT, Float2 thisPoint) = cache[boundC];
if (thisT == t) return (0, thisPoint, boundC);
else if (thisT > t)
{
boundA = boundC;
}
else // thisT < t
{
boundB = boundC;
}
} while (boundB - boundA > 1);
(double resultT, Float2 resultPoint) = cache[boundA];
return (Math.Abs(resultT - t), resultPoint, boundA);
}
}
public override void Preload(Float2 xRange, Float2 yRange, double step)
{
for (double t = InitialT; t <= FinalT; t += step) GetFromCache(t, step);
}
}
public delegate double ParametricDelegate(double t);