Vectors (bouncing ball)

The difference between velocity and speed is that velocity has a direction associated with it. Speed is a scalar and velocity is a vector.
So, an object can be moving with a speed of 2 m/s to the right, hit a wall, bounce back, and still be moving at a speed of 2 m/s. However, its velocity would have changed: it would be 2 m/s when moving to the right, but -2 m/s after the bounce when moving to the left.
We can draw an arrow that points in the direction of motion, and scale the length of the arrow to show the magnitude of the vector (the speed in this case).
Notes Code and Results

Creating a ball and an arrow (for its velocity vector) in vpython.

We'll start by creating a simple ball, assigning it a velocity, and draw an arrow to show the vector.
Notice that:
  • Line 1:
    
                      ball = sphere()
                    
    Create a sphere named ball (the default position is (0,0,0) and the default radius is 1.)
  • Line 3:
    
                      ball.v = vector(5, 0, 0)
                    
    Give the ball a velocity that is a vector with x, y, and z components (5, 0, 0). We could have made a separate variable for v, but making it a property of the ball makes more intuitive sense.
  • Line 5:
    
                      ball.arrow = arrow(pos = ball.pos, axis = ball.v)
                    
    Create an arrow. There are a few thing to unpack here.
    • The arrow is also a property of the ball. It does not have to be but it makes sense to tie the two together.
    • The arrow's position is set to the ball's position because the arrow's position property is the location of the arrow's base.
    • The axis of the arrow is the velocity vector. The axis property sets the length and direction of the arrow.

              ball = sphere()

              ball.v = vector(5, 0, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v)
            
The result should look like:
result

Making the Ball Move (Animation)

As we all know, velocity is the change in distance over time. Considering only motion in the x direction (vx): $$ v_x = \dfrac{\Delta x}{\Delta t} $$ We can write the change in distance (Δx) as the difference in the old and new positions: $$ v_x = \dfrac{x_{new} - x_{old}}{\Delta t} $$ Therefore, if we know the velocity (v), the time step (Δt), and the starting position (xold), then we can solve this equation to find the new position (xnew). $$ x_{new} = x_{old} + v_x \cdot \Delta t \tag{1}$$ This could be read as: the new position is equal to the old position plus the change in position.

Making the Ball Move (Animation): The Code

Notice that:
  • Line 9:
    
                      sleep(dt)
                    
    The sleep function tells the program to pause for the specified length of time. Otherwise the ball would move so fast, we would not be able to see the move.
  • Line 10:
    
                      ball.pos.x = ball.pos.x + ball.v.x * dt
                    
    Use the equation above (Eqn. 1) to calculate the new position in the x direction.
  • Line 11:
    
                      ball.arrow.pos = ball.pos
                    
    Move the arrow as well, since it does not move automatically.

              ball = sphere()

              ball.v = vector(5, 0, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v)

              dt = 0.1

              sleep(dt)
              ball.pos.x = ball.pos.x + ball.v.x * dt
              ball.arrow.pos = ball.pos

            
The result should look like this (but only move once):
result

Continuous Motion

To make the ball move continuously, we put the position update (and time sleep delay) into an infinite loop.
Notice that:
  • Line 9:
    
                      while True:
                    
    The while loop tests to see if a condition is True, and continues to loop as long as the condition is True. Here we just tell it to test the built-in True, which is always true, so the loop continues on forever (or until you stop the program).

              ball = sphere()

              ball.v = vector(5, 0, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v)

              dt = 0.1

              while True:
                  sleep(dt)
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.arrow.pos = ball.pos
            
The result should look like this (but continues on forever):
result

Vertical Motion

Thus far, we have only seen horizontal motion. The ball has a component of velocity in the x-direction, but no velocity in any other direction.

The velocities in the 3 orthogonal directions are:

$$ \begin{align} v_x &= 5 \\ v_y &= 0 \\ v_z &= 0 \end{align} $$

So the velocity vector can be written as:

$$ \vec{v} = \: <5, 0, 0> $$

Vertical velocity component: vy

Let's introduce a vertical component to velocity of 2 m/s. Therefore: $$ v_y = 2 $$ and, $$ \vec{v} = \: <5, 2, 0> $$ So, we need to change our program to account for the vertical component.
Notice that:
  • Line 3: Gives the new velocity vector:
    
                      ball.v = vector(5, 2, 0)
                    
  • Line 12: We add the equation to update the ball's y position based on vy.
    
                      ball.pos.y = ball.pos.y + ball.v.y * dt
                    

              ball = sphere()

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v)

              dt = 0.1

              while True:
                  sleep(dt)
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt
                  ball.arrow.pos = ball.pos
            
The result should look like this (but continues on forever):
result

Showing the Component Vectors (vx and vy )

We can draw two vectors to show the components of the main vector in the x and y directions.
Notice that:
  • Line 6: Creates a red arrow for only the horizontal component of velocity (vx)
    
                      ball.vx_arrow = arrow(pos = ball.pos,
                                            axis = vec(ball.v.x, 0, 0),
                                            color=color.red)
                    

              ball = sphere()

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v)
              ball.vx_arrow = arrow(pos = ball.pos, axis = vec(ball.v.x, 0, 0), color=color.red)
              ball.vy_arrow = arrow(pos = ball.pos, axis = vec(0, ball.v.y, 0), color=color.yellow)

              dt = 0.1

              while True:
                  sleep(dt)
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt
                  ball.arrow.pos = ball.pos
            
The result should look like this:
result
You may notice that when the ball moves off the component vectors stay at the origin. It would be nice if you could alter the code so the component vectors move with the ball.

A Little Styling

To make things a little easer to see, we'll:

  1. split the code for the arrows across multiple lines to make it easier to read (e.g. lines 6-9),
  2. make the shaft sizes of all the arrows the same: (shaftwidth = 0.2 ) (on lines 5-13)
  3. reduce the radius of the ball on line 1 (radius = 0.5)

