Applications: The Mathematics of Movement, Part 3

Previously: Part 1, Part 2

As promised in the previous post, this post will cover two variations of the marble move program. The first one, Infinite Move, keeps the marble moving towards the click point, rebounding it off the screen edges and changing its direction when the user clicks again. The second version, Finite Move, is the same as first except that the marble does not move forever. It moves towards the click point, rebounds off the screen edges and slowly comes to rest. The amount of time that it moves depends on the distance between the click point and marble.

Infinite Move

This case is simple (actually both cases are simple). In this case all we need is the direction information which is exactly what the unit vector stores. So when the user clicks, you calculate the unit vector towards the click point and then keep updating the marbles position like crazy. And, of course, there is no stop condition. There’s a little more additional code in the bounds checking conditions. Whenever the marble goes off the screen boundaries, we need to reverse its direction.  Here is the code for mouse up event and UpdatePosition() method,

//stores the unit vector
double unitX = 0, unitY = 0;

double speed = 6;

//speed times the unit vector
double incrX = 0, incrY = 0;

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
    double x = e.X – marble1.x;
    double y = e.Y – marble1.y;

    //calculate distance between click point and current marble position
    double lenSqrd = x * x + y * y;
    double len = Math.Sqrt(lenSqrd);

    //unit vector along the same direction (from marble towards click point)
    unitX = x / len;
    unitY = y / len;

    timer1.Enabled = true;
}

private void UpdatePosition()
{
    //amount by which to increment marble position
    incrX = speed * unitX;
    incrY = speed * unitY;

    marble1.x += incrX;
    marble1.y += incrY;

    //check for bounds
    if ((int)marble1.x < MinX + marbleWidth / 2)
    {
        marble1.x = MinX + marbleWidth / 2;
        unitX *= -1;
    }
    else if ((int)marble1.x > (MaxX – marbleWidth / 2))
    {
        marble1.x = MaxX – marbleWidth / 2;
        unitX *= -1;
    }

    if ((int)marble1.y < MinY + marbleHeight / 2)
    {
        marble1.y = MinY + marbleHeight / 2;
        unitY *= -1;
    }
    else if ((int)marble1.y > (MaxY – marbleHeight / 2))
    {
        marble1.y = MaxY – marbleHeight / 2;
        unitY *= -1;
    }

}

So whenever the user clicks we calculate the unit vector along that direction and also the amount by which the marble position needs to be incremented. The speed in this case is fixed at 6. You can experiment with different values. And under bounds checking, whenever the marble position goes out of bounds along the x or y direction we reverse the direction of the unit vector along that direction. Here’s a video of it running;

 

Finite Move

The code for finite move is almost exactly same as that of Infinite Move, except for the difference that the speed is not fixed and there is an end condition, so the marble comes to rest after a while. Code follows,

//unit vector along the direction of click point
double unitX = 0, unitY = 0;

//speed of the marble
double speed = 0;

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
    double x = 0, y = 0;
    double lengthSqrd = 0, length = 0;

    x = e.X – marble1.x;
    y = e.Y – marble1.y;
    lengthSqrd = x * x + y * y;

    //length in pixels (between click point and current marble pos)
    length = Math.Sqrt(lengthSqrd);

    //unit vector along the same direction as vector(x, y)
    unitX = x / length;
    unitY = y / length;

    speed = length / 12;

    timer1.Enabled = true;
}

private void UpdatePosition()
{
    marble1.x += speed * unitX;
    marble1.y += speed * unitY;

    //check for bounds
    if ((int)marble1.x < MinX + marbleWidth / 2)
    {
        marble1.x = MinX + marbleWidth / 2;
        unitX *= -1;
    }
    else if ((int)marble1.x > (MaxX – marbleWidth / 2))
    {
        marble1.x = MaxX – marbleWidth / 2;
        unitX *= -1;
    }

    if ((int)marble1.y < MinY + marbleHeight / 2)
    {
        marble1.y = MinY + marbleHeight / 2;
        unitY *= -1;
    }
    else if ((int)marble1.y > (MaxY – marbleHeight / 2))
    {
        marble1.y = MaxY – marbleHeight / 2;
        unitY *= -1;
    }

    //reduce speed by 3% in every loop
    speed = speed * 0.97f;
    if ((int)speed <= 0)
    {
        timer1.Enabled = false;
    }

}

So the only difference is that the speed is calculated as a function of length when the mouse up event occurs. Again, this can be experimented with. Bounds checking is same as before. In the update and draw cycle, we reduce the speed by 3% in every cycle. Since speed is calculated as a function of length, speed = length/12, the amount of time it takes speed to reach zero is directly proportional to length. Note that the speed is in ‘pixels per 40ms’ because the timeout value of the timer is 40ms.  The readability can be improved by representing speed in ‘pixels per second’. This would require you to add some more calculations to the code, which I leave out as an exercise. Here’s a video of this second version,

 

Applications: The Mathematics of Movement, Part 2

