Version 1.1 is out. #1

Merged
That-One-Nerd merged 6 commits from canary into main 2024-03-13 10:36:23 -04:00
6 changed files with 147 additions and 8 deletions
Showing only changes of commit 21c498f445 - Show all commits

View File

@ -0,0 +1,32 @@
namespace Graphing.Extensions;
public static class FormattingExtensions
{
private static readonly string[] sizeUnits =
[
" bytes",
" KB",
" MB",
" GB",
" TB",
" PB",
];
public static string FormatAsBytes(this long bytes)
{
double val = bytes;
int unitIndex = 0;
while (val > 1024)
{
unitIndex++;
val /= 1024;
}
string result;
if (unitIndex == 0) result = val.ToString("0");
else result = val.ToString("0.00");
return result + sizeUnits[unitIndex];
}
}

View File

@ -39,6 +39,8 @@
MenuEquations = new ToolStripMenuItem(); MenuEquations = new ToolStripMenuItem();
MenuEquationsDerivative = new ToolStripMenuItem(); MenuEquationsDerivative = new ToolStripMenuItem();
MenuEquationsIntegral = new ToolStripMenuItem(); MenuEquationsIntegral = new ToolStripMenuItem();
MenuMisc = new ToolStripMenuItem();
MenuMiscCaches = new ToolStripMenuItem();
GraphMenu.SuspendLayout(); GraphMenu.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
@ -58,7 +60,7 @@
// GraphMenu // GraphMenu
// //
GraphMenu.ImageScalingSize = new Size(32, 32); GraphMenu.ImageScalingSize = new Size(32, 32);
GraphMenu.Items.AddRange(new ToolStripItem[] { MenuViewport, MenuColors, MenuEquations }); GraphMenu.Items.AddRange(new ToolStripItem[] { MenuViewport, MenuColors, MenuEquations, MenuMisc });
GraphMenu.Location = new Point(0, 0); GraphMenu.Location = new Point(0, 0);
GraphMenu.Name = "GraphMenu"; GraphMenu.Name = "GraphMenu";
GraphMenu.Size = new Size(1449, 42); GraphMenu.Size = new Size(1449, 42);
@ -125,6 +127,20 @@
MenuEquationsIntegral.Size = new Size(360, 44); MenuEquationsIntegral.Size = new Size(360, 44);
MenuEquationsIntegral.Text = "Compute Integral"; MenuEquationsIntegral.Text = "Compute Integral";
// //
// MenuMisc
//
MenuMisc.DropDownItems.AddRange(new ToolStripItem[] { MenuMiscCaches });
MenuMisc.Name = "MenuMisc";
MenuMisc.Size = new Size(83, 38);
MenuMisc.Text = "Misc";
//
// MenuMiscCaches
//
MenuMiscCaches.Name = "MenuMiscCaches";
MenuMiscCaches.Size = new Size(359, 44);
MenuMiscCaches.Text = "View Caches";
MenuMiscCaches.Click += MenuMiscCaches_Click;
//
// GraphForm // GraphForm
// //
AutoScaleDimensions = new SizeF(13F, 32F); AutoScaleDimensions = new SizeF(13F, 32F);
@ -154,5 +170,7 @@
private ToolStripMenuItem MenuEquations; private ToolStripMenuItem MenuEquations;
private ToolStripMenuItem MenuEquationsDerivative; private ToolStripMenuItem MenuEquationsDerivative;
private ToolStripMenuItem MenuEquationsIntegral; private ToolStripMenuItem MenuEquationsIntegral;
private ToolStripMenuItem MenuMisc;
private ToolStripMenuItem MenuMiscCaches;
} }
} }

View File

@ -1,4 +1,6 @@
using Graphing.Graphables; using Graphing.Extensions;
using Graphing.Graphables;
using System.Text;
namespace Graphing.Forms; namespace Graphing.Forms;
@ -346,7 +348,7 @@ public partial class GraphForm : Form
static double Integrate(EquationDelegate e, double lower, double upper) static double Integrate(EquationDelegate e, double lower, double upper)
{ {
// TODO: a better rendering method could make this much faster. // TODO: a better rendering method could make this much faster.
const double step = 1e-1; const double step = 1e-2;
double factor = 1; double factor = 1;
if (upper < lower) if (upper < lower)
@ -364,4 +366,33 @@ public partial class GraphForm : Form
return sum * factor; return sum * factor;
} }
} }
private void MenuMiscCaches_Click(object? sender, EventArgs e)
{
// TODO: Replace with a form with a pie chart of the use by equation
// and the ability to reset them.
StringBuilder message = new();
long total = 0;
foreach (Graphable able in ables)
{
if (able is Equation equ)
{
long size = equ.GetCacheBytes();
message.AppendLine($"{able.Name}: {size.FormatAsBytes()}");
total += size;
}
}
message.AppendLine($"\nTotal: {total.FormatAsBytes()}\n\nClick \"No\" to erase caches.");
DialogResult result = MessageBox.Show(message.ToString(), "Graph Caches", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (result == DialogResult.No)
{
foreach (Graphable able in ables)
{
if (able is Equation equ) equ.EraseCache();
}
}
}
} }

