diff --git a/Base/Forms/GraphForm.cs b/Base/Forms/GraphForm.cs index 4d13472..2ea0c2f 100644 --- a/Base/Forms/GraphForm.cs +++ b/Base/Forms/GraphForm.cs @@ -431,38 +431,7 @@ public partial class GraphForm : Form } private void EquationComputeIntegral_Click(Equation equation) { - EquationDelegate equ = equation.GetDelegate(); - string oldName = equation.Name, newName; - if (oldName.StartsWith("Integral of ")) newName = "Second Integral of " + oldName[12..]; - else if (oldName.StartsWith("Second Integral of ")) newName = "Third Integral of " + oldName[19..]; - else newName = "Integral of " + oldName; - // TODO: anti-derive (maybe) - - Graph(new Equation(x => Integrate(equ, 0, x)) - { - Name = newName - }); - - static double Integrate(EquationDelegate e, double lower, double upper) - { - // TODO: a better rendering method could make this much faster. - const double step = 1e-2; - - double factor = 1; - if (upper < lower) - { - factor = -1; - (lower, upper) = (upper, lower); - } - - double sum = 0; - for (double x = lower; x <= upper; x += step) - { - sum += e(x) * step; - } - - return sum * factor; - } + Graph(equation.Integrate()); } private void MenuMiscCaches_Click(object? sender, EventArgs e) diff --git a/Base/Graphables/Equation.cs b/Base/Graphables/Equation.cs index 50864f1..fa8859a 100644 --- a/Base/Graphables/Equation.cs +++ b/Base/Graphables/Equation.cs @@ -48,6 +48,8 @@ public class Equation : Graphable return lines; } + public IntegralEquation Integrate() => new(this); + public EquationDelegate GetDelegate() => equ; public override void EraseCache() => cache.Clear(); diff --git a/Base/Graphables/IntegralEquation.cs b/Base/Graphables/IntegralEquation.cs new file mode 100644 index 0000000..52ff821 --- /dev/null +++ b/Base/Graphables/IntegralEquation.cs @@ -0,0 +1,133 @@ +using Graphing.Forms; +using Graphing.Parts; +using System; +using System.Collections.Generic; + +namespace Graphing.Graphables; + +public class IntegralEquation : Graphable +{ + protected readonly Equation baseEqu; + protected readonly EquationDelegate baseEquDel; + + public IntegralEquation(Equation baseEquation) + { + string oldName = baseEquation.Name, newName; + if (oldName.StartsWith("Integral of ")) newName = "Second Integral of " + oldName[12..]; + else if (oldName.StartsWith("Second Integral of ")) newName = "Third Integral of " + oldName[19..]; + else newName = "Integral of " + oldName; + + Name = newName; + + baseEqu = baseEquation; + baseEquDel = baseEquation.GetDelegate(); + } + + public override Graphable DeepCopy() => new IntegralEquation(baseEqu); + + public override IEnumerable GetItemsToRender(in GraphForm graph) + { + const int step = 10; + double epsilon = Math.Abs(graph.ScreenSpaceToGraphSpace(new Int2(0, 0)).x + - graph.ScreenSpaceToGraphSpace(new Int2(step / 2, 0)).x) / 5; + epsilon *= graph.DpiFloat / 192; + List lines = []; + + Int2 originLocation = graph.GraphSpaceToScreenSpace(new Float2(0, 0)); + + if (originLocation.x < 0) + { + // Origin is off the left side of the screen. + // Get to the left side from the origin. + double previousY = 0; + double start = graph.MinVisibleGraph.x, end = graph.MaxVisibleGraph.x; + for (double x = 0; x <= start; x += epsilon) previousY += baseEquDel(x) * epsilon; + + // Now we can start. + double previousX = start; + + for (double x = start; x <= end; x += epsilon) + { + double currentX = x, currentY = previousY + baseEquDel(x) * epsilon; + lines.Add(new GraphLine(new Float2(previousX, previousY), new Float2(currentX, currentY))); + + previousX = currentX; + previousY = currentY; + } + } + else if (originLocation.x > graph.ClientRectangle.Width) + { + // Origin is off the right side of the screen. + // Get to the right side of the origin. + double previousY = 0; + double start = graph.MaxVisibleGraph.x, end = graph.MinVisibleGraph.x; + for (double x = 0; x >= start; x -= epsilon) previousY -= baseEquDel(x) * epsilon; + + // Now we can start. + double previousX = start; + + for (double x = start; x >= end; x -= epsilon) + { + double currentX = x, currentY = previousY - baseEquDel(x) * epsilon; + lines.Add(new GraphLine(new Float2(previousX, previousY), new Float2(currentX, currentY))); + + previousX = currentX; + previousY = currentY; + } + } + else + { + // Origin is on-screen. + // We need to do two cycles. + + // Start with right. + double start = 0, end = graph.MaxVisibleGraph.x; + double previousX = start; + double previousY = 0; + + for (double x = start; x <= end; x += epsilon) + { + double currentX = x, currentY = previousY + baseEquDel(x) * epsilon; + lines.Add(new GraphLine(new Float2(previousX, previousY), new Float2(currentX, currentY))); + + previousX = currentX; + previousY = currentY; + } + + // Now do left. + start = 0; + end = graph.MinVisibleGraph.x; + previousX = start; + previousY = 0; + + for (double x = start; x >= end; x -= epsilon) + { + double currentX = x, currentY = previousY - baseEquDel(x) * epsilon; + lines.Add(new GraphLine(new Float2(previousX, previousY), new Float2(currentX, currentY))); + + previousX = currentX; + previousY = currentY; + } + } + + return lines; + } + + public Equation AsEquation() => new(GetIntegralAtPoint); + + // Standard integral method. + // Inefficient for successive calls. + public double GetIntegralAtPoint(double x) + { + EquationDelegate equ = baseEqu.GetDelegate(); + + double start = Math.Min(0, x), end = Math.Max(0, x); + const double step = 1e-3; + double sum = 0; + + for (double t = start; t <= end; t += step) sum += equ(t) * step; + if (x < 0) sum = -sum; + + return sum; + } +} diff --git a/Testing/Program.cs b/Testing/Program.cs index d0253c6..036d4ee 100644 --- a/Testing/Program.cs +++ b/Testing/Program.cs @@ -16,10 +16,8 @@ internal static class Program GraphForm graph = new("One Of The Graphing Calculators Of All Time"); - Equation equ = new(x => Math.Sin(x)); - SlopeField sf = new(2, (x, y) => Math.Cos(x)); - TangentLine tl = new(2, 2, equ); - graph.Graph(equ, sf, tl); + Equation equ = new(Math.Sin); + graph.Graph(equ); // You can preload graphs in by going Misc > Preload Cache. // Keep in mind this uses more memory than usual and can take