Applications: The Mathematics of Movement, Part 3
- by TechTwaddle
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,