Ready for 1.1. Made a graph cache viewer.

This commit is contained in:
That_One_Nerd 2024-03-13 09:42:03 -04:00
parent fc829d6a6b
commit 6d8787cac7
14 changed files with 602 additions and 71 deletions

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Forms\Controls\PieChart.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Update="Forms\GraphColorPickerForm.cs">
<SubType>Form</SubType>
</Compile>
@ -10,5 +13,8 @@
<Compile Update="Forms\SetZoomForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Forms\ViewCacheForm.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>

44
Base/Forms/Controls/PieChart.Designer.cs generated Normal file
View File

@ -0,0 +1,44 @@
namespace Graphing.Forms.Controls
{
partial class PieChart
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
SuspendLayout();
//
// PieChart
//
AutoScaleDimensions = new SizeF(13F, 32F);
AutoScaleMode = AutoScaleMode.Font;
Name = "PieChart";
Size = new Size(500, 500);
ResumeLayout(false);
}
#endregion
}
}

View File

@ -0,0 +1,59 @@
using System.Drawing.Drawing2D;
namespace Graphing.Forms.Controls;
public partial class PieChart : UserControl
{
public List<(Color, double)> Values { get; set; }
public PieChart()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
Values = [];
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
int size = Math.Min(Width, Height);
Rectangle rect = new(5, 5, size - 10, size - 10);
double sum = 0;
foreach ((Color, double v) item in Values)
sum += item.v;
// Draw them.
double current = 0;
foreach ((Color color, double value) item in Values)
{
double start = 360 * current / sum,
end = 360 * (current + item.value) / sum;
Brush filler = new SolidBrush(item.color);
g.FillPie(filler, rect, (float)start, (float)(end - start));
current += item.value;
}
// Draw the outline.
Pen outlinePartsPen = new(Color.FromArgb(unchecked((int)0xFF_202020)), 3);
current = 0;
foreach ((Color, double value) item in Values)
{
double start = 360 * current / sum,
end = 360 * (current + item.value) / sum;
g.DrawPie(outlinePartsPen, rect, (float)start, (float)(end - start));
current += item.value;
}
// Outline
Pen outlinePen = new(Color.FromArgb(unchecked((int)0xFF_202020)), 5);
g.DrawEllipse(outlinePen, rect);
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -7,6 +7,10 @@ namespace Graphing.Forms;
public partial class GraphForm : Form
{
public static readonly Color MainAxisColor = Color.Black;
public static readonly Color SemiAxisColor = Color.FromArgb(unchecked((int)0xFF_999999));
public static readonly Color QuarterAxisColor = Color.FromArgb(unchecked((int)0xFF_E0E0E0));
public Float2 ScreenCenter { get; private set; }
public Float2 Dpi { get; private set; }
@ -97,7 +101,7 @@ public partial class GraphForm : Form
double axisScale = Math.Pow(2, Math.Round(Math.Log2(ZoomLevel)));
// Draw horizontal/vertical quarter-axis.
Brush quarterBrush = new SolidBrush(Color.FromArgb(unchecked((int)0xFF_E0E0E0)));
Brush quarterBrush = new SolidBrush(QuarterAxisColor);
Pen quarterPen = new(quarterBrush, 2);
for (double x = Math.Ceiling(MinVisibleGraph.x * 4 / axisScale) * axisScale / 4; x <= Math.Floor(MaxVisibleGraph.x * 4 / axisScale) * axisScale / 4; x += axisScale / 4)
@ -114,7 +118,7 @@ public partial class GraphForm : Form
}
// Draw horizontal/vertical semi-axis.
Brush semiBrush = new SolidBrush(Color.FromArgb(unchecked((int)0xFF_999999)));
Brush semiBrush = new SolidBrush(SemiAxisColor);
Pen semiPen = new(semiBrush, 2);
for (double x = Math.Ceiling(MinVisibleGraph.x / axisScale) * axisScale; x <= Math.Floor(MaxVisibleGraph.x / axisScale) * axisScale; x += axisScale)
@ -130,7 +134,7 @@ public partial class GraphForm : Form
g.DrawLine(semiPen, startPos, endPos);
}
Brush mainLineBrush = new SolidBrush(Color.Black);
Brush mainLineBrush = new SolidBrush(MainAxisColor);
Pen mainLinePen = new(mainLineBrush, 3);
// Draw the main axis (on top of the semi axis).
@ -251,14 +255,14 @@ public partial class GraphForm : Form
colorItem.Click += (o, e) => GraphColorPickerButton_Click(able);
MenuColors.DropDownItems.Add(colorItem);
if (able is Equation)
if (able is Equation equ)
{
ToolStripMenuItem derivativeItem = new()
{
ForeColor = able.Color,
Text = able.Name
};
derivativeItem.Click += (o, e) => EquationComputeDerivative_Click((able as Equation)!);
derivativeItem.Click += (o, e) => EquationComputeDerivative_Click(equ);
MenuEquationsDerivative.DropDownItems.Add(derivativeItem);
ToolStripMenuItem integralItem = new()
@ -266,7 +270,7 @@ public partial class GraphForm : Form
ForeColor = able.Color,
Text = able.Name
};
integralItem.Click += (o, e) => EquationComputeIntegral_Click((able as Equation)!);
integralItem.Click += (o, e) => EquationComputeIntegral_Click(equ);
MenuEquationsIntegral.DropDownItems.Add(integralItem);
}
}
@ -361,23 +365,13 @@ public partial class GraphForm : Form
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)
ViewCacheForm cacheForm = new(this)
{
long size = able.GetCacheBytes();
message.AppendLine($"{able.Name}: {size.FormatAsBytes()}");
total += size;
}
StartPosition = FormStartPosition.Manual
};
message.AppendLine($"\nTotal: {total.FormatAsBytes()}\n\nErase cache?");
DialogResult result = MessageBox.Show(message.ToString(), "Graph Caches", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
foreach (Graphable able in ables) able.EraseCache();
}
cacheForm.Location = new Point(Location.X + ClientRectangle.Width + 10,
Location.Y + (ClientRectangle.Height - cacheForm.ClientRectangle.Height) / 2);
cacheForm.Show();
}
}

