1

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

}
Community
  • 1
  • 1
Corey Ford
  • 178
  • 1
  • 13

1 Answers1

1

Alright so it seems that the code aspect of things you can puzzle out for yourself and to be honest I really am not a c# expert, but I will try to handle the physics aspects of this.

What's cool about a newton's cradle is that regardless of how many balls there are between the two end pieces, energy will be conserved or at least conserved approximately. Before you go any further you need to decide how realistic you want this to be. As we all know, a real Newton's cradle wont swing forever, but determining how much energy is lost on each collision is a very complicated physics problem.

If you want to determine how much energy is lost on each collision you're going to need so many extra details and then need to calculate that experimentally by actually measuring the heights of pendulums and all sorts of things.

From here on I will assume that you are talking about perfectly elastic collisions in which both energy and momentum are conserved.

When dealing with elastic collisions there's a few things you need to know:

M1*V1 = M2*V2 according to momentum; M is mass, V is velocity
E_kinetic = 1/2*m*v^2 
E_potential = mgh where m = mass, g = acceleration due to gravity, and h = height

Now for some pendulum theory:

When you pull a pendulum back, you pull it back at some angle theta, but that angle is only important to determine how high you have raised the pendulum in the y direction. The length of your pendulum is very important, as in the length of the string supporting the mass. Once you have figured out how high you have raised the pendulum, calculated using theta or just knowing the y displacement, you can calculate the speed of the pendulum at the bottom of it's swing, which in a newton's cradle is where the pendulum will contact the next ball.

To calculate the height of the pendulum using theta you multiply the length of the pendulum by the cosine of theta and then subtract that from the length of the pendulum. Or in math:

l = length of pendulum
theta = angle of the pulled back pendulum relative to the vertical axis
h = height
now actual calculations:
h = l - l*cos(theta)

With that h in hand we can now determine the speed of the pendulum at the bottom of its path using the energy formulas from before.

Since energy is conserved in this system and the ball is at rest when we pull it back, we know that it has its maximum potential energy at the top of its pulled back path and doesnt have any kinetic energy when we've pulled it back since it's velocity is zero while we're holding it.

KE = PE
PE = m * g * h
KE = 1/2 * m * v^2
m * g * h = 1/2 * m * v^2
v cancels
g * h = 1/2 * v^2
v = sqrt(2 * g * h)
(g = 9.8 m/s^2 or 32 f/s^2)

now that you have v you can figure out how other pendulums of different mass will react to being struck by your first one.

The thing that is boring about this in terms of it being a program is that the middle balls in the pendulum dont matter if you're assuming elastic collisions.

Additionally, the pendulum at the other end of the cradle will swing to the exact height that you pulled the first pendulum to. Though in reality there will be some energy loss. If you want to figure out how much energy is lost in a somewhat cheating way you can look at how many times a newtons cradle will click before it stops moving.

Lets say the system starts with 100 joules of energy and it takes 100 times of the balls colliding before they stop moving, you know that you are losing about 1 joule of energy every time they collide. The systems starting energy can be calculated using either of the energy formulas, though you have to remember to add mass back in.

Once you know how much energy is lost every collision you work those formulas in reverse to see how high the other side will go up.

Calculate the kinetic energy
subtract your calculated loss
plug into the potential energy formula
determine the height from the potential energy formula
Nick Chapman
  • 4,402
  • 1
  • 27
  • 41