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;
}
In HandleCollision, when a collision is detected, move the pendulums so that they are touching, but not overlapping, in addition to changing the velocities.