© B.J. Korites 2018
B.J. KoritesPython Graphicshttps://doi.org/10.1007/978-1-4842-3378-8_3

3. Graphics in Three Dimensions

B. J. Korites1 
(1)
Duxbury, Massachusetts, USA
 

In this chapter, you will learn how to create, translate, and rotate three-dimensional objects in a three-dimensional space. You will also learn how to project and display them on the two-dimensional surface of your computer screen. General movement of an object implies both translation and rotation. I discussed this in two dimensions in the previous chapter. You saw that translation in two dimensions is trivial. Just add or subtract a quantity from the x coordinates to translate in the x direction, similarly for the y direction. In three dimensions, it is still trivial, although you are able now to translate in the third dimension, the z direction, simply by adding or subtracting an amount to an object’s z coordinates. Rotation is another matter, however. The analysis follows the method you used in two dimensions but is complicated by the fact that you now are able to rotate an object around three coordinate directions. In this chapter, I will not discuss 3D translation any further but will concentrate instead on 3D rotation.

3.1 The Three-Dimensional Coordinate System

In the previous discussion of two-dimensional rotation, you rotated two-dimensional objects in the two-dimensional x,y plane. You now extend those concepts to three dimensions by introducing a third axis, the z axis, as shown in Figure 3-1. Notice that the z axis points into the screen, not out. This is not an arbitrary choice. We are following the right-hand rule convention where the direction of positive z is found by rotating the x axis toward the y axis through the smaller angle between them. The positive z axis will then point in the direction that would be followed by a right-handed screw when turned in this fashion. In this case, the screw would progress into the screen; that is then the direction of the positive z axis. We could construct an entire mathematical theory based on a left-handed screw but the convention used most everywhere is that of a right-handed system. Some books and papers label the coordinate axes as x1,x2,x3. Following the right-hand rule, the direction of x3 would be found by rotating x1 into x2, as described above. In this work, we will stay with the x,y,z notation for the directions.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig1_HTML.jpg
Figure 3-1

Three-dimensional coordinate axes with point P at coordinates (x,y,z)

It should be apparent now why I used the nomenclature Rz in the previous discussion of two-dimensional rotation; it refers to rotation about the z axis. This appears as a clockwise rotation in the x-y plane when x goes to the right and y goes down. If x went to the right and y were to go up, z would point out of the screen and a positive rotation about the z axis would appear to go counterclockwise.

Following the methods used in this analysis of two-dimensional rotation, in the remainder of this chapter I will discuss separate rotations around the x,y, and z axes and then combined rotations around all three axes. Incidentally, when I say “rotation around the x axis”, for example, I am implying that this is equivalent to “rotation around the x direction” and vice versa. While rotation around an imaginary axis that is parallel to the x axis is not precisely the same as rotation around the x axis, the difference is only a matter of translation. I will use both terms interchangeably except when confusion may result.

Figure 3-2 shows the right-hand x,y,z system . Imagine you’re standing at the origin, looking out in the direction of the x axis. If you were to turn a right-handed screw clockwise, it would progress in the direction of the positive x axis. The double-headed arrow is the conventional way of indicating the direction of a right-hand rotation, Rx; similarly for Ry and Rz.

Why have I chosen to orient the coordinate system as shown in Figure 3-2? Standard matplotlib uses a different orientation as shown, for example, in https://matplotlin.org/mpl_toolkits/mplot3d/tutorial.html#scatter-plots .
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig2_HTML.jpg
Figure 3-2

Three-dimensional coordinate axes showing right-hand rotation around each coordinate direction

As explained earlier, the orientation in Figure 3-2 is somewhat more intuitive. The object being constructed is inside a space defined by the x,y,z axes. In this situation, the observer is outside the space looking in. The object may be translated and rotated at will to give any view desired. You can look straight in at an object or view it from above or below, as shown in the images of Saturn in Chapter 10. The matplotlib orientation, on the other hand, is the one commonly used for data plotting and is the one you’ll use for that purpose in Chapter 9; look at Figures 9-1 through 9-5. If you prefer the standard matplotlib system, it is easy to change to that orientation; just rotate the axes to any orientation you want, as is done in Chapter 9 where, to get z pointing up, you rotate around the global x direction by -100 degrees (tilts z slightly forward), the global y axis by -135 degrees, and the global z direction by +8 degrees (see lines 191-193 in Listing 9-1). You can fine-tune the orientation by small rotations about the global axes. After you complete this chapter, you should find it easy to shade the background planes, as shown in matplotlib, if you want. You can orient the axes any way you want as long as they follow the right-hand rule.

3.2 Projections onto the Coordinate Planes

How do we display a three-dimensional object on a two-dimensional computer monitor? We do so by projecting the object onto either of the three two-dimensional coordinate planes (x,y; x,z; and y,z) and then plotting either of those images on the monitor. Figure 3-3 show a three-dimensional line (black) running from A to B. Looking down from above the plotting space onto the x,z plane, you see it as the red line, which is the black line’s projection onto the x,z plane. Similarly, the green line shows its projection onto the y,z plane; the blue line is its projection onto the x,y plane. I will use only one of these projections for visualization, normally the x,y projection.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig3_HTML.jpg
Figure 3-3

Projection of a three-dimensional line (black) onto the three coordinate planes: red=x,z projection, green=y,z projection, and blue=x,y projection

The x,y projection is obtained by plotting a point’s x and y coordinates in the x,y plane; for a line, you plot a line between the x and y coordinates of the line’s endpoints. In the case of the black line, which runs from spatial coordinates (xA,yA,zA) to (xB,yB,zB), you plot a line between xA,yA and xB,yB:

plt.plot([xA,xB],[yA,yB],color='b')

This gives you the blue line, which is the projection onto the x,y plane as shown in Figure 3-4. If you want to obtain the top view, you plot the black line’s z,x coordinates. If plotting with your normal coordinate axes with x running from left to right and y running down on the left, the y axis replaces the z axis. This is equivalent to a -90 degree rotation about the x axis. You then plot between the line’s z and x coordinates of

plt.plot([zA,zB],[xA,xB],color='r')

to get the red line. To get the green y, z projection, you plot the z and y coordinates using the command

plt.plot([zA,zB],[yA,yB],color='g')
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig4_HTML.jpg
Figure 3-4

Projection of a three-dimensional line onto the x, y plane

In this case, you must reorient the screen coordinate axes such that +z runs from left to right across the top of the screen with the y axis running down the right side. This will give a z, y view from outside of the x,y,z coordinate system.

Note that in the case of a projection onto the x,y plane, you do not use the object’s z coordinates. But you still need them in order to carry out rotations. Similarly for the other projections, one coordinate is not needed for the projection but is needed for rotations so it must be included in the analysis.

To simplify everything, you will use the x,y projection in most of the work that follows. As you will see, rotating an object around the three coordinate directions and projecting the object’s (x,y) coordinates onto the x,y plane will produce a three-dimensional view.

The projections of three-dimensional objects onto two-dimensional coordinate planes are called isometric projections . They are commonly used in engineering and drafting. These images do not appear as they would to the human eye or as they would in a photograph because of the absence of what artists call foreshortening, more commonly known as perspective . As an example of foreshortening, if you look down a line of telephone poles that are running off into the distance alongside railroad tracks, the pole closest to you would look taller than those further away and the rails would appear to merge as they near the horizon. What causes foreshortening? It happens simply because there is more area for the eye to cover in the far distance than close up. In the case of telephone poles, it’s because there is more vertical space in the distance so the poles, which are of fixed height, occupy a smaller percentage of it; for the railroad tracks, it’s the expanding horizontal space. Isometric projections do not take foreshortening into account, but I will in Chapter 4 when I discuss perspective transformations.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig5_HTML.jpg
Figure 3-5

Isometric vs. perspective views

While you have seen how to project a simple three-dimensional line and its end points onto the three coordinate planes, you could have worked with a more complicated object consisting of many points and lines . As you have seen, even a circle can be constructed from just points (dots) or lines with any degree of refinement desired.

