Learning and Applying Mathematics using Computing

Burning Rubber

This post is about rotating coordinates by using polar coordinates.

In our last post, we added a braking capability to our bus. One visual effect that’s quite neat is adding tyre (US: tire) marks where the vehicle has braked sharply and left rubber on the ground. In this post, we’ll add these tyre tracks to our bus scenario.

Tyre Tracks

The basic idea for adding tyre tracks is the same as things like smoke effects in Greenfoot: we will have a tyre-track actor, of which we’ll add many to the world, and once added they will remain stationary but slowly fade away until they remove themselves entirely. The code for the tyre-track actor is not very mathematical, but just so you can see how it’s done, here it is:

public class TyreTrack extends Actor
{
    public void act()
    {
        // Make a faded copy of the image:
        Color color = getImage().getColorAt(0, 0);
        color = new Color(color.getRed(), color.getGreen(), color.getBlue(),
                          Math.max(0,color.getAlpha() - 2));
        GreenfootImage img = new GreenfootImage(getImage().getWidth(),
                                                getImage().getHeight());
        img.setColor(color);
        img.fill();
        setImage(img);

        if (color.getAlpha() == 0)
            getWorld().removeObject(this);
    }
}

Where’s Ya Wheels?

The main difficulty in adding tyre tracks is where exactly to add them. Obviously, we should add them under the wheels. The first step is to load up the bus image and determine suitable coordinates (relative to the centre of the image) of the four wheels — that is (45, 26) for my bus drawing, and the image is symmetrical in both dimensions. This tells us where to add the tyre tracks, when the bus is pointing to the right. Here’s the first version of the code:

    private void layDownRubber()
    {
        double wheelPos[][] = 
          { {45, 26}, {45, -26}
          , {-45, 26}, {-45, -26} };
          
        for (double[] pos : wheelPos)
        {
            double x = pos[0];
            double y = pos[1];
            
            double worldX = getX() + x;
            double worldY = getY() + y;
            
            if (worldX >= 0 && worldX < getWorld().getWidth()
               && worldY >= 0 && worldY < getWorld().getHeight())
            {
                TyreTrack track = new TyreTrack();
                // Orient it in the same direction as the bus (wheels point same way as bus):
                track.setRotation(getRotation());
                getWorld().addObject(track, (int)worldX, (int)worldY);
            }
        }
    }

The basic idea is that we have four wheel positions (each position is a two-item array, with X and Y in it), which we go through, and add a tyre-track at that coordinate, relative to our own position (which we get with getX() and getY()). The last if-check is just to make sure we’re not trying to add them outside the world (which Greenfoot will not like).

The problem is that as soon as we rotate the bus, these positions won’t be correct any more, and the tyre marks will start appearing in odd places that don’t look right given the bus’s current orientation. We need to rotate these positions (not just rotate the tyre tracks, but rotate the position relative to the bus’s centre) according to the bus’s rotation.

Rotating A Vector

There’s a couple of different ways we could go about rotating the position of the wheels. I’m going to choose the method that re-uses code we’ve already seen in a previous post. We can convert the position of the wheels (which is relative to the centre of the bus) to a polar coordinate. This allows us to easily rotate them, by manipulating the polar rotation, before converting them back to cartesian. So the new version of our code looks like this:

    private void layDownRubber()
    {
        double wheelPos[][] = 
          { {45, 26}, {45, -26}
          , {-45, 26}, {-45, -26} };
          
        for (double[] pos : wheelPos)
        {
            double x = pos[0];
            double y = pos[1];
            
            double dir = calculateDirection(x, y);
            double dist = calculateMagnitude(x, y);
            
            dir = dir + getRotation();
            
            double worldX = getX() + calculateX(dir, dist);
            double worldY = getY() + calculateY(dir, dist);
            
            if (worldX >= 0 && worldX < getWorld().getWidth()
               && worldY >= 0 && worldY < getWorld().getHeight())
            {
                TyreTrack track = new TyreTrack();
                // Orient it in the same direction as the bus (wheels point same way as bus):
                track.setRotation(getRotation());
                getWorld().addObject(track, (int)worldX, (int)worldY);
            }
        }
    }

The difference here is that now we convert the X and Y into direction and magnitude (distance), then alter the rotation by adding our current rotation, then convert it back to cartesian X and Y coordinates. This will position the tyre tracks in the right place. To make this all work, we add a call to the layDownRubber() function in the braking code from the last post, to make us skid whenever we’re braking at high speed:

        if (Greenfoot.isKeyDown("down"))
        {
            speed = Math.max(0, speed - 0.1);
            if (speed > 5)
            {
                layDownRubber();
            }
        }

Now you can perform cool-looking skids like this one:

Have a play yourself or look at the source code.

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 253 other followers