It has come to the point in my program now where I have 5 swinging pendulums that are all modified at once by slider values. These values are drawn onto the from and passed through the class pendulum. To change the values the update button is run, and their is a default option to use a setup that works. The values are also shown on screen through some simple labels. The zero button sets all values (except length) to zero.
Now my next task is to make the pendulums collide "realistically" and recreate a newtons cradle type effect. I've looked into elastic collision solutions on particle e.g. here is an excellent example: Ball to Ball Collision - Detection and Handling
I've also looked at the raw physics behind it here but my brain couldn't get to grips with the raw physics side of things.
So I'm wondering if anybody could help me figuring out the theory for the collisions.
My current idea is when the co-ordinates meet the velocity will simply change polarity e.g. +10 to -10 in the opposing pendulum. Am I correct here?
Here is my Pendulum Class:
class Pendulum
{
//all public static so they are actually global (can be used between classes and not just global to this class).
public static int length = 10;//length of arm /** can't be less than 0 or will break.
public static double angle = 0; //pendulums arm angle
public static double aAcc = 0.00; //Angle acceleration
public static double aVel = 0.00; //anglular velocity
public static double damping = 0.000; //friction //friction
public static double gravity = 0.0; //make gravity a constant
int originX = 0;
int originY = 0; //allways 0
int bobX; // = frmWidth / 2;
int bobY; // = (int)length; Drawn in pendulm as don't really need to be global. Are currently for flexibilty.
Timer timer; //global for flexibility
public Pendulum(int frmWidth, int frmHeight)
{
timer = new Timer() { Interval = 30 };
timer.Tick += delegate(object sender, EventArgs e)
{
//-----------------drawing variables------------------------------//
originX = frmWidth / 2;
originY = 0;
//to be relative to origin we go:
bobX = originX + (int)(Math.Sin(angle) * length);
bobY = originY + (int)(Math.Cos(angle) * length);
//gravity
aAcc = (-1 * gravity / length) * Math.Sin(angle); //calculate acceleration
aVel += aAcc;//increment velcocity
angle += aVel;//incrment angle
aVel *= damping;//friction action, pendulum eventually 0's
};
timer.Start();
}
public void DrawPendulum(Graphics g)
{
g.DrawLine(Pens.Black, originX, originY, bobX, bobY);
g.FillEllipse(Brushes.Red, bobX - 8, bobY, 20, 20); //-8 to make it look more central!
}
}
And here is the form code:
public partial class frmPendulum : Form
{
private Timer timer;
private Pendulum p1 = null;
private Pendulum p2 = null;
private Pendulum p3 = null;
private Pendulum p4 = null;
private Pendulum p5 = null;
public frmPendulum()
{
InitializeComponent();
this.Shown += new EventHandler(frmPendulum_Shown);
this.Paint += new PaintEventHandler(frmPendulum_Paint);
}
void frmPendulum_Shown(object sender, EventArgs e)
{
p1 = new Pendulum(this.ClientRectangle.Width, this.ClientRectangle.Height);
p2 = new Pendulum(this.ClientRectangle.Width + 40, this.ClientRectangle.Height);
p3 = new Pendulum(this.ClientRectangle.Width - 40, this.ClientRectangle.Height);
p4 = new Pendulum(this.ClientRectangle.Width - 80, this.ClientRectangle.Height);
p5 = new Pendulum(this.ClientRectangle.Width + 80, this.ClientRectangle.Height);
timer = new Timer() { Interval = 100 };
timer.Tick += delegate(object s2, EventArgs e2)
{
this.Refresh();
Value.Text = string.Format("Length: " + Pendulum.length + "{0}Angle: " + Pendulum.angle + "{0}Acc: " + Pendulum.aAcc + "{0}Vel: " + Pendulum.aVel + "{0}Damping: " + Pendulum.damping + "{0}Gravity: " + Pendulum.gravity, Environment.NewLine);
value2.Text = string.Format("Length: " + tbrLength.Value + "{0}Angle: " + ((double)tbrAngle.Value) / 100.0 + "{0}Vel: " + ((double)tbrAVel.Value) / 100.0 + "{0}Damping: " + ((double)tbrDamp.Value) / 100.0 + "{0}Gravity: " + ((double)tbrGrav.Value) / 100.0, Environment.NewLine);
};
timer.Start();
}
void frmPendulum_Paint(object sender, PaintEventArgs e)
{
switch (tbrNoOfPend.Value)
{
case 1:
if (p1 != null) //if used because the Paint() event could occur BEFORE "p1"etc. has been instantiated.
{
p1.DrawPendulum(e.Graphics);
}
break;
case 2:
if (p2 != null)
{
p2.DrawPendulum(e.Graphics);
}
goto case 1;
case 3:
if (p3 != null)
{
p3.DrawPendulum(e.Graphics);
}
goto case 2;
case 4:
if (p4 != null)
{
p4.DrawPendulum(e.Graphics);
}
goto case 3;
case 5:
if (p5 != null)
{
p5.DrawPendulum(e.Graphics);
}
goto case 4;
default:
break;
}
}
private void btnDefault_Click(object sender, EventArgs e)
{
//sets values to a good calibration by default.
Pendulum.length = 50;
Pendulum.angle = Math.PI / 2; //pendulums arm angle
Pendulum.aAcc = 0.00; //Angle acceleration
Pendulum.aVel = 0.00; //anglular velocity
Pendulum.damping = 0.995; //friction //friction
Pendulum.gravity = 0.4; //make gravity a constant
UpdateSliders();
}
private void UpdateValues()
{
/** The trackbars only use integer values so to increment in decimals certain values have to be divided by 100 so they are correct in the simulation.
* For example is I want the gravity to be 0.4 my track bars value will have to be 40 / 100 = 0.40
* Another example will be angle that will go from -3 to 3 incrementing in decimals we go from -300 to 300 /100 = 3 **/
Pendulum.length = tbrLength.Value; //length is ok as it is an integer
Pendulum.angle = ((double)tbrAngle.Value) / 100.0; //both numerator and denominator must be of the same type.
// acceleration is calculated so isn't sent
Pendulum.aVel = ((double)tbrAVel.Value) / 100.0;
Pendulum.damping = ((double)tbrDamp.Value) / 100.0;
Pendulum.gravity = ((double)tbrGrav.Value) / 100.0;
}
private void UpdateSliders()
{
tbrLength.Value = Pendulum.length;
tbrAngle.Value = (int)(Pendulum.angle * 100.0); //pendulums arm angle
//tbrAAcc.Value = (int) Pendulum.aAcc; //Removed acceleration as it is re-calculated anyway.
tbrAVel.Value = (int)(Pendulum.aVel* 100.0); //anglular velocity
tbrDamp.Value = (int)(Pendulum.damping * 100.0); //friction //friction
tbrGrav.Value = (int)(Pendulum.gravity * 100.0); //make gravity a constant
}
private void btnUpdateValues_Click(object sender, EventArgs e)
{
UpdateValues();
//this.Shown += new EventHandler(frmPendulum_Shown);
//this.Paint += new PaintEventHandler(frmPendulum_Paint);
}
private void button1_Click(object sender, EventArgs e)
{
//zero's everything except length.
Pendulum.angle = 0; //pendulums arm angle
Pendulum.aAcc = 0.00; //Angle acceleration
Pendulum.aVel = 0.00; //anglular velocity
Pendulum.damping = 0; //friction //friction
Pendulum.gravity = 0; //make gravity a constant
UpdateSliders();
}
}