- 1.1. The canvas Element
- 1.2. Canvas Contexts
- 1.3. Canonical Examples in This Book
- 1.4. Getting Started
- 1.5. Fundamental Drawing Operations
- 1.6. Event Handling
- 1.7. Saving and Restoring the Drawing Surface
- 1.8. Using HTML Elements in a Canvas
- 1.9. Printing a Canvas
- 1.10. Offscreen Canvases
- 1.11. A Brief Math Primer
- 1.12. Conclusion
1.11. A Brief Math Primer
To do anything interesting with Canvas, you need a good understanding of basic mathematics, especially working with algebraic equations, trigonometry, and vectors. It also helps, for more complex applications like video games, to be able to derive equations, given units of measure.
Feel free to skim this section if you’re comfortable with basic algebra and trigonometry and you can make your way to pixels/frame given pixels/second and milliseconds/frame. Otherwise, spending time in this section will prove fruitful throughout the rest of this book.
Let’s get started with solving algebraic equations and trigonometry, and then we’ll look at vectors and deriving equations from units of measure.
1.11.1. Solving Algebraic Equations
For any algebraic equation, such as (10x + 5) × 2 = 110, you can do the following, and the equation will still be true:
- Add any real number to both sides
- Subtract any real number from both sides
- Multiply any real number by both sides
- Divide both sides by any real number
- Multiply or divide one or both sides by 1
For example, for (10x + 5) × 2 = 110, you can solve the equation by dividing both sides by 2, to get: 10x + 5 = 55; then you can subtract 5 from both sides to get: 10x = 50; and finally, you can solve for x by dividing both sides by 10: x = 5.
The last rule above may seem rather odd. Why would you want to multiply or divide one or both sides of an equation by 1? In Section 1.11.4, “Deriving Equations from Units of Measure,” on p. 62), where we derive equations from units of measure, we will find a good use for that simple rule.
1.11.2. Trigonometry
Even the simplest uses of Canvas require a rudimentary understanding of trigonometry; for example, in the next chapter you will see how to draw polygons, which requires an understanding of sine and cosine. Let’s begin with a short discussion of angles, followed by a look at right triangles.
1.11.2.1. Angles: Degrees and Radians
All the functions in the Canvas API that deal with angles require you to specify angles in radians. The same is true for the JavaScript functions Math.sin(), Math.cos(), and Math.tan(). Most people think of angles in terms of degrees, so you need to know how to convert from degrees to radians.
180 degrees is equal to π radians. To convert from degrees to radians, you can create an algebraic equation for that relationship, as shown in Equation 1.1.
Equation 1.1. Degrees and radians
Solving Equation 1.1 for radians, and then degrees, results in Equations 1.2 and 1.3.
Equation 1.2. Degrees to radians
Equation 1.3. Radians to degrees
π is roughly equal to 3.14, so, for example, 45 degrees is equal to (3.14 / 180) × 45 radians, which works out to 0.7853.
1.11.2.2. Sine, Cosine, and Tangent
To make effective use of Canvas, you must have a basic understanding of sin, cos, and tan, so if you’re not already familiar with Figure 1.21, you should commit it to memory.
Figure 1.21. Sine, cosine, and tangent
You can also think of sine and cosine in terms of the X and Y coordinates of a circle, as illustrated in Figure 1.22.
Figure 1.22. Radius, x, and y
Given the radius of a circle and a counterclockwise angle from 0 degrees, you can calculate the corresponding X and Y coordinates on the circumference of the circle by multiplying the radius times the cosine of the angle, and multiplying the radius by the sine of the angle, respectively.
1.11.3. Vectors
The two-dimensional vectors that we use in this book encapsulate two values: direction and magnitude; they are used to express all sorts of physical characteristics, such as forces and motion.
In Chapter 8, “Collision Detection,” we make extensive use of vectors, so in this section we discuss the fundamentals of vector mathematics. If you’re not interested in implementing collision detection, you can safely skip this section.
Near the end of Chapter 8 we explore how to react to a collision between two polygons by bouncing one polygon off another, as illustrated in Figure 1.23.
Figure 1.23. Using vectors to bounce one polygon off another
In Figure 1.23, the top polygon is moving toward the bottom polygon, and the two polygons are about to collide. The top polygon’s incoming velocity and outgoing velocity are both modeled with vectors. The edge of the bottom polygon with which the top polygon is about to collide is also modeled as a vector, known as a edge vector.
Feel free to skip ahead to Chapter 8 if you can’t wait to find out how to calculate the outgoing velocity, given the incoming velocity and two points on the edge of the bottom polygon. If you’re not familiar with basic vector math, however, you might want to read through this section before moving to Chapter 8.
1.11.3.1. Vector Magnitude
Although two-dimensional vectors model two quantities—magnitude and direction—it’s often useful to calculate one or the other, given a vector. You can use the Pythagorean theorem, which you may recall from math class in school (or alternatively, from the movie the Wizard of Oz), to calculate a vector’s magnitude, as illustrated in Figure 1.24.
Figure 1.24. Calculating a vector’s magnitude
The Pythagorean theorem states that the hypotenuse of any right triangle is equal to the square root of the squares of the other two sides, which is a lot easier to understand if you look at Figure 1.24. The corresponding JavaScript looks like this:
var
vectorMagnitude=
Math.
sqrt
(
Math.
pow
(
vector.
x,
2
) +
Math.
pow
(
vector.
y,
2
));
The preceding snippet of JavaScript shows how to calculate the magnitude of a vector referenced by a variable named vector.
Now that you know how to calculate a vector’s magnitude, let’s look at how you can calculate a vector’s other quantity, direction.
1.11.3.2. Unit Vectors
Vector math often requires what’s known as a unit vector. Unit vectors, which indicate direction only, are illustrated in Figure 1.25.
Figure 1.25. A unit vector
Unit vectors are so named because their magnitude is always 1 unit. To calculate a unit vector given a vector with an arbitrary magnitude, you need to strip away the magnitude, leaving behind only the direction. Here’s how you do that in JavaScript:
var
vectorMagnitude=
Math.
sqrt
(
Math.
pow
(
vector.
x,
2
) +
Math.
pow
(
vector.
y,
2
)),
unitVector=
new
Vector
();
unitVector.
x=
vector.
x/
vectorMagnitude;
unitVector.
y=
vector.
y/
vectorMagnitude;
The preceding code listing, given a vector named vector, first calculates the magnitude of the vector as you saw in the preceding section. The code then creates a new vector—see Chapter 8 for a listing of a Vector object—and sets that unit vector’s X and Y values to the corresponding values of the original vector, divided by the vector’s magnitude.
Now that you’ve seen how to calculate the two components of any two-dimensional vector, let’s see how you combine vectors.
1.11.3.3. Adding and Subtracting Vectors
It’s often useful to add or subtract vectors. For example, if you have two forces acting on a body, you can sum two vectors representing those forces together to calculate a single force. Likewise, subtracting one positional vector from another yields the edge between the two vectors.
Figure 1.26 shows how to add vectors, given two vectors named A and B.
Figure 1.26. Adding vectors
Adding vectors is simple: You just add the components of the vector together, as shown in the following code listing:
var
vectorSum=
new
Vector
();
vectorSum.
x=
vectorOne.
x+
vectorTwo.
x;
vectorSum.
y=
vectorOne.
y+
vectorTwo.
y;
Subtracting vectors is also simple: you subtract the components of the vector, as shown in the following code listing:
var
vectorSubtraction=
new
Vector
();
vectorSubtraction.
x=
vectorOne.
x-
vectorTwo.
x;
vectorSubtraction.
y=
vectorOne.
y-
vectorTwo.
y;
Figure 1.27 shows how subtracting one vector from another yields a third vector whose direction is coincident with the edge between the two vectors. In Figure 1.27, the vectors A-B and B-A are parallel to each other and are also parallel to the edge vector between vectors A and B.
Figure 1.27. Subtracting vectors
Now that you know how to add and subtract vectors and, more importantly, what it means to do that, let’s take a look at one more vector quantity: the dot product.
1.11.3.4. The Dot Product of Two Vectors
To calculate the dot product of two vectors you multiply the components of each vector by each other, and sum the values. Here is how you calculate the dot product for two two-dimensional vectors:
var
dotProduct=
vectorOne.
x*
vectorTwo.
x+
vectorOne.
y*
vectorTwo.
y;
Calculating the dot product between two vectors is easy; however, understanding what a dot product means is not so intuitive. First, notice that unlike the result of adding or subtracting two vectors, the dot product is not a vector—it’s what engineers refer to as a scalar, which means that it’s simply a number. To understand what that number means, study Figure 1.28.
Figure 1.28. A positive dot product
The dot product of the two vectors in Figure 1.28 is 528. The significance of that number, however, is not so much its magnitude but the fact that it’s greater than zero. That means that the two vectors point in roughly the same direction.
Now look at Figure 1.29, where the dot product of the two vectors is –528. Because that value is less than zero, we can surmise that the two vectors point in roughly different directions.
Figure 1.29. A negative dot product
The ability to determine whether or not two vectors point in roughly the same direction can be critical to how you react to collisions between objects. If a moving object collides with a stationary object and you want the moving object to bounce off the stationary object, you need to make sure that the moving object bounces away from the stationary object, and not toward the stationary object’s center. Using the dot product of two vectors, you can do exactly that, as you’ll see in Chapter 8.
That’s pretty much all you need to know about vectors to implement collision detection, so let’s move on to the last section in this brief math primer and see how to derive the equations from units of measure.
1.11.4. Deriving Equations from Units of Measure
As you will see in Chapter 5, motion in an animation should be time based, because the rate at which an object moves should not change with an animation’s frame rate. Time-based motion is especially important for multiplayer games; after all, you don’t want a game to progress more quickly for players with more powerful computers.
To implement time-based motion, we specify velocity in this book in terms of pixels per second. To calculate how many pixels to move an object for the current animation frame, therefore, we have two pieces of information: the object’s velocity in pixels per second, and the current frame rate of the animation in milliseconds per frame. What we need to calculate is the number of pixels per frame to move any given object. To do that, we must derive an equation that has pixels per frame on the left side of the equation, and pixels per second (the object’s velocity) and milliseconds per frame (the current frame rate) on the right of the equation, as shown in Equation 1.4.
Equation 1.4. Deriving an equation for time-based motion, part I
In this inequality, X represents the animation’s frame rate in milliseconds/frame, and Y is the object’s velocity in pixels/second. As that inequality suggests, however, you cannot just multiply milliseconds/frame times pixels/second, because you end up with a nonsensical milliseconds-pixels/frame-seconds. So what do you do?
Recall the last rule we discussed in Section 1.11.1, “Solving Algebraic Equations,” on p. 54) for solving algebraic equations: You can multiply or divide one or both sides of an equation by 1. Because of that rule, and because one second is equal to 1000 ms, and therefore 1 second / 1000 ms is equal to 1, we can multiply the right side of the equation by that fraction, as shown in Equation 1.5.
Equation 1.5. Deriving an equation for time-based motion, part 2
And now we are ready to move in for the kill because when you multiply two fractions together, a unit of measure in the numerator of one fraction cancels out the same unit of measure in the denominator of the other fraction. In our case, we cancel units of measure as shown in Equation 1.6.
Equation 1.6. Deriving an equation for time-based motion, part 3
Canceling those units of measure results in Equation 1.7.
Equation 1.7. Deriving an equation for time-based motion, part 4
Carrying out the multiplication results in the simplified equation, shown in Equation 1.8.
Equation 1.8. Deriving an equation for time-based motion, part 5
Whenever you derive an equation, you should plug some simple numbers into your equation to see if the equation makes sense. In this case, if an object is moving at 100 pixels per second, and the frame rate is 500 ms per frame, you can easily figure out, without any equations at all, that the object should move 50 pixels in that 1/2 second.
Plugging those numbers into Equation 1.8 results in 500 × 100 / 1000, which equals 50, so it appears that we have a valid equation for any velocity and any frame rate.
In general, to derive an equation from variables with known units of measure, follow these steps:
- Start with an inequality, where the result is on the left, and the other variables are on the right.
- Given the units of measure on both sides of the equation, multiply the right side of the equation by one or more fractions, each equal to 1, whose units of measure cancel out the units of measure on the right side of the equation to yield the units of measure on the left side of the equation.
- Cancel out the units of measure on the right side of the equation.
- Multiply the fractions on the right side of the equation.
- Plug simple values whose result you can easily verify into the equation to make sure the equation yields the expected value.