The code should appear more organized:


              ball = sphere()

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v, shaftwidth=0.2)
              ball.vx_arrow = arrow(pos = ball.pos,
                                    axis = vec(ball.v.x, 0, 0),
                                    color=color.red,
                                    shaftwidth=0.2)
              ball.vy_arrow = arrow(pos = ball.pos,
                                    axis = vec(0, ball.v.y, 0),
                                    color=color.yellow,
                                    shaftwidth=0.2)

              dt = 0.1

              while True:
                  sleep(dt)
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt
                  ball.arrow.pos = ball.pos
            
The result should be a little prettier:
result

Summing the Component Vectors

You can graphically add vectors by placing the vectors end-to-end. So where the last vector stops, the new one begins.

We'll do this to sum the component vectors by moving the vy vector to the end of the vx arrow.

Notice that:
  • Line 10: Moves the vy arrow by adding the ball's position to the axis= of the vx arrow.
    
                      ball.vx_arrow = arrow(pos = ball.pos + ball.vx_arrow.axis,
                    

              ball = sphere(radius=0.5)

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v, shaftwidth=0.2)
              ball.vx_arrow = arrow(pos = ball.pos,
                                    axis = vec(ball.v.x, 0, 0),
                                    color=color.red,
                                    shaftwidth=0.2)
              ball.vy_arrow = arrow(pos = ball.pos + ball.vx_arrow.axis,
                                    axis = vec(0, ball.v.y, 0),
                                    color=color.yellow,
                                    shaftwidth=0.2)

              dt = 0.1

              while True:
                  sleep(dt)
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt
                  ball.arrow.pos = ball.pos
            
The result should look like this:
result

Building a Wall

As the ball moves to the right, it seems to get smaller as the field of view zooms out. Let's make it static, but putting in a wall for the ball to bounce off of.

First we draw the wall as a simple, vertical line that's 20 m tall and 10 m from the origin (along the x-axis).

Notice that:
  • Line 2: Draw a line to represent a wall.
    
                      wall = curve(pos=[vec(20,-20,0), vec(20,20,0)])
                    

              ball = sphere(radius=0.5)
              wall = curve(pos=[vec(10,-10,0), vec(10,10,0)])

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v, shaftwidth=0.2)
              ball.vx_arrow = arrow(pos = ball.pos,
                                    axis = vec(ball.v.x, 0, 0),
                                    color=color.red,
                                    shaftwidth=0.2)
              ball.vy_arrow = arrow(pos = ball.pos + ball.vx_arrow.axis,
                                    axis = vec(0, ball.v.y, 0),
                                    color=color.yellow,
                                    shaftwidth=0.2)

              dt = 0.01

              while True:
                  sleep(dt)

                  # update ball position based on velocity vector
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt

                  #update positions of the arrows
                  ball.arrow.pos = ball.pos
                  ball.vx_arrow.pos = ball.pos
                  ball.vy_arrow.pos = ball.pos + ball.vx_arrow.axis
            
The result should look like this, but the ball will not bounce off the wall:
result

Bouncing off the Wall

The ball does not know that there is a wall. We need to write some code to let it know.

Since the wall is a vertical line 10 m away from the origin we can just say:

If the ball hits the wall,
then reverse the x component of the velocity of the ball (vx )

However, we have to be more specific about what the condition is when the ball hits the wall. So, we can recognise that:

If the ball's x position is greater than or equal to 10 m,
then the ball must have hit the wall.

So we combine these two logical statements to set up a bounce:

If the ball's x position is greater than or equal to 10 m,
then reverse the x component of the velocity vector (vx ):

Codewise, this looks like:

              if ball.pos.x >= 10:
                  ball.v.x = -1 * ball.pos.x
            
Note that we also need to update the directions of the arrows (axes in this case) so that they keep track of the actual vectors:

              ball.arrow.axis = ball.v
              ball.vx_arrow.axis = vec(ball.v.x, 0, 0)
              ball.vy_arrow.axis = vec(0, ball.v.y, 0)
            

              ball = sphere(radius=0.5)
              wall = curve(pos=[vec(10,-10,0), vec(10,10,0)])

              ball.v = vector(5, 2, 0)

              ball.arrow = arrow(pos = ball.pos, axis = ball.v, shaftwidth=0.2)
              ball.vx_arrow = arrow(pos = ball.pos,
                                    axis = vec(ball.v.x, 0, 0),
                                    color=color.red,
                                    shaftwidth=0.2)
              ball.vy_arrow = arrow(pos = ball.pos + ball.vx_arrow.axis,
                                    axis = vec(0, ball.v.y, 0),
                                    color=color.yellow,
                                    shaftwidth=0.2)

              dt = 0.1

              while True:
                  sleep(dt)
                  #sleep(1)

                  # update ball position based on velocity vector
                  ball.pos.x = ball.pos.x + ball.v.x * dt
                  ball.pos.y = ball.pos.y + ball.v.y * dt

                  #update positions of the arrows
                  ball.arrow.pos = ball.pos
                  ball.vx_arrow.pos = ball.pos
                  ball.vy_arrow.pos = ball.pos + ball.vx_arrow.axis
                  # update directions of arrows
                  ball.arrow.axis = ball.v
                  ball.vx_arrow.axis = vec(ball.v.x, 0, 0)
                  ball.vy_arrow.axis = vec(0, ball.v.y, 0)

                  # check to see if the ball needs to bounce
                  if ball.pos.x >= 10:
                      ball.v.x = -1 * ball.v.x
            
The result should look like this:
result

Make Four Walls

Now, you should be able to make three more walls, such that the ball is bouncing around inside a box.
result