so last year I wrote a program in C for my course that demonstrated chaos using the Lorenz equations and the Runge Kutta method for solving differential equations.
I decided recently that I wanted to revisit this and to produce a program that was able to draw out the particle trajectories. I got this to successfully work but now want to expand this so that a user can input parameters such as the starting positions of particles, and other parameters (a, b and r in my case). Currently I have the trajectories being drawn as soon as the program runs, but I would like to delay this until a user enters their parameters into some textboxes and then presses a button. To do this I thought I should create a new class and put my current code into there and then call this in the main .cs file under a btn1_Click method. However I am having considerable trouble with this, mainly in not really knowing how to go about it. In my best attempt so far I have an error do to with a line involving a "createGraphics()" which is that there is no definition for it in the class file. I have all the same references at the top in the using part at the top of the class as are in the main file where it worked fine.
Also if anyone can give me any feedback about my code (ie any bad practices or places where I've overcomplicated things) or any suggestions to make it better I'd be very grateful, and if you would need any more info from me to help then I shall do my best to answer!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Lorenz_chaos
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
double a = 10, b = (8 / 3), r = 28; //standard values for lorenz model
/*m defines the number of iterations of the for loop so the number of lines drawn
good idea to keep m inversely proportional to dt (the time interval). A smaller dt will
mean smaller lines so smoother overall drawing m=50000 and dt=0.0005 is a good starting point
that demonstrates chaos well*/
double m = 500000, dt = 0.00005;
//EVOLUTION VALUE FOR RUNGE_KUTTA METHOD
//values for first particle
double y11, y12, y13;
double y21, y22, y23;
double y31, y32, y33;
double y41, y42, y43;
double y51, y52, y53;
double xi, yi, xf, yf; //coordinates for drawing particle 1 trajectory
double f10, f11, f12, f13; //function values to be calculated,
double f20, f21, f22, f23; //for fxy (x>1) these are intermediate fn calculations at different
double f30, f31, f32, f33; //times in Runga Kutta
//values for second particle
double z11, z12, z13;
double z21, z22, z23;
double z31, z32, z33;
double z41, z42, z43;
double z51, z52, z53;
double ai, bi, af, bf; //coordinates for drawing particle 2 trajectory (these are badly named...)
double g10, g11, g12, g13; //equivalent of f values for particle 2
double g20, g21, g22, g23;
double g30, g31, g32, g33;
//OTHER NEEDED QUANTITIES
int i; //for loop iteration integer
int k1 = 20; //scaling factors to make drawing fill form
int k2 = 9;
int y0 = 450; //offset values to centre drawing on form
int x0 = 550;
int start = 10; //starting position for calculations
double diff = 0.01;//initial displacement between two particles
//starting positions for particles
y11 = start;//particle 1
y12 = start;
y13 = start;
z11 = start + diff;//particle 2
z12 = start + diff;
z13 = start + diff;
//initial coords for particles at t=0
xi = (y11) * k1 + x0;
yi = (y12) * k2 + y0;
ai = (z11) * k1 + x0;
bi = (z12) * k2 + y0;
for (i = 1; i <= m; i++)
{
f10 = a * (y12 - y11);
f20 = r * y11 - y12 - y11 * y13;
f30 = y11 * y12 - b * y13;
y21 = y11 + f10 * dt / 2; //finding y1 y2 y3 at the first
y22 = y12 + f20 * dt / 2; //fraction of dt
y23 = y13 + f30 * dt / 2;
f11 = a * (y22 - y21);
f21 = r * y21 - y22 - y21 * y23;
f31 = y21 * y22 - b * y23;
y31 = y11 + f11 * dt / 2; //finding y1 y2 y3 at the second
y32 = y12 + f21 * dt / 2; //fraction of dt
y33 = y13 + f31 * dt / 2;
f12 = a * (y32 - y31);
f22 = r * y31 - y32 - y31 * y33;
f32 = y31 * y32 - b * y33;
y41 = y11 + f12 * dt; //finding y1 y2 y3 at the third
y42 = y12 + f22 * dt; //fraction of dt
y43 = y13 + f32 * dt;
f13 = a * (y42 - y41);
f23 = r * y41 - y42 - y41 * y43;
f33 = y41 * y42 - b * y43;
y51 = y11 + (f10 + 2 * f11 + 2 * f12 + f13) * dt / 6; //final y values at y(t+dt)
y52 = y12 + (f20 + 2 * f21 + 2 * f22 + f23) * dt / 6; //then to be repesated in for loop for all steps
y53 = y13 + (f30 + 2 * f31 + 2 * f32 + f33) * dt / 6;
xf = (y51) * k1 + x0;
yf = (y52) * k2 + y0;
//second particle calculation
g10 = a * (z12 - z11);
g20 = r * z11 - z12 - z11 * z13;
g30 = z11 * z12 - b * z13;
z21 = z11 + g10 * dt / 2; //finding y1 y2 y3 at the first
z22 = z12 + g20 * dt / 2; //fraction of dt
z23 = z13 + g30 * dt / 2;
g11 = a * (z22 - z21);
g21 = r * z21 - z22 - z21 * z23;
g31 = z21 * z22 - b * z23;
z31 = z11 + g11 * dt / 2; //finding y1 y2 y3 at the second
z32 = z12 + g21 * dt / 2; //fraction of dt
z33 = z13 + g31 * dt / 2;
g12 = a * (z32 - z31);
g22 = r * z31 - z32 - z31 * z33;
g32 = z31 * z32 - b * z33;
z41 = z11 + g12 * dt; //finding y1 y2 y3 at the third
z42 = z12 + g22 * dt; //fraction of dt
z43 = z13 + g32 * dt;
g13 = a * (z42 - z41);
g23 = r * z41 - z42 - z41 * z43;
g33 = z41 * z42 - b * z43;
z51 = z11 + (g10 + 2 * g11 + 2 * g12 + g13) * dt / 6; //final y values at y(t+dt)
z52 = z12 + (g20 + 2 * g21 + 2 * g22 + g23) * dt / 6; //then to be repesated in for loop for all steps
z53 = z13 + (g30 + 2 * g31 + 2 * g32 + g33) * dt / 6;
af = (z51) * k1 + x0;
bf = (z52) * k2 + y0;
//DRAWING LINE JUST CALCULATED
System.Drawing.Graphics graphicsObj;
graphicsObj = this.CreateGraphics();
Pen myPen = new Pen(System.Drawing.Color.Red, 1);
//myPen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDotDot;
graphicsObj.DrawLine(myPen, (int)xi, (int)yi, (int)xf, (int)yf);
myPen.Color = System.Drawing.Color.RoyalBlue;
graphicsObj.DrawLine(myPen, (int)ai, (int)bi, (int)af, (int)bf);
//REDEFINING COORDS AND VALUES FOR NEXT LOOP
//first particle
xi = (y51) * k1 + x0;
yi = (y52) * k2 + y0;
y11 = y51;
y12 = y52;
y13 = y53;
//second particle
ai = (z51) * k1 + x0;
bi = (z52) * k2 + y0;
z11 = z51;
z12 = z52;
z13 = z53;
/*even at 1 the below makes the program far too slow, need an alternative
intention was for it to allow user to see the particle trajectories better*/
//System.Threading.Thread.Sleep(1);
}
}
}
}
Put your code of the Form1_Paint
in a separate method, for instance DrawLorenzChaos(Graphics graphicsObj , double a, double b, double r)
. When you set the parameters in your form, just set some bool
value to true
. Check the code
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(startDrawing)
DrawLorenzChaos(e.Graphics, aVal, bVal, rVal);
}
Also, in the DrawLorenzChaos method just remove these two lines:
System.Drawing.Graphics graphicsObj;
graphicsObj = this.CreateGraphics();
EDIT: You can try this code just to start with and you can gradually improve it, this is how I would do it (I would add better synchronization but basically this is it). In order to try the code you will need one button and one PictureBox with size(1000,1000). Note that I changed the starting location a little bit.
Basically here a separate thread draws the Lorenz Chaos on the Bitmap. That Bitmap is drawn on the PictureBox in UI thread after each line is drawn in a separate thread. You have Mutex to control the accessing the Bitmap.
public partial class Form1 : Form
{
Bitmap offScrBuff;
Mutex mut;
int index = 0;
public Form1()
{
InitializeComponent();
offScrBuff = new Bitmap(1000, 1000);
mut = new Mutex();
pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
button1.Click += new System.EventHandler(this.button1_Click);
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
mut.WaitOne();
e.Graphics.DrawImage(offScrBuff, 0, 0);
mut.ReleaseMutex();
}
void DrawLorenzChaos(double a, double b, double r)
{
//double a = 10, b = (8.0 / 3.0), r = 28; //standard values for lorenz model
/*m defines the number of iterations of the for loop so the number of lines drawn
good idea to keep m inversely proportional to dt (the time interval). A smaller dt will
mean smaller lines so smoother overall drawing m=50000 and dt=0.0005 is a good starting point
that demonstrates chaos well*/
double m = 500000, dt = 0.00005;
//EVOLUTION VALUE FOR RUNGE_KUTTA METHOD
//values for first particle
double y11, y12, y13;
double y21, y22, y23;
double y31, y32, y33;
double y41, y42, y43;
double y51, y52, y53;
double xi, yi, xf, yf; //coordinates for drawing particle 1 trajectory
double f10, f11, f12, f13; //function values to be calculated,
double f20, f21, f22, f23; //for fxy (x>1) these are intermediate fn calculations at different
double f30, f31, f32, f33; //times in Runga Kutta
//values for second particle
double z11, z12, z13;
double z21, z22, z23;
double z31, z32, z33;
double z41, z42, z43;
double z51, z52, z53;
double ai, bi, af, bf; //coordinates for drawing particle 2 trajectory (these are badly named...)
double g10, g11, g12, g13; //equivalent of f values for particle 2
double g20, g21, g22, g23;
double g30, g31, g32, g33;
//OTHER NEEDED QUANTITIES
int i; //for loop iteration integer
int k1 = 20; //scaling factors to make drawing fill form
int k2 = 9;
int y0 = 280; //offset values to centre drawing on form
int x0 = 400;
int start = 10; //starting position for calculations
double diff = 0.01;//initial displacement between two particles
//starting positions for particles
y11 = start;//particle 1
y12 = start;
y13 = start;
z11 = start + diff;//particle 2
z12 = start + diff;
z13 = start + diff;
//initial coords for particles at t=0
xi = (y11) * k1 + x0;
yi = (y12) * k2 + y0;
ai = (z11) * k1 + x0;
bi = (z12) * k2 + y0;
for (i = 1; i <= m; i++)
{
f10 = a * (y12 - y11);
f20 = r * y11 - y12 - y11 * y13;
f30 = y11 * y12 - b * y13;
y21 = y11 + f10 * dt / 2; //finding y1 y2 y3 at the first
y22 = y12 + f20 * dt / 2; //fraction of dt
y23 = y13 + f30 * dt / 2;
f11 = a * (y22 - y21);
f21 = r * y21 - y22 - y21 * y23;
f31 = y21 * y22 - b * y23;
y31 = y11 + f11 * dt / 2; //finding y1 y2 y3 at the second
y32 = y12 + f21 * dt / 2; //fraction of dt
y33 = y13 + f31 * dt / 2;
f12 = a * (y32 - y31);
f22 = r * y31 - y32 - y31 * y33;
f32 = y31 * y32 - b * y33;
y41 = y11 + f12 * dt; //finding y1 y2 y3 at the third
y42 = y12 + f22 * dt; //fraction of dt
y43 = y13 + f32 * dt;
f13 = a * (y42 - y41);
f23 = r * y41 - y42 - y41 * y43;
f33 = y41 * y42 - b * y43;
y51 = y11 + (f10 + 2 * f11 + 2 * f12 + f13) * dt / 6; //final y values at y(t+dt)
y52 = y12 + (f20 + 2 * f21 + 2 * f22 + f23) * dt / 6; //then to be repesated in for loop for all steps
y53 = y13 + (f30 + 2 * f31 + 2 * f32 + f33) * dt / 6;
xf = (y51) * k1 + x0;
yf = (y52) * k2 + y0;
//second particle calculation
g10 = a * (z12 - z11);
g20 = r * z11 - z12 - z11 * z13;
g30 = z11 * z12 - b * z13;
z21 = z11 + g10 * dt / 2; //finding y1 y2 y3 at the first
z22 = z12 + g20 * dt / 2; //fraction of dt
z23 = z13 + g30 * dt / 2;
g11 = a * (z22 - z21);
g21 = r * z21 - z22 - z21 * z23;
g31 = z21 * z22 - b * z23;
z31 = z11 + g11 * dt / 2; //finding y1 y2 y3 at the second
z32 = z12 + g21 * dt / 2; //fraction of dt
z33 = z13 + g31 * dt / 2;
g12 = a * (z32 - z31);
g22 = r * z31 - z32 - z31 * z33;
g32 = z31 * z32 - b * z33;
z41 = z11 + g12 * dt; //finding y1 y2 y3 at the third
z42 = z12 + g22 * dt; //fraction of dt
z43 = z13 + g32 * dt;
g13 = a * (z42 - z41);
g23 = r * z41 - z42 - z41 * z43;
g33 = z41 * z42 - b * z43;
z51 = z11 + (g10 + 2 * g11 + 2 * g12 + g13) * dt / 6; //final y values at y(t+dt)
z52 = z12 + (g20 + 2 * g21 + 2 * g22 + g23) * dt / 6; //then to be repesated in for loop for all steps
z53 = z13 + (g30 + 2 * g31 + 2 * g32 + g33) * dt / 6;
af = (z51) * k1 + x0;
bf = (z52) * k2 + y0;
//DRAWING LINE JUST CALCULATED
mut.WaitOne();
System.Drawing.Graphics graphicsObj = Graphics.FromImage(offScrBuff);
graphicsObj.DrawLine(Pens.Red, (int)xi, (int)yi, (int)xf, (int)yf);
graphicsObj.DrawLine(Pens.RoyalBlue, (int)ai, (int)bi, (int)af, (int)bf);
graphicsObj.Dispose();
mut.ReleaseMutex();
pictureBox1.Invalidate();
//REDEFINING COORDS AND VALUES FOR NEXT LOOP
//first particle
xi = (y51) * k1 + x0;
yi = (y52) * k2 + y0;
y11 = y51;
y12 = y52;
y13 = y53;
//second particle
ai = (z51) * k1 + x0;
bi = (z52) * k2 + y0;
z11 = z51;
z12 = z52;
z13 = z53;
/*even at 1 the below makes the program far too slow, need an alternative
intention was for it to allow user to see the particle trajectories better*/
//System.Threading.Thread.Sleep(1);
}
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => { DrawLorenzChaos(10.0, 8.0 / 3.0, 28); });
}
}