Search code examples
c#graphic

Visual Studio, drawing Lorenz chaos on user clicking button


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);
        }

    }


}
}

Solution

  • 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); });
        }
    }