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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
@ -11,7 +10,6 @@ using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Graphing.Forms;
|
||||
@ -278,6 +276,13 @@ public partial class GraphForm : Form
|
||||
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 Int2 initialMouseLocation;
|
||||
private Float2 initialScreenCenter;
|
||||
@ -371,6 +376,8 @@ public partial class GraphForm : Form
|
||||
MenuConvertEquation.DropDownItems.Clear();
|
||||
MenuConvertSlopeField.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)
|
||||
{
|
||||
|
||||
@ -100,6 +100,8 @@ public partial class TranslateForm : Form
|
||||
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;
|
||||
maxX = 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 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);
|
||||
}
|
||||
|
||||
private void TrackY_Scroll(object sender, EventArgs e)
|
||||
{
|
||||
UpdateFromSliderY(true);
|
||||
|
||||
@ -23,6 +23,7 @@ public class ColumnTable : Graphable
|
||||
}
|
||||
public ColumnTable(double step, Equation equation, double min, double max)
|
||||
{
|
||||
Color = equation.Color;
|
||||
Name = $"Column Table for {equation.Name}";
|
||||
|
||||
tableXY = [];
|
||||
@ -45,7 +46,7 @@ public class ColumnTable : Graphable
|
||||
foreach (KeyValuePair<double, double> col in tableXY)
|
||||
{
|
||||
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;
|
||||
|
||||
@ -6,10 +6,12 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Graphing.Graphables;
|
||||
|
||||
public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, IConvertSlopeField
|
||||
public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, IConvertSlopeField,
|
||||
IConvertColumnTable
|
||||
{
|
||||
private static int equationNum;
|
||||
|
||||
public bool UngraphWhenConvertedToColumnTable => false;
|
||||
public bool UngraphWhenConvertedToSlopeField => false;
|
||||
|
||||
public double OffsetX { get; set; }
|
||||
@ -65,7 +67,7 @@ public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, ICo
|
||||
protected double DerivativeAtPoint(double x)
|
||||
{
|
||||
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);
|
||||
@ -78,6 +80,8 @@ public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, ICo
|
||||
Color = Color,
|
||||
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();
|
||||
protected double GetFromCache(double x, double epsilon)
|
||||
|
||||
@ -164,7 +164,7 @@ public class IntegralEquation : Graphable, IIntegrable, IDerivable
|
||||
}
|
||||
else
|
||||
{
|
||||
stepY += baseEquDel!(stepX) * dX;
|
||||
stepY += (baseEquDel!(stepX - baseEqu!.OffsetX) + baseEqu.OffsetY) * dX;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Graphing.Graphables;
|
||||
|
||||
public class ParametricEquation : Graphable, ITranslatableXY
|
||||
public class ParametricEquation : Graphable, IDerivable, ITranslatableXY
|
||||
{
|
||||
private static int equationNum;
|
||||
|
||||
@ -17,6 +17,7 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
||||
public double FinalT { get; set; }
|
||||
|
||||
protected readonly ParametricDelegate equX, equY;
|
||||
protected readonly List<(double t, Float2 point)> cache;
|
||||
|
||||
public ParametricEquation(double initialT, double finalT,
|
||||
ParametricDelegate equX, ParametricDelegate equY)
|
||||
@ -29,6 +30,7 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
||||
|
||||
this.equX = equX;
|
||||
this.equY = equY;
|
||||
cache = [];
|
||||
}
|
||||
|
||||
public override Graphable ShallowCopy() => new ParametricEquation(InitialT, FinalT, equX, equY);
|
||||
@ -38,15 +40,16 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
||||
const int step = 10;
|
||||
|
||||
double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x
|
||||
- graph.ScreenSpaceToGraphSpace(new Int2(step / 2, 0)).x) / 5;
|
||||
epsilon *= graph.DpiFloat / 192;
|
||||
- graph.ScreenSpaceToGraphSpace(new Int2(step, 0)).x);
|
||||
|
||||
List<IGraphPart> lines = [];
|
||||
|
||||
Float2 previousPoint = GetPointAt(InitialT);
|
||||
Float2 previousPoint = GetFromCache(InitialT, epsilon);
|
||||
for (double t = InitialT; t <= FinalT; t += epsilon)
|
||||
{
|
||||
Float2 currentPoint = GetPointAt(t);
|
||||
Float2 currentPoint = GetFromCache(t, epsilon);
|
||||
if (graph.IsGraphPointVisible(currentPoint) ||
|
||||
graph.IsGraphPointVisible(previousPoint))
|
||||
lines.Add(new GraphLine(previousPoint, currentPoint));
|
||||
previousPoint = currentPoint;
|
||||
}
|
||||
@ -54,9 +57,77 @@ public class ParametricEquation : Graphable, ITranslatableXY
|
||||
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 Float2 min, max;
|
||||
public double opacity;
|
||||
|
||||
public GraphRectangle()
|
||||
{
|
||||
min = 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,
|
||||
center.y - size.y / 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,
|
||||
max = max
|
||||
max = max,
|
||||
opacity = opacity,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,8 @@ internal static class Program
|
||||
Equation equA = new(Math.Sin),
|
||||
equB = new(Math.Cos);
|
||||
EquationDifference diff = new(2, equA, equB);
|
||||
ParametricEquation equC = new(0, 4, t => t * t - 1, t => Math.Sqrt(t) + t + 1);
|
||||
graph.Graph(equA, equB, diff, equC);
|
||||
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, equA.ToColumnTable(-1, 1, 2));
|
||||
|
||||
Application.Run(graph);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user