View File

@ -8,23 +8,33 @@ public class Equation : Graphable
private readonly EquationDelegate equ; private readonly EquationDelegate equ;
private readonly List<Float2> cache;
public Equation(EquationDelegate equ) public Equation(EquationDelegate equ)
{ {
equationNum++; equationNum++;
Name = $"Equation {equationNum}"; Name = $"Equation {equationNum}";
this.equ = equ; this.equ = equ;
cache = [];
} }
public override IEnumerable<Line2d> GetItemsToRender(in GraphForm graph) public override IEnumerable<Line2d> GetItemsToRender(in GraphForm graph)
{ {
const int step = 10;
double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x
- graph.ScreenSpaceToGraphSpace(new Int2(step / 2, 0)).x) / 5;
List<Line2d> lines = []; List<Line2d> lines = [];
bool addedToDictionary = false;
double previousX = graph.MinVisibleGraph.x; double previousX = graph.MinVisibleGraph.x;
double previousY = equ(previousX); double previousY = GetFromCache(previousX, epsilon, ref addedToDictionary);
for (int i = 1; i < graph.ClientRectangle.Width; i += 10)
for (int i = 1; i < graph.ClientRectangle.Width; i += step)
{ {
double currentX = graph.ScreenSpaceToGraphSpace(new Int2(i, 0)).x; double currentX = graph.ScreenSpaceToGraphSpace(new Int2(i, 0)).x;
double currentY = equ(currentX); double currentY = GetFromCache(currentX, epsilon, ref addedToDictionary);
if (Math.Abs(currentY - previousY) <= 10) if (Math.Abs(currentY - previousY) <= 10)
{ {
lines.Add(new Line2d(new Float2(previousX, previousY), new Float2(currentX, currentY))); lines.Add(new Line2d(new Float2(previousX, previousY), new Float2(currentX, currentY)));
@ -32,10 +42,50 @@ public class Equation : Graphable
previousX = currentX; previousX = currentX;
previousY = currentY; previousY = currentY;
} }
if (addedToDictionary) cache.Sort((a, b) => a.y.CompareTo(b.y));
return lines; return lines;
} }
public EquationDelegate GetDelegate() => equ; public EquationDelegate GetDelegate() => equ;
public void EraseCache() => cache.Clear();
private double GetFromCache(double x, double epsilon, ref bool addedToDictionary)
{
(double dist, double nearest) = NearestCachedPoint(x);
if (dist < epsilon) return nearest;
else
{
addedToDictionary = true;
double result = equ(x);
cache.Add(new(x, result));
// TODO: Rather than sorting the whole list when we add a single number,
// we could just insert it.
return result;
}
}
private (double dist, double y) NearestCachedPoint(double x)
{
// TODO: Replace with a binary search system.
double closestDist = double.PositiveInfinity;
double closest = 0;
foreach (Float2 p in cache)
{
double dist = Math.Abs(x - p.x);
if (dist < closestDist)
{
closestDist = dist;
closest = p.y;
}
}
return (closestDist, closest);
}
public long GetCacheBytes() => cache.Count * 16;
} }
public delegate double EquationDelegate(double x); public delegate double EquationDelegate(double x);

View File

@ -13,7 +13,8 @@ internal static class Program
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
GraphForm graph = new("One Of The Graphing Calculators Of All Time"); GraphForm graph = new("One Of The Graphing Calculators Of All Time");
graph.Graph(new Equation(Math.Cos)); graph.Graph(new Equation(x => Math.Pow(2, x)));
graph.Graph(new Equation(Math.Log2));
Application.Run(graph); Application.Run(graph);
} }

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
@ -9,6 +8,14 @@
<RootNamespace>Graphing.Testing</RootNamespace> <RootNamespace>Graphing.Testing</RootNamespace>
<AssemblyName>ThatOneNerd.Graphing.Testing</AssemblyName> <AssemblyName>ThatOneNerd.Graphing.Testing</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputType>WinExe</OutputType>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Base\Base.csproj" /> <ProjectReference Include="..\Base\Base.csproj" />