Question

I have a problem with my current Pendulum simulation where two pendulums are colliding and they occasionally get stuck inside each over.
This occurs when they hit each over too quickly when they are drawn on one iteration.
Here is the code for Form1::

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private bool running = false;
        private int updateInterval = 15;
        private List<Pendulum> pendulums;
        private Graphics graphics;
    private int userID;

    public Form1(int UserID)
    {
        userID = UserID;
        InitializeComponent();

        graphics = this.CreateGraphics();

        //Pendulum setup
        Pendulum.Gravity = 9.81;
        Pendulum.SimSpeed = 0.005;

        tbrNoOfPendulums.Value = 5;
        pendulums = new List<Pendulum>(tbrNoOfPendulums.Maximum);
        for (int i = 0; i < tbrNoOfPendulums.Maximum; i++)
        {
            pendulums.Add(new Pendulum(this.Width + i * 40 - 80, (int)nudLength.Value));
        }


        Task t = new Task(new Action(UpdatePendulums));
        t.Start();

    }

    private void btnRun_Click(object sender, EventArgs e)
    {
        running = !running;
        if (running)
        {
            btnRun.Text = "Running";
            tbrNoOfPendulums.Enabled = false;
        }
        else
        {
            btnRun.Text = "Paused";
            tbrNoOfPendulums.Enabled = true;
        }
    }

    private void UpdatePendulums()
    {
        while (true)
        {

            System.Threading.Thread.Sleep(updateInterval);
            if (!running) continue;

            //Update pendulums
            foreach (Pendulum p in pendulums)
            {
                p.Update();
            }

            //Detect Collision
            for (int i = 0; i < pendulums.Count-1; i++)
            {
                pendulums[i].HandleCollision(pendulums[i+1]);
            }

            foreach (Pendulum p in pendulums)
            {
                p.Angle += p.Velocity * Pendulum.SimSpeed;
            }


            //Draw pendulums
            try
            {
                graphics.Clear(this.BackColor);
            }
            catch { }

            foreach (Pendulum p in pendulums)
            {
                p.Draw(graphics);
            }
        }
    }

    private void btnSet_Click(object sender, EventArgs e)
    {
        int index;

        //which pendulum side
        if (rdbLeft.Checked)
        {
            index = 0;
        }
        else
        {
            index = tbrNoOfPendulums.Value - 1;
        }

        //update angle
        double angle = (double)nudAngle.Value;
        //Convert from degrees to rads
        angle /= 180;
        angle *= Math.PI;
        pendulums[index].Angle = angle;

        //update velocity
        pendulums[index].Velocity = (double)nudVelocity.Value;

        //update damping
        pendulums[index].damping = (double)nudDamping.Value;

        //update gravity
        Pendulum.Gravity = (double)nudGravity.Value;
    }

    private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        //stops simulation running before closing to avoid "A generic error occurred in GDI+."
        running = !running;
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void rdbLeft_CheckedChanged(object sender, EventArgs e)
    {
        nudAngle.Minimum = -179;
        nudAngle.Maximum = 0;
    }

    private void rdbRight_CheckedChanged(object sender, EventArgs e)
    {
        nudAngle.Minimum = 0;
        nudAngle.Maximum = 179;
    }

    private void tbrNoOfPendulums_Scroll(object sender, EventArgs e)
    {
            if (tbrNoOfPendulums.Value < pendulums.Count)
            {
                pendulums.RemoveRange(tbrNoOfPendulums.Value, pendulums.Count - tbrNoOfPendulums.Value);
                return;
            }

            if (tbrNoOfPendulums.Value > pendulums.Count)
            {
                for (int i = pendulums.Count; i < tbrNoOfPendulums.Value; i++)
                {
                    pendulums.Add(new Pendulum(this.Width + i * 40 - 80, (int)nudLength.Value));
                }
            }
    }
}

Pendulum Class:

class Pendulum
{
    public int bobX;
    public int bobY;
    /* -- Statics -- */
    //Attributes
    public static double Gravity { get; set; }
    public static double SimSpeed { get; set; }

    /* -- Non statics -- */
    //Attributes
    public int Length { get; set; } 
    public int Mass { get; set; }
    public double Angle { get; set; }
    public double Velocity { get; set; }
    public int frmWidth { get; set; }

    //Fields for attributes
    public double damping;
    //Other fields
    public int originX;
    public int originY = 0;

    public Pendulum(int frmwidth, int length)
    {
        frmWidth = frmwidth;
        Mass = 20;
        Length = length;
        Angle = 0;
    }

    public void Update()
    {
        if (Angle != 0 || Velocity != 0)
        {
            double aAcc = -Gravity * Math.Sin(Angle) / Length;
            Velocity += aAcc;
        }
    }

    public void Draw(Graphics g)
    {
        originX = frmWidth / 2;
        originY = 0;
         bobX = originX + (int)(Math.Sin(Angle) * Length);
         bobY = originY + (int)(Math.Cos(Angle) * Length);

         try
         {
             g.DrawLine(Pens.Red, originX, originY, bobX, bobY);
             g.FillEllipse(Brushes.Black, bobX - 8, bobY, 20, 20);
         }
         catch { }
    }

    internal void HandleCollision(Pendulum other)
    {
         originX = frmWidth / 2;
         originY = 0;
        bobX = originX + (int)(Math.Sin(Angle) * Length);
         bobY = originY + (int)(Math.Cos(Angle) * Length);
         if (other.bobX-bobX  < 20)
         {
             double temp = Velocity;
             Velocity = other.Velocity;
             other.Velocity = temp;


         }
    }
}

UPDATE - EDIT: Here is just the collision method:

   if (Math.Abs(other.bobX-bobX ) < 20) //Detects if pendulums intersect
         {
             do //attempt at separating
             {
                 other.bobX += 1;
                 bobX -= 1;
             }
             while (!(Math.Abs(other.bobX - bobX) > 20));

             double temp = Velocity;
             Velocity = other.Velocity;
             other.Velocity = temp;


         }
Was it helpful?

Solution

In HandleCollision, when a collision is detected, move the pendulums so that they are touching, but not overlapping, in addition to changing the velocities.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top