I have a problem with my Pendulum simulation currently.
In my program you choose the pendulum you want to modify the values of via a slider and then update that pendulum to the users chosen properties on the click of an update button.
There is also a default and zero button, both modifying the values for that pendulum. Length gets changed on each timer tick so that its the same universally.
There is also a second slider choosing how many pendulums (1 to 5) are being shown on the screen.
The issue is that other pendulums aren't visible on the screen.
Here is my code...
frmPendulm:
public partial class frmPendulum : Form
{
private Timer timer;
private List<Pendulum> pendulums;
//all public static so they are actually global (can be used between classes and not just global to this class, mock flexibility).
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
public frmPendulum()
{
InitializeComponent();
this.Shown += new EventHandler(frmPendulum_Shown);
this.Paint += new PaintEventHandler(frmPendulum_Paint);
}
void frmPendulum_Shown(object sender, EventArgs e)
{
//initialize the range for tbrPend
tbrPend.Maximum = tbrNoOfPend.Value;
pendulums = new List<Pendulum>(tbrNoOfPend.Maximum);
for (int i = 0; i < tbrNoOfPend.Value; i++)
{
pendulums.Add(new Pendulum(this.Width + i * 40, this.Height, 0,0,0,0,0));
}
timer = new Timer() { Interval = 100 };
timer.Tick += delegate(object s2, EventArgs e2)
{
this.SuspendLayout();
this.Refresh();
this.ResumeLayout();
Pendulum.length = tbrLength.Value; //means length is changed on all pendulums
updateValueVisuals();
};
timer.Start();
}
public void updateValueVisuals()
{
lblLength.Text= ("Length: " + Pendulum.length);
lblAngle.Text = ("Angle: " + ((double)tbrAngle.Value) / 100.0);
lblVel.Text = ("Vel: " + ((double)tbrAVel.Value) / 100.0);
lblDamp.Text = ("Damping: " + ((double)tbrDamp.Value) / 100.0);
lblGrav.Text = ("Gravity: " + ((double)tbrGrav.Value) / 100.0);
}
void frmPendulum_Paint(object sender, PaintEventArgs e)
{
foreach (Pendulum pendulum in pendulums)
{
pendulum.DrawPendulum(e.Graphics);
}
}
private void btnDefault_Click(object sender, EventArgs e)
{
//sets values to a good calibration by default.
Pendulum.length = 50;
angle = Math.PI / 2;
aAcc = 0.00;
aVel = 0.00;
damping = 0.995;
gravity = 0.4;
UpdateSliders();
effectSelectedPendulum(aAcc, aVel, damping, angle, gravity);
}
private void UpdateSliders()
{
tbrLength.Value = Pendulum.length;
tbrAngle.Value = (int)(angle * 100.0);
//tbrAAcc.Value = (int) Pendulum.aAcc; //Removed acceleration as it is re-calculated anyway.
tbrAVel.Value = (int)(aVel* 100.0);
tbrDamp.Value = (int)(damping * 100.0);
tbrGrav.Value = (int)(gravity * 100.0);
}
private void btnUpdateValues_Click(object sender, EventArgs e)
{
/** The trackbars only use ilnteger values so to increment in decimals certain vaues 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
angle = ((double)tbrAngle.Value) / 100.0; //both numerator and denominator must be of the same type.
// acceleration is calculated so isn't sent
aVel = ((double)tbrAVel.Value) / 100.0;
damping = ((double)tbrDamp.Value) / 100.0;
gravity = ((double)tbrGrav.Value) / 100.0;
effectSelectedPendulum(aAcc, aVel, damping, angle, gravity);
}
private void button1_Click(object sender, EventArgs e)
{
//zero's everything except length.
Pendulum.length = 10; //can't be 0 must be 10
angle = 0;
aAcc = 0.00;
aVel = 0.00;
damping = 0;
gravity = 0;
UpdateSliders();
effectSelectedPendulum(aAcc, aVel, damping, angle, gravity);
}
private void effectSelectedPendulum(double aAcc, double aVel, double damping, double angle, double gravity)
{
int index = tbrPend.Value - 1;
pendulums[index] = new Pendulum(Width + index * 40, ClientRectangle.Height, aAcc, aVel, damping, angle, gravity);
}
private void tbrNoOfPend_ValueChanged(object sender, EventArgs e)
{
tbrPend.Maximum = tbrNoOfPend.Value;
if (tbrNoOfPend.Value < pendulums.Count)
{
pendulums.RemoveRange(tbrNoOfPend.Value, pendulums.Count - tbrNoOfPend.Value);
return;
}
if (tbrNoOfPend.Value > pendulums.Count)
{
for (int i = pendulums.Count; i < tbrNoOfPend.Value; i++)
{
pendulums.Add(new Pendulum(this.Width + i * 40, this.ClientRectangle.Height, 0, 0, 0, 0, 0));
}
}
}
}
Pendulum Class:
class Pendulum
{
public static int length = 10;//length of arm /** can't be less than 0 or will break.
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.
//public static int returnvalue;
Timer timer; //global for flexibility
public Pendulum(int frmWidth, int frmHeight, double aAcc, double aVel, double damping, double angle, double gravity)
{
timer = new Timer() { Interval = 30 };
originX = frmWidth / 2;
timer.Tick += delegate(object sender, EventArgs e)
{
//-----------------drawing variables------------------------------//
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
};
//returnvalue = string.Format("{0}Length:" + length + ",{0} Angle: " + angle + ",{0}Vel: " + aVel + ",{0}Damping: " + damping + ",{0}Gravity: " + gravity, Environment.NewLine);
timer.Start();
}
public void DrawPendulum(Graphics g)
{
g.DrawLine(Pens.Red, originX, originY, bobX, bobY);
g.FillEllipse(Brushes.Black, bobX - 8 , bobY, 20, 20); //-8 to make it look more central!
}
}