Programming 2D Computer Graphics
Total Page:16
File Type:pdf, Size:1020Kb
04 4989 ch03 4/4/03 9:26 AM Page 33 Programming 2D 3 Computer Graphics IN THIS CHAPTER . Understanding Screen and As you’ve learned, Direct3D provides many powerful Cartesian Coordinates functions for creating 3D graphics on your computer. These functions hide many programming details that must Defining Vertex and Shape be dealt with to produce sophisticated graphics. Still, to Data Types understand Direct3D, you need to have a little background . Transforming Shapes in standard 3D programming practices. The first step toward that goal is to understand how your computer . Using Matrix Math in programs can manipulate simpler 2D images. Transformations This chapter, then, introduces you to the basics of 2D . Building a 2D Graphics graphics programming, including the formulas needed to Application transform (move, rotate, scale, and so on) 2D shapes in . various ways. Although transforming 2D shapes requires Running the Graphics2D that you know a set of formulas for manipulating the Application points that define a shape, you’ll discover that these . Understanding How the formulas are easy to use, even if you don’t really under- Graphics2D Program stand exactly how they work. Works In this chapter, you learn: . Exploring the Program • About screen and Cartesian coordinates Listings • About using vertices to define 2D shapes . In Brief • About translating, scaling, and rotating 2D shapes • How to use matrices to transform 2D shapes Understanding Screen and Cartesian Coordinates Undoubtedly, you have at least some minimal experience with drawing images on a computer screen under Windows. For example, you probably know that to draw a 04 4989 ch03 4/4/03 9:26 AM Page 34 34 CHAPTER 3 Programming 2D Computer Graphics line in a window, you must call the GDI function MoveToEx() to position the starting point of the line and then call the function LineTo() to draw the line. In a program, those two function calls would look something like this: MoveToEx(hDC, x, y, 0); LineTo(hDC, x, y); The arguments for the MoveToEx() function are a handle to a device context (DC), the X,Y coordinates of the point in the window to which you want to move, and a pointer to a POINT structure in which the function should store the current position before moving to the new position. If this last argument is 0, MoveToEx() doesn’t supply the current position. The arguments for the LineTo() function are a handle to a DC and the X,Y coordinates of the end of the line. Of interest here are the X,Y coordinates used for the end points that define the line. Assuming the default mapping mode of MM_TEXT, both sets of coordinates are based on window coordinates, which, like normal screen coordinates, begin in the upper- left corner of the window, with the X coordinate increasing as you move to the right and the Y coordinate increasing as you move down. Figure 3.1 shows this coordinate system. 0 Max X 0 Max Y FIGURE 3.1 A window’s MM_TEXT coordinate system. Most computer programmers are familiar with the coordinate system shown in Figure 3.1. Unfortunately, most objects in computer graphics are defined using the Cartesian coordinate system, which reverses the Y coordinates so that they increase as you move up from the origin. Also, as shown in Figure 3.2, the Cartesian coordi- nate system allows negative coordinates. If you remember any of your high school math, you’ll recognize Figure 3.2 as the plane on which you graphed equations. In computer graphics, however, you’ll use the Cartesian plane as a surface that repre- sents the world in which your graphical objects exist. 04 4989 ch03 4/4/03 9:26 AM Page 35 Understanding Screen and Cartesian Coordinates 35 Y 7 6 5 4 3 2 1 -7 -6 -5 -4 -3 -2 -10 1 2 3 4 5 6 7 X -1 -2 -3 -4 -5 -6 -7 FIGURE 3.2 The Cartesian coordinate system. You define graphical objects in the Cartesian coordinate system by specifying the coordinates of their vertices, which are the points at which the lines that make up the object connect. For example, a triangle can be defined by three points, as shown in Figure 3.3. The defining points in the triangle are (2,5), (5,2), and (2,2). Y 7 6 5 4 3 2 1 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 X -1 -2 -3 -4 -5 -6 -7 FIGURE 3.3 Defining a triangle in the Cartesian coordinate system. A problem arises, however, when you try to draw an object defined in the Cartesian coordinate system onscreen. As you can see by Figure 3.4, the figure comes out upside-down due to the reversal of the Y coordinates in the screen’s coordinate system as compared with the Cartesian coordinate system. The C++ code that produces the triangle looks like this: MoveToEx(hDC, 2, 5, 0); LineTo(hDC, 5, 2); LineTo(hDC, 2, 2); LineTo(hDC, 2, 5); 04 4989 ch03 4/4/03 9:26 AM Page 36 36 CHAPTER 3 Programming 2D Computer Graphics 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 FIGURE 3.4 Drawing a triangle with no mapping between the Cartesian and screen coordinate systems. Because of the differences between a screen display and the Cartesian coordinate system, you need a way to translate points from one system to the other. In graphics terms, you must map points in the Cartesian coordinate system to points in the screen coordinate system so that objects you draw onscreen are positioned correctly. Forgetting about negative coordinates for the time being, mapping point (x1,y1) in the Cartesian coordinate system to point (x2,y2) in the screen coordinate system requires the following simple formulas, shown in C++ program code: x2 = y1; y2 = maxY - y1; Because the X coordinate is unaffected by the mapping, x2 is simply assigned the value of x1. To reverse the Y coordinate, the original Y coordinate is subtracted from the window’s maximum Y coordinate. Of course, for this formula to work, you must know the current size of the window. You can get this value by calling the Windows API function GetClientRect(), which fills a RECT structure with the size of the window’s client area. Listing 3.1 draws a triangle in the window, mapping between the Cartesian coordinates and the screen coordinates. LISTING 3.1 Drawing a Triangle int triangle[6] = {2, 5, 5, 2, 2, 2}; int newX, newY, startX, startY; RECT clientRect; GetClientRect(hWnd, &clientRect); 04 4989 ch03 4/4/03 9:26 AM Page 37 Understanding Screen and Cartesian Coordinates 37 LISTING 3.1 Continued int maxY = clientRect.bottom; for (int x=0; x<3; ++x) { newX = triangle[x*2]; newY = maxY - triangle[x*2+1]; if (x == 0) { MoveToEx(hDC, newX, newY, 0); startX = newX; startY = newY; } else LineTo(hDC, newX, newY); } LineTo(hDC, startX, startY); (Note that the preceding code segment is not complete and will not run on your computer. Later in this chapter, you develop a complete Windows program that demonstrates the topics discussed in this chapter.) In the preceding code, the first line defines an array that contains the Cartesian coor- dinates of the triangle. The variables newX and newY will hold the screen coordinates for a point, and the variables startX and startY will hold the screen coordinates for the first point in the triangle. The RECT structure, clientRect, will hold the size of the window’s client area. After declaring the local variables, the code calls GetClientRect() to fill in the clientRect structure, at which point the structure’s bottom member will hold the height of the window’s client area. The code assigns this value to the local variable maxY. A for loop then iterates through the triangle’s coordinate array. Inside the loop, the currently indexed X,Y coordinates are mapped from Cartesian coordinates to screen coordinates. The first mapped point is used to set the starting point of the triangle. The code uses subsequent points to draw the lines that make up the triangle. The call to LineTo() outside the loop connects the last point of the triangle to the first point. 04 4989 ch03 4/4/03 9:26 AM Page 38 38 CHAPTER 3 Programming 2D Computer Graphics Defining Vertex and Shape Data Types As you learned in the previous section, 2D shapes are defined by a set of points, called vertices. Each vertex is connected to the next by a line. When all of the vertices are connected, the shape is finished. To make handling various types of shapes in a program easier, you need to define a couple of new data types. The first data type is for a vertex, and it looks like this: typedef struct vertex { int x, y; } VERTEX; This structure simply holds the X and Y Cartesian coordinates of a vertex. The next data type defines a complete shape, like this: typedef struct shape { int numVerts; VERTEX* vertices; } SHAPE; This structure contains an integer to hold the number of vertices in the shape and a pointer to an array of VERTEX structures. By using these new data types, you can write a more generalized version of the shape-drawing code, placing the loop and the variables on which it depends into its own function. That program code would look something like Listing 3.2. LISTING 3.2 Drawing Shapes from Generalized Data VERTEX triangleVerts[3] = {2, 5, 5, 2, 2, 2}; SHAPE shape1 = {3, triangleVerts}; DrawShape(shape1); void DrawShape(SHAPE& shape1) { int newX, newY, startX, startY; RECT clientRect; GetClientRect(hWnd, &clientRect); int maxY = clientRect.bottom; for (int x=0; x<shape1.numVerts; ++x) { 04 4989 ch03 4/4/03 9:26 AM Page 39 Transforming Shapes 39 LISTING 3.2 Continued newX = shape1.vertices[x].x; newY = maxY - shape1.vertices[x].y; if (x == 0) { MoveToEx(hDC, newX, newY, 0); startX = newX; startY = newY; } else LineTo(hDC, newX, newY); } LineTo(hDC, startX, startY); } Because the DrawShape() function has been generalized to work with SHAPE structures, the function can draw any type of shape you want to define.