97
Base/Forms/ViewCacheForm.Designer.cs generated Normal file
View File

@ -0,0 +1,97 @@
namespace Graphing.Forms
{
partial class ViewCacheForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ViewCacheForm));
CachePie = new Controls.PieChart();
TotalCacheText = new Label();
EraseAllCacheButton = new Button();
SpecificCachePanel = new Panel();
SuspendLayout();
//
// CachePie
//
CachePie.Location = new Point(50, 50);
CachePie.Name = "CachePie";
CachePie.Size = new Size(450, 450);
CachePie.TabIndex = 0;
//
// TotalCacheText
//
TotalCacheText.Font = new Font("Segoe UI Semibold", 10.125F, FontStyle.Bold, GraphicsUnit.Point, 0);
TotalCacheText.Location = new Point(62, 540);
TotalCacheText.Name = "TotalCacheText";
TotalCacheText.Size = new Size(425, 45);
TotalCacheText.TabIndex = 1;
TotalCacheText.Text = "Total Cache: Something";
TotalCacheText.TextAlign = ContentAlignment.TopCenter;
//
// EraseAllCacheButton
//
EraseAllCacheButton.Location = new Point(200, 580);
EraseAllCacheButton.Name = "EraseAllCacheButton";
EraseAllCacheButton.Size = new Size(150, 46);
EraseAllCacheButton.TabIndex = 2;
EraseAllCacheButton.Text = "Erase All";
EraseAllCacheButton.UseVisualStyleBackColor = true;
EraseAllCacheButton.Click += EraseAllCacheButton_Click;
//
// SpecificCachePanel
//
SpecificCachePanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
SpecificCachePanel.AutoScroll = true;
SpecificCachePanel.Location = new Point(520, 12);
SpecificCachePanel.Name = "SpecificCachePanel";
SpecificCachePanel.Size = new Size(542, 657);
SpecificCachePanel.TabIndex = 3;
//
// ViewCacheForm
//
AutoScaleDimensions = new SizeF(13F, 32F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1074, 679);
Controls.Add(SpecificCachePanel);
Controls.Add(EraseAllCacheButton);
Controls.Add(TotalCacheText);
Controls.Add(CachePie);
FormBorderStyle = FormBorderStyle.SizableToolWindow;
MinimumSize = new Size(885, 750);
Name = "ViewCacheForm";
Text = "Graph Caches";
ResumeLayout(false);
}
#endregion
private Controls.PieChart CachePie;
private Label TotalCacheText;
private Button EraseAllCacheButton;
private Panel SpecificCachePanel;
}
}

View File

