commit
a93b5855d0
@ -12,9 +12,9 @@
|
|||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<PackageId>ThatOneNerd.Graphing</PackageId>
|
<PackageId>ThatOneNerd.Graphing</PackageId>
|
||||||
<Title>ThatOneNerd.Graphing</Title>
|
<Title>ThatOneNerd.Graphing</Title>
|
||||||
<Version>1.0.0</Version>
|
<Version>1.1.0</Version>
|
||||||
<Authors>That_One_Nerd</Authors>
|
<Authors>That_One_Nerd</Authors>
|
||||||
<Description>A fairly adept graphing calculator made in Windows Forms. </Description>
|
<Description>A fairly adept graphing calculator made in Windows Forms.</Description>
|
||||||
<Copyright>MIT</Copyright>
|
<Copyright>MIT</Copyright>
|
||||||
<RepositoryUrl>https://github.com/That-One-Nerd/Graphing</RepositoryUrl>
|
<RepositoryUrl>https://github.com/That-One-Nerd/Graphing</RepositoryUrl>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
@ -22,6 +22,8 @@
|
|||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<IncludeSymbols>True</IncludeSymbols>
|
<IncludeSymbols>True</IncludeSymbols>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
|
<PackageReleaseNotes>View the GitHub release for the changelog:
|
||||||
|
https://github.com/That-One-Nerd/Graphing/releases/tag/1.1.0</PackageReleaseNotes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<_LastSelectedProfileId>C:\Users\kyley\Desktop\Coding\C#\Graphing\Base\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Update="Forms\Controls\PieChart.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Update="Forms\GraphColorPickerForm.cs">
|
<Compile Update="Forms\GraphColorPickerForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -10,5 +16,8 @@
|
|||||||
<Compile Update="Forms\SetZoomForm.cs">
|
<Compile Update="Forms\SetZoomForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="Forms\ViewCacheForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
32
Base/Extensions/FormattingExtensions.cs
Normal file
32
Base/Extensions/FormattingExtensions.cs
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Base/Forms/Controls/PieChart.Designer.cs
generated
Normal file
44
Base/Forms/Controls/PieChart.Designer.cs
generated
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
59
Base/Forms/Controls/PieChart.cs
Normal file
59
Base/Forms/Controls/PieChart.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
120
Base/Forms/Controls/PieChart.resx
Normal file
120
Base/Forms/Controls/PieChart.resx
Normal 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>
|
||||||
20
Base/Forms/GraphForm.Designer.cs
generated
20
Base/Forms/GraphForm.Designer.cs
generated
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,16 @@
|
|||||||
using Graphing.Graphables;
|
using Graphing.Extensions;
|
||||||
|
using Graphing.Graphables;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Graphing.Forms;
|
namespace Graphing.Forms;
|
||||||
|
|
||||||
public partial class GraphForm : Form
|
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 ScreenCenter { get; private set; }
|
||||||
public Float2 Dpi { get; private set; }
|
public Float2 Dpi { get; private set; }
|
||||||
|
|
||||||
@ -94,7 +101,7 @@ public partial class GraphForm : Form
|
|||||||
double axisScale = Math.Pow(2, Math.Round(Math.Log2(ZoomLevel)));
|
double axisScale = Math.Pow(2, Math.Round(Math.Log2(ZoomLevel)));
|
||||||
|
|
||||||
// Draw horizontal/vertical quarter-axis.
|
// 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);
|
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)
|
for (double x = Math.Ceiling(MinVisibleGraph.x * 4 / axisScale) * axisScale / 4; x <= Math.Floor(MaxVisibleGraph.x * 4 / axisScale) * axisScale / 4; x += axisScale / 4)
|
||||||
@ -111,7 +118,7 @@ public partial class GraphForm : Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw horizontal/vertical semi-axis.
|
// 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);
|
Pen semiPen = new(semiBrush, 2);
|
||||||
|
|
||||||
for (double x = Math.Ceiling(MinVisibleGraph.x / axisScale) * axisScale; x <= Math.Floor(MaxVisibleGraph.x / axisScale) * axisScale; x += axisScale)
|
for (double x = Math.Ceiling(MinVisibleGraph.x / axisScale) * axisScale; x <= Math.Floor(MaxVisibleGraph.x / axisScale) * axisScale; x += axisScale)
|
||||||
@ -127,7 +134,7 @@ public partial class GraphForm : Form
|
|||||||
g.DrawLine(semiPen, startPos, endPos);
|
g.DrawLine(semiPen, startPos, endPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Brush mainLineBrush = new SolidBrush(Color.Black);
|
Brush mainLineBrush = new SolidBrush(MainAxisColor);
|
||||||
Pen mainLinePen = new(mainLineBrush, 3);
|
Pen mainLinePen = new(mainLineBrush, 3);
|
||||||
|
|
||||||
// Draw the main axis (on top of the semi axis).
|
// Draw the main axis (on top of the semi axis).
|
||||||
@ -143,6 +150,7 @@ public partial class GraphForm : Form
|
|||||||
protected override void OnPaint(PaintEventArgs e)
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
{
|
{
|
||||||
Graphics g = e.Graphics;
|
Graphics g = e.Graphics;
|
||||||
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||||
|
|
||||||
Brush background = new SolidBrush(Color.White);
|
Brush background = new SolidBrush(Color.White);
|
||||||
g.FillRectangle(background, e.ClipRectangle);
|
g.FillRectangle(background, e.ClipRectangle);
|
||||||
@ -152,19 +160,9 @@ public partial class GraphForm : Form
|
|||||||
// Draw the actual graphs.
|
// Draw the actual graphs.
|
||||||
for (int i = 0; i < ables.Count; i++)
|
for (int i = 0; i < ables.Count; i++)
|
||||||
{
|
{
|
||||||
IEnumerable<Line2d> lines = ables[i].GetItemsToRender(this);
|
IEnumerable<IGraphPart> lines = ables[i].GetItemsToRender(this);
|
||||||
Brush graphBrush = new SolidBrush(ables[i].Color);
|
Brush graphBrush = new SolidBrush(ables[i].Color);
|
||||||
Pen penBrush = new(graphBrush, 3);
|
foreach (IGraphPart gp in lines) gp.Render(this, g, graphBrush);
|
||||||
|
|
||||||
foreach (Line2d l in lines)
|
|
||||||
{
|
|
||||||
if (!double.IsNormal(l.a.x) || !double.IsNormal(l.a.y) ||
|
|
||||||
!double.IsNormal(l.b.x) || !double.IsNormal(l.b.y)) continue;
|
|
||||||
|
|
||||||
Int2 start = GraphSpaceToScreenSpace(l.a),
|
|
||||||
end = GraphSpaceToScreenSpace(l.b);
|
|
||||||
g.DrawLine(penBrush, start, end);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnPaint(e);
|
base.OnPaint(e);
|
||||||
@ -175,9 +173,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);
|
||||||
}
|
}
|
||||||
@ -257,14 +255,14 @@ public partial class GraphForm : Form
|
|||||||
colorItem.Click += (o, e) => GraphColorPickerButton_Click(able);
|
colorItem.Click += (o, e) => GraphColorPickerButton_Click(able);
|
||||||
MenuColors.DropDownItems.Add(colorItem);
|
MenuColors.DropDownItems.Add(colorItem);
|
||||||
|
|
||||||
if (able is Equation)
|
if (able is Equation equ)
|
||||||
{
|
{
|
||||||
ToolStripMenuItem derivativeItem = new()
|
ToolStripMenuItem derivativeItem = new()
|
||||||
{
|
{
|
||||||
ForeColor = able.Color,
|
ForeColor = able.Color,
|
||||||
Text = able.Name
|
Text = able.Name
|
||||||
};
|
};
|
||||||
derivativeItem.Click += (o, e) => EquationComputeDerivative_Click((able as Equation)!);
|
derivativeItem.Click += (o, e) => EquationComputeDerivative_Click(equ);
|
||||||
MenuEquationsDerivative.DropDownItems.Add(derivativeItem);
|
MenuEquationsDerivative.DropDownItems.Add(derivativeItem);
|
||||||
|
|
||||||
ToolStripMenuItem integralItem = new()
|
ToolStripMenuItem integralItem = new()
|
||||||
@ -272,7 +270,7 @@ public partial class GraphForm : Form
|
|||||||
ForeColor = able.Color,
|
ForeColor = able.Color,
|
||||||
Text = able.Name
|
Text = able.Name
|
||||||
};
|
};
|
||||||
integralItem.Click += (o, e) => EquationComputeIntegral_Click((able as Equation)!);
|
integralItem.Click += (o, e) => EquationComputeIntegral_Click(equ);
|
||||||
MenuEquationsIntegral.DropDownItems.Add(integralItem);
|
MenuEquationsIntegral.DropDownItems.Add(integralItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,7 +344,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 +362,16 @@ public partial class GraphForm : Form
|
|||||||
return sum * factor;
|
return sum * factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MenuMiscCaches_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ViewCacheForm cacheForm = new(this)
|
||||||
|
{
|
||||||
|
StartPosition = FormStartPosition.Manual
|
||||||
|
};
|
||||||
|
|
||||||
|
cacheForm.Location = new Point(Location.X + ClientRectangle.Width + 10,
|
||||||
|
Location.Y + (ClientRectangle.Height - cacheForm.ClientRectangle.Height) / 2);
|
||||||
|
cacheForm.Show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,131 +1,119 @@
|
|||||||
using System;
|
namespace Graphing.Forms;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace Graphing.Forms
|
public partial class SetZoomForm : Form
|
||||||
{
|
{
|
||||||
public partial class SetZoomForm : Form
|
private double minZoomRange;
|
||||||
|
private double maxZoomRange;
|
||||||
|
|
||||||
|
private double zoomLevel;
|
||||||
|
|
||||||
|
private readonly GraphForm form;
|
||||||
|
|
||||||
|
public SetZoomForm(GraphForm form)
|
||||||
{
|
{
|
||||||
private double minZoomRange;
|
InitializeComponent();
|
||||||
private double maxZoomRange;
|
|
||||||
|
|
||||||
private double zoomLevel;
|
minZoomRange = 1 / (form.ZoomLevel * 2);
|
||||||
|
maxZoomRange = 2 / form.ZoomLevel;
|
||||||
|
zoomLevel = 1 / form.ZoomLevel;
|
||||||
|
|
||||||
private readonly GraphForm form;
|
ZoomTrackBar.Value = (int)(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum);
|
||||||
|
|
||||||
public SetZoomForm(GraphForm form)
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
|
{
|
||||||
|
ZoomMaxValue.Text = maxZoomRange.ToString("0.00");
|
||||||
|
ZoomMinValue.Text = minZoomRange.ToString("0.00");
|
||||||
|
|
||||||
|
ValueLabel.Text = $"{zoomLevel:0.00}x";
|
||||||
|
|
||||||
|
base.OnPaint(e);
|
||||||
|
|
||||||
|
form.ZoomLevel = 1 / zoomLevel;
|
||||||
|
form.Invalidate(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double FactorToZoom(double factor)
|
||||||
|
{
|
||||||
|
return minZoomRange + (factor * factor) * (maxZoomRange - minZoomRange);
|
||||||
|
}
|
||||||
|
private double ZoomToFactor(double zoom)
|
||||||
|
{
|
||||||
|
double sqrValue = (zoom - minZoomRange) / (maxZoomRange - minZoomRange);
|
||||||
|
return Math.Sign(sqrValue) * Math.Sqrt(Math.Abs(sqrValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomTrackBar_Scroll(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
||||||
|
zoomLevel = FactorToZoom(factor);
|
||||||
|
|
||||||
|
Invalidate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ZoomMinValue_TextChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
double original = minZoomRange;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
double value;
|
||||||
|
if (string.IsNullOrWhiteSpace(ZoomMinValue.Text) ||
|
||||||
|
ZoomMinValue.Text.EndsWith('.'))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = double.Parse(ZoomMinValue.Text);
|
||||||
|
if (value < 1e-2 || value > 1e3 || value > maxZoomRange) throw new();
|
||||||
|
}
|
||||||
|
|
||||||
minZoomRange = 1 / (form.ZoomLevel * 2);
|
minZoomRange = value;
|
||||||
maxZoomRange = 2 / form.ZoomLevel;
|
ZoomTrackBar.Value = (int)Math.Clamp(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum, ZoomTrackBar.Minimum, ZoomTrackBar.Maximum);
|
||||||
zoomLevel = 1 / form.ZoomLevel;
|
|
||||||
|
|
||||||
ZoomTrackBar.Value = (int)(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum);
|
|
||||||
|
|
||||||
this.form = form;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e)
|
|
||||||
{
|
|
||||||
ZoomMaxValue.Text = maxZoomRange.ToString("0.00");
|
|
||||||
ZoomMinValue.Text = minZoomRange.ToString("0.00");
|
|
||||||
|
|
||||||
ValueLabel.Text = $"{zoomLevel:0.00}x";
|
|
||||||
|
|
||||||
base.OnPaint(e);
|
|
||||||
|
|
||||||
form.ZoomLevel = 1 / zoomLevel;
|
|
||||||
form.Invalidate(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double FactorToZoom(double factor)
|
|
||||||
{
|
|
||||||
return minZoomRange + (factor * factor) * (maxZoomRange - minZoomRange);
|
|
||||||
}
|
|
||||||
private double ZoomToFactor(double zoom)
|
|
||||||
{
|
|
||||||
double sqrValue = (zoom - minZoomRange) / (maxZoomRange - minZoomRange);
|
|
||||||
return Math.Sign(sqrValue) * Math.Sqrt(Math.Abs(sqrValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ZoomTrackBar_Scroll(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
||||||
zoomLevel = FactorToZoom(factor);
|
double newZoom = FactorToZoom(factor);
|
||||||
|
|
||||||
Invalidate(true);
|
zoomLevel = newZoom;
|
||||||
|
if (newZoom != factor) Invalidate(true);
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
private void ZoomMinValue_TextChanged(object? sender, EventArgs e)
|
|
||||||
{
|
{
|
||||||
double original = minZoomRange;
|
minZoomRange = original;
|
||||||
try
|
ZoomMinValue.Text = minZoomRange.ToString("0.00");
|
||||||
{
|
|
||||||
double value;
|
|
||||||
if (string.IsNullOrWhiteSpace(ZoomMinValue.Text) ||
|
|
||||||
ZoomMinValue.Text.EndsWith('.'))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = double.Parse(ZoomMinValue.Text);
|
|
||||||
if (value < 1e-2 || value > 1e3 || value > maxZoomRange) throw new();
|
|
||||||
}
|
|
||||||
|
|
||||||
minZoomRange = value;
|
|
||||||
ZoomTrackBar.Value = (int)Math.Clamp(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum, ZoomTrackBar.Minimum, ZoomTrackBar.Maximum);
|
|
||||||
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
|
||||||
double newZoom = FactorToZoom(factor);
|
|
||||||
|
|
||||||
zoomLevel = newZoom;
|
|
||||||
if (newZoom != factor) Invalidate(true);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
minZoomRange = original;
|
|
||||||
ZoomMinValue.Text = minZoomRange.ToString("0.00");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ZoomMaxValue_TextChanged(object sender, EventArgs e)
|
private void ZoomMaxValue_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
double original = maxZoomRange;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
double original = maxZoomRange;
|
double value;
|
||||||
try
|
if (string.IsNullOrWhiteSpace(ZoomMaxValue.Text) ||
|
||||||
|
ZoomMaxValue.Text.EndsWith('.'))
|
||||||
{
|
{
|
||||||
double value;
|
return;
|
||||||
if (string.IsNullOrWhiteSpace(ZoomMaxValue.Text) ||
|
|
||||||
ZoomMaxValue.Text.EndsWith('.'))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = double.Parse(ZoomMaxValue.Text);
|
|
||||||
if (value < 1e-2 || value > 1e3 || value < minZoomRange) throw new();
|
|
||||||
}
|
|
||||||
|
|
||||||
maxZoomRange = value;
|
|
||||||
ZoomTrackBar.Value = (int)Math.Clamp(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum, ZoomTrackBar.Minimum, ZoomTrackBar.Maximum);
|
|
||||||
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
|
||||||
double newZoom = FactorToZoom(factor);
|
|
||||||
|
|
||||||
zoomLevel = newZoom;
|
|
||||||
if (newZoom != factor) Invalidate(true);
|
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
{
|
||||||
maxZoomRange = original;
|
value = double.Parse(ZoomMaxValue.Text);
|
||||||
ZoomMaxValue.Text = maxZoomRange.ToString("0.00");
|
if (value < 1e-2 || value > 1e3 || value < minZoomRange) throw new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxZoomRange = value;
|
||||||
|
ZoomTrackBar.Value = (int)Math.Clamp(ZoomToFactor(zoomLevel) * (ZoomTrackBar.Maximum - ZoomTrackBar.Minimum) + ZoomTrackBar.Minimum, ZoomTrackBar.Minimum, ZoomTrackBar.Maximum);
|
||||||
|
double factor = (ZoomTrackBar.Value - ZoomTrackBar.Minimum) / (double)(ZoomTrackBar.Maximum - ZoomTrackBar.Minimum);
|
||||||
|
double newZoom = FactorToZoom(factor);
|
||||||
|
|
||||||
|
zoomLevel = newZoom;
|
||||||
|
if (newZoom != factor) Invalidate(true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
maxZoomRange = original;
|
||||||
|
ZoomMaxValue.Text = maxZoomRange.ToString("0.00");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
97
Base/Forms/ViewCacheForm.Designer.cs
generated
Normal file
97
Base/Forms/ViewCacheForm.Designer.cs
generated
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
Base/Forms/ViewCacheForm.cs
Normal file
89
Base/Forms/ViewCacheForm.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
139
Base/Forms/ViewCacheForm.resx
Normal file
139
Base/Forms/ViewCacheForm.resx
Normal 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>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Graphing.Forms;
|
using Graphing.Forms;
|
||||||
|
using Graphing.Parts;
|
||||||
|
|
||||||
namespace Graphing;
|
namespace Graphing;
|
||||||
|
|
||||||
@ -26,5 +27,10 @@ public abstract class Graphable
|
|||||||
Name = "Unnamed Graphable.";
|
Name = "Unnamed Graphable.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract IEnumerable<Line2d> GetItemsToRender(in GraphForm graph);
|
public abstract IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph);
|
||||||
|
|
||||||
|
public abstract Graphable DeepCopy();
|
||||||
|
|
||||||
|
public abstract void EraseCache();
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected readonly Dictionary<double, double> tableXY;
|
||||||
|
protected 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Graphing.Forms;
|
using Graphing.Forms;
|
||||||
|
using Graphing.Parts;
|
||||||
|
|
||||||
namespace Graphing.Graphables;
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
@ -6,7 +7,8 @@ public class Equation : Graphable
|
|||||||
{
|
{
|
||||||
private static int equationNum;
|
private static int equationNum;
|
||||||
|
|
||||||
private readonly EquationDelegate equ;
|
protected readonly EquationDelegate equ;
|
||||||
|
protected readonly List<Float2> cache;
|
||||||
|
|
||||||
public Equation(EquationDelegate equ)
|
public Equation(EquationDelegate equ)
|
||||||
{
|
{
|
||||||
@ -14,20 +16,27 @@ public class Equation : Graphable
|
|||||||
Name = $"Equation {equationNum}";
|
Name = $"Equation {equationNum}";
|
||||||
|
|
||||||
this.equ = equ;
|
this.equ = equ;
|
||||||
|
cache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<Line2d> GetItemsToRender(in GraphForm graph)
|
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
||||||
{
|
{
|
||||||
List<Line2d> lines = [];
|
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<IGraphPart> lines = [];
|
||||||
|
|
||||||
double previousX = graph.MinVisibleGraph.x;
|
double previousX = graph.MinVisibleGraph.x;
|
||||||
double previousY = equ(previousX);
|
double previousY = GetFromCache(previousX, epsilon);
|
||||||
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);
|
||||||
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 GraphLine(new Float2(previousX, previousY), new Float2(currentX, currentY)));
|
||||||
}
|
}
|
||||||
previousX = currentX;
|
previousX = currentX;
|
||||||
previousY = currentY;
|
previousY = currentY;
|
||||||
@ -36,6 +45,57 @@ public class Equation : Graphable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public EquationDelegate GetDelegate() => equ;
|
public EquationDelegate GetDelegate() => equ;
|
||||||
|
|
||||||
|
public override void EraseCache() => cache.Clear();
|
||||||
|
protected double GetFromCache(double x, double epsilon)
|
||||||
|
{
|
||||||
|
(double dist, double nearest, int index) = NearestCachedPoint(x);
|
||||||
|
if (dist < epsilon) return nearest;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double result = equ(x);
|
||||||
|
cache.Insert(index + 1, new(x, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty sure this works. Certainly works pretty well with "hard-to-compute"
|
||||||
|
// equations.
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
|
||||||
|
if (pointC.x == x) return (0, pointC.y, boundC);
|
||||||
|
else if (pointC.x > x)
|
||||||
|
{
|
||||||
|
boundA = boundC;
|
||||||
|
}
|
||||||
|
else // pointC.x < x
|
||||||
|
{
|
||||||
|
boundB = boundC;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (boundB - boundA > 1);
|
||||||
|
|
||||||
|
return (Math.Abs(cache[boundA].x - x), cache[boundA].y, boundA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Graphable DeepCopy() => new Equation(equ);
|
||||||
|
|
||||||
|
public override long GetCacheBytes() => cache.Count * 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate double EquationDelegate(double x);
|
public delegate double EquationDelegate(double x);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Graphing.Forms;
|
using Graphing.Forms;
|
||||||
|
using Graphing.Parts;
|
||||||
|
|
||||||
namespace Graphing.Graphables;
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
@ -6,8 +7,10 @@ public class SlopeField : Graphable
|
|||||||
{
|
{
|
||||||
private static int slopeFieldNum;
|
private static int slopeFieldNum;
|
||||||
|
|
||||||
private readonly SlopeFieldsDelegate equ;
|
protected readonly SlopeFieldsDelegate equ;
|
||||||
private readonly double detail;
|
protected readonly int detail;
|
||||||
|
|
||||||
|
protected readonly List<(Float2, GraphLine)> cache;
|
||||||
|
|
||||||
public SlopeField(int detail, SlopeFieldsDelegate equ)
|
public SlopeField(int detail, SlopeFieldsDelegate equ)
|
||||||
{
|
{
|
||||||
@ -16,25 +19,26 @@ public class SlopeField : Graphable
|
|||||||
|
|
||||||
this.equ = equ;
|
this.equ = equ;
|
||||||
this.detail = detail;
|
this.detail = detail;
|
||||||
|
cache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<Line2d> GetItemsToRender(in GraphForm graph)
|
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
||||||
{
|
{
|
||||||
List<Line2d> lines = [];
|
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)
|
||||||
{
|
{
|
||||||
double slope = equ(x, y);
|
lines.Add(GetFromCache(epsilon, x, y));
|
||||||
lines.Add(MakeSlopeLine(new Float2(x, y), slope));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Line2d MakeSlopeLine(Float2 position, double slope)
|
protected GraphLine MakeSlopeLine(Float2 position, double slope)
|
||||||
{
|
{
|
||||||
double size = detail;
|
double size = detail;
|
||||||
|
|
||||||
@ -46,6 +50,30 @@ public class SlopeField : Graphable
|
|||||||
|
|
||||||
return new(new(position.x + dirX, position.y + dirY), new(position.x - dirX, position.y - dirY));
|
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 delegate double SlopeFieldsDelegate(double x, double y);
|
public delegate double SlopeFieldsDelegate(double x, double y);
|
||||||
|
|||||||
51
Base/Graphables/TangentLine.cs
Normal file
51
Base/Graphables/TangentLine.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
using Graphing.Parts;
|
||||||
|
|
||||||
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
|
public class TangentLine : Graphable
|
||||||
|
{
|
||||||
|
public double Position { get; set; }
|
||||||
|
|
||||||
|
protected readonly Equation parent;
|
||||||
|
protected readonly EquationDelegate parentEqu;
|
||||||
|
|
||||||
|
protected readonly double length;
|
||||||
|
|
||||||
|
public TangentLine(double length, double position, Equation parent)
|
||||||
|
{
|
||||||
|
Name = $"Tangent Line of {parent.Name}";
|
||||||
|
|
||||||
|
parentEqu = parent.GetDelegate();
|
||||||
|
Position = position;
|
||||||
|
this.length = length;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
|
||||||
|
{
|
||||||
|
Float2 point = new(Position, parentEqu(Position));
|
||||||
|
return [MakeSlopeLine(point, DerivativeAtPoint(Position)),
|
||||||
|
new GraphUiCircle(point, 8)];
|
||||||
|
}
|
||||||
|
protected GraphLine MakeSlopeLine(Float2 position, double slope)
|
||||||
|
{
|
||||||
|
double dirX = length, dirY = slope * length;
|
||||||
|
double magnitude = Math.Sqrt(dirX * dirX + dirY * dirY);
|
||||||
|
|
||||||
|
dirX /= magnitude * 2 / length;
|
||||||
|
dirY /= magnitude * 2 / length;
|
||||||
|
|
||||||
|
return new(new(position.x + dirX, position.y + dirY), new(position.x - dirX, position.y - dirY));
|
||||||
|
}
|
||||||
|
protected double DerivativeAtPoint(double x)
|
||||||
|
{
|
||||||
|
const double step = 1e-3;
|
||||||
|
return (parentEqu(x + step) - parentEqu(x)) / step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Graphable DeepCopy() => new TangentLine(length, Position, parent);
|
||||||
|
|
||||||
|
public override void EraseCache() { }
|
||||||
|
public override long GetCacheBytes() => 0;
|
||||||
|
}
|
||||||
8
Base/IGraphPart.cs
Normal file
8
Base/IGraphPart.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
|
||||||
|
namespace Graphing;
|
||||||
|
|
||||||
|
public interface IGraphPart
|
||||||
|
{
|
||||||
|
public void Render(in GraphForm form, in Graphics g, in Brush brush);
|
||||||
|
}
|
||||||
@ -1,18 +0,0 @@
|
|||||||
namespace Graphing;
|
|
||||||
|
|
||||||
public record struct Line2d
|
|
||||||
{
|
|
||||||
public Float2 a;
|
|
||||||
public Float2 b;
|
|
||||||
|
|
||||||
public Line2d()
|
|
||||||
{
|
|
||||||
a = new();
|
|
||||||
b = new();
|
|
||||||
}
|
|
||||||
public Line2d(Float2 a, Float2 b)
|
|
||||||
{
|
|
||||||
this.a = a;
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
Base/Parts/GraphLine.cs
Normal file
32
Base/Parts/GraphLine.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
|
||||||
|
namespace Graphing.Parts;
|
||||||
|
|
||||||
|
public record struct GraphLine : IGraphPart
|
||||||
|
{
|
||||||
|
public Float2 a;
|
||||||
|
public Float2 b;
|
||||||
|
|
||||||
|
public GraphLine()
|
||||||
|
{
|
||||||
|
a = new();
|
||||||
|
b = new();
|
||||||
|
}
|
||||||
|
public GraphLine(Float2 a, Float2 b)
|
||||||
|
{
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(a.x) || !double.IsFinite(a.y) ||
|
||||||
|
!double.IsFinite(b.x) || !double.IsFinite(b.y)) return;
|
||||||
|
|
||||||
|
Int2 start = form.GraphSpaceToScreenSpace(a),
|
||||||
|
end = form.GraphSpaceToScreenSpace(b);
|
||||||
|
|
||||||
|
Pen pen = new(brush, 3);
|
||||||
|
g.DrawLine(pen, start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
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 record 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Base/Parts/GraphUiCircle.cs
Normal file
31
Base/Parts/GraphUiCircle.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Graphing.Forms;
|
||||||
|
|
||||||
|
namespace Graphing.Parts;
|
||||||
|
|
||||||
|
public record struct GraphUiCircle : IGraphPart
|
||||||
|
{
|
||||||
|
public Float2 center;
|
||||||
|
public int radius;
|
||||||
|
|
||||||
|
public GraphUiCircle()
|
||||||
|
{
|
||||||
|
center = new();
|
||||||
|
radius = 1;
|
||||||
|
}
|
||||||
|
public GraphUiCircle(Float2 center, int radius)
|
||||||
|
{
|
||||||
|
this.center = center;
|
||||||
|
this.radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void Render(in GraphForm form, in Graphics g, in Brush brush)
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(center.x) || !double.IsFinite(center.y) ||
|
||||||
|
!double.IsFinite(radius) || radius == 0) return;
|
||||||
|
|
||||||
|
Int2 centerPix = form.GraphSpaceToScreenSpace(center);
|
||||||
|
g.FillEllipse(brush, new Rectangle(new Point(centerPix.x - radius,
|
||||||
|
centerPix.y - radius),
|
||||||
|
new Size(radius * 2, radius * 2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Base/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
13
Base/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||||
|
-->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Any CPU</Platform>
|
||||||
|
<PublishDir>bin\Release\net8.0-windows\publish\</PublishDir>
|
||||||
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
|
<_TargetId>Folder</_TargetId>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
10
Base/Properties/PublishProfiles/FolderProfile.pubxml.user
Normal file
10
Base/Properties/PublishProfiles/FolderProfile.pubxml.user
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||||
|
-->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<History>True|2024-03-13T14:31:43.4569441Z;False|2024-03-13T10:30:01.4347009-04:00;False|2024-03-13T10:27:31.9554551-04:00;</History>
|
||||||
|
<LastFailureDetails />
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -10,10 +10,31 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
|
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||||
|
|
||||||
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));
|
|
||||||
|
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 + 2;
|
||||||
|
});
|
||||||
|
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);
|
Application.Run(graph);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
@ -10,6 +9,14 @@
|
|||||||
<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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user