Some stuff. Satisfied for now with the slope-field generation and the parametric equations.
This commit is contained in:
parent
3b6ebc7b99
commit
470a70a97a
10
Base/Abstract/IConvertColumnTable.cs
Normal file
10
Base/Abstract/IConvertColumnTable.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Graphing.Graphables;
|
||||||
|
|
||||||
|
namespace Graphing.Abstract;
|
||||||
|
|
||||||
|
public interface IConvertColumnTable
|
||||||
|
{
|
||||||
|
public bool UngraphWhenConvertedToColumnTable { get; }
|
||||||
|
|
||||||
|
public ColumnTable ToColumnTable(double start, double end, int detail);
|
||||||
|
}
|
||||||
@ -3,7 +3,6 @@ using Graphing.Helpers;
|
|||||||
using Graphing.Parts;
|
using Graphing.Parts;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -11,7 +10,6 @@ using System.Net.Http;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace Graphing.Forms;
|
namespace Graphing.Forms;
|
||||||
@ -278,6 +276,13 @@ public partial class GraphForm : Form
|
|||||||
Invalidate(false);
|
Invalidate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsGraphPointVisible(Float2 point)
|
||||||
|
{
|
||||||
|
Int2 pixelPos = GraphSpaceToScreenSpace(point);
|
||||||
|
return pixelPos.x >= 0 && pixelPos.x < ClientRectangle.Width &&
|
||||||
|
pixelPos.y >= 0 && pixelPos.y < ClientRectangle.Height;
|
||||||
|
}
|
||||||
|
|
||||||
private bool mouseDrag = false;
|
private bool mouseDrag = false;
|
||||||
private Int2 initialMouseLocation;
|
private Int2 initialMouseLocation;
|
||||||
private Float2 initialScreenCenter;
|
private Float2 initialScreenCenter;
|
||||||
@ -371,6 +376,8 @@ public partial class GraphForm : Form
|
|||||||
MenuConvertEquation.DropDownItems.Clear();
|
MenuConvertEquation.DropDownItems.Clear();
|
||||||
MenuConvertSlopeField.DropDownItems.Clear();
|
MenuConvertSlopeField.DropDownItems.Clear();
|
||||||
MenuOperationsTranslate.DropDownItems.Clear();
|
MenuOperationsTranslate.DropDownItems.Clear();
|
||||||
|
// At some point, we'll have a Convert To Column Table button,
|
||||||
|
// but I'll need to make a form for the ranges when I do that.
|
||||||
|
|
||||||
foreach (Graphable able in ables)
|
foreach (Graphable able in ables)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -100,6 +100,8 @@ public partial class TranslateForm : Form
|
|||||||
TitleLabel.Text = $"There doesn't seem to be anything you can translate for {ableRaw.Name}.";
|
TitleLabel.Text = $"There doesn't seem to be anything you can translate for {ableRaw.Name}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe replace these default limits with what's visible on screen?
|
||||||
|
// Tried it and it got a bit confusing so maybe not.
|
||||||
minX = -10;
|
minX = -10;
|
||||||
maxX = 10;
|
maxX = 10;
|
||||||
minY = -10;
|
minY = -10;
|
||||||
@ -272,11 +274,10 @@ public partial class TranslateForm : Form
|
|||||||
private static double Lerp(double a, double b, double t) => a + t * (b - a);
|
private static double Lerp(double a, double b, double t) => a + t * (b - a);
|
||||||
private static double InverseLerp(double a, double b, double c) => (c - a) / (b - a);
|
private static double InverseLerp(double a, double b, double c) => (c - a) / (b - a);
|
||||||
|
|
||||||
private void TrackX_Scroll(object? sender, EventArgs e)
|
private void TrackX_Scroll(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateFromSliderX(true);
|
UpdateFromSliderX(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TrackY_Scroll(object sender, EventArgs e)
|
private void TrackY_Scroll(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateFromSliderY(true);
|
UpdateFromSliderY(true);
|
||||||
|
|||||||
@ -23,6 +23,7 @@ public class ColumnTable : Graphable
|
|||||||
}
|
}
|
||||||
public ColumnTable(double step, Equation equation, double min, double max)
|
public ColumnTable(double step, Equation equation, double min, double max)
|
||||||
{
|
{
|
||||||
|
Color = equation.Color;
|
||||||
Name = $"Column Table for {equation.Name}";
|
Name = $"Column Table for {equation.Name}";
|
||||||
|
|
||||||
tableXY = [];
|
tableXY = [];
|
||||||
@ -45,7 +46,7 @@ public class ColumnTable : Graphable
|
|||||||
foreach (KeyValuePair<double, double> col in tableXY)
|
foreach (KeyValuePair<double, double> col in tableXY)
|
||||||
{
|
{
|
||||||
items.Add(GraphRectangle.FromSize(new Float2(col.Key, col.Value / 2),
|
items.Add(GraphRectangle.FromSize(new Float2(col.Key, col.Value / 2),
|
||||||
new Float2(width, col.Value)));
|
new Float2(width, col.Value), 0.625));
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
|||||||
@ -6,10 +6,12 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Graphing.Graphables;
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, IConvertSlopeField
|
public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, IConvertSlopeField,
|
||||||
|
IConvertColumnTable
|
||||||
{
|
{
|
||||||
private static int equationNum;
|
private static int equationNum;
|
||||||
|
|
||||||
|
public bool UngraphWhenConvertedToColumnTable => false;
|
||||||
public bool UngraphWhenConvertedToSlopeField => false;
|
public bool UngraphWhenConvertedToSlopeField => false;
|
||||||
|
|
||||||
public double OffsetX { get; set; }
|
public double OffsetX { get; set; }
|
||||||
@ -65,7 +67,7 @@ public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, ICo
|
|||||||
protected double DerivativeAtPoint(double x)
|
protected double DerivativeAtPoint(double x)
|
||||||
{
|
{
|
||||||
const double step = 1e-3;
|
const double step = 1e-3;
|
||||||
return (equ(x + step) - equ(x)) / step;
|
return (equ(x + step - OffsetX) - equ(x - OffsetX)) / step;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Graphable Derive() => new Equation(DerivativeAtPoint);
|
public Graphable Derive() => new Equation(DerivativeAtPoint);
|
||||||
@ -78,6 +80,8 @@ public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, ICo
|
|||||||
Color = Color,
|
Color = Color,
|
||||||
Name = $"Slope Field of {Name}"
|
Name = $"Slope Field of {Name}"
|
||||||
};
|
};
|
||||||
|
public ColumnTable ToColumnTable(double start, double end, int detail)
|
||||||
|
=> new(1.0 / detail, this, start, end);
|
||||||
|
|
||||||
public override void EraseCache() => cache.Clear();
|
public override void EraseCache() => cache.Clear();
|
||||||
protected double GetFromCache(double x, double epsilon)
|
protected double GetFromCache(double x, double epsilon)
|
||||||
|
|||||||
@ -164,7 +164,7 @@ public class IntegralEquation : Graphable, IIntegrable, IDerivable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stepY += baseEquDel!(stepX) * dX;
|
stepY += (baseEquDel!(stepX - baseEqu!.OffsetX) + baseEqu.OffsetY) * dX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Graphing.Graphables;
|
namespace Graphing.Graphables;
|
||||||
|
|
||||||
public class ParametricEquation : Graphable, ITranslatableXY
|
public class ParametricEquation : Graphable, IDerivable, ITranslatableXY
|
||||||
{
|
{
|
||||||
private static int equationNum;
|
private static int equationNum;
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
|||||||
public double FinalT { get; set; }
|
public double FinalT { get; set; }
|
||||||
|
|
||||||
protected readonly ParametricDelegate equX, equY;
|
protected readonly ParametricDelegate equX, equY;
|
||||||
|
protected readonly List<(double t, Float2 point)> cache;
|
||||||
|
|
||||||
public ParametricEquation(double initialT, double finalT,
|
public ParametricEquation(double initialT, double finalT,
|
||||||
ParametricDelegate equX, ParametricDelegate equY)
|
ParametricDelegate equX, ParametricDelegate equY)
|
||||||
@ -29,6 +30,7 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
|||||||
|
|
||||||
this.equX = equX;
|
this.equX = equX;
|
||||||
this.equY = equY;
|
this.equY = equY;
|
||||||
|
cache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Graphable ShallowCopy() => new ParametricEquation(InitialT, FinalT, equX, equY);
|
public override Graphable ShallowCopy() => new ParametricEquation(InitialT, FinalT, equX, equY);
|
||||||
@ -38,25 +40,94 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
|||||||
const int step = 10;
|
const int step = 10;
|
||||||
|
|
||||||
double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x
|
double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x
|
||||||
- graph.ScreenSpaceToGraphSpace(new Int2(step / 2, 0)).x) / 5;
|
- graph.ScreenSpaceToGraphSpace(new Int2(step, 0)).x);
|
||||||
epsilon *= graph.DpiFloat / 192;
|
|
||||||
|
|
||||||
List<IGraphPart> lines = [];
|
List<IGraphPart> lines = [];
|
||||||
|
|
||||||
Float2 previousPoint = GetPointAt(InitialT);
|
Float2 previousPoint = GetFromCache(InitialT, epsilon);
|
||||||
for (double t = InitialT; t <= FinalT; t += epsilon)
|
for (double t = InitialT; t <= FinalT; t += epsilon)
|
||||||
{
|
{
|
||||||
Float2 currentPoint = GetPointAt(t);
|
Float2 currentPoint = GetFromCache(t, epsilon);
|
||||||
lines.Add(new GraphLine(previousPoint, currentPoint));
|
if (graph.IsGraphPointVisible(currentPoint) ||
|
||||||
|
graph.IsGraphPointVisible(previousPoint))
|
||||||
|
lines.Add(new GraphLine(previousPoint, currentPoint));
|
||||||
previousPoint = currentPoint;
|
previousPoint = currentPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float2 GetPointAt(double t)
|
public Graphable Derive() =>
|
||||||
|
new ParametricEquation(InitialT, FinalT, GetDerivativeAtPointX, GetDerivativeAtPointY);
|
||||||
|
|
||||||
|
public ParametricDelegate GetXDelegate() => equX;
|
||||||
|
public ParametricDelegate GetYDelegate() => equY;
|
||||||
|
|
||||||
|
public double GetDerivativeAtPointX(double t)
|
||||||
{
|
{
|
||||||
return new(equX(t) + OffsetX, equY(t) + OffsetY);
|
const double step = 1e-3;
|
||||||
|
return (equX(t + step) - equX(t)) / step;
|
||||||
|
}
|
||||||
|
public double GetDerivativeAtPointY(double t)
|
||||||
|
{
|
||||||
|
const double step = 1e-3;
|
||||||
|
return (equY(t + step) - equY(t)) / step;
|
||||||
|
}
|
||||||
|
public Float2 GetDerivativeAtPoint(double t) =>
|
||||||
|
new(GetDerivativeAtPointX(t), GetDerivativeAtPointY(t));
|
||||||
|
|
||||||
|
public Float2 GetPointAt(double t) => GetFromCache(t, 0);
|
||||||
|
|
||||||
|
public override void EraseCache() => cache.Clear();
|
||||||
|
protected Float2 GetFromCache(double t, double epsilon)
|
||||||
|
{
|
||||||
|
(double dist, Float2 nearest, int index) = NearestCachedPoint(t);
|
||||||
|
if (dist < epsilon) return new(nearest.x + OffsetX, nearest.y + OffsetY);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Float2 result = new(equX(t), equY(t));
|
||||||
|
cache.Insert(index + 1, (t, result));
|
||||||
|
return new(result.x + OffsetX, result.y + OffsetY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override long GetCacheBytes() => cache.Count * 24;
|
||||||
|
|
||||||
|
protected (double dist, Float2 point, int index) NearestCachedPoint(double t)
|
||||||
|
{
|
||||||
|
if (cache.Count <= 1) return (double.PositiveInfinity, new(double.NaN, double.NaN), -1);
|
||||||
|
else if (cache.Count == 1)
|
||||||
|
{
|
||||||
|
(double resultT, Float2 resultPoint) = cache[0];
|
||||||
|
return (Math.Abs(resultT - t), resultPoint, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int boundA = 0, boundB = cache.Count;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int boundC = (boundA + boundB) / 2;
|
||||||
|
|
||||||
|
(double thisT, Float2 thisPoint) = cache[boundC];
|
||||||
|
if (thisT == t) return (0, thisPoint, boundC);
|
||||||
|
else if (thisT > t)
|
||||||
|
{
|
||||||
|
boundA = boundC;
|
||||||
|
}
|
||||||
|
else // thisT < t
|
||||||
|
{
|
||||||
|
boundB = boundC;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (boundB - boundA > 1);
|
||||||
|
|
||||||
|
(double resultT, Float2 resultPoint) = cache[boundA];
|
||||||
|
return (Math.Abs(resultT - t), resultPoint, boundA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Preload(Float2 xRange, Float2 yRange, double step)
|
||||||
|
{
|
||||||
|
for (double t = InitialT; t <= FinalT; t += step) GetFromCache(t, step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,24 +6,28 @@ namespace Graphing.Parts;
|
|||||||
public record struct GraphRectangle : IGraphPart
|
public record struct GraphRectangle : IGraphPart
|
||||||
{
|
{
|
||||||
public Float2 min, max;
|
public Float2 min, max;
|
||||||
|
public double opacity;
|
||||||
|
|
||||||
public GraphRectangle()
|
public GraphRectangle()
|
||||||
{
|
{
|
||||||
min = new();
|
min = new();
|
||||||
max = new();
|
max = new();
|
||||||
|
opacity = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GraphRectangle FromSize(Float2 center, Float2 size) => new()
|
public static GraphRectangle FromSize(Float2 center, Float2 size, double opacity = 1) => new()
|
||||||
{
|
{
|
||||||
min = new(center.x - size.x / 2,
|
min = new(center.x - size.x / 2,
|
||||||
center.y - size.y / 2),
|
center.y - size.y / 2),
|
||||||
max = new(center.x + size.x / 2,
|
max = new(center.x + size.x / 2,
|
||||||
center.y + size.y / 2)
|
center.y + size.y / 2),
|
||||||
|
opacity = opacity,
|
||||||
};
|
};
|
||||||
public static GraphRectangle FromRange(Float2 min, Float2 max) => new()
|
public static GraphRectangle FromRange(Float2 min, Float2 max, double opacity = 1) => new()
|
||||||
{
|
{
|
||||||
min = min,
|
min = min,
|
||||||
max = max
|
max = max,
|
||||||
|
opacity = opacity,
|
||||||
};
|
};
|
||||||
|
|
||||||
public void Render(in GraphForm form, in Graphics g, in Pen pen)
|
public void Render(in GraphForm form, in Graphics g, in Pen pen)
|
||||||
@ -41,6 +45,7 @@ public record struct GraphRectangle : IGraphPart
|
|||||||
start.y - end.y);
|
start.y - end.y);
|
||||||
|
|
||||||
if (size.x == 0 || size.y == 0) return;
|
if (size.x == 0 || size.y == 0) return;
|
||||||
|
pen.Color = Color.FromArgb((int)(opacity * 255), pen.Color);
|
||||||
g.FillRectangle(pen.Brush, new Rectangle(start.x, end.y, size.x, size.y));
|
g.FillRectangle(pen.Brush, new Rectangle(start.x, end.y, size.x, size.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,8 @@ internal static class Program
|
|||||||
Equation equA = new(Math.Sin),
|
Equation equA = new(Math.Sin),
|
||||||
equB = new(Math.Cos);
|
equB = new(Math.Cos);
|
||||||
EquationDifference diff = new(2, equA, equB);
|
EquationDifference diff = new(2, equA, equB);
|
||||||
ParametricEquation equC = new(0, 4, t => t * t - 1, t => Math.Sqrt(t) + t + 1);
|
ParametricEquation equC = new(0, 20, t => 0.0375 * t * Math.Cos(t), t => 0.0625 * t * Math.Sin(t) + 3);
|
||||||
graph.Graph(equA, equB, diff, equC);
|
graph.Graph(equA, equB, diff, equC, equA.ToColumnTable(-1, 1, 2));
|
||||||
|
|
||||||
Application.Run(graph);
|
Application.Run(graph);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user