In this chapter, you will learn how to construct two-dimensional images using points and lines. You learned the basic tools for creating images with Python in Chapter 1. In this chapter, you will expand on that and learn methods to create, translate and rotate shapes in two dimensions. You will also learn about the concept of relative coordinates, which will be used extensively throughout the remainder of this book. As usual, you will explore these concepts through sample programs.
2.1 Lines from Dots
You saw how to create a line with the command
This draws a line from (x1,y1) to (x2,y2) with attributes specifying the line’s width, color, and style. At times it may be desirable to construct a line using dots. Figures 2-1 and 2-2 show the geometry: an inclined line beginning at point 1 and ending at point 2. Its length is Q. Shown on the line in Figure 2-2 is point p at coordinates x,y. To draw the line, you start at 1 and advance toward 2 in steps, calculating coordinates of p at each step and plotting a dot at each step as you go. This analysis utilizes vectors, which will be used extensively later.
Note that you do not have coordinate axes in these models. This analysis is generic; it is applicable to any two-dimensional orthogonal coordinate directions.
ux is the cosine of the angle between û and the x axis; uy is the cosine of the angle between û and the y axis. ux and uy are often referred to as direction cosines. It is easy to show they are cosines: the cosine of the angle between û and the x axis is ux/|û| , where |û| is the scalar magnitude of û. Since û is a unit vector, |û|=1;. The cosine of the angle is then ux/(1)=ux. Similarly for uy.
Program DOTLINE
This program should be self-explanatory since the definitions are consistent with the prior analysis.
2.2 Dot Art
In line 7, you import random. This is a library of random functions that you use in lines 42, 43, and 44 to produce random primary r,g,b color components. They are mixed in line 45. You will use random’s random.randrange(a,b,c) function to obtain the random values. You could also use the random functions that are included in numpy, although the syntax is a bit different. The random library is being used here to illustrate that there are other math libraries besides numpy .
random.randrange(a,b,c) returns a random number between a and b in increments c. a, b, and c must be integers. To obtain a wide selection of random numbers, let a=1, b=100, and c=1 in lines 42-44. But rr in line 42 must be between 0 and 1.0 so you divide by 100 in line 42. This provides a random value for rr, the red component of the color mix, between 0 and 1.0. Similarly for rg and rb, the green and blue components, in lines 43 and 44. As you can see, the results in Klee are quite interesting.
Program DOTART
2.3 Circular Arcs from Dots
Listing 2-3 draws a circular arc using points. This is your first program dealing with circular coordinates, angles, and trig functions. The geometry used by Listing 2-3 is shown in Figure 2-5. The output is shown in Figure 2-6.
Program PARC
(The following is the program that created Figure 2-5)
Program PARCGEOMETRY
2.4 Circular Arcs from Line Segments
Instead of plotting dots with np.scatter() at points along the arc, you can create a finer arc using straight-line segments between points. If you replace the “plot arc” routine in Listing 2-3, beginning at line 24, with
you get the arc shown in Figure 2-7. In lines 28 and 29 of the code above you define xlast and ylast. These are the last x and y coordinate values plotted at the end of the previous line segment. Since you are just starting to plot the arc before the loop begins, these are initially set equal to the arc’s starting point where p=p1. You will need them to plot the first arc segment in line 33. Parameters p, p1, p2, and dp are the same as before. Imagine the loop 30-35 is just starting to run. Lines 31 and 32 calculate the global coordinates of the end of the first line segment, which is dp into the arc. Using the previously set values xlast and ylast, which are the coordinates of the beginning of that line segment in 28 and 29, line 33 plots the first line segment. Lines 34 and 35 update the end coordinates of the first segment as xlast, ylast. These will be used as the beginning coordinates of the second line segment. The loop continues to the end of the arc using the end of the preceding segment as the beginning of the next one. Notice in line 30 the loop begins at p1+dp, the end angle of the first line segment. This isn’t actually necessary and the beginning of the loop could be set to p1 as before, in which case the first line segment would have zero length. The loop would continue to the end of the arc as before.
In future work, you will sometimes use curves constructed of dots instead of line segments. Even though dots do not produce as fine results, they avoid complicating the plotting algorithm, which can sometimes obscure the logic of the script. However, line segments do produce superior results so you will use them as well.
2.5 Circles
A full circle is just a 360 ° arc. You can make a full circle by changing the beginning and end angles of the arc in the previous section to p1=0 and p2=360 degrees. This is done in lines 24 and 25 of Listing 2-5. The output is shown in Figure 2-8. Three circles and a solid disc are plotted at different locations. They have different colors and widths. Half the green circle is plotted with solid-line segments, the other half with dashed lines 29-37. The decision to plot a solid or dashed line is made by the if logic between lines 32 and 35. This changes the linestyle attribute in line 33. The blue solid disc is made by plotting concentric circles with radii from r1=0 to the disc’s outer radius r2. You could, of course, also make a solid disk with the np.scatter() function . You should be able to follow the logic used here to create the various circles by examining the script in Listing 2-5.
This program could have been shortened by the use of functions. It has been left open for the sake of clarity by using cut and paste to reproduce sections of redundant code.
Program CIRCLES
2.6 Dot Discs
The “equal arc” disc, beginning in line 38, appears better visually. As with the “r,p” disc, the dots are equally spaced in the radial direction. However, in the “equal arc” disc, the number of dots in the circumferential direction at each radial location becomes larger as the radius increases, thus keeping the circumferential arc spacing between dots constant. The model used is shown in Figure 2-10. dc is the circumferential spacing between dots a and b at rmax, the outer edge of the disk. dp is the angular spacing between radii to a and b. To achieve more uniform spacing across the disc, you hold dc constant at all radii. A typical radial location is shown at r=rmax/2. dc at this radius is the same as at rmax and is equal to dc. To accommodate this spacing, the angle between adjacent dots must increase to drp.
Program DOTDISCS
2.7 Ellipses
Ellipses are shown in Figure 2-12. They were drawn by Listing 2-7. The model used by Listing 2-7 is shown in Figure 2-11. This was drawn by Listing 2-8. The dimension a is called the semi-major since it refers to half the greater width; b is the semi-minor. 2a and 2b are the major and minor dimensions .
This seems easy enough. The green ellipse in Figure 2-12 was drawn this way. However, there is a problem. Look at Listing 2-7, lines 48, 49, and 50; the square root in Equation 2-9 and in line 48 gives uncertain results as x approaches +a and line 48 tries to take the square root of a number very close to zero. This is caused by roundoff errors in Python’s calculations. The manifestation of this shows up as a gap at the +a side of the ellipse. In the algorithm for the green ellipse, this gap is closed by lines 54 and 55. You can get a decent ellipse this way but you have to be careful.
Program ELLIPSES
(The following program was used to create Figure 2-11.)
Program ELLIPSEMODEL
2.8 2D Translation
Program 2DTRANSLATION
2.9 2D Rotation
So far in this chapter, you have seen how to construct images on a two-dimensional plane using points and lines. In this section, you’ll learn how to rotate a two-dimensional planar object within its own plane. A 2D object that you might want to rotate, a rectangle for example, or something more complicated which will normally consist of any number of points and lines. Lines, of course, are defined by their end points or a series of points if constructed from dots. As you have seen, curves can also be constructed from line segments or dots. If you can determine how to rotate a point, you will then be able to rotate any planar object defined by points. In Chapter 3, you will extend these concepts to the rotation of three-dimensional objects around three coordinate directions.
The black x,y system is the local system . A position (xp,yp) in the local system is equivalent to (xc+xp, yc+yp) in the global system. You use the local system to construct shapes by specifying the coordinates of the points that comprise them. For example, if you want to plot a circle somewhere in the plotting area, you could place (xc,yc) at the circle’s center, calculate the points defining the circle around it in reference to the local (black) system, and then relate them back to the xg,yg (blue) system for plotting by translating each point by xc and yc.
Figure 2-14 shows a point P that is rotated through a clockwise angle Rz to a new position at P′. The red coordinate system rotates through the angle Rz. P rotates along with it. The coordinates of P′ in the rotated system, (xp,yp), are the same as they were in the local system. However, in the global system, they are obviously different. Your goal now is to determine the coordinates of P′ in the local system and then in the global system, so you can plot it.
I am using the terminology Rz for the angle because a clockwise rotation in the x,y plane is actually a rotation about the z direction, which points into the plane of the paper. This was illustrated in Chapter 1. It will be explained in more detail in Chapter 3.
Your task now is to determine relations for iˆ′ and jˆ′ in relation to iˆ and jˆ and then substitute them into Equation 2-19. This will give you the coordinates of P′ in relation to the local x,y system. By simply adding xc and yc you will then get the coordinates of P′ in the global system, which you need for plotting.
These last two equations are all you need to rotate a point from (xp,yp) through the angle Rz to new coordinates (xp′,yp′). Note that both sets of coordinates, (xp,yp) and (xp′,yp′), are in reference to the local x,y axes. They can then be easily translated by xc and yc to get them in the global system for plotting.
The definitions in Equations 2-31 through 2-34 will be used in the Python programs that follow. They represent a rotation in the x,y plane in the clockwise direction; use a negative value of Rz to rotate in the counterclockwise direction. Note that [Rz] is purely a function of the angle of rotation, Rz.
As an illustration of the above concepts, Listing 2-10 rotates a point P1 about (xc,yc) from its original unrotated location at (xp,yp)=(60,0) in 30 degree increments. Results are shown in Figure 2-15. The coordinates of the center of rotation, (xc,yc), are set in lines 16 and 17.
Lines 28-37 of Listing 2-10 define a function rotz(xp1,yp1,Rz), which uses the elements of the transformation matrix [Rz] in Equations 2-31 through 2-34 and the angle of rotation Rz to calculate and return the transformed (rotated and translated) coordinates (xg,yg). Lines 35 and 36 in function rotz relate the local coordinates to the xg,yg system for plotting. Note that rotz rotates each point and simultaneously translates it by xc and yc in lines 35 and 36. This puts the coordinates in the global system ready for plotting. You are rotating the point four times: Rz=0,30,60,90. The use of the function rotz(xp,yp,Rz) enables you to avoid coding the transformation for every point.
Lines 39 and 40 set the original coordinates of P to (60,0). It is important to note that these coordinates are relative to the center of rotation (xc,yc). Line 43 starts the calculation of the first point. This is at Rz=0. Line 44 converts Rz from degrees to radians. Later, I will show how to use the radians() function to do this. Line 45 invokes the function rotz(xp,yp,Rz). xp and yp were set in lines 39 and 40; Rz was set in line 43. The function returns the coordinates of the rotated point (xg,yg) in line 37. Since Rz was zero in this first transformation, they are the same as the coordinates of the unrotated point P1.
The plotting of point P2 begins in line 50. You set the angle of rotation to 30 degrees in line 50. The routine is the same as before and P2 is plotted as a grey point. Sections P3 and P4 increase Rz to 60 and 90 degrees, plotting the red and final grey point.
Lines 74, 77, 80, and 83 illustrate the use of Latex in printing text on a plot. Looking at line 74, for example,
the text starts at coordinates xg=28, yg=6. As discussed in Chapter 1, the r tells Python to treat the string as raw. This keeps the backward slashes needed by the Latex code between the dollar signs; in this case, $mathbf{C}$. mathbf{} makes whatever is between the braces {} bold. In line 80, ^{prime} places a superscript prime next to P. This won’t work if the prefix r is not included.
Program 2DROT1
Next, you rotate a rectangle around its center, as shown in Figure 2-16. The center of rotation is point c at (xc,yc). The black rectangle shows the rectangle in its unrotated orientation. Its corners are numbered 1-4, as shown. The program plots the unrotated rectangle and then rotates it around point c to the rotated position and displays it in red.
Since the rectangle is defined by its corner points, you can rotate it by rotating the corners around c. The methodology is detailed in Listing 2-11. First, you plot the unrotated rectangle (black). The local coordinates of its four corner points are specified relative to the center of rotation c in lines 42-49. The points are labelled and plotted as dots in lines 51-58 where the local coordinates are converted to global by adding xc and yc in lines 55-58.
Next, you connect the corners by lines. Lines 61-68 translate the local corner coordinates by xc and yc. These points are labelled xg and yg to indicate that they are relative to the global plotting axes. They are set up as lists in lines 70 and 71, and then plotted in line 73, which draws lines between sequential xg,yg pairs.
Note the sequence of coordinate pairs in lines 70 and 71. When line 73 is invoked, it connects (xg1,yg1) to (xg2,yg2), then (xg2,yg2) to (xg3,yg3), and so on. But when it gets to corner 4, it has to connect corner 4 back to corner 1 in order to close the rectangle; hence you have (xg4,yg4) connected to (xg1,yg1) at the end of 70 and 71.
The plotting of the rotated rectangle begins at line 76. Rz is the angle of rotation. It is set to 45 degrees here and then converted from degrees to radians in line 77 (you could have used the radians() function to do this).
The function rotz(xp,yp,Rz) is defined in lines 29-38. The elements of the rotation transformation matrix shown in Equations 2-31 through 2-34 are evaluated in lines 30-33. xp and yp are the coordinates of an unrotated point. xpp and ypp (xp′ and yp′), coordinates in the rotated system, are evaluated in lines 34 and 35 using Equations 2-24 and 2-25. xg and yg, the coordinates in the global system after rotation and translation, are evaluated in lines 36-37 in accordance with Equations 2-35 and 2-36. Note that these lines rotate the points and simultaneously translate them relative to point c. The transformed coordinates are returned as a list in line 38.
Lines 80-101 transform each of the corner coordinates one by one by invoking function rotz(xp,yp,Rz). For example, lines 80-83 transform corner 1 from local, unrotated coordinates xp1,yp1 to global coordinates xg and yg. The remaining three points are transformed in the same way. The lines connecting the corners are plotted in red in lines 104-107 using lists.
Program 2DROTRECTANGLE
To summarize the procedure, you first construct the object, in this case a simple rectangle, using points located at coordinates xp,yp in the local x,y system. This is done by specifying the coordinates relative to the center of rotation at c. Next, you specify Rz, evaluate the elements of the transformation matrix, transform each coordinate by Rz, translate the rotated points by xc,yc to get everything into the global xg,yg system, and then plot. The transformations are carried out by the function rotz(xp,yp,rz), which simultaneously rotates and translates the coordinates into the xg,yg system for plotting. In this case, you transformed all the coordinates first and then plotted at the end using lists. In some programs, you will plot points or lines immediately after transforming.
The center of rotation c does not have to be contiguous with the object; you could put it anywhere as long as the corner coordinates relative to the center of rotation are updated.
Figure 2-18 shows an example of constructing and rotating a circular object. Obviously, without some distinctive feature, you wouldn’t be able to see if a circle had been rotated so you make the top half of the starting circle green and the bottom half red. You also add a bar across the diameter with dots at each end. Figure 2-19 shows the model used by Listing 2-12 to generate Figure 2-18.
As shown in Figures 2-19 and 2-18, and referring to Listing 2-12, you construct the starting circle with a center at xcc,ycc in program lines 41 and 42. It has a radius r=10, which is set in line 43. The angle p starts at p1=0 and goes to p2=2π in steps dp in lines 45-47. Note that you are not using the angle definition Rz since p is a local angle about point xcc,ycc (the center of the circle), not xc,yc, the center of rotation. Points along the circle’s perimeter are calculated in lines 55 and 56 in local coordinates. When alpha=0, this produces the starting circle.
When alpha >0, the other four circles are drawn. The alpha loop starting at line 53 moves the circle’s center (xcc,ycc) clockwise around the center of rotation in steps dalpha, which is set in line 51. The local coordinates are transformed in line 57 by invoking rotz. Alpha’s inclusion in the rotz function call has the effect of rotating the circle about its own center (xcc,ycc). Lines 58-61 determine if each circumferential point lies between p=0 and p=π. If so, the point is plotted as red, otherwise as green. Thus, the circle’s top half is red, its bottom half is green. Lines 62-70 plot the diametrical bars and points.
An important feature of this approach is that not only is the circle’s center rotated around point c in steps dalpha, but each circle itself is rotated about its own center, as can be seen from the reorientation of the red and green sectors and the diametrical bars in the rotated circles. In the next program, you will rotate each circle’s center around point c while keeping each circle unrotated about its own center.
Why am I using circles in this demonstration ? Primarily because it illustrates how to construct circular shapes at any location relative to a center of rotation and rotate them. It illustrates the importance of being aware of the location of the center of rotation; it isn’t necessarily the same as the center of the circle.
In this case, you are rotating in the plane of the circle, which admittedly isn’t very illuminating. But later, these concepts will become useful when I show how to rotate objects, such as a circle, in three dimensions. In the case of a circle, when rotated out of its plane, it produces an oval, which is essential in portraying circular and spherical objects such as cylinders and spheres in three dimensions. Simply rotate a circle out of plane about a coordinate direction and you get an oval.
Program 2DROTCIRCLE1
As shown in Figure 2-20, Listing 2-13 rotates the starting circle through increments of angle dalpha while keeping the orientation of each circle unchanged. The program is similar to the preceding one, with the exception that only the local center of each circle is rotated about point c while the circumferential points, as defined by the starting circle, remain unrotated. The program should be self-explanatory.
Program 2DROTCIRCLE2
2.10 Summary
In this chapter, you saw how to use dots and lines to construct shapes in two dimensions. You learned the concept of relative coordinates, specifically the local system , which is used to construct an image with coordinate values relative to a center, which in the case of rotation may be used as the center of rotation, and the global system which is used for plotting. You saw how local coordinates must be transformed into the global system for plotting, the origin of the global system being defined through the plt.axes() function. You saw how to construct lines from dots; arrange colored dots in artistic patterns; and draw arcs, discs, circles, and ellipses using dots and line segments. Then you learned about the concepts of translation (easy) and rotation (not so easy). You applied all this to points, rectangles, and circles. In the next chapter, you will extend these ideas to three dimensions.