While a simple example, the three-dimensional line illustrates the method you will use in the following work: define a shape within the three-dimensional x,y,z space in terms of points and lines having coordinates (x,y,z); operate on them by rotating and translating; project them onto the x,y plane; and then plot them using their x and y coordinates. Thus you are able to project a 3D object onto your computer monitor’s screen.

To rotate a point in three dimensions implies rotating it around the x,y, and z directions. You saw how to carry out two-dimensional rotation around the z direction, Rz, in the previous chapter. Here, you will derive transformations in three dimensions for rotation around the y,x, and z directions.

3.3 Rotation Around the y Direction

Figure 3-6 shows the unit vector geometry for rotation around the y direction, Ry. This is the view that would be seen by looking down onto the top of the x,y,z system. The y axis runs into the plane of the paper.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig6_HTML.jpg
Figure 3-6

Unit vectors for rotation about the y direction. This is a view looking down on the plotting space. The y axis runs into the plane of the paper.

Following the method used in Chapter 2, a point whose position is initially defined by the vector P is rotated to P. Vectors defining the location of P and P’ in the x,y,z (unrotated) and x’,y’,z’ (rotated) systems are
$$ mathbf{P}= xpwidehat{mathbf{i}}+ ypwidehat{mathbf{j}}+ zpwidehat{mathbf{k}} $$
(3-1)
$$ {mathbf{P}}^{prime }=x{p}^{prime}widehat{mathbf{i}}+y{p}^{prime}widehat{mathbf{j}}+z{p}^{prime}widehat{mathbf{k}} $$
(3-2)
$$ {mathbf{P}}^{prime }= xp{widehat{mathbf{i}}}^{prime }+ yp{widehat{mathbf{j}}}^{prime }+ zp{widehat{mathbf{k}}}^{prime } $$
(3-3)
where , , and are unit vector in the x,y, and z directions and ′, ′, and ′ are unit vectors in the x′,y′, and z′ directions. From Figure 3-6, you can see that
$$ {widehat{mathbf{i}}}^{prime }=mathit{cos}(Ry)kern0.1em widehat{mathbf{i}}+(0)widehat{mathbf{j}}-mathit{sin}(Ry)kern0.1em widehat{mathbf{k}} $$
(3-4)
$$ {widehat{mathbf{j}}}^{prime }=(0)kern0.1em widehat{mathbf{i}}+(1)widehat{mathbf{j}}-(0)kern0.1em widehat{mathbf{k}} $$
(3-5)
$$ {widehat{mathbf{k}}}^{prime }=mathit{sin}(Ry)kern0.1em widehat{mathbf{i}}+(0)widehat{mathbf{j}}-mathit{cos}(Ry)kern0.1em widehat{mathbf{k}} $$
(3-6)
Plugging them into Equation 3-3 yields
$$ {mathbf{P}}^{prime }= xpleft[mathit{cos}(Ry)widehat{mathbf{i}}-mathit{sin}(Ry)widehat{mathbf{k}}
ight]+ ypwidehat{mathbf{j}}+ zpleft[mathit{sin}(Ry)widehat{mathbf{i}}+mathit{cos}(Ry)widehat{mathbf{k}}
ight] $$
(3-7)
Separating into ˆi, ˆj, and kˆ components, you get
$$ {mathbf{P}}^{prime }=underset{x{p}^{prime }}{underbrace{left[ xpcos(Ry)+ zpkern0.1em mathit{sin}(Ry)
ight]}}widehat{mathbf{i}}+underset{y{p}^{prime }}{underbrace{left[ yp
ight]}}widehat{mathbf{j}}+underset{z{p}^{prime }}{underbrace{left[- xpkern0.1em mathit{sin}(Ry)+ zpkern0.1em mathit{cos}(Ry)
ight]}}widehat{mathbf{k}} $$
(3-8))
With Equation 3-2,
$$ x{p}^{prime }= xpkern0.1em mathit{cos}(Ry)+ zpkern0.1em mathit{sin}(Ry) $$
(3-9)
$$ y{p}^{prime }= yp $$
(3-10)
$$ z{p}^{prime }=- xpkern0.1em mathit{sin}(Ry)+ zpkern0.1em mathit{cos}(Ry) $$
(3-11)

Equations 3-9 through 3-11 give the coordinates of the rotated point in the local x,y,z system. Of course, yp′=yp in Equation 3-10 since the y coordinate doesn’t change with rotation about the y axis.