@ -0,0 +1,89 @@
using Graphing.Extensions;
namespace Graphing.Forms;
public partial class ViewCacheForm : Form
{
private readonly GraphForm refForm;
private readonly List<Label> labelCache;
private readonly List<Button> buttonCache;
public ViewCacheForm(GraphForm thisForm)
{
InitializeComponent();
refForm = thisForm;
refForm.Paint += (o, e) => UpdatePieChart();
labelCache = [];
buttonCache = [];
UpdatePieChart();
}
private void UpdatePieChart()
{
CachePie.Values.Clear();
long totalBytes = 0;
int index = 0;
foreach (Graphable able in refForm.Graphables)
{
long thisBytes = able.GetCacheBytes();
CachePie.Values.Add((able.Color, thisBytes));
totalBytes += thisBytes;
if (index < labelCache.Count)
{
Label reuseLabel = labelCache[index];
reuseLabel.ForeColor = able.Color;
reuseLabel.Text = $"{able.Name}: {thisBytes.FormatAsBytes()}";
}
else
{
Label newText = new()
{
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
AutoEllipsis = true,
ForeColor = able.Color,
Location = new Point(0, labelCache.Count * 46),
Parent = SpecificCachePanel,
Size = new Size(SpecificCachePanel.Width - 98, 46),
Text = $"{able.Name}: {thisBytes.FormatAsBytes()}",
TextAlign = ContentAlignment.MiddleLeft,
};
labelCache.Add(newText);
}
if (index >= buttonCache.Count)
{
Button newButton = new()
{
Anchor = AnchorStyles.Top | AnchorStyles.Right,
Location = new Point(SpecificCachePanel.Width - 92, buttonCache.Count * 46),
Parent = SpecificCachePanel,
Size = new Size(92, 46),
Text = "Clear"
};
newButton.Click += (o, e) => EraseSpecificGraphable_Click(able);
buttonCache.Add(newButton);
}
index++;
}
TotalCacheText.Text = $"Total Cache: {totalBytes.FormatAsBytes()}";
Invalidate(true);
}
private void EraseAllCacheButton_Click(object? sender, EventArgs e)
{
foreach (Graphable able in refForm.Graphables) able.EraseCache();
refForm.Invalidate(false);
}
private void EraseSpecificGraphable_Click(Graphable able)
{
able.EraseCache();
refForm.Invalidate(false);
}
}

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CachePie.Values" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAEAQAAAM0CU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tT
eXN0ZW0uVmFsdWVUdXBsZWAyW1tTeXN0ZW0uRHJhd2luZy5Db2xvciwgU3lzdGVtLkRyYXdpbmcsIFZl
cnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iMDNmNWY3ZjExZDUw
YTNhXSxbU3lzdGVtLkRvdWJsZSwgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0
cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0sIG1zY29ybGliLCBWZXJzaW9uPTQu
MC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dAwAA
AAZfaXRlbXMFX3NpemUIX3ZlcnNpb24DAADdAVN5c3RlbS5WYWx1ZVR1cGxlYDJbW1N5c3RlbS5EcmF3
aW5nLkNvbG9yLCBTeXN0ZW0uRHJhd2luZywgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWws
IFB1YmxpY0tleVRva2VuPWIwM2Y1ZjdmMTFkNTBhM2FdLFtTeXN0ZW0uRG91YmxlLCBtc2NvcmxpYiwg
VmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkz
NGUwODldXVtdCAgJAgAAAAAAAAAAAAAABwIAAAAAAQAAAAAAAAAD2wFTeXN0ZW0uVmFsdWVUdXBsZWAy
W1tTeXN0ZW0uRHJhd2luZy5Db2xvciwgU3lzdGVtLkRyYXdpbmcsIFZlcnNpb249NC4wLjAuMCwgQ3Vs
dHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iMDNmNWY3ZjExZDUwYTNhXSxbU3lzdGVtLkRvdWJs
ZSwgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tl
bj1iNzdhNWM1NjE5MzRlMDg5XV0L
</value>
</data>
</root>

View File