In part 1 of this series we saw how we can make the marble move towards the click point, with a fixed speed. In this post we’ll see, first, how to get rid of Atan2(), sine() and cosine() in our calculations, and, second, reducing the speed of the marble as it approaches the destination, so it looks like the marble is easing into it’s final position. As I mentioned in one of the previous posts, this is achieved by making the speed of the marble a function of the distance between the marble and the destination point.

Getting rid of Atan2(), sine() and cosine()

Ok, to be fair we are not exactly getting rid of these trigonometric functions, rather, replacing one form with another. So instead of writing sin(θ), we write y/length. You see the point. So instead of using the trig functions as below,

double x = destX – marble1.x;
double y = destY – marble1.y;

//distance between destination and current position, before updating marble position
distanceSqrd = x * x + y * y;

double angle = Math.Atan2(y, x);

//Cos and Sin give us the unit vector, 6 is the value we use to magnify the unit vector along the same direction
incrX = speed * Math.Cos(angle);
incrY = speed * Math.Sin(angle);

marble1.x += incrX;
marble1.y += incrY;

we use the following,

double x = destX – marble1.x;
double y = destY – marble1.y;

//distance between destination and marble (before updating marble position)
lengthSqrd = x * x + y * y;
length = Math.Sqrt(lengthSqrd);

//unit vector along the same direction as vector(x, y)
unitX = x / length;
unitY = y / length;

//update marble position
incrX = speed * unitX;
incrY = speed * unitY;
marble1.x += incrX;
marble1.y += incrY;

so we replaced cos(θ) with x/length and sin(θ) with y/length. The result is the same.

 

Adding oomph to the way it moves

In the last post we had the speed of the marble fixed at 6,

double speed = 6;

to make the marble decelerate as it moves, we have to keep updating the speed of the marble in every frame such that the speed is calculated as a function of the length. So we may have, speed = length/12; ‘length’ keeps decreasing as the marble moves and so does speed. The Form1_MouseUp() function remains the same as before, here is the UpdatePosition() method,

private void UpdatePosition()
{
    double incrX = 0, incrY = 0;
    double lengthSqrd = 0, length = 0, lengthSqrdNew = 0;
    double unitX = 0, unitY = 0;
    double speed = 0;

    double x = destX – marble1.x;
    double y = destY – marble1.y;

    //distance between destination and marble (before updating marble position)
    lengthSqrd = x * x + y * y;
    length = Math.Sqrt(lengthSqrd);

    //unit vector along the same direction as vector(x, y)
    unitX = x / length;
    unitY = y / length;

    //speed as a function of length
    speed = length / 12;

    //update marble position
    incrX = speed * unitX;
    incrY = speed * unitY;
    marble1.x += incrX;
    marble1.y += incrY;

    //check for bounds
    if ((int)marble1.x < MinX + marbleWidth / 2)
    {
        marble1.x = MinX + marbleWidth / 2;
    }
    else if ((int)marble1.x > (MaxX – marbleWidth / 2))
    {
        marble1.x = MaxX – marbleWidth / 2;
    }

    if ((int)marble1.y < MinY + marbleHeight / 2)
    {
        marble1.y = MinY + marbleHeight / 2;
    }
    else if ((int)marble1.y > (MaxY – marbleHeight / 2))
    {
        marble1.y = MaxY – marbleHeight / 2;
    }

    //distance between destination and marble (after updating marble position)
    x = destX – (marble1.x);
    y = destY – (marble1.y);
    lengthSqrdNew = x * x + y * y;

    /*
     * End Condition:
     * 1. If there is not much difference between lengthSqrd and lengthSqrdNew
     * 2. If the marble has moved more than or equal to a distance of totLenToTravel (see Form1_MouseUp)
     */
    x = startPosX – marble1.x;
    y = startPosY – marble1.y;
    double totLenTraveledSqrd = x * x + y * y;
    if ((int)totLenTraveledSqrd >= (int)totLenToTravelSqrd)
    {
        System.Console.WriteLine("Stopping because Total Len has been traveled");
        timer1.Enabled = false;
    }
    else if (Math.Abs((int)lengthSqrd – (int)lengthSqrdNew) < 4)
    {
        System.Console.WriteLine("Stopping because no change in Old and New");
        timer1.Enabled = false;
    }
}

A point to note here is that, in this implementation, the marble never stops because it travelled a distance of totLenToTravelSqrd (first if condition). This happens because speed is a function of the length. During the final few frames length becomes very small and so does speed; and so the amount by which the marble shifts is quite small, and the second if condition always hits true first.

I’ll end this series with a third post. In part 3 we will cover two things, one, when the user clicks, the marble keeps moving in that direction, rebounding off the screen edges and keeps moving forever. Two, when the user clicks on the screen, the marble moves towards it, with it’s speed reducing by every frame. It doesn’t come to a halt when the destination point is reached, instead, it continues to move, rebounds off the screen edges and slowly comes to halt. The amount of time that the marble keeps moving depends on how far the user clicks from the marble. I had mentioned this second situation here.

Finally, here’s a video of this program running, 

 

Part 1, Part 3