Equations 3-9, 3-10, and 3-11 can be expressed in matrix form, as shown in Equation 3-12:
$$ left[egin{array}{c}x{p}^{prime}\ {}y{p}^{prime}\ {}z{p}^{prime}end{array}
ight]=left[egin{array}{ccc}mathit{cos}(Ry)& 0& mathit{sin}(Ry)\ {}0& 1& 0\ {}-mathit{sin}(Ry)& 0& mathit{cos}(Ry)end{array}
ight]kern0.1em left[egin{array}{c} xp\ {} yp\ {} zpend{array}
ight] $$
(3-12)
It can be abbreviated as
$$ left[{P}^{hbox{'}}
ight]=left[ Ry
ight]kern0.1em left[P
ight] $$
(3-13)
[Ry], the transformation matrix for y axis rotation, is
$$ left[ Ry
ight]=left[egin{array}{ccc} Cyleft(1,1
ight)& Cyleft(1,2
ight)& Cyleft(1,3
ight)\ {} Cyleft(2,1
ight)& Cyleft(2,2
ight)& Cyleft(2,3
ight)\ {} Cyleft(3,1
ight)& Cyleft(3,2
ight)& Cyleft(3,3
ight)end{array}
ight] $$
(3-14)
$$ Cyleft(1,1
ight)=mathit{cos}(Ry) $$
(3-15)
$$ Cyleft(1,2
ight)=0 $$
(3-16)
$$ Cyleft(1,3
ight)=mathit{sin}(Ry) $$
(3-17)
$$ Cyleft(2,1
ight)=0 $$
(3-18)
$$ Cyleft(2,2
ight)=0 $$
(3-19)
$$ Cyleft(2,3
ight)=0 $$
(3-20)
$$ Cyleft(3,1
ight)=-mathit{sin}(Ry) $$
(3-21)
$$ Cyleft(3,2
ight)=0 $$
(3-22)
$$ Cyleft(3,3
ight)=mathit{cos}(Ry) $$
(3-23)

These elements will be used in the programs that follow.

3.4 Rotation Around the x Direction

Figure 3-7 shows the unit vector geometry for rotation around the x direction.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig7_HTML.jpg
Figure 3-7

Unit vectors for rotation around the x direction. The x axis runs into the plane of the paper.

You see that
$$ {widehat{mathbf{i}}}^{prime }=(1)kern0.1em widehat{mathbf{i}}+(0)widehat{mathbf{j}}+(0)kern0.1em widehat{mathbf{k}} $$
(3-24)
$$ {widehat{mathbf{j}}}^{prime }=(0)kern0.1em widehat{mathbf{i}}+mathit{cos}(Rx)kern0.1em widehat{mathbf{j}}+mathit{sin}(Rx)kern0.1em widehat{mathbf{k}} $$
(3-25)
$$ {widehat{mathbf{k}}}^{prime }=(0)kern0.1em widehat{mathbf{i}}-mathit{sin}(Rx)kern0.1em widehat{mathbf{j}}+mathit{cos}(Rx)kern0.1em widehat{mathbf{k}} $$
(3-26)
Following the methods in the previous section,
$$ mathbf{P}= xpwidehat{mathbf{i}}+ ypwidehat{mathbf{j}}+ zpwidehat{mathbf{k}} $$
(3-27)
$$ {mathbf{P}}^{prime }= xpwidehat{mathbf{i}}+ ypleft[mathit{cos}(Rx)widehat{mathbf{j}}+mathit{sin}(Rx)widehat{mathbf{k}}
ight]+ zpleft[-mathit{sin}(Rx)widehat{mathbf{j}}+mathit{cos}(Rx)widehat{mathbf{k}}
ight] $$
(3-28)
$$ =underset{x{p}^{prime }}{underbrace{xp}}widehat{mathbf{i}}+underset{y{p}^{prime }}{underbrace{left[ ypkern0.1em mathit{cos}(Rx)- zpkern0.1em mathit{sin}(Rx)
ight]}}widehat{mathbf{j}}+underset{z{p}^{prime }}{underbrace{left[ ypkern0.1em mathit{sin}(Rx)+ zpkern0.1em mathit{cos}(Rx)
ight]}}widehat{mathbf{k}} $$
(3-29)
In matrix form, it’s
$$ left[egin{array}{c}x{p}^{prime}\ {}y{p}^{prime}\ {}z{p}^{prime}end{array}
ight]=left[egin{array}{ccc}1& 0& 0\ {}0& mathit{cos}(Rx)& -mathit{sin}(Rx)\ {}0& mathit{sin}(Rx)& mathit{cos}(Rx)end{array}
ight]kern0.1em left[egin{array}{c} xp\ {} yp\ {} zpend{array}
ight] $$
(3-30)
which can be abbreviated as
$$ left[{P}^{hbox{'}}
ight]=left[ Rx
ight]kern0.1em left[P
ight] $$
(3-31)
This leads to the transformation matrix for x direction rotation of
$$ left[ Rx
ight]=left[egin{array}{ccc}1& 0& 0\ {}0& mathit{cos}(Rx)& -mathit{sin}(Rx)\ {}0& mathit{sin}(Rx)& mathit{cos}(Rx)end{array}
ight] $$
(3-32)
$$ left[ Rx
ight]=left[egin{array}{ccc} Cxleft(1,1
ight)& Cxleft(1,2
ight)& Cxleft(1,3
ight)\ {} Cxleft(2,1
ight)& Cxleft(2,2
ight)& Cxleft(2,3
ight)\ {} Cxleft(3,1
ight)& Cxleft(3,2
ight)& Cxleft(3,3
ight)end{array}
ight] $$
(3-33)
$$ Cxleft(1,1
ight)=1 $$
(3-34)
$$ Cxleft(1,2
ight)=0 $$
(3-35)
$$ Cxleft(1,3
ight)=0 $$
(3-36)
$$ Cxleft(2,1
ight)=0 $$
(3-37)
$$ Cxleft(2,2
ight)=mathit{cos}(Rx) $$
(3-38)
$$ Cxleft(2,3
ight)=-mathit{sin}(Rx) $$
(3-39)
$$ Cxleft(3,1
ight)=0 $$
(3-40)
$$ Cxleft(3,2
ight)=mathit{sin}(Rx) $$
(3-41)
$$ Cxleft(3,3
ight)=mathit{cos}(Rx) $$
(3-42)

3.5 Rotation Around the z Direction

In Chapter 2, you derived the transformation matrix for two-dimensional rotation around the z direction. You will now do it in three dimensions. Repeating the two-dimensional Rz matrix (Equation 3-43) from Chapter 2:
$$ left[egin{array}{c}x{p}^{prime}\ {}y{p}^{prime}end{array}
ight]=left[egin{array}{cc}mathit{cos}(Rz)& -mathit{sin}(Rz)\ {}mathit{sin}(Rz)& mathit{cos}(Rz)end{array}
ight]kern0.1em left[egin{array}{c} xp\ {} ypend{array}
ight] $$
(3-43)
In three dimensions, you have the following:
$$ left[egin{array}{c}x{p}^{prime}\ {}y{p}^{prime}\ {}z{p}^{prime}end{array}
ight]=left[egin{array}{ccc}mathit{cos}(Rz)& -mathit{sin}(Rz)& 0\ {}mathit{sin}(Rz)& mathit{cos}(Rz)& 0\ {}0& 0& 1end{array}
ight]kern0.1em left[egin{array}{c} xp\ {} yp\ {} zpend{array}
ight] $$
(3-44)
$$ left[ Rz
ight]=left[egin{array}{ccc} Czleft(1,1
ight)& Czleft(1,2
ight)& Czleft(1,3
ight)\ {} Czleft(2,1
ight)& Czleft(2,2
ight)& Czleft(2,3
ight)\ {} Czleft(3,1
ight)& Czleft(3,2
ight)& Czleft(3,3
ight)end{array}
ight] $$
(3-45)
$$ Czleft(1,1
ight)=mathit{cos}(Rz) $$
(3-46)
$$ Czleft(1,2
ight)=-mathit{sin}(Rz) $$
(3-47)
$$ Czleft(1,3
ight)=0 $$
(3-48)
$$ Czleft(2,1
ight)=mathit{sin}(Rz) $$
(3-49)
$$ Czleft(2,2
ight)=mathit{cos}(Rz) $$
(3-50)
$$ Czleft(2,3
ight)=0 $$
(3-51)
$$ Czleft(3,1
ight)=0 $$
(3-52)
$$ Czleft(3,2
ight)=0 $$
(3-53)
$$ Czleft(3,3
ight)=1 $$
(3-54)

You can extend the two-dimensional matrix equation to three-dimensions in Equation 3-44 by simply observing that in the first row xp′ does not depend on zp, hence C(1,3)=0; in the second row, yp′ also does not depend on zp, hence c(2,3)=0; in the third row, zp′ does not depend on either xp′ or yp′, hence C(3,1) and C(3,2) both equal 0. C(3,3)=1 since the z coordinate remains unchanged after rotation about the z axis.

The three transformations are summarized as follows:
$$ left[ Rx
ight]=left[egin{array}{ccc}1& 0& 0\ {}0& mathit{cos}(Rx)& -mathit{sin}(Rx)\ {}0& mathit{sin}(Rx)& mathit{cos}(Rx)end{array}
ight] $$
(3-55)
$$ left[ Ry
ight]=left[egin{array}{ccc}mathit{cos}(Ry)& 0& mathit{sin}(Ry)\ {}0& 1& 0\ {}-mathit{sin}(Ry)& 0& mathit{cos}(Ry)end{array}
ight] $$
(3-56)
$$ left[ Rz
ight]=left[egin{array}{ccc}mathit{cos}(Rz)& -mathit{sin}(Rz)& 0\ {}mathit{sin}(Rz)& mathit{cos}(Rz)& 0\ {}0& 0& 1end{array}
ight] $$
(3-57)

3.6 Separate Rotations Around the Coordinate Directions

Figure 3-8 shows separate rotations of a box (a) about the x,y, and z directions. The figure was created using Listing 3-1. The rotations are separate, not sequential. That is, box (b) is box (a) rotated by Rx; box (c) is (a) rotated by Ry; and box (d) is (a) rotated by Rz. The rotations are not additive, which means Ry is not added to the results of Rx and Rz is not added to the results of Rx and Ry; they are each separate rotations of the original box (a). The rotations take place around the center of the box.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig8_HTML.jpg
Figure 3-8

Output from Listing 3-1. Projection (a) of an unrotated box on the x,y plane, (b) rotated around the x direction by Rx=45°, (c) around the y direction by Ry=30°, and (d) around the z direction by Rz=30°. Double-headed red arrows show the direction of rotation using the right-hand rule convention. Heavy lines indicate the top and bottom. The boxes are rotated about their center, which is indicated by a black dot.

Listing 3-1 makes use of functions and lists. Without them, the program would more than double in size. Using them reduces the program size considerably. It could be shortened even further by the use of arrays but the savings would be minimal and tends to obscure the methodology.

Figure 3-9 shows the corner numbering scheme used by Listing 3-1. The corner numbers are in blue. They are Python list numberings and start at 0. Normally we number the corners from 1 to 8. However, in Python, the first element in a list is always 0. In the case of an eight-cornered box, the last, the eighth, is element 7 in the list. For example, the x coordinate of the first point is x[0], the second is x[1], and so on. It’s like numbering the first rung of a ladder as the zeroth rung. Confusing? Yes. Blame it on the C programming language, from which this trap is a carryover. Perhaps the best way to avoid problems is to get in the habit of numbering things from 0 instead of 1, which is what I have done in Figure 3-9. I could have used a different arrangement of numbering in Figure 3-9 but starting with the top left corner and proceeding clockwise seems logical (e.g. I could have started the numbering at the top right-front corner instead of the top upper-left). It doesn’t matter as long as the chosen scheme is consistent with the program.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig9_HTML.jpg
Figure 3-9

Numbering scheme for the box’s corners in Lsiting 3-1. Lists at the upper right contain the coordinate values. They are the same as the lists in Listing 3-1, lines 14, 15, and 16. The center coordinates xc,yc,zc are not the same as used in Listing 3-1. The z axis is not shown.

The lists shown in the figure define the corner coordinates . There are eight elements in each list because there are eight corners in the box. Corner 2, which is the third element in the list, has coordinates x[3]=10,y[3]=-10,z[3]=3. These are local coordinates; in other words, they are relative to the box’s center, which is the center of rotation.

Listing 3-1 starts off by defining lists for [x],[y], and [z] in lines 14-16. These lines hold the coordinates of the box’s corners relative to its center. [xg],[yg], and [zg] in lines 18-20 will hold the global plotting coordinates after transformations have been done. Space is reserved for eight in each list since there are eight corners in the box.

Next are the definitions of the rotation functions rotx, roty, and rotz. They rotate a point’s coordinates xp,yp,zp around the x,y, and z directions, respectively. Each function returns a new set of coordinates: xg,yg, and zg, which are the global coordinates of the rotated point. These coordinates will be used for plotting.

Looking at the definition of rotx , which begins in line 23, when invoked to do a transformation about the x direction rotx receives the box’s center coordinates xc,yc,zc, which, in this case is the center of rotation, plus the point’s unrotated coordinates xp,yp,zp and the angle of rotation about the x direction, Rx. The list a=[xp,yp,zp] in line 24 contains the coordinates of the unrotated point. This is, in effect, a vector to point xp,yp,zp. In line 25, b=[1,0,0] is a list of the first row of the Rx transformation matrix shown in Equation 3-55. Line 26, xpp=np.inner(a,b), forms the dot or scalar product of these lists. There is also an np.dot(a,b) function that could be used. For simple non-complex vectors, np.inner(a,b) and np.dot(a,b) give the same results. But for higher dimensional arrays the results may differ.

To illustrate the calculation of ypp for rotation around the x direction, you have seen that vector p is related to p by
$$ left[egin{array}{c} xp p\ {} yp p\ {} zp pend{array}
ight]=left[egin{array}{ccc}1& 0& 0\ {}0& mathit{cos}(Rx)& -mathit{sin}(Rx)\ {}0& mathit{sin}(Rx)& mathit{cos}(Rx)end{array}
ight]kern0.1em left[egin{array}{c} xp\ {} yp\ {} zpend{array}
ight] $$
(3-58)
where ypp (i.e. yp′) is the y coordinate of the rotated point. Line 27 in the program is the second row of Equation 3-57. The scalar product of a and b is formed in line 28 producing ypp (yp′). That is,
$$ a=left[ xp, yp, zp
ight] $$
(3-59)
$$ b=left[0,mathit{cos}(Rx),-mathit{sin}(Rx)
ight] $$
(3-60)
$$ ypp=mathrm{np}.mathrm{inner}left(mathrm{a},mathrm{b}
ight) $$
(3-61)
$$ = xp(0)+ ypleft(mathit{cos}(Rx)
ight)+ zpleft(-mathit{sin}(Rx)
ight) $$
(3-62)
$$ = ypcos(Rx)- zpsin(Rx) $$
(3-63)
which is line 28. Lines 29 and 30 repeat the process using the third row of Equation 3-57, producing zpp (zp′). Line 31 adds xc,yc,zc, the coordinates of the box’s center, to xpp,ypp,zpp, thus translating the rotated points relative to the origin of the global coordinate system producing [xg,yg,zg] which are the global plotting coordinates. roty and rotz follow the same structure using the rows of [Ry] and [Rz] in their b lists.

Next is the function plotbox in line 56. This plots the box using its global corner coordinates xg,yg, and zg. The loop starting in line 57 plots the top by connecting the first three corners with lines. Line 60 closes the top by plotting a line between corners 3 and 0. This has not been included in the loop, which was set up to plot one corner with the next. The problem comes when you try to connect corner 3 with 0; the algorithm in the loop doesn’t work. It could be modified to handle it, but it’s easier to just add line 60 rather than complicate the loop. The rest of plotbox up to line 68 completes the box. Line 70 plots a dot at its center.

Line 72 starts function plotboxx. This transforms the corner coordinates to get them ready for plotting by plotbox. The loop from line 73 to 74 rotates all eight corners around the x direction by invoking rotx. Line 76 invokes function plotbox, which does the plotting. plotboxy and plotboxz do the same for rotations about the y and z directions.

Up to this point, you have been defining functions. You use functions in this program since many of the operations are repetitive. If you tried to write this program using single statements, it would be at least twice as long.

Control of the program lies between lines 91 and 116. Lines 91-95 plot the first box (a). Since this first box (a) is unrotated, you specify Rx=0 in line 91. You use function plotboxx with the Rx=0 parameter to do the plotting. You could use Ry=0 with plotboxy or Rz=0 with plotboxz. It doesn’t matter since the angle of rotation is 0. Lines 92-94 specify the box’s center coordinates. Line 95 invokes plotboxx. The result is shown in Figure 3-8 as (a). Lines 98-116 produce the rotated boxes (b), (c), and (d).

To summarize the procedure using box (b) as an example, the angle of rotation is set in line 98; the box’s center coordinates in lines 99-101. Then, in line 102, function plotboxx is invoked. The center coordinates and the angle Rx are passed as arguments. plotboxx, which begins in line 72, rotates the eight corners by invoking rotx. plotboxx doesn’t use xc,yc, and zc, but it passes them onto rotx, which needs them. rotx rotates and translates the coordinates producing xg,yg,zg. Line 76 invokes function plotbox , which does the plotting.

In lines 91, 98, 105, and 112 you use the function radians(), which was imported from the math library in line 7. (Note that you could have used numpy for this). It converts an argument in degrees to one in radians, which are required by sin() and cos(). In earlier programs, you did the conversion with np.pi/180.

  1   """
  2   4BOXES
  3   """
  4
  5   import numpy as np
  6   import matplotlib.pyplot as plt
  7   from math import sin, cos, radians #–or use numpy
  8
  9   plt.axis([0,150,100,0])
 10   plt.axis('on')
 11   plt.grid(True)
 12
 13   #————————————————————————-lists
 14   x=[-10,-10,10,10,-10,-10,10,10] #–un-rotated corner coordinates
 15   y=[-10,-10,-10,-10,10,10,10,10] #–relative to box's center
 16   z=[ -3, 3, 3, -3,-3, 3, 3,-3]
 17
 18   xg=[0,1,2,3,4,5,6,7] #–define global coordinates
 19   yg=[0,1,2,3,4,5,6,7]
 20   zg=[0,1,2,3,4,5,6,7]
 21
 22   #———————————————————–function definitions
 23   def rotx(xc,yc,zc,xp,yp,zp,Rx):
 24        a=[xp,yp,zp]
 25        b=[1,0,0] #———————————-[cx11,cx12,cx13]
 26        xpp=np.inner(a,b) #—–scalar product of a,b=xp*cx11+yp*cx12+ zp*cx13
 27        b=[0,cos(Rx),-sin(Rx)] #—————[cx21,cx22,cx23]
 28        ypp=np.inner(a,b)
 29        b=[0,sin(Rx),cos(Rx)] #—————[cx31,cx32,cx33]
 30        zpp=np.inner(a,b)
 31        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 32        return[xg,yg,zg]
 33
 34   def roty(xc,yc,zc,xp,yp,zp,Ry):
 35        a=[xp,yp,zp]
 36        b=[cos(Ry),0,sin(Ry)] #——————–[cx11,cx12,cx13]
 37        xpp=np.inner(a,  b)
 38        b=[0,1,0] #—————[cx21,cx22,cx23]
 39        ypp=np.inner(a,b) #——————–scalar product of a,b
 40        b=[-sin(Ry),0,cos(Ry)] #—————[cx31,cx32,cx33]
 41        zpp=np.inner(a,b)
 42        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 43        return[xg,yg,zg]
 44
 45   def rotz(xc,yc,zc,xp,yp,zp,Rz):
 46        a=[xp,yp,zp]
 47        b=[cos(Rz),-sin(Rz),0] #——————-[cx11,cx12,cx13]
 48        xpp=np.inner(a, b)
 49        b=[sin(Rz),cos(Rz),0] #—————[cx21,cx22,cx23]
 50        ypp=np.inner(a,b)
 51        b=[0,0,1] #—————[cx31,cx32,cx33]
 52        zpp=np.inner(a,b) #———————scalar product of a,b
 53        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 54        return[xg,yg,zg]
 55
 56   def plotbox(xg,yg,zg): # –plots the box using its rotated coordinates xg,yg,zg
 57        for i in (0,1,2): #———————————————-plot top
 58              plt.plot([xg[i],xg[i+1]],[yg[i],yg[i+1]],linewidth=3,color='k')
 59
 60        plt.plot([xg[3],xg[0]],[yg[3],yg[0]],linewidth=3,color='k') #-close top
 61
 62        for i in (4,5,6): #——————————————-plot bottom
 63              plt.plot([xg[i],xg[i+1]],[yg[i],yg[i+1]],linewidth=3,color='k')
 64
 65        plt.plot([xg[7],xg[4]],[yg[7],yg[4]],linewidth=3,color='k') #–close bottom
 66
 67        for i in (0,1,2,3): #——————————————plot sides
 68             plt.plot([xg[i],xg[i-4]],[yg[i],yg[i-4]],linewidth=1,color='k')
 69
 70        plt.scatter(xc,yc,s=5) #–plot a dot at the center
 71
 72   def plotboxx(xc,yc,zc,Rx):
 73        for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
 74              [xg[i],yg[i],zg[i]]=rotx(xc,yc,zc,x[i],y[i],z[i],Rx)
 75
 76        plotbox(xg,yg,zg)
 77
 78   def plotboxy(xc,yc,zc,Ry):
 79        for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
 80             [xg[i],yg[i],zg[i]]=roty(xc,yc,zc,x[i],y[i],z[i],Ry)
 81
 82        plotbox(xg,yg,zg)
 83
 84   def plotboxz(xc,yc,zc,Rz):
 85        for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
 86              [xg[i],yg[i],zg[i]]=rotz(xc,yc,zc,x[i],y[i],z[i],Rz)
 87
 88        plotbox(xg,yg,zg)
 89
 90   #——————————————————————–R=0 box(a)
 91   Rx=radians(0)
 92   xc=25 #—————box (a) center coordinates
 93   yc=40
 94   zc=20
 95   plotboxx(xc,yc,zc,Rx) #–since Rx=0 we could use plotboxy or plotboxz
 96
 97   #———————————————————————Rx box(b)
 98   Rx=radians(45)
 99   xc=55
100   yc=40
101   zc=20
102   plotboxx(xc,yc,zc,Rx)
103
104   #——————————————————————–Ry box (c)
105   Ry=radians(30)
106   xc=85
107   yc=40
108   zc=20
109   plotboxy(xc,yc,zc,Ry)
110
111   #——————————————————————–Rz box (d)
112   Rz=radians(30)
113   xc=115
114   yc=40
115   zc=20
116   plotboxz(xc,yc,zc,Rz)
117
118   #————————————————————————-notes
119   plt.text(23,63,'(a)')
120   plt.text(53,63,'(b)')
121   plt.text(83,63,'(c)')
122   plt.text(112,63,'(d)')
123   plt.text(21,73,'R=0')
124   plt.text(47,73,'Rx=45°')
125   plt.text(77,73,'Ry=30°')
126   plt.text(107,73,'Rz=30°')
127   plt.arrow(42,40,25,0,head_width=2,head_length=3,color='r') #–red arrows
128   plt.arrow(42,40,28,0,head_width=2,head_length=3,color='r')
129   plt.arrow(85,25,0,27,head_width=2,head_length=2,color='r')
130   plt.arrow(85,25,0,29,head_width=2,head_length=2,color='r')
131   plt.plot([8,130],[8,8],color='k') #–axes
132   plt.plot([8,8],[8,85],color='k')
133   plt.text(120,6,'X')
134   plt.text(3,80,'Y')
135   plt.scatter(115,40,s=30,color='r') #———–red dot center of box (d)
136
137   plt.show()
Listing 3-1

Program 4BOXES

3.7 Sequential Rotations Around the Coordinate Directions

In Listing 3-1, you operated on a box’s initial corner coordinates defined by the lists in lines 14, 15, and 16. The program produced separate rotations around the x,y, and z coordinate directions. In this section, you begin with the same set of corner coordinates but you rotate sequentially. That is, after a rotation Rx about the x direction (b), rotation Ry is added to the results of Rx (c). Rz is then added to the results of Ry (d). The rotations are thus not independent as before but are additive. You do this by replacing the x,y, and z definitions in lines 14, 15, and 16 with a new set of coordinates following each rotation. That is, the box’s corner coordinates are updated after each rotation so that the next rotation starts with the updates coordinates. This is accomplished by simply modifying functions plotboxx, plotboxy, and plotboxz between lines 72-88 in Listing 3-1. In Listing 3-2, lines 74b, 80b, and 86b are added. They do the updating by replacing the initial corner coordinates x,y,z with the transformed ones xg,yg,zg after each rotation. The code replaces lines 72-88 in Listing 3-1.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig10_HTML.jpg
Figure 3-10

Sequential rotations of a box. Box (a) is rotated by Rx=30° to (b), then by an additional rotation of Ry=30° to (c), and then by an additional rotation of Rz=15° to (d). x and y axes show direction only. Coordinate values are indicated by the grid.

71
72   def plotboxx(xc,yc,zc,Rx):
73         for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
74                [xg[i],yg[i],zg[i]]=rotx(xc,yc,zc,x[i],y[i],z[i],Rx)
74b               [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
75
76         plotbox(xg,yg,zg)
77
78   def plotboxy(xc,yc,zc,Ry):
79         for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
80                 [xg[i],yg[i],zg[i]]=roty(xc,yc,zc,x[i],y[i],z[i],Ry)
80b                [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
81
82         plotbox(xg,yg,zg)
83
84   def plotboxz(xc,yc,zc,Rz):
85         for i in (0,1,2,3,4,5,6,7): #————————–rotate eight corners
86                [xg[i],yg[i],zg[i]]=rotz(xc,yc,zc,x[i],y[i],z[i],Rz)
86b               [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
87
88         plotbox(xg,yg,zg)
89
Listing 3-2

Program 4BOXESUPDATE

The transformation parameters are set in lines 91-116 by the values of rotations Rx, Ry, and Rz and the box center coordinates xc, yc, zc.

The sequence of rotations in this program is hard-wired to produce Figure 3-10 with (a) first, followed by (b), (c), and (d). In a general program, the sequence and values of rotations and center coordinates could be set to anything suitable by moving sections of code around or by entering the sequences through the keyboard. You will do both shortly. But first, you will do sequential rotations of a circle.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig11_HTML.jpg
Figure 3-11

Sequential rotations of a circle created by Listing 3-3. Circle (a) is rotated by Rx=45° to (b), then by an additional rotation of Ry=70° to (c), and then by an additional rotation of Rz=90° to (d). Red indicates the upper half of circle. x and y axes show direction only, not coordinate values, which are indicated by the grid.

Listing 3-3 is similar to the preceding modified version of Listings 3-1 and 3-2 where you did sequential rotations of a box. In that program, the box had eight corners, which had to be transformed and updated with every rotation. Here you have a circle, which has many more points, to transform and update.

In lines 23-38, you fill lists between lines 33 and 38 with starting values of local and global coordinates of points around the circumference of the circle. They are spaced dphi=5° apart as shown in line 25. The circle’s radius is 10 as shown in line 27. The empty lists were previously defined in lines 14-20. As the loop starting at line 29 advances around the circle with angle phi, lines 30 to 32 calculate the local coordinates of each point. Lines 33-38 add the coordinates to the list using the append() function, which adds elements to a list. For example, with each cycle through the loop line 33 appends (adds) the local value of xp at the current angle phi to the x list. Since you are just filling the list at this point, you can use xp,yp,zp to also fill the xg, yg, and zg lists in lines 36-38. Note that zp=0 (program line 32) in this initial definition of the circle. That is, the circle starts off flat in the x,y plane. Subsequent rotations will be around that initial orientation.

Lines 41-72 define the transformation functions as before. The circle plotting function extends from line 75-86. Lines are used to plot the circle. The plotting loop runs from 78-82. Line 86 plots a dot at the center.

Rather than counting the number of points around the circle, you use the range(len(x)) function to give the number of elements in the lists. You can use the length of x as a measure since all lists have the same length. Lines 79-82 plot the top half red and the bottom half green. Lines 83-84 update the last xg any yg global coordinates to use when plotting the lines as before. You don’t need to include zg here since you use only xg and yg when plotting. Lines 89-108 transform coordinates as was done in Listings 3-1 and 3-2. The difference is here you have to deal with lists len(x) long whereas previously you had only eight corners.

  1   """
  2   SEQUENTIALCIRCLES
  3   """
  4
  5   import numpy as np
  6   import matplotlib.pyplot as plt
  7   from math import sin, cos, radians
  8
  9   plt.axis([0,150,100,0])
 10   plt.axis('on')
 11   plt.grid(True)
 12
 13   #——————————————————————define lists
 14   x=[]
 15   y=[]
 16   z=[]
 17
 18   xg=[]
 19   yg=[]
 20   zg=[]
 21
 22   #——————————————fill lists with starting coordinates
 23   phi1=radians(0)
 24   phi2=radians(360)
 25   dphi=radians(5) #–circumferential points spaced 5 degrees
 26
 27   r=10 #–circle's radius
 28
 29   for phi in np.arange(phi1,phi2+dphi,dphi): #–establish coordinates of circumferential points
 30         xp=r*cos(phi)
 31         yp=r*sin(phi)
 32   zp=0
 33   x.append(xp)   #–fill lists
 34   y.append(yp)
 35   z.append(zp)
 36   xg.append(xp)
 37   yg.append(yp)
 38   zg.append(zp)
 39
 40   #—————————————————–define rotation functions
 41   def rotx(xc,yc,zc,xp,yp,zp,Rx):
 42        a=[xp,yp,zp]
 43        b=[1,0,0] #———————————-[cx11,cx12,cx13]
 44        xpp=np.inner(a,b) #—–scalar product of a,b=xp*cx11+yp*cx12+ zp*cx13
 45        b=[0,cos(Rx),-sin(Rx)] #—————[cx21,cx22,cx23]
 46        ypp=np.inner(a,b)
 47        b=[0,sin(Rx),cos(Rx)] #—————[cx31,cx32,cx33]
 48        zpp=np.inner(a,b)
 49        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 50        return[xg,yg,zg]
 51
 52   def roty(xc,yc,zc,xp,yp,zp,Ry):
 53        a=[xp,yp,zp]
 54        b=[cos(Ry),0,sin(Ry)] #——————–[cx11,cx12,cx13]
 55        xpp=np.inner(a, b)
 56        b=[0,1,0] #—————[cx21,cx22,cx23]
 57        ypp=np.inner(a,b) #——————–scalar product of a,b
 58        b=[-sin(Ry),0,cos(Ry)] #—————[cx31,cx32,cx33]
 59        zpp=np.inner(a,b)
 60        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 61        return[xg,yg,zg]
 62
 63   def rotz(xc,yc,zc,xp,yp,zp,Rz):
 64        a=[xp,yp,zp]
 65        b=[cos(Rz),-sin(Rz),0] #——————-[cx11,cx12,cx13]
 66        xpp=np.inner(a, b)
 67        b=[sin(Rz),cos(Rz),0] #—————[cx21,cx22,cx23]
 68        ypp=np.inner(a,b)
 69        b=[0,0,1] #—————[cx31,cx32,cx33]
 70        zpp=np.inner(a,b) #———————scalar product of a,b
 71        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 72        return[xg,yg,zg]
 73
 74   #——————————————————define circle plotting function
 75   def plotcircle(xg,yg,zg):
 76        lastxg=xg[0]
 77        lastyg=yg[0]
 78        for i in range(len(x)): #—–len(x)=length of all lists
 79              if i < len(x)/2: #—–half green
 80                     plt.plot([lastxg,xg[i]],[lastyg,yg[i]],linewidth=1,color='g')
 81              else:
 82                     plt.plot([lastxg,xg[i]],[lastyg,yg[i]],linewidth=1,color='r')
 83        lastxg=xg[i]
 84        lastyg=yg[i]
 85
 86        plt.scatter(xc,yc,s=5) #–plot a dot at the center
 87
 88   #———————————————–transform coordinates and plot
 89   def plotcirclex(xc,yc,zc,Rx): #—————-transform & plot Rx circle
 90       for i in range(len(x)): #–for i in range(len(x)): ok too
 91              [xg[i],yg[i],zg[i]]=rotx(xc,yc,zc,x[i],y[i],z[i],Rx)
 92              [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
 93
 94       plotcircle(xg,yg,zg) #—————plot
 95
 96   def plotcircley(xc,yc,zc,Ry):
 97        for i in range(len(x)): #—————–transform & plot Ry circle
 98              [xg[i],yg[i],zg[i]]=roty(xc,yc,zc,x[i],y[i],z[i],Ry)
 99              [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
100
101        plotcircle(xg,yg,zg)
102
103   def plotcirclez(xc,yc,zc,Rz):
104        for i in range(len(x)): #—————–transform &  plot Rz circle
105              [xg[i],yg[i],zg[i]]=rotz(xc,yc,zc,x[i],y[i],z[i],Rz)
106              [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
107
108        plotcircle(xg,yg,zg)
109
110   #——————————————————————plot circles
111   Rx=radians(0)
112   xc=25 #—————circle (a) center coordinates
113   yc=40
114   zc=20
115   plotcirclex(xc,yc,zc,Rx) #–since R=0 we could use plotcircley or plotcirclez
116
117   #—————————————————————–Rx circle (b)
118   Rx=radians(45)
119   xc=55
120   yc=40
121   zc=20
122   plotcirclex(xc,yc,zc,Rx)
123
124   #—————————————————————–Ry circle (c)
125   Ry=radians(70)
126   xc=85
127   yc=40
128   zc=20
129   plotcircley(xc,yc,zc,Ry)
130
131   #—————————————————————–Rz circle (d)
132   Rz=radians(90)
133   xc=115
134   yc=40
135   zc=20
136   plotcirclez(xc,yc,zc,Rz)
137
138   #——————————————————————-notes
139   plt.text(23,63,'(a)')
140   plt.text(53,63,'(b)')
141   plt.text(83,63,'(c)')
142   plt.text(112,63,'(d)')
143   plt.text(21,73,'R=0')
144   plt.text(47,73,'Rx=45 ° ')
145   plt.text(77,73,'Ry=70 ° ')
146   plt.text(107,73,'Rz=90 ° ')
147   plt.arrow(42,40,25,0,head_width=2,head_length=3,color='r') #–red arrows
148   plt.arrow(42,40,28,0,head_width=2,head_length=3,color='r')
149   plt.arrow(85,25,0,27,head_width=2,head_length=2,color='r')
150   plt.arrow(85,25,0,29,head_width=2,head_length=2,color='r')
151   plt.plot([8,130],[8,8],color='k') #–axes
152   plt.plot([8,8],[8,85],color='k')
153   plt.text(120,6,'X')
154   plt.text(3,80,'Y')
155   plt.scatter(115,40,s=30,color='r') #———–red dot center of box (d)
156
157   plt.show()
Listing 3-3

Program SEQUENTIALCIRCLES

3.8 Matrix Concatenation

Comparing Figure 3-12 with 3-11, you can see that, although Rx,Ry, and Rz have the same values in both figures, the resulting orientations of the circle in (c) and (d) are different. This is because the order of the rotation in Figure 3-11 is Rx,Ry,Rz while in Figure 3-12 it is Rx,Rz,Ry. Clearly the order of rotations is important.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig12_HTML.jpg
Figure 3-12

Circle (a) is rotated sequentially by Rx=45° to (b), then by an additional rotation of Rz=90° to (c), followed by an additional rotation of Ry=70 to (d). Red indicates the lower half of circle. x and y axes show direction only, not coordinate values, which are indicated by the grid.

You can demonstrate this yourself. Take a book and place it on the edge of your desk front side up, top facing to the right. Imagine the desk’s edge is the x direction going from left to right. Next, rotate it 90 degrees around the x direction, followed by 90 degrees around the z direction. This is RxRz. The book will be upside down with the front facing you. Then reverse the order by rotating around the z direction first followed by the x direction. This is RzRx. As you can see, you get a different final orientation of the book in the two cases.

While you have carried out sequential rotations by ordering them and updating rotated coordinates in the program’s code, mathematically it amounts to a multiplication of matrices. For example, the following equation produces a rotation Rx of vector [P] followed by a rotation Rz. The two rotations produce the vector [P′].
$$ left[{P}^{prime}
ight]=left[ Rz
ight]kern0.1em left[ Rx
ight]kern0.1em left[P
ight] $$
(3-64)
[Rx] operates on the vector [P], [Rz] then operates on the result of [Rx][P]. To rotate by Rz followed by Rx,
$$ left[{P}^{prime}
ight]=left[ Rx
ight]kern0.1em left[ Rz
ight]kern0.1em left[P
ight] $$
(3-65)
In general,
$$ left[ Rx
ight]kern0.1em left[ Rz
ight]
e left[ Rz
ight]kern0.1em left[ Rx
ight] $$
(3-66)
You can show this by a simple example using two-dimensional matrices. Consider two matrices, A and B, where
$$ left[A
ight]=left[egin{array}{cc}a&amp; b\ {}c&amp; dend{array}
ight] $$
(3-67)
$$ left[B
ight]=left[egin{array}{cc}e&amp; f\ {}g&amp; hend{array}
ight] $$
(3-68)
$$ AB=left[egin{array}{cc}a&amp; b\ {}c&amp; dend{array}
ight]kern0.1em left[egin{array}{cc}e&amp; f\ {}g&amp; hend{array}
ight]=left[egin{array}{cc} ae+ bg&amp; af+ bh\ {} ce+ dg&amp; cf+ dhend{array}
ight] $$
(3-69)
$$ BA=left[egin{array}{cc}e&amp; f\ {}g&amp; hend{array}
ight]kern0.1em left[egin{array}{cc}a&amp; b\ {}c&amp; dend{array}
ight]=left[egin{array}{cc} ae+ cf&amp; be+ df\ {} ag+ ch&amp; bg+ dhend{array}
ight] $$
(3-70)
$$ 	herefore AB
e BA $$
(3-71)
For only three rotations around three different coordinate directions, there are six combinations of possible transformation sequences:
$$ RxRyRz $$
(3-72)
$$ RxRzRy $$
(3-73)
$$ RyRxRz $$
(3-74)
$$ RyRzRx $$
(3-75)
$$ RzRxRy $$
(3-76)
$$ RzRyRx $$
(3-77)

Each of these combinations involves three separate rotations. You could multiply the three transformation matrices shown in Equations 3-55, 3-56, and 3-57 to get a single transformation matrix for each of these combinations. You could then write a program that would execute each of these combinations: select one combination, input the three angles, and then get the final rotation. But what if you wanted more than three rotations, such as RyRzRxRyRz? That would require a lot of matrix multiplying! Clearly it’s much easier to incorporate the sequencing by coding it into the Python program and updating coordinates after each transformation, as you have learned how to do here.

To produce Figure 3-12, lines 110-136 of Listing 3-3 were replaced with the code in Listing 3-4.

109
110   #——————————————————————plot circles
111   Rx=radians(0)
112   xc=25 #—————circle (a) center coordinates
113   yc=40
114   zc=20
115   plotcirclex(xc,yc,zc,Rx) #–since R=0 we could use plotcircley or plotcirclez
116
117   #—————————————————————–Rx circle (b)
118   Rx=radians(45)
119   xc=55
120   yc=40
121   zc=20
122   plotcirclex(xc,yc,zc,Rx)
123
124   #—————————————————————–Rz circle (d)
125   Rz=radians(90)
126   xc=85
127   yc=40
128   zc=20
129   plotcirclez(xc,yc,zc,Rz)
130
131   #—————————————————————–Ry circle (c)
132   Ry=radians(70)
133   xc=115
134   yc=40
135   zc=20
136   plotcircley(xc,yc,zc,Ry)
137
Listing 3-4

Program SEQUENTIALCIRCLESUPDATE

Here you have performed the operation RxRzRy, reversing the order of the last two transformations. Circle (a) is plotted as before with Rx=0 in line 111. Also as before, circle (b) is plotted next with Rx=45 degrees in line 118. The difference is in lines 124-136 where the rotations Ry and Rz are reversed and Rz is plotted before Ry. The angles have the same values as before. Rearranging the order of plotting is easy; just cut and paste sections of the code. But be sure to update the center coordinates xc, yc, and zc. You could make the program a lot more user-friendly by introducing the input() function, which will give you the ability to input the order of transformations through the keyboard. You could then enter the rotations Rx,Ry, or Rz and the amount and the center coordinates in any order. You will do that next.

3.9 Keyboard Data Entry with Functional Program Structure

As you saw in the discussion of matrix concatenation, rearranging the order of rotations in a program can be a useful option. However, as you will see in this section, entering data via the keyboard is much more satisfactory. You will also use a functional programming structure where a few lines of code control various predefined functions that carry out the various operations. This will give you great flexibility in controlling the program.

Listing 3-5 produced the results shown in Figures 3-13 through 3-16. The first figure shows a circle rotated around the x direction by 0°; the second around the y direction by 60°; the third around the x direction by 45°; and the fourth around the z direction by 90°. All rotations are added to the previous orientation of the circle. The axis of rotation and the amount were entered through the keyboard. The sequence of rotation directions did not matter, nor did the number of rotations.

Referring to Listing 3-5, lines 111-113 specify the circle’s center coordinates. All circles have the same center coordinates. The while True: statement in line 115 keeps the data entry loop running so you can do an unlimited number of sequential rotations. Line 116 asks you to specify the axis of rotation in the Spyder output pane. Enter x,y, or z in lower case letters. To exit the loop, press the Enter key. (Important: If you are using the Spyder console, be sure to click the mouse with the cursor in the output pane before entering anything. If you forget and leave it in the program pane, you are liable to get an unwanted x,y, or z imbedded somewhere in the program. If this happens, go to the top of the screen and open a new console. This essentially starts the program over.). If you enter x (lower case), line 118 asks for the angle of rotation Rx. Enter it as a positive or negative angle in degrees. The input() function returns a string. The float command converts it to a float. Line 119 then invokes function plotcirclex(), which plots the rotated circle. Ry and Rz rotations are carried out in a similar way. Note there is no restriction on the sequence or the number of rotations. Line 126 checks to see if you entered a blank for axis, in which case line 127 exits the program. All circles are rotated around the same center, xc,yc,zc. If you want to be able to move the centers of each circle, just add input() lines for the center coordinates between lines 115 and 116.

Lines 89-108 rotate and update the coordinates of the circle’s circumferential points as was done in Listing 3-3. In function plotcircle() , lines 71-86 do the plotting. Each time this function is invoked, the axes and grid are replotted. Line 86 shows the latest plot.

This program is an important illustration of program control. Just the few lines between 115 and 127 control the entire operation of the program and give great flexibility in controlling the sequence of operations and the data used. In other programming languages, such as Basic and Fortran, this is referred to as top-down programming . In those languages subroutines, which are the equivalents of Python functions, are generally placed at the bottom, while the controlling code is put at the top. In Python, you normally put the functions at the top with the control at the bottom, a style called bottom-up programming . Whether control is at the top or the bottom, this program structure is called functional programming since the controlling code uses functions to carry out the various operations. Since controlling data is input through the keyboard, it offers considerable flexibility.
../images/456962_1_En_3_Chapter/456962_1_En_3_Fig13_HTML.jpg
Figure 3-13

The circle is rotated around the x axis by 0°

../images/456962_1_En_3_Chapter/456962_1_En_3_Fig14_HTML.jpg
Figure 3-14

The previous circle is rotated around the y axis by 60°

../images/456962_1_En_3_Chapter/456962_1_En_3_Fig15_HTML.jpg
Figure 3-15

The previous circle is rotated around the y axis by 45°

../images/456962_1_En_3_Chapter/456962_1_En_3_Fig16_HTML.jpg
Figure 3-16

The previous circle is rotated around the z axis by 90°

  1   """
  2   KEYBOARDDATAENTRY
  3   """
  4
  5   import numpy as np
  6   import matplotlib.pyplot as plt
  7   from math import sin, cos, radians
  8
  9   #——————————————————————-define  lists
 10   x=[]
 11   y=[]
 12   z=[]
 13
 14   xg=[]
 15   yg=[]
 16   zg=[]
 17
 18   #——————————————fill lists with starting coordinates
 19   phi1=radians(0)
 20   phi2=radians(360)
 21   dphi=radians(5) #–circumferential points spaced 5 degrees
 22
 23   radius=15 #–circle's radius
 24
 25   for phi in np.arange(phi1,phi2+dphi,dphi): #–establish coordinates of circumferential points
 26         xp=radius*cos(phi)
 27         yp=radius*sin(phi)
 28         zp=0
 29         x.append(xp) #–fill lists
 30         y.append(yp)
 31         z.append(zp)
 32         xg.append(xp)
 33         yg.append(yp)
 34         zg.append(zp)
 35
 36   #—————————————————–define rotation functions
 37   def rotx(xc,yc,zc,xp,yp,zp,Rx):
 38        a=[xp,yp,zp]
 39        b=[1,0,0] #———————————-[cx11,cx12,cx13]
 40        xpp=np.inner(a,b) #—–scalar product of a,b=xp*cx11+yp*cx12+ zp*cx13
 41        b=[0,cos(Rx),-sin(Rx)] #—————[cx21,cx22,cx23]
 42        ypp=np.inner(a,b)
 43        b=[0,sin(Rx),cos(Rx)] #—————[cx31,cx32,cx33]
 44        zpp=np.inner(a,b)
 45        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 46        return[xg,yg,zg]
 47
 48   def roty(xc,yc,zc,xp,yp,zp,Ry):
 49        a=[xp,yp,zp]
 50        b=[cos(Ry),0,sin(Ry)] #——————–[cx11,cx12,cx13]
 51        xpp=np.inner(a, b)
 52        b=[0,1,0] #—————[cx21,cx22,cx23]
 53        ypp=np.inner(a,b) #——————–scalar product of a,b
 54        b=[-sin(Ry),0,cos(Ry)] #—————[cx31,cx32,cx33]
 55        zpp=np.inner(a,b)
 56        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 57        return[xg,yg,zg]
 58
 59   def rotz(xc,yc,zc,xp,yp,zp,Rz):
 60        a=[xp,yp,zp]
 61        b=[cos(Rz),-sin(Rz),0] #——————-[cx11,cx12,cx13]
 62        xpp=np.inner(a, b)
 63        b=[sin(Rz),cos(Rz),0] #—————[cx21,cx22,cx23]
 64        ypp=np.inner(a,b)
 65        b=[0,0,1] #—————[cx31,cx32,cx33]
 66        zpp=np.inner(a,b) #———————scalar product of a,b
 67        [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
 68        return[xg,yg,zg]
 69
 70   #———————————————–define circle plotting function
 71   def plotcircle(xg,yg,zg):
 72        lastxg=xg[0]
 73        lastyg=yg[0]
 74        for i in range(len(x)): #–for i in range(len(x)): ok too
 75               if i < len(x)/2: #—–half green
 76                      plt.plot([lastxg,xg[i]],[lastyg,yg[i]],linewidth=1  ,color='g')
 77               else:
 78                     plt.plot([lastxg,xg[i]],[lastyg,yg[i]],linewidth=1  ,color='r')
 79        lastxg=xg[i]
 80        lastyg=yg[i]
 81
 82        plt.scatter(xc,yc,s=5,color='k') #–plot a dot at the center
 83        plt.axis([0,150,100,0]) #–replot axes and grid
 84        plt.axis('on')
 85        plt.grid(True)
 86        plt.show() #–plot latest rotation
 87
 88   #————————————————transform coordinates and plot
 89   def plotcirclex(xc,yc,zc,Rx): #————-transform and plot Rx circle
 90        for i in range(len(x)):
 91               [xg[i],yg[i],zg[i]]=rotx(xc,yc,zc,x[i],y[i],z[i],Rx)
 92               [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
 93
 94        plotcircle(xg,yg,zg) #—————plot
 95
 96   def plotcircley(xc,yc,zc,Ry):
 97         for i in range(len(x)): #—————–transform and plot Ry circle
 98                [xg[i],yg[i],zg[i]]=roty(xc,yc,zc,x[i],y[i],z[i],Ry)
 99                [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
100
101        plotcircle(xg,yg,zg)
102
103   def plotcirclez(xc,yc,zc,Rz):
104        for i in range(len(x)): #—————–transform and plot Rz circle
105               [xg[i],yg[i],zg[i]]=rotz(xc,yc,zc,x[i],y[i],z[i],Rz)
106               [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
107
108        plotcircle(xg,yg,zg)
109
110   #——————————————————————plot circles
111   xc=75 #–center coordinates
112   yc=50
113   zc=50
114
115   while True:
116        axis=input('x, y or z?: ') #–input axis of rotation (lower case)
117        if axis == 'x': #–if x axis
118               Rx=radians(float(input('Rx degrees?: ')))
119               plotcirclex(xc,yc,zc,Rx) #–call function plotcirclex
120        if axis == 'y':
121              Ry=radians(float(input('Ry degrees?: ')))
122              plotcircley(xc,yc,zc,Ry)
123        if axis == 'z':
124               Rz=radians(float(input('Rz degrees?: ')))
125               plotcirclez(xc,yc,zc,Rz)
126        if axis == ":
127               break
Listing 3-5

Program KEYBOARDDATAENTRY

3.10 Summary

In this chapter, you learned how to construct three-dimensional coordinate axes and three-dimensional shapes and rotate them around the three coordinate directions. This involved derivation of rotation transformations around the three coordinate directions. You saw the difference between rotating an object once from its original orientation and rotating it in sequential steps where each subsequent rotation uses the object’s coordinates from the prior rotation as the starting point. You explored the idea that the sequence of rotations is important; Rx,Ry,Rz does not produce the same results as Rx,Rz,Ry. This was shown by matrix concatenation. Finally, you developed a program where sequential rotations could be entered through the keyboard as opposed to specifying them in the program. All of this work involved the use of lists.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.66.156