@ -7,8 +7,8 @@ public class ColumnTable : Graphable
{
private static int tableNum;
private readonly Dictionary<double, double> tableXY;
private readonly double width;
protected readonly Dictionary<double, double> tableXY;
protected readonly double width;
public ColumnTable(double width, Dictionary<double, double> tableXY)
{

View File

@ -7,8 +7,8 @@ public class Equation : Graphable
{
private static int equationNum;
private readonly EquationDelegate equ;
private readonly List<Float2> cache;
protected readonly EquationDelegate equ;
protected readonly List<Float2> cache;
public Equation(EquationDelegate equ)
{
@ -47,7 +47,7 @@ public class Equation : Graphable
public EquationDelegate GetDelegate() => equ;
public override void EraseCache() => cache.Clear();
private double GetFromCache(double x, double epsilon)
protected double GetFromCache(double x, double epsilon)
{
(double dist, double nearest, int index) = NearestCachedPoint(x);
if (dist < epsilon) return nearest;
@ -61,7 +61,7 @@ public class Equation : Graphable
// Pretty sure this works. Certainly works pretty well with "hard-to-compute"
// equations.
private (double dist, double y, int index) NearestCachedPoint(double x)
protected (double dist, double y, int index) NearestCachedPoint(double x)
{
if (cache.Count == 0) return (double.PositiveInfinity, double.NaN, -1);
else if (cache.Count == 1)

View File

@ -7,10 +7,10 @@ public class SlopeField : Graphable
{
private static int slopeFieldNum;
private readonly SlopeFieldsDelegate equ;
private readonly int detail;
protected readonly SlopeFieldsDelegate equ;
protected readonly int detail;
private readonly List<(Float2, GraphLine)> cache;
protected readonly List<(Float2, GraphLine)> cache;
public SlopeField(int detail, SlopeFieldsDelegate equ)
{
@ -24,12 +24,12 @@ public class SlopeField : Graphable
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
{
double epsilon = 1 / (detail * 2);
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 / detail)
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 / 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));
}
@ -38,7 +38,7 @@ public class SlopeField : Graphable
return lines;
}
private GraphLine MakeSlopeLine(Float2 position, double slope)
protected GraphLine MakeSlopeLine(Float2 position, double slope)
{
double size = detail;
@ -50,7 +50,7 @@ public class SlopeField : Graphable
return new(new(position.x + dirX, position.y + dirY), new(position.x - dirX, position.y - dirY));
}
private GraphLine GetFromCache(double epsilon, double x, double y)
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.

View File

@ -7,10 +7,10 @@ public class TangentLine : Graphable
{
public double Position { get; set; }
private readonly Equation parent;
private readonly EquationDelegate parentEqu;
protected readonly Equation parent;
protected readonly EquationDelegate parentEqu;
private readonly double length;
protected readonly double length;
public TangentLine(double length, double position, Equation parent)
{
@ -28,7 +28,7 @@ public class TangentLine : Graphable
return [MakeSlopeLine(point, DerivativeAtPoint(Position)),
new GraphUiCircle(point, 8)];
}
private GraphLine MakeSlopeLine(Float2 position, double slope)
protected GraphLine MakeSlopeLine(Float2 position, double slope)
{
double dirX = length, dirY = slope * length;
double magnitude = Math.Sqrt(dirX * dirX + dirY * dirY);
@ -38,7 +38,7 @@ public class TangentLine : Graphable
return new(new(position.x + dirX, position.y + dirY), new(position.x - dirX, position.y - dirY));
}
private double DerivativeAtPoint(double x)
protected double DerivativeAtPoint(double x)
{
const double step = 1e-3;
return (parentEqu(x + step) - parentEqu(x)) / step;

View File

@ -1,27 +0,0 @@
namespace Graphing;
public record struct Range2d
{
public double minX;
public double minY;
public double maxX;
public double maxY;
public Range2d()
{
minX = 0;
minY = 0;
maxX = 0;
maxY = 0;
}
public Range2d(double minX, double minY, double maxX, double maxY)
{
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
public readonly bool Contains(Float2 p) =>
p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY;
}

View File

@ -14,17 +14,27 @@ internal static class Program
GraphForm graph = new("One Of The Graphing Calculators Of All Time");
Equation equ = new(x =>
Equation equ1 = new(x =>
{
// 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.
// Remove this loop to make the equation fast again. I didn't
// slow the engine down much more with this improvement, so any
// speed decrease you might notice is likely this function.
for (int i = 0; i < 1_000_000; i++) ;
return x * x;
return -x * x + 2;
});
graph.Graph(equ);
Equation equ2 = new(x => x);
Equation equ3 = new(x => -Math.Sqrt(x));
SlopeField sf = new(2, (x, y) => (x * x - y * y) / x);
graph.Graph(equ1, equ2, equ3, sf);
// You can also now view and reset caches in the UI by going to
// Misc > View Caches.
Application.Run(graph);
}