Made binary search work with the caching. Much faster, very happy with it.
This commit is contained in:
parent
e31e6bfdb6
commit
fc829d6a6b
@ -1,6 +1,5 @@
|
|||||||
using Graphing.Extensions;
|
using Graphing.Extensions;
|
||||||
using Graphing.Graphables;
|
using Graphing.Graphables;
|
||||||
using Graphing.Parts;
|
|
||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -170,9 +169,9 @@ public partial class GraphForm : Form
|
|||||||
Invalidate(false);
|
Invalidate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Graph(Graphable able)
|
public void Graph(params Graphable[] able)
|
||||||
{
|
{
|
||||||
ables.Add(able);
|
ables.AddRange(able);
|
||||||
RegenerateMenuItems();
|
RegenerateMenuItems();
|
||||||
Invalidate(false);
|
Invalidate(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,8 @@ public abstract class Graphable
|
|||||||
|
|
||||||
public abstract IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph);
|
public abstract IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph);
|
||||||
|
|
||||||
|
public abstract Graphable DeepCopy();
|
||||||
|
|
||||||
public abstract void EraseCache();
|
public abstract void EraseCache();
|
||||||
public abstract long GetCacheBytes();
|
public abstract long GetCacheBytes();
|
||||||
}
|
}
|
||||||
|
|||||||
51
Base/Graphables/ColumnTable.cs
Normal file
51
Base/Graphables/ColumnTable.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
using Graphing.Parts;
|
||||||
|
|
||||||
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
|
public class ColumnTable : Graphable
|
||||||
|
{
|
||||||
|
private static int tableNum;
|
||||||
|
|
||||||
|
private readonly Dictionary<double, double> tableXY;
|
||||||
|
private readonly double width;
|
||||||
|
|
||||||
|
public ColumnTable(double width, Dictionary<double, double> tableXY)
|
||||||
|
{
|
||||||
|
tableNum++;
|
||||||
|
Name = $"Column Table {tableNum}";
|
||||||
|
|
||||||
|
this.tableXY = tableXY;
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
public ColumnTable(double step, Equation equation, double min, double max)
|
||||||
|
{
|
||||||
|
Name = $"Column Table for {equation.Name}";
|
||||||
|
|
||||||
|
tableXY = [];
|
||||||
|
EquationDelegate equ = equation.GetDelegate();
|
||||||
|
width = 0.75 * step;
|
||||||
|
|
||||||
|
double minRounded = Math.Round(min / step) * step,
|
||||||
|
maxRounded = Math.Round(max / step) * step;
|
||||||
|
for (double x = minRounded; x <= maxRounded; x += step)
|
||||||
|
tableXY.Add(x, equ(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EraseCache() { }
|
||||||
|
public override long GetCacheBytes() => 16 * tableXY.Count;
|
||||||
|
|
||||||
|
public override Graphable DeepCopy() => new ColumnTable(width / 0.75, tableXY.ToArray().ToDictionary());
|
||||||
|
|
||||||
|
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
||||||
|
{
|
||||||
|
List<IGraphPart> items = [];
|
||||||
|
foreach (KeyValuePair<double, double> col in tableXY)
|
||||||
|
{
|
||||||
|
items.Add(GraphRectangle.FromSize(new Float2(col.Key, col.Value / 2),
|
||||||
|
new Float2(width, col.Value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ public class Equation : Graphable
|
|||||||
private static int equationNum;
|
private static int equationNum;
|
||||||
|
|
||||||
private readonly EquationDelegate equ;
|
private readonly EquationDelegate equ;
|
||||||
|
|
||||||
private readonly List<Float2> cache;
|
private readonly List<Float2> cache;
|
||||||
|
|
||||||
public Equation(EquationDelegate equ)
|
public Equation(EquationDelegate equ)
|
||||||
@ -42,9 +41,6 @@ public class Equation : Graphable
|
|||||||
previousX = currentX;
|
previousX = currentX;
|
||||||
previousY = currentY;
|
previousY = currentY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (addedToDictionary) cache.Sort((a, b) => a.y.CompareTo(b.y)); todo: not required until binary search
|
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,33 +54,46 @@ public class Equation : Graphable
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
double result = equ(x);
|
double result = equ(x);
|
||||||
cache.Add(new(x, result));
|
cache.Insert(index + 1, new(x, result));
|
||||||
// TODO: Rather than sorting the whole list when we add a single number,
|
|
||||||
// we could just insert it in its proper place.
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pretty sure this works. Certainly works pretty well with "hard-to-compute"
|
||||||
|
// equations.
|
||||||
private (double dist, double y, int index) NearestCachedPoint(double x)
|
private (double dist, double y, int index) NearestCachedPoint(double x)
|
||||||
{
|
{
|
||||||
// TODO: Replace with a binary search system.
|
if (cache.Count == 0) return (double.PositiveInfinity, double.NaN, -1);
|
||||||
double closestDist = double.PositiveInfinity;
|
else if (cache.Count == 1)
|
||||||
double closest = 0;
|
{
|
||||||
int closestIndex = -1;
|
Float2 single = cache[0];
|
||||||
|
return (Math.Abs(single.x - x), single.y, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int boundA = 0, boundB = cache.Count;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int boundC = (boundA + boundB) / 2;
|
||||||
|
Float2 pointC = cache[boundC];
|
||||||
|
|
||||||
for (int i = 0; i < cache.Count; i++)
|
if (pointC.x == x) return (0, pointC.y, boundC);
|
||||||
|
else if (pointC.x > x)
|
||||||
{
|
{
|
||||||
double dist = Math.Abs(x - cache[i].x);
|
boundA = boundC;
|
||||||
if (dist < closestDist)
|
}
|
||||||
|
else // pointC.x < x
|
||||||
{
|
{
|
||||||
closestDist = dist;
|
boundB = boundC;
|
||||||
closest = cache[i].y;
|
}
|
||||||
closestIndex = i;
|
|
||||||
|
} while (boundB - boundA > 1);
|
||||||
|
|
||||||
|
return (Math.Abs(cache[boundA].x - x), cache[boundA].y, boundA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (closestDist, closest, closestIndex);
|
public override Graphable DeepCopy() => new Equation(equ);
|
||||||
}
|
|
||||||
|
|
||||||
public override long GetCacheBytes() => cache.Count * 16;
|
public override long GetCacheBytes() => cache.Count * 16;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Graphing.Forms;
|
using Graphing.Forms;
|
||||||
using Graphing.Parts;
|
using Graphing.Parts;
|
||||||
using static System.Windows.Forms.LinkLabel;
|
|
||||||
|
|
||||||
namespace Graphing.Graphables;
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
@ -9,7 +8,7 @@ public class SlopeField : Graphable
|
|||||||
private static int slopeFieldNum;
|
private static int slopeFieldNum;
|
||||||
|
|
||||||
private readonly SlopeFieldsDelegate equ;
|
private readonly SlopeFieldsDelegate equ;
|
||||||
private readonly double detail;
|
private readonly int detail;
|
||||||
|
|
||||||
private readonly List<(Float2, GraphLine)> cache;
|
private readonly List<(Float2, GraphLine)> cache;
|
||||||
|
|
||||||
@ -71,6 +70,8 @@ public class SlopeField : Graphable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Graphable DeepCopy() => new SlopeField(detail, equ);
|
||||||
|
|
||||||
public override void EraseCache() => cache.Clear();
|
public override void EraseCache() => cache.Clear();
|
||||||
public override long GetCacheBytes() => cache.Count * 48;
|
public override long GetCacheBytes() => cache.Count * 48;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ public class TangentLine : Graphable
|
|||||||
{
|
{
|
||||||
public double Position { get; set; }
|
public double Position { get; set; }
|
||||||
|
|
||||||
|
private readonly Equation parent;
|
||||||
private readonly EquationDelegate parentEqu;
|
private readonly EquationDelegate parentEqu;
|
||||||
|
|
||||||
private readonly double length;
|
private readonly double length;
|
||||||
@ -18,13 +19,14 @@ public class TangentLine : Graphable
|
|||||||
parentEqu = parent.GetDelegate();
|
parentEqu = parent.GetDelegate();
|
||||||
Position = position;
|
Position = position;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
||||||
{
|
{
|
||||||
Float2 point = new(Position, parentEqu(Position));
|
Float2 point = new(Position, parentEqu(Position));
|
||||||
return [MakeSlopeLine(point, DerivativeAtPoint(Position)),
|
return [MakeSlopeLine(point, DerivativeAtPoint(Position)),
|
||||||
new GraphCircle(point, 8)];
|
new GraphUiCircle(point, 8)];
|
||||||
}
|
}
|
||||||
private GraphLine MakeSlopeLine(Float2 position, double slope)
|
private GraphLine MakeSlopeLine(Float2 position, double slope)
|
||||||
{
|
{
|
||||||
@ -42,6 +44,8 @@ public class TangentLine : Graphable
|
|||||||
return (parentEqu(x + step) - parentEqu(x)) / step;
|
return (parentEqu(x + step) - parentEqu(x)) / step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Graphable DeepCopy() => new TangentLine(length, Position, parent);
|
||||||
|
|
||||||
public override void EraseCache() { }
|
public override void EraseCache() { }
|
||||||
public override long GetCacheBytes() => 0;
|
public override long GetCacheBytes() => 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ public record struct GraphLine : IGraphPart
|
|||||||
|
|
||||||
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
||||||
{
|
{
|
||||||
if (!double.IsNormal(a.x) || !double.IsNormal(a.y) ||
|
if (!double.IsFinite(a.x) || !double.IsFinite(a.y) ||
|
||||||
!double.IsNormal(b.x) || !double.IsNormal(b.y)) return;
|
!double.IsFinite(b.x) || !double.IsFinite(b.y)) return;
|
||||||
|
|
||||||
Int2 start = form.GraphSpaceToScreenSpace(a),
|
Int2 start = form.GraphSpaceToScreenSpace(a),
|
||||||
end = form.GraphSpaceToScreenSpace(b);
|
end = form.GraphSpaceToScreenSpace(b);
|
||||||
|
|||||||
45
Base/Parts/GraphRectangle.cs
Normal file
45
Base/Parts/GraphRectangle.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
|
||||||
|
namespace Graphing.Parts;
|
||||||
|
|
||||||
|
public struct GraphRectangle : IGraphPart
|
||||||
|
{
|
||||||
|
public Float2 min, max;
|
||||||
|
|
||||||
|
public GraphRectangle()
|
||||||
|
{
|
||||||
|
min = new();
|
||||||
|
max = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GraphRectangle FromSize(Float2 center, Float2 size) => new()
|
||||||
|
{
|
||||||
|
min = new(center.x - size.x / 2,
|
||||||
|
center.y - size.y / 2),
|
||||||
|
max = new(center.x + size.x / 2,
|
||||||
|
center.y + size.y / 2)
|
||||||
|
};
|
||||||
|
public static GraphRectangle FromRange(Float2 min, Float2 max) => new()
|
||||||
|
{
|
||||||
|
min = min,
|
||||||
|
max = max
|
||||||
|
};
|
||||||
|
|
||||||
|
public void Render(in GraphForm form, in Graphics g, in Brush brush)
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(max.x) || !double.IsFinite(max.y) ||
|
||||||
|
!double.IsFinite(min.x) || !double.IsFinite(min.y)) return;
|
||||||
|
|
||||||
|
if (min.x > max.x) (min.x, max.x) = (max.x, min.x);
|
||||||
|
if (min.y > max.y) (min.y, max.y) = (max.y, min.y);
|
||||||
|
|
||||||
|
Int2 start = form.GraphSpaceToScreenSpace(min),
|
||||||
|
end = form.GraphSpaceToScreenSpace(max);
|
||||||
|
|
||||||
|
Int2 size = new(end.x - start.x + 1,
|
||||||
|
start.y - end.y);
|
||||||
|
|
||||||
|
if (size.x == 0 || size.y == 0) return;
|
||||||
|
g.FillRectangle(brush, new Rectangle(start.x, end.y, size.x, size.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
namespace Graphing.Parts;
|
namespace Graphing.Parts;
|
||||||
|
|
||||||
public record struct GraphCircle : IGraphPart
|
public record struct GraphUiCircle : IGraphPart
|
||||||
{
|
{
|
||||||
public Float2 center;
|
public Float2 center;
|
||||||
public int radius;
|
public int radius;
|
||||||
|
|
||||||
public GraphCircle()
|
public GraphUiCircle()
|
||||||
{
|
{
|
||||||
center = new();
|
center = new();
|
||||||
radius = 1;
|
radius = 1;
|
||||||
}
|
}
|
||||||
public GraphCircle(Float2 center, int radius)
|
public GraphUiCircle(Float2 center, int radius)
|
||||||
{
|
{
|
||||||
this.center = center;
|
this.center = center;
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
@ -20,8 +20,8 @@ public record struct GraphCircle : IGraphPart
|
|||||||
|
|
||||||
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
||||||
{
|
{
|
||||||
if (!double.IsNormal(center.x) || !double.IsNormal(center.y) ||
|
if (!double.IsFinite(center.x) || !double.IsFinite(center.y) ||
|
||||||
!double.IsNormal(radius)) return;
|
!double.IsFinite(radius) || radius == 0) return;
|
||||||
|
|
||||||
Int2 centerPix = form.GraphSpaceToScreenSpace(center);
|
Int2 centerPix = form.GraphSpaceToScreenSpace(center);
|
||||||
g.FillEllipse(brush, new Rectangle(new Point(centerPix.x - radius,
|
g.FillEllipse(brush, new Rectangle(new Point(centerPix.x - radius,
|
||||||
@ -14,17 +14,18 @@ internal static class Program
|
|||||||
|
|
||||||
GraphForm graph = new("One Of The Graphing Calculators Of All Time");
|
GraphForm graph = new("One Of The Graphing Calculators Of All Time");
|
||||||
|
|
||||||
Equation equ = new(x => x * x);
|
Equation equ = new(x =>
|
||||||
TangentLine tangent = new(5, 2, equ);
|
{
|
||||||
|
// Demonstrate the caching abilities of the software.
|
||||||
|
// This extra waiting is done every time the form requires a
|
||||||
|
// calculation done. At the start, it'll be laggy, but as you
|
||||||
|
// move around and zoom in, more pieces are cached, and when
|
||||||
|
// you reset, the viewport will be a lot less laggy.
|
||||||
|
for (int i = 0; i < 1_000_000; i++) ;
|
||||||
|
return x * x;
|
||||||
|
});
|
||||||
graph.Graph(equ);
|
graph.Graph(equ);
|
||||||
graph.Graph(tangent);
|
|
||||||
|
|
||||||
Application.Run(graph);
|
Application.Run(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double PopulationGraph(double max, double k, double A, double t)
|
|
||||||
{
|
|
||||||
return max / (1 + A * Math.Exp(-k * t));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user