Question

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!
    }
}
Was it helpful?

Solution

Don't create all pendulums in the beginning. The number of pendulums created and the maximum value for the tbrPend slider should be affected by the actual value of the tbrNoOfPend slider.

I would suggest to use a List<Pendulum> and get rid of the variables p1 to p5.

private List<Pendulum> pendulums;

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.ClientRectangle.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();
}

Now the Paint event handler and the effectSelectedPendulum method become much nicer:

void frmPendulum_Paint(object sender, PaintEventArgs e)
{
    foreach (Pendulum pendulum in pendulums)
    {
        pendulum.DrawPendulum(e.Graphics);
    }
}

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

The only thing you have to add is a handler to the ValueChanged event of the tbrNoOfPend slider. There you update the maximum of the tbrPend slider and create or remove the pendulumns respectively:

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));
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top