JavaApplications of the Vector Dot Product, Math for Java Game Programmers

Applications of the Vector Dot Product, Math for Java Game Programmers

Java Programming Notes # 1716


Preface

General

Next in a series

This tutorial is the next lesson in a series designed to teach you some of the mathematical skills that you will need (in addition to good programming skills) to become a successful game programmer. The first lesson was titled Math for Java Game Programmers, Getting Started. The previous lesson was titled Math for Java Game Programmers, Getting Started with the Vector Dot Product (seeResources).

In addition to helping you with your math skills, I will also help you learn how to incorporate those skills into object-oriented programming using Java. If you are familiar with other object-oriented programming languages such as C#, you should have no difficulty porting this material from Java to those other programming languages.

Lots of graphics

Since most computer games make heavy use of either 2D or 3D graphics, you will need skills in the mathematical areas that are required for success in 2D and 3D graphics programming. As a minimum, this includes but is not limited to skills in:

  • Geometry
  • Trigonometry
  • Vectors
  • Matrices
  • 2D and 3D transforms
  • Transformations between coordinate systems
  • Projections

Of course, game programming requires mathematical skills beyond those required for graphics. However, for starters, this series will concentrate on the last five items in the above list.

What you have learned

In the previous lesson, which was the first part of a two-part miniseries on the vector dot product, you learned the fundamentals of the vector dot product in both 2D and 3D. You learned how to update the game-math library to support various aspects of the vector dot product, and you learned how to write 2D and 3D programs that use the vector dot product methods in the game-math library.

What you will learn

In this lesson, you will learn how to apply the vector dot product to three different applications. First, you will learn how to use the dot product to compute nine different angles of interest that a vector makes with various elements in 3D space. Next, you will learn how to use the dot product to find six of the infinite set of vectors that are perpendicular to a given vector as shown in Figure 3, and finally, you will learn how to use the dot product to perform back-face culling to convert the left image in Figure 1 to the right image in Figure 1.

Figure 1. Using the vector dot product for back-face culling.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. Using the vector dot product for back-face culling.
  • Figure 2. Screen shot of the output from the program named DotProd3D05.
  • Figure 3. Six (magenta) vectors that are perpendicular to a given (black) vector.
  • Figure 4. Screen output when one coordinate has a value of zero.
  • Figure 5. A general formulation of 3D vector perpendicularity.

Listings

  • Listing 1. Beginning of the actionPerformed method in the program named DotProd3D05.
  • Listing 2. Create ColMatrix3D objects that represent projections.
  • Listing 3. Create and draw vectors.
  • Listing 4. Compute and display the nine angles.
  • Listing 5. Beginning of the actionPerformed method for the program named DotProd3D06.
  • Listing 6. Remainder of the actionPerformed method.
  • Listing 7. The method named drawTheCylinder in DotProd3D04.
  • Listing 8. The method named drawTheCylinder in DotProd3D03.
  • Listing 9. Source code for the game-math library named GM02.
  • Listing 10. Source code for the program named DotProd3D05.
  • Listing 11. Source code for the program named DotProd3D06.
  • Listing 12. Source code for the program named DotProb3D04.
  • Listing 13. Source code for the program named DotProb3D03.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

General background information

In this series, I will frequently refer you to an excellent interactive tutorial on Vector Math for 3D Computer Graphics by Dr. Bradley P. Kjell (see Resources) for the required technical background. Then I will help you learn how to incorporate the knowledge that you gain from Dr. Kjell’s tutorial into Java code with a heavy emphasis on object-oriented programming.

In the process, I will develop and explain a game-math library that you can use to experiment with and to confirm what you learn from the Kjell tutorial. The library started out small and will grow as we progress through more and more material in subsequent lessons.

For this lesson, I recommend that you concurrently study the Kjell tutorial through Chapter 10, Angle between 3D Vectors.

Preview

This lesson will build on what you learned about the vector dot product in the previous lesson titled Math for Java Game Programmers, Getting Started with the Vector Dot Product (see Resources). In that lesson, you learned some of the theory behind the dot product. In this lesson, you will learn how to use the dot-product methods of the game-math library to write several applications. I will present and explain the following four programs:

  • DotProd3D05 – Demonstrates how the dot product can be used to compute nine different angles of interest that a vector makes with various elements in 3D space.
  • DotProd3D06 – Demonstrates the use of the dot product to find six of the infinite set of vectors that are perpendicular to a given vector. (See Figure 3.)
  • DotProd3D04 – Draws the same 3D object as the one drawn in DotProd3D03 but without the benefit of back-face culling. (See Figure 1.)
  • DotProd3D03 – Demonstrates use of the vector dot product for back-face culling. (See Figure 1.)

Discussion and sample code

 
Testing
All of the programs in this lesson were tested using JDK 1.6 running under Windows XP.

The game-math library named GM02

The game-math library has not been modified since the previous lesson. Therefore, there is nothing new to discuss and explain insofar as the library is concerned. For your convenience, a complete listing of the source code for the library is provided in Listing 9 near the end of the lesson.

The program named DotProd3D05

In order to understand this and the following programs, you need to understand the material in the Kjell tutorial through Chapter 10, Angle between 3D Vectors (see Resources).

Game programming frequently requires the determination of angles of various types. The purpose of this program is to demonstrate how the dot product can be used to compute nine different angles of interest that a vector makes with various elements in 3Dspace.

Figure 2 shows a screen shot of the graphical user interface provided by this program.

Figure 2. Screen shot of the output from the program named DotProd3D05.

Angles relative to the axes

First, the program computes and displays the angle between a user-specified vector (drawn in black in Figure 2) and each of the X, Y, and Z axes. These values are displayed with the labels Angle X, Angle Y, and Angle Z in Figure 2.

Angles relative to the XY, YZ, and ZX planes

Then the program computes and displays the angle between the vector and each of the XY, YZ, and ZX planes. In this case, the program computes the smallest possible angle by projecting the vector onto the plane and then computing the angle between the vector and its projection. These values are displayed with the labels Angle XY, Angle YZ, and Angle ZX.

Angles of projections relatives to the axes

Finally, the program computes and displays the angle between the projection of the vector on each of the three planes and one of the axes that defines each plane. The angle between the projection and the other axis that defines the plane is 90 degrees minus the computed angle. Specifically the values that are computed and displayed are:

  • Projection onto the XY plane relative to the x-axis, displayed with the label Angle PX.
  • Projection onto the YZ plane relative to the y-axis, displayed with the label Angle PY.
  • Projection onto the ZX plane relative to the z-axis, displayed with the label Angle PZ.

The graphical user interface

All angles are reported as positive angles in degrees. As you can see in Figure 2, a GUI is provided that allows the user to enter three double values that define a GM02.Vector3D object. The GUI also provides an OK button as well as nine text fields that are used to display the computed results described above. In addition, the GUI provides a 3D drawing area.

When the user clicks the OK button, the program draws the user-specified vector in black with the tail located at the origin in 3D space. It also draws the projection of that vector in magenta on each of the XY, YZ, AND ZX planes.

Very similar to previously-explained code

Much of the code in this program is very similar to code that I have explained in previous lessons. I won’t repeat those explanations here. Most of the new code is contained in the method named actionPerformed, so I will explain the code in that method. A complete listing of this program is provided in Listing 10 near the end of the lesson.

Beginning of the actionPerformed method in the program named DotProd3D05

Listing 1 shows the beginning of the actionPerformed method. This method is called to respond to a click on the OK button shown in Figure 2.

Listing 1. Beginning of the actionPerformed method in the program named DotProd3D05.

  public void actionPerformed(ActionEvent e){

    //Erase the off-screen image and draw the axes.
    setCoordinateFrame(g2D);

    //Create one ColMatrix3D object based on the user
    // input values.
    GM02.ColMatrix3D matrixA = new GM02.ColMatrix3D(
                  Double.parseDouble(vecX.getText()),
                  Double.parseDouble(vecY.getText()),
                  Double.parseDouble(vecZ.getText()));

    //Create ColMatrix3D objects that represent each of
    // the three axes.
    GM02.ColMatrix3D matrixX = 
                              new GM02.ColMatrix3D(1,0,0);
    GM02.ColMatrix3D matrixY = 
                              new GM02.ColMatrix3D(0,1,0);
    GM02.ColMatrix3D matrixZ = 
                              new GM02.ColMatrix3D(0,0,1);

You have seen code similar to that in Listing 1 many times before. Therefore, this code should not require further explanation beyond the embedded comments.

Create ColMatrix3D objects that represent projections

Listing 2 creates ColMatrix3D objects that represent the projection of the user-specified vector onto each of the three planes.

Listing 2. Create ColMatrix3D objects that represent projections.

    GM02.ColMatrix3D matrixXY = new GM02.ColMatrix3D(
                      Double.parseDouble(vecX.getText()),
                      Double.parseDouble(vecY.getText()),
                      0);

    GM02.ColMatrix3D matrixYZ = new GM02.ColMatrix3D(
                      0,
                      Double.parseDouble(vecY.getText()),
                      Double.parseDouble(vecZ.getText()));

    GM02.ColMatrix3D matrixZX = new GM02.ColMatrix3D(
                      Double.parseDouble(vecX.getText()),
                      0,
                      Double.parseDouble(vecZ.getText()));

Although the actual code in Listing 2 should be familiar to you, the application may be new. The important thing to note is that the projection onto each of the three planes in Listing 2 is accomplished by setting one of the coordinate values to zero for each projection. For example, in order to project the vector onto the XY plane, the value for the z-axis coordinate is set to zero as shown in boldface in Listing 2.

Create and draw vectors

Listing 3 uses the ColMatrix3D objects created in Listing 1 and Listing 2 to create and draw vectors based on those ColMatrix3D objects.

Listing 3. Create and draw vectors.

    //Use the ColMatrix3D objects to create Vector3D
    // objects representing the user-specified vector and
    // each of the axes.
    GM02.Vector3D vecA = new GM02.Vector3D(matrixA);
    GM02.Vector3D vecX = new GM02.Vector3D(matrixX);
    GM02.Vector3D vecY = new GM02.Vector3D(matrixY);
    GM02.Vector3D vecZ = new GM02.Vector3D(matrixZ);

    //Create Vector3D objects that represent the
    // projection of the user-specified vector on each of
    // the planes.
    GM02.Vector3D vecXY = new GM02.Vector3D(matrixXY);
    GM02.Vector3D vecYZ = new GM02.Vector3D(matrixYZ);
    GM02.Vector3D vecZX = new GM02.Vector3D(matrixZX);

    //Draw the projection of the user specified vector on
    // each of the three planes.
    g2D.setColor(Color.MAGENTA);
    vecXY.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    vecYZ.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    vecZX.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));

    //Draw the user-specified vector with its tail at the
    // origin.
    g2D.setColor(Color.BLACK);
    vecA.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));

Once the ColMatrix3D objects have been created to serve each specific purpose, there is nothing new in Listing 3. Therefore, no explanation of the code beyond the embedded comments should be necessary.

Compute and display the nine angles

Listing 4 calls the angle method of the GM02.Vector3D class nine times in succession, using references to the vectors created in Listing 3, to compute and display the nine angle values shown in Figure 2.

Listing 4. Compute and display the nine angles.

    //Compute and display the angle relative to the
    // x-axis.
    double angle = vecA.angle(vecX);
    angleX.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle relative to the
    // y-axis.
    angle = vecA.angle(vecY);
    angleY.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle relative to the
    // z-axis.
    angle = vecA.angle(vecZ);
    angleZ.setText("" +  prepareForDisplay(angle));


    //Compute and display the angle relative to the
    // XY plane
    angle = vecA.angle(vecXY);
    angleXY.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle relative to the
    // YZ plane
    angle = vecA.angle(vecYZ);
    angleYZ.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle relative to the
    // ZX plane
    angle = vecA.angle(vecZX);
    angleZX.setText("" +  prepareForDisplay(angle));


    //Compute and display the angle of the projection onto
    // the XY plane relative to the x-axis
    angle = vecXY.angle(vecX);
    anglePX.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle of the projection onto
    // the YZ plane relative to the y-axis
    angle = vecYZ.angle(vecY);
    anglePY.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle of the projection onto
    // the ZX plane relative to the z-axis
    angle = vecZX.angle(vecZ);
    anglePZ.setText("" +  prepareForDisplay(angle));    


    myCanvas.repaint();//Copy off-screen image to canvas.
    
  }//end actionPerformed

Once the required vectors had been created in Listing 3, there is nothing new in the code in Listing 4. Therefore, no explanation of Listing 4 should be required beyond the comments embedded in the code.

The bottom line on the program named DotProd3D05

The bottom line is that because the methods in the game-math library named GM02 were designed to do most of the hard work, writing application programs such as DotProd3D05 using the game-math library is not difficult at all. You simply need to understand how to organize your code to accomplish the things that you need to accomplish.

That concludes the explanation of the program named DotProd3D05.

The program named DotProd3D06

This program demonstrates how the dot product can be used to find vectors that are perpendicular to a given vector.

An infinite number of possibilities

Recall that you learned in the previous lesson that there are an infinite number of vectors that are perpendicular to a given vector in 3D. This program computes and displays normalized and scaled versions of six of the infinite set of vectors that are perpendicular to a user specified vector.

The graphic output

The screen output from this program is shown in Figure 3.

Figure 3. Six (magenta) vectors that are perpendicular to a given (black) vector.

Output on the command-line screen

In addition to the graphic output shown in Figure 3, the program also displays the values of three of the perpendicular vectors on the command-line screen along with the angle between the perpendicular vector and the user-specified vector. The angle should always be 90 degrees or very close to 90 degrees if the program is working properly. The other three of the six perpendicular vectors are simply negated versions of the three for which the values are displayed.

Special case of one zero coordinate value

If the user specifies one of the coordinate values to be zero (or close to zero), the program only computes and displays four of the possible vectors in order to avoid performing division by a near-zero value. In this case, the orientation of two of the vectors will overlay the orientation of the other two. Because the vectors are normalized to the same length and occupy the same space, you will only see two vectors instead of four. This is illustrated in Figure 4 where the value of the z-axis coordinate value was set to zero relative to the value used in Figure 3.

Figure 4. Screen output when one coordinate has a value of zero.

Special case of two coordinates with a value of zero

If the user specifies two of the coordinate values to be zero or close to zero, the program doesn’t produce a valid result. Instead, it draws a perpendicular vector where all of the coordinate values are zero resulting in a magenta vector head at the origin. It also displays NaN (Not a Number) for the angle on the command line screen.

The graphical user interface

The GUI shown in Figure 4 is provided to allow the user to enter three double values that define a GM02.Vector3D object. The GUI also provides an OK button in addition to a 3D drawing area.

When the user clicks the OK button, the program draws the user-specified vector in black with the tail located at the origin in 3D space. It draws normalized versions of the perpendicular vectors in magenta with their tails also located at the origin. Each normalized vector is scaled by a factor of 50 before it is drawn to cause it to be long enough to be seen.

A short review

Before getting into the programming details, we need to review some material from the previous lesson. Recall that I did some algebraic manipulations in the previous lesson and produced the equations shown in Figure 5.

Figure 5. A general formulation of 3D vector perpendicularity.

ot product = x1*x2 + y1*y2 + z1*z2

If the two vectors are perpendicular:

x1*x2 + y1*y2 + z1*z2 = 0.0
x1*x2 =  -(y1*y2 + z1*z2)

x2 = -(y1*y2 + z1*z2)/x1

or

y2 = -(x1*x2 + z1*z2)/y1

or

z2 = -(x1*x2 + z1*z2)/z1

An infinite set of perpendicular vectors

The boldface equations in Figure 5 describe an infinite set of vectors that are all perpendicular to a given vector. Given these equations, and given the coordinates (x1, y1, and z1) of a vector for which we need to produce perpendicular vectors, we can assume values for any two of x2, y2, and z2.; We can then determine the value for the other coordinate that will cause the new vector to be perpendicular to the given vector.

That is the procedure that is used by this program to
produce three of the perpendicular vectors shown in Figure 3. The remaining three perpendicular vectors shown in Figure 3 are produced by negating the three vectors that are created using the procedure described above.

Beginning of the actionPerformed method

The only code in this program that is new to this lesson is contained in the actionPerformed method. This method is called to respond to a click on the OK button in Figure 4. Therefore, I will confine my explanation to portions of the actionPerformed method. You can view a complete listing of this program in Listing 11 near the end of the lesson.

The actionPerformed method begins in Listing 5. Note that I deleted some of the code early in the method because it is very similar to code that I have explained before.

Listing 5. Beginning of the actionPerformed method for the program named DotProd3D06.

  public void actionPerformed(ActionEvent e){

    //Code deleted for brevity.

    GM02.Vector3D tempVec;
    GM02.ColMatrix3D tempMatrix;
    g2D.setColor(Color.MAGENTA);

    if(Math.abs(zCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
          xCoor,yCoor,-(xCoor*xCoor + yCoor*yCoor)/zCoor);

      tempVec = new GM02.Vector3D(tempMatrix);

      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if

Behavior of the actionPerformed method

The actionPerformed method contains three sections of code, each of which implements one of the boldface equations in Figure 5 to compute and draw one of the perpendicular vectors shown in Figure 3. In addition, the code in the actionPerformed method draws the negative of those three vectors to produce the other three perpendicular vectors shown in Figure 3.

Implement the last boldface equation

Listing 5 implements the last boldface equation from Figure 5, provided that the z-axis coordinate value for the given vector is greater than 0.001. As mentioned earlier, if the z-axis coordinate value is not greater than 0.001, the code in Listing 5 is skipped and no effort is made to create and draw that particular vector. This is done to prevent an attempt to divide by a zero or near-zero value.

A new ColMatrix3D object

Listing 5 creates a new ColMatrix3D object with the x and y-axes coordinate values matching the corresponding values for the user specified vector. It executes the expression shown in boldface in Listing 5 to compute the value of the z-axis coordinate that will cause the new vector to be perpendicular to the user-specified vector. (The boldface expression in Listing 5 matches the last boldface equation in Figure 5.)

A new Vector3D object

Then it uses the ColMatrix3D object described above to create, normalize, scale, and draw the first perpendicular vector. Following that, the code negates the perpendicular vector to create another perpendicular vector that points in the opposite direction.

Along the way, some information is displayed on the command-line screen.

The most important code

The most important code in Listing 5, insofar as the objective of the program is concerned, is the boldface expression that computes the z-axis coordinate value that will cause the new vector to be perpendicular to the user-specified vector.

Remainder of the actionPerformed method

Listing 6 does essentially the same thing two more times to implement the other two boldface equations shown in Figure 5, creating and drawing four more perpendicular vectors in the process.

Listing 6. Remainder of the actionPerformed method.

    if(Math.abs(yCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
          xCoor,-(xCoor*xCoor + zCoor*zCoor)/yCoor,zCoor);
      tempVec = new GM02.Vector3D(tempMatrix);
      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if

    if(Math.abs(xCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
         -(yCoor*yCoor + zCoor*zCoor)/xCoor, yCoor,zCoor);
      tempVec = new GM02.Vector3D(tempMatrix);
      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if


    myCanvas.repaint();//Copy off-screen image to canvas.
    System.out.println();//blank line
  }//end actionPerformed
  //----------------------------------------------------//

Listing 6 also causes the off-screen image to be displayed on the canvas.

That concludes the explanation of the program named DotProd3D06.

The program named DotProd3D04

The purpose of this program is to serve as a counterpoint to the program named Prob3D03, which demonstrates backface culling. (I will explain the program named Prob3D03 shortly.)

This program draws the 3D object shown on the left side of Figure 1, while the program named DotProd3D03 draws the 3D object shown on the right side of Figure 1. The difference between the two is that the object drawn by this program (on the left in Figure 1) does not incorporate backface culling to hide the lines on the back of the object. The object on the right in Figure 1 does incorporate backface culling. The difference is easy to see.

This program draws a 3D circular cylinder by stacking 20circular disks on the x-z plane as shown in Figure 1. The disks are centered on the y-axis and are parallel to the x-z plane. The thickness of each disk is 5 vertical units. As mentioned above, there is no backface culling in this program, so all of the lines that should be hidden show through.

Will discuss in fragments

A complete listing of this program is provided in Listing 12 near the end of the lesson. I will explain portions of the program using code fragments. However, I won’t repeat explanations of code that I have already explained in this or in earlier lessons (see Resources).

The method named drawTheCylinder

All of the interesting code in this program is contained in the method named drawTheCylinder, which is shown in Listing 7. (Note that some of the code was deleted from Listing 7 for brevity.) This method is used to set the axes to the center of the off-screen image and to draw a 3D cylinder that is centered on the y-axis.

Listing 7. The method named drawTheCylinder in DotProd3D04.

  private void drawTheCylinder(Graphics2D g2D){

    //Code deleted for brevity.

    //Draw a cylinder by stacking 20 circular disks on
    // the x-z plane. The disks are centered on the
    // y-axis and are parallel to the x-z plane. The
    // thickness of each disk is 5 vertical units.
    GM02.Point3D tempPointA;
    GM02.Point3D tempPointB;
    g2D.setColor(Color.BLACK);

    for(int y = 0;y < 105;y += 5){//iterate on disks
      //Define the starting point on the circle for this
      // disk.
      tempPointA = new GM02.Point3D(
              new GM02.ColMatrix3D(76,y - osiHeight/4,0));
      //Iterate on points on the circle that represents
      // this disk.
      for(int cnt = 0;cnt < 360;cnt++){//360 points
        //Compute the next point on the circle.
        tempPointB = 
            new GM02.Point3D(new GM02.ColMatrix3D(
              76*Math.cos(Math.toRadians(cnt*360/360)),
              y - osiHeight/4,
              76*Math.sin(Math.toRadians(cnt*360/360))));

        //Draw the line in 3D. Note that there is no
        // backface culling in this program.
        new GM02.Line3D(tempPointA,tempPointB).draw(g2D);

        //Save the point for use in drawing the next line.
        tempPointA = tempPointB;
      }//end for loop on points
    }//end for loop on disks

  }//end drawTheCylinder method

Behavior of the code

Basically, the code in Listing 7 draws 21 circles, one above the other to represent the edges of the 20 circular disks. If you understand the trigonometry involved in drawing a circle, you should find the code in Listing 7 to be straightforward and no explanation beyond the embedded comments should be required. If you don’t understand the trigonometry, you will simply have to take my word for it that the expressions in Listing 7 are correct for drawing circles. A study of trigonometry is beyond the scope of this lesson.

The program named DotProd3D03

The purpose of this program is to demonstrate a practical use of the vector dot product — backface culling. As before, the program draws a 3D circular cylinder by stacking 20 circular disks on the x-z plane. The disks are centered on the y-axis and are parallel to the x-z plane. The thickness of each disk is 5 vertical units.

Backface culling is incorporated using the dot product between a vector that is parallel to the viewpoint of the viewer and a vector that is perpendicular to the line being drawn to form the outline of a circle. The results are shown in the right image in Figure 1.

Will discuss in fragments

A complete listing of this program is provided in Listing 13 near the end of the lesson. I will explain portions of the program using code fragments. However, I won’t repeat explanations of code that I have already explained in this or in earlier lessons (see Resources).

The method named drawTheCylinder

As before, all of the interesting code in this program is contained in the method named drawTheCylinder, which is shown in Listing 8. (Note that some of the code was deleted from Listing 8 for brevity.) This method is used to set the axes to the center of the off-screen image and to draw a 3D cylinder that is centered on the y-axis.

Listing 8. The method named drawTheCylinder in DotProd3D03.

  private void drawTheCylinder(Graphics2D g2D){

    //Code deleted for brevity.

    //Get a vector that is approximately parallel to the
    // viewpoint of the viewer. The best values for this
    // vector were determined experimentally using this
    // program and the earlier program named DotProd3D02.
    GM02.Vector3D viewPoint = 
        new GM02.Vector3D(new GM02.ColMatrix3D(43,5,50));

    //Draw a cylinder by stacking 20 circular disks on
    // the x-z plane. The disks are centered on the
    // y-axis and are parallel to the x-z plane. The
    // thickness of each disk is 5 vertical units.
    GM02.Point3D tempPointA;
    GM02.Point3D tempPointB;
    g2D.setColor(Color.BLACK);

    for(int y = 0;y < 105;y += 5){//iterate on disks
      //Define the starting point on the circle for this
      // disk.
      tempPointA = new GM02.Point3D(new GM02.ColMatrix3D(
                                   76,y - osiHeight/4,0));
      //Iterate on points on the circle that represents
      // this disk.
      for(int cnt = 0;cnt < 360;cnt++){//360 points
        //Compute the next point on the circle.
        tempPointB = 
            new GM02.Point3D(new GM02.ColMatrix3D(
              76*Math.cos(Math.toRadians(cnt*360/360)),
              y - osiHeight/4,
              76*Math.sin(Math.toRadians(cnt*360/360))));

        //Do backface culling using the dot product of the
        // viewpoint vector and a vector that is almost
        // perpendicular to the line being drawn. If the
        // dot product is negative, or if the disk being
        // drawn is the top disk on the stack, draw the
        // line. Otherwise, don't draw the line.
        //The perpendicular vector used in the dot
        // product is the displacement vector from the
        // origin to a point that defines one end of the
        // line being drawn. Note that this vector is not
        // perfectly perpendicular to the line being 
        // drawn. Later in the series, we will learn about
        // and use the cross product to get perpendicular
        // vectors.
        if((tempPointB.getDisplacementVector(
                           new GM02.Point3D(
                             new GM02.ColMatrix3D(0,0,0)))
                                   .dot(viewPoint) < 0.0) 
                                           || (y == 100)){
          //Draw the line in 3D.
          new GM02.Line3D(
                         tempPointA,tempPointB).draw(g2D);
        }//end if

        //Save the point for use in drawing the next line.
        tempPointA = tempPointB;
      }//end for loop on points
    }//end for loop on disks

  }//end drawTheCylinder method

The new code and an exercise for the student

The new code in Listing 8 is highlighted in boldface. I will leave it as an exercise for the student to think about the rationale behind deciding which lines to draw and which lines to suppress depending on the algebraic sign of the dot product between the two vectors.

Otherwise, no explanation of the code should be necessary beyond the embedded comments.

Run the programs

I encourage you to copy the code from Listing 9 through Listing 13, compile the code, and execute the programs in conjunction with the game-math library named GM02. Experiment with the code, making changes, and observing the results of your changes. Make certain that you can explain why your changes behave as they do.

Summary

In this lesson, you learned how to apply the vector dot product to three different applications. First, you learned how to use the dot product to compute nine different angles of interest that a vector makes with various elements in 3D space. Next, you learned how to use the dot product to find six of the infinite set of vectors that are perpendicular to a given vector as shown in Figure 3. Finally, you learned how to use the dot product to perform back-face culling to convert the left image in Figure 1 to the right image in Figure 1.

What’s next?

In the next lesson, you will learn about first-person computer games in general, and how to use the game-math library to write a first-person game in a 3D world in Java.

Resources

Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 9 through Listing 13 below.

Listing 9. Source code for the game-math library named GM02.

/*GM02.java
Copyright 2008, R.G.Baldwin
Revised 02/08/08

This is an update to the game-math library named GM01.

The main purpose of this update was to add vector dot
product and related capabilities to the library.

Please see the comments at the beginning of the library
class named GM01 for a general description of the library.

The following methods are new instance methods of the 
indicated static top-level classes belonging to the class
named GM02.

GM02.ColMatrix2D.dot - compute dot product of two
 ColMatrix2D objects.
GM02.Vector2D.dot - compute dot product of two
 Vector2D objects.
GM02.Vector2D.angle - compute angle between two Vector2D
 objects.

GM02.ColMatrix3D.dot - compute dot product of two
 ColMatrix3D objects
GM02.Vector3D.dot - compute dot product of two
 Vector3D objects.
GM02.Vector3D.angle - compute angle between two Vector3D
 objects.

Tested using JDK 1.6 under WinXP.
*********************************************************/
import java.awt.geom.*;
import java.awt.*;

public class GM02{
  //----------------------------------------------------//

  //This method converts a ColMatrix3D object to a
  // ColMatrix2D object. The purpose is to accept
  // x, y, and z coordinate values and transform those
  // values into a pair of coordinate values suitable for
  // display in two dimensions.
  //See http://local.wasp.uwa.edu.au/~pbourke/geometry/
  // classification/ for technical background on the
  // transform from 3D to 2D.
  //The transform equations are:
  // x2d = x3d + z3d * cos(theta)/tan(alpha)
  // y2d = y3d + z3d * sin(theta)/tan(alpha);
  //Let theta = 30 degrees and alpha = 45 degrees
  //Then:cos(theta) = 0.866
  //     sin(theta) = 0.5
  //     tan(alpha) = 1;
  //Note that the signs in the above equations depend
  // on the assumed directions of the angles as well as
  // the assumed positive directions of the axes. The
  // signs used in this method assume the following:
  //   Positive x is to the right.
  //   Positive y is up the screen.
  //   Positive z is protruding out the front of the
  //     screen.
  //   The viewing position is above the x axis and to the
  //     right of the z-y plane.
  public static GM02.ColMatrix2D convert3Dto2D(
                                   GM02.ColMatrix3D data){
    return new GM02.ColMatrix2D(
                  data.getData(0) - 0.866*data.getData(2),
                  data.getData(1) - 0.50*data.getData(2));
  }//end convert3Dto2D 
  //----------------------------------------------------//

  //This method wraps around the translate method of the
  // Graphics2D class. The purpose is to cause the
  // positive direction for the y-axis to be up the screen
  // instead of down the screen. When you use this method,
  // you should program as though the positive direction
  // for the y-axis is up.
  public static void translate(Graphics2D g2D,
                               double xOffset,
                               double yOffset){
    //Flip the sign on the y-coordinate to change the
    // direction of the positive y-axis to go up the
    // screen.
    g2D.translate(xOffset,-yOffset);
  }//end translate
  //----------------------------------------------------//

  //This method wraps around the drawLine method of the
  // Graphics class. The purpose is to cause the positive
  // direction for the y-axis to be up the screen instead
  // of down the screen. When you use this method, you
  // should program as though the positive direction for
  // the y-axis is up.
  public static void drawLine(Graphics2D g2D,
                              double x1,
                              double y1,
                              double x2,
                              double y2){
    //Flip the sign on the y-coordinate value.
    g2D.drawLine((int)x1,-(int)y1,(int)x2,-(int)y2);
  }//end drawLine
  //----------------------------------------------------//

  //This method wraps around the fillOval method of the
  // Graphics class. The purpose is to cause the positive
  // direction for the y-axis to be up the screen instead
  // of down the screen. When you use this method, you
  // should program as though the positive direction for
  // the y-axis is up.
  public static void fillOval(Graphics2D g2D,
                              double x,
                              double y,
                              double width,
                              double height){
    //Flip the sign on the y-coordinate value.
    g2D.fillOval((int)x,-(int)y,(int)width,(int)height);
  }//end fillOval
  //----------------------------------------------------//

  //This method wraps around the drawOval method of the
  // Graphics class. The purpose is to cause the positive
  // direction for the y-axis to be up the screen instead
  // of down the screen. When you use this method, you
  // should program as though the positive direction for
  // the y-axis is up.
  public static void drawOval(Graphics2D g2D,
                              double x,
                              double y,
                              double width,
                              double height){
    //Flip the sign on the y-coordinate value.
    g2D.drawOval((int)x,-(int)y,(int)width,(int)height);
  }//end drawOval
  //----------------------------------------------------//

  //This method wraps around the fillRect method of the
  // Graphics class. The purpose is to cause the positive
  // direction for the y-axis to be up the screen instead
  // of down the screen. When you use this method, you
  // should program as though the positive direction for
  // the y-axis is up.
  public static void fillRect(Graphics2D g2D,
                              double x,
                              double y,
                              double width,
                              double height){
    //Flip the sign on the y-coordinate value.
    g2D.fillRect((int)x,-(int)y,(int)width,(int)height);
  }//end fillRect
  //----------------------------------------------------//



  //An object of this class represents a 2D column matrix.
  // An object of this class is the fundamental building
  // block for several of the other classes in the
  // library.
  public static class ColMatrix2D{
    double[] data = new double[2];

    public ColMatrix2D(double data0,double data1){
      data[0] = data0;
      data[1] = data1;
    }//end constructor
    //--------------------------------------------------//

    //Overridden toString method.
    public String toString(){
      return data[0] + "," + data[1];
    }//end overridden toString method
    //--------------------------------------------------//

    public double getData(int index){
      if((index < 0) || (index > 1)){ 
        throw new IndexOutOfBoundsException();
      }else{
        return data[index];
      }//end else
    }//end getData method
    //--------------------------------------------------//

    public void setData(int index,double data){
      if((index < 0) || (index > 1)){ 
        throw new IndexOutOfBoundsException();
      }else{
        this.data[index] = data;
      }//end else
    }//end setData method
    //--------------------------------------------------//

    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in two matrices and returns true if the
    // values are equal or almost equal and returns false
    // otherwise.
    public boolean equals(Object obj){
      if(obj instanceof GM02.ColMatrix2D &&
         Math.abs(((GM02.ColMatrix2D)obj).getData(0) - 
                                 getData(0)) <= 0.00001 &&
         Math.abs(((GM02.ColMatrix2D)obj).getData(1) - 
                                  getData(1)) <= 0.00001){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//

    //Adds one ColMatrix2D object to another ColMatrix2D
    // object, returning a ColMatrix2D object.
    public GM02.ColMatrix2D add(GM02.ColMatrix2D matrix){
      return new GM02.ColMatrix2D(
                            getData(0)+matrix.getData(0),
                            getData(1)+matrix.getData(1));
    }//end add
    //--------------------------------------------------//
    
    //Subtracts one ColMatrix2D object from another
    // ColMatrix2D object, returning a ColMatrix2D object.
    // The object that is received as an incoming
    // parameter  is subtracted from the object on which
    // the method is called.
    public GM02.ColMatrix2D subtract(
                                 GM02.ColMatrix2D matrix){
      return new GM02.ColMatrix2D(
                            getData(0)-matrix.getData(0),
                            getData(1)-matrix.getData(1));
    }//end subtract
    //--------------------------------------------------//
    
    //Computes the dot product of two ColMatrix2D
    // objects and returns the result as type double.
    public double dot(GM02.ColMatrix2D matrix){
      return getData(0) * matrix.getData(0) 
           + getData(1) * matrix.getData(1);
    }//end dot
    //--------------------------------------------------//
  }//end class ColMatrix2D
  //====================================================//


  //An object of this class represents a 3D column matrix.
  // An object of this class is the fundamental building
  // block for several of the other classes in the
  // library.
  public static class ColMatrix3D{
    double[] data = new double[3];
    
    public ColMatrix3D(
                  double data0,double data1,double data2){
      data[0] = data0;
      data[1] = data1;
      data[2] = data2;
    }//end constructor
    //--------------------------------------------------//
    
    public String toString(){
      return data[0] + "," + data[1] + "," + data[2];
    }//end overridden toString method
    //--------------------------------------------------//
    
    public double getData(int index){
      if((index < 0) || (index > 2)){ 
        throw new IndexOutOfBoundsException();
      }else{
        return data[index];
      }//end else
    }//end getData method
    //--------------------------------------------------//
    
    public void setData(int index,double data){
      if((index < 0) || (index > 2)){ 
        throw new IndexOutOfBoundsException();
      }else{
        this.data[index] = data;
      }//end else
    }//end setData method
    //--------------------------------------------------//
    
    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in two matrices and returns true if the
    // values are equal or almost equal and returns false
    // otherwise. 
    public boolean equals(Object obj){
      if(obj instanceof GM02.ColMatrix3D &&
         Math.abs(((GM02.ColMatrix3D)obj).getData(0) - 
                                 getData(0)) <= 0.00001 &&
         Math.abs(((GM02.ColMatrix3D)obj).getData(1) - 
                                 getData(1)) <= 0.00001 &&
         Math.abs(((GM02.ColMatrix3D)obj).getData(2) - 
                                  getData(2)) <= 0.00001){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//

    //Adds one ColMatrix3D object to another ColMatrix3D
    // object, returning a ColMatrix3D object.
    public GM02.ColMatrix3D add(GM02.ColMatrix3D matrix){
      return new GM02.ColMatrix3D(
                            getData(0)+matrix.getData(0),
                            getData(1)+matrix.getData(1),
                            getData(2)+matrix.getData(2));
    }//end add
    //--------------------------------------------------//
    
    //Subtracts one ColMatrix3D object from another
    // ColMatrix3D object, returning a ColMatrix3D object.
    // The object that is received as an incoming
    // parameter is subtracted from the object on which
    // the method is called.
    public GM02.ColMatrix3D subtract(
                                 GM02.ColMatrix3D matrix){
      return new GM02.ColMatrix3D(
                            getData(0)-matrix.getData(0),
                            getData(1)-matrix.getData(1),
                            getData(2)-matrix.getData(2));
    }//end subtract
    //--------------------------------------------------//
    
    //Computes the dot product of two ColMatrix3D
    // objects and returns the result as type double.
    public double dot(GM02.ColMatrix3D matrix){
      return getData(0) * matrix.getData(0) 
           + getData(1) * matrix.getData(1)
           + getData(2) * matrix.getData(2);
    }//end dot
    //--------------------------------------------------//
  }//end class ColMatrix3D
  //====================================================//
  //====================================================//


  public static class Point2D{
    GM02.ColMatrix2D point;
    
    public Point2D(GM02.ColMatrix2D point){//constructor
      //Create and save a clone of the ColMatrix2D object
      // used to define the point to prevent the point
      // from being corrupted by a later change in the
      // values stored in the original ColMatrix2D object
      // through use of its set method.
      this.point = new ColMatrix2D(
                       point.getData(0),point.getData(1));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return point.getData(0) + "," + point.getData(1);
    }//end toString
    //--------------------------------------------------//
    
    public double getData(int index){
      if((index < 0) || (index > 1)){ 
        throw new IndexOutOfBoundsException();
      }else{
        return point.getData(index);
      }//end else
    }//end getData
    //--------------------------------------------------//
    
    public void setData(int index,double data){
      if((index < 0) || (index > 1)){ 
        throw new IndexOutOfBoundsException();
      }else{
        point.setData(index,data);
      }//end else
    }//end setData
    //--------------------------------------------------//

    //This method draws a small circle around the location
    // of the point on the specified graphics context.
    public void draw(Graphics2D g2D){
      drawOval(g2D,getData(0)-3,
                   getData(1)+3,6,6);
    }//end draw

    //--------------------------------------------------//
    
    //Returns a reference to the ColMatrix2D object that
    // defines this Point2D object.
    public GM02.ColMatrix2D getColMatrix(){
      return point;
    }//end getColMatrix
    //--------------------------------------------------//

    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in the ColMatrix2D objects that define two
    // Point2D objects and returns true if they are equal
    // and false otherwise. 
    public boolean equals(Object obj){
      if(point.equals(((GM02.Point2D)obj).
                                         getColMatrix())){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//

    //Gets a displacement vector from one Point2D object
    // to a second Point2D object. The vector points from
    // the object on which the method is called to the
    // object passed as a parameter to the method. Kjell
    // describes this as the distance you would have to
    // walk along the x and then the y axes to get from
    // the first point to the second point.
    public GM02.Vector2D getDisplacementVector(
                                      GM02.Point2D point){
      return new GM02.Vector2D(new GM02.ColMatrix2D(
                            point.getData(0)-getData(0),
                            point.getData(1)-getData(1)));
    }//end getDisplacementVector
    //--------------------------------------------------//
    
    //Adds a Vector2D to a Point2D producing a
    // new Point2D.
    public GM02.Point2D addVectorToPoint(
                                      GM02.Vector2D vec){
      return new GM02.Point2D(new GM02.ColMatrix2D(
                          getData(0) + vec.getData(0),
                          getData(1) + vec.getData(1)));
    }//end addVectorToPoint
    //--------------------------------------------------//
    
    //Returns a new Point2D object that is a clone of
    // the object on which the method is called.
    public Point2D clone(){
      return new Point2D(
                  new ColMatrix2D(getData(0),getData(1)));
    }//end clone
    //--------------------------------------------------//
    
    //The purpose of this method is to rotate a point
    // around a specified anchor point in the x-y plane.
    //The rotation angle is passed in as a double value
    // in degrees with the positive angle of rotation
    // being counter-clockwise.
    //This method does not modify the contents of the
    // Point2D object on which the method is called.
    // Rather, it uses the contents of that object to
    // instantiate, rotate, and return a new Point2D
    // object.
    //For simplicity, this method translates the
    // anchorPoint to the origin, rotates around the
    // origin, and then translates back to the
    // anchorPoint.
    /*
    See http://www.ia.hiof.no/~borres/cgraph/math/threed/
    p-threed.html for a definition of the equations
    required to do the rotation.

    x2 = x1*cos - y1*sin
    y2 = x1*sin + y1*cos
    */ 
    public GM02.Point2D rotate(GM02.Point2D anchorPoint,
                               double angle){
      GM02.Point2D newPoint = this.clone();
      
      double tempX ;
      double tempY;
 
      //Translate anchorPoint to the origin
      GM02.Vector2D tempVec = 
            new GM02.Vector2D(anchorPoint.getColMatrix());
      newPoint = 
              newPoint.addVectorToPoint(tempVec.negate());
      
      //Rotate around the origin.
      tempX = newPoint.getData(0);
      tempY = newPoint.getData(1);
      newPoint.setData(//new x coordinate
                      0,
                      tempX*Math.cos(angle*Math.PI/180) -
                      tempY*Math.sin(angle*Math.PI/180));

      newPoint.setData(//new y coordinate
                      1,
                      tempX*Math.sin(angle*Math.PI/180) +
                      tempY*Math.cos(angle*Math.PI/180));

      //Translate back to anchorPoint
      newPoint = newPoint.addVectorToPoint(tempVec);
      
      return newPoint;

    }//end rotate
    //--------------------------------------------------//
    
    //Multiplies this point by a scaling matrix received
    // as an incoming parameter and returns the scaled
    // point.
    public GM02.Point2D scale(GM02.ColMatrix2D scale){
      return new GM02.Point2D(new ColMatrix2D(
                          getData(0) * scale.getData(0),
                          getData(1) * scale.getData(1)));
    }//end scale
    //--------------------------------------------------//
  }//end class Point2D
  //====================================================//


  public static class Point3D{
    GM02.ColMatrix3D point;
    
    public Point3D(GM02.ColMatrix3D point){//constructor
      //Create and save a clone of the ColMatrix3D object
      // used to define the point to prevent the point
      // from being corrupted by a later change in the
      // values stored in the original ColMatrix3D object
      // through use of its set method.
      this.point = 
         new ColMatrix3D(point.getData(0),
                         point.getData(1),
                         point.getData(2));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return point.getData(0) + "," + point.getData(1) 
                                 + "," + point.getData(2);
    }//end toString
    //--------------------------------------------------//
    
    public double getData(int index){
      if((index < 0) || (index > 2)){ 
        throw new IndexOutOfBoundsException();
      }else{
        return point.getData(index);
      }//end else
    }//end getData
    //--------------------------------------------------//
    
    public void setData(int index,double data){
      if((index < 0) || (index > 2)){ 
        throw new IndexOutOfBoundsException();
      }else{
        point.setData(index,data);
      }//end else
    }//end setData
    //--------------------------------------------------//

    //This method draws a small circle around the location
    // of the point on the specified graphics context.
    public void draw(Graphics2D g2D){
      
      //Get 2D projection coordinate values.
      ColMatrix2D temp = convert3Dto2D(point);
      drawOval(g2D,temp.getData(0)-3,
                   temp.getData(1)+3,
                   6,
                   6);
    }//end draw
    //--------------------------------------------------//
    
    //Returns a reference to the ColMatrix3D object that
    // defines this Point3D object.
    public GM02.ColMatrix3D getColMatrix(){
      return point;
    }//end getColMatrix
    //--------------------------------------------------//

    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in the ColMatrix3D objects that define two
    // Point3D objects and returns true if they are equal
    // and false otherwise. 
    public boolean equals(Object obj){
      if(point.equals(((GM02.Point3D)obj).
                                         getColMatrix())){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//

    //Gets a displacement vector from one Point3D object
    // to a second Point3D object. The vector points from
    // the object on which the method is called to the
    // object passed as a parameter to the method. Kjell
    // describes this as the distance you would have to
    // walk along the x and then the y axes to get from
    // the first point to the second point.
    public GM02.Vector3D getDisplacementVector(
                                      GM02.Point3D point){
      return new GM02.Vector3D(new GM02.ColMatrix3D(
                            point.getData(0)-getData(0),
                            point.getData(1)-getData(1),
                            point.getData(2)-getData(2)));
    }//end getDisplacementVector
    //--------------------------------------------------//
    
    //Adds a Vector3D to a Point3D producing a
    // new Point3D.
    public GM02.Point3D addVectorToPoint(
                                      GM02.Vector3D vec){
      return new GM02.Point3D(new GM02.ColMatrix3D(
                          getData(0) + vec.getData(0),
                          getData(1) + vec.getData(1),
                          getData(2) + vec.getData(2)));
    }//end addVectorToPoint
    //--------------------------------------------------//
    
    //Returns a new Point3D object that is a clone of
    // the object on which the method is called.
    public Point3D clone(){
      return new Point3D(new ColMatrix3D(getData(0),
                                         getData(1),
                                         getData(2)));
    }//end clone
    //--------------------------------------------------//
    
    //The purpose of this method is to rotate a point
    // around a specified anchor point in the following
    // order:
    // Rotate around z - rotation in x-y plane.
    // Rotate around x - rotation in y-z plane.
    // Rotate around y - rotation in x-z plane.
    //The rotation angles are passed in as double values
    // in degrees (based on the right-hand rule) in the
    // order given above, packaged in an object of the
    // class GM02.ColMatrix3D. (Note that in this case,
    // the ColMatrix3D object is simply a convenient
    // container and it has no significance from a matrix
    // viewpoint.)
    //The right-hand rule states that if you point the
    // thumb of your right hand in the positive direction
    // of an axis, the direction of positive rotation
    // around that axis is given by the direction that
    // your fingers will be pointing.
    //This method does not modify the contents of the
    // Point3D object on which the method is called.
    // Rather, it uses the contents of that object to
    // instantiate, rotate, and return a new Point3D
    // object.
    //For simplicity, this method translates the
    // anchorPoint to the origin, rotates around the
    // origin, and then translates back to the
    // anchorPoint.
    /*
    See http://www.ia.hiof.no/~borres/cgraph/math/threed/
    p-threed.html for a definition of the equations
    required to do the rotation.
    z-axis 
    x2 = x1*cos - y1*sin
    y2 = x1*sin + y1*cos
    
    x-axis
    y2 = y1*cos(v) - z1*sin(v)
    z2 = y1*sin(v) + z1* cos(v) 
    
    y-axis
    x2 = x1*cos(v) + z1*sin(v)
    z2 = -x1*sin(v) + z1*cos(v)
    */ 
    public GM02.Point3D rotate(GM02.Point3D anchorPoint,
                               GM02.ColMatrix3D angles){
      GM02.Point3D newPoint = this.clone();
      
      double tempX ;
      double tempY;
      double tempZ;
 
      //Translate anchorPoint to the origin
      GM02.Vector3D tempVec = 
            new GM02.Vector3D(anchorPoint.getColMatrix());
      newPoint = 
              newPoint.addVectorToPoint(tempVec.negate());

      double zAngle = angles.getData(0);
      double xAngle = angles.getData(1);
      double yAngle = angles.getData(2);
      
      //Rotate around z-axis
      tempX = newPoint.getData(0);
      tempY = newPoint.getData(1);
      newPoint.setData(//new x coordinate
                      0,
                      tempX*Math.cos(zAngle*Math.PI/180) -
                      tempY*Math.sin(zAngle*Math.PI/180));

      newPoint.setData(//new y coordinate
                      1,
                      tempX*Math.sin(zAngle*Math.PI/180) +
                      tempY*Math.cos(zAngle*Math.PI/180));
      
      //Rotate around x-axis
      tempY = newPoint.getData(1);
      tempZ = newPoint.getData(2);
      newPoint.setData(//new y coordinate
                      1,
                      tempY*Math.cos(xAngle*Math.PI/180) -
                      tempZ*Math.sin(xAngle*Math.PI/180));

      newPoint.setData(//new z coordinate
                      2,
                      tempY*Math.sin(xAngle*Math.PI/180) +
                      tempZ*Math.cos(xAngle*Math.PI/180));
      
      //Rotate around y-axis
      tempX = newPoint.getData(0);
      tempZ = newPoint.getData(2);
      newPoint.setData(//new x coordinate
                      0,
                      tempX*Math.cos(yAngle*Math.PI/180) +
                      tempZ*Math.sin(yAngle*Math.PI/180));

      newPoint.setData(//new z coordinate
                     2,
                     -tempX*Math.sin(yAngle*Math.PI/180) +
                     tempZ*Math.cos(yAngle*Math.PI/180));
      
      //Translate back to anchorPoint
      newPoint = newPoint.addVectorToPoint(tempVec);
      
      return newPoint;

    }//end rotate
    //--------------------------------------------------//
    
    //Multiplies this point by a scaling matrix received
    // as an incoming parameter and returns the scaled
    // point.
    public GM02.Point3D scale(GM02.ColMatrix3D scale){
      return new GM02.Point3D(new ColMatrix3D(
                          getData(0) * scale.getData(0),
                          getData(1) * scale.getData(1),
                          getData(2) * scale.getData(2)));
    }//end scale
    //--------------------------------------------------//
  }//end class Point3D
  //====================================================//
  //====================================================//

  
  public static class Vector2D{
    GM02.ColMatrix2D vector;
    
    public Vector2D(GM02.ColMatrix2D vector){//constructor
      //Create and save a clone of the ColMatrix2D object
      // used to define the vector to prevent the vector
      // from being corrupted by a later change in the
      // values stored in the original ColVector2D object.
      this.vector = new ColMatrix2D(
                     vector.getData(0),vector.getData(1));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return vector.getData(0) + "," + vector.getData(1);
    }//end toString
    //--------------------------------------------------//
    
    public double getData(int index){
      if((index < 0) || (index > 1)){
        throw new IndexOutOfBoundsException();
      }else{
        return vector.getData(index);
      }//end else
    }//end getData
    //--------------------------------------------------//
    
    public void setData(int index,double data){
      if((index < 0) || (index > 1)){ 
        throw new IndexOutOfBoundsException();
      }else{
        vector.setData(index,data);
      }//end else
    }//end setData
    //--------------------------------------------------//
    
    //This method draws a vector on the specified graphics
    // context, with the tail of the vector located at a
    // specified point, and with a small filled circle at
    // the head.
    public void draw(Graphics2D g2D,GM02.Point2D tail){

      drawLine(g2D,
               tail.getData(0),
               tail.getData(1),
               tail.getData(0)+vector.getData(0),
               tail.getData(1)+vector.getData(1));

      fillOval(g2D,
               tail.getData(0)+vector.getData(0)-3,
               tail.getData(1)+vector.getData(1)+3,
               6,
               6);
    }//end draw
    //--------------------------------------------------//
    
    //Returns a reference to the ColMatrix2D object that
    // defines this Vector2D object.
    public GM02.ColMatrix2D getColMatrix(){
      return vector;
    }//end getColMatrix
    //--------------------------------------------------//

    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in the ColMatrix2D objects that define two
    // Vector2D objects and returns true if they are equal
    // and false otherwise. 
    public boolean equals(Object obj){
      if(vector.equals((
                     (GM02.Vector2D)obj).getColMatrix())){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//
    
    //Adds this vector to a vector received as an incoming
    // parameter and returns the sum as a vector.
    public GM02.Vector2D add(GM02.Vector2D vec){
      return new GM02.Vector2D(new ColMatrix2D(
                       vec.getData(0)+vector.getData(0),
                       vec.getData(1)+vector.getData(1)));
    }//end add
    //--------------------------------------------------//
    
    //Returns the length of a Vector2D object.
    public double getLength(){
      return Math.sqrt(
           getData(0)*getData(0) + getData(1)*getData(1));
    }//end getLength
    //--------------------------------------------------//
    
    //Multiplies this vector by a scale factor received as
    // an incoming parameter and returns the scaled
    // vector.
    public GM02.Vector2D scale(Double factor){
      return new GM02.Vector2D(new ColMatrix2D(
                                    getData(0) * factor,
                                    getData(1) * factor));
    }//end scale
    //--------------------------------------------------//
    
    //Changes the sign on each of the vector components
    // and returns the negated vector.
    public GM02.Vector2D negate(){
      return new GM02.Vector2D(new ColMatrix2D(
                                            -getData(0),
                                            -getData(1)));
    }//end negate
    //--------------------------------------------------//
    
    //Returns a new vector that points in the same
    // direction but has a length of one unit.
    public GM02.Vector2D normalize(){
      double length = getLength();
      return new GM02.Vector2D(new ColMatrix2D(
                                      getData(0)/length,
                                      getData(1)/length));
    }//end normalize
    //--------------------------------------------------//
    
    //Computes the dot product of two Vector2D
    // objects and returns the result as type double.
    public double dot(GM02.Vector2D vec){
      GM02.ColMatrix2D matrixA = getColMatrix();
      GM02.ColMatrix2D matrixB = vec.getColMatrix();
      return matrixA.dot(matrixB);
    }//end dot
    //--------------------------------------------------//
    
    //Computes and returns the angle between two Vector2D
    // objects. The angle is returned in degrees as type
    // double.
    public double angle(GM02.Vector2D vec){
      GM02.Vector2D normA = normalize();
      GM02.Vector2D normB = vec.normalize();
      double normDotProd = normA.dot(normB);
      return Math.toDegrees(Math.acos(normDotProd));
    }//end angle
    //--------------------------------------------------//
  }//end class Vector2D
  //====================================================//


  public static class Vector3D{
    GM02.ColMatrix3D vector;
    
    public Vector3D(GM02.ColMatrix3D vector){//constructor
      //Create and save a clone of the ColMatrix3D object
      // used to define the vector to prevent the vector
      // from being corrupted by a later change in the
      // values stored in the original ColMatris3D object.
      this.vector = new ColMatrix3D(vector.getData(0),
                                    vector.getData(1),
                                    vector.getData(2));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return vector.getData(0) + "," + vector.getData(1) 
                                + "," + vector.getData(2);
    }//end toString
    //--------------------------------------------------//
    
    public double getData(int index){
      if((index < 0) || (index > 2)){
        throw new IndexOutOfBoundsException();
      }else{
        return vector.getData(index);
      }//end else
    }//end getData
    //--------------------------------------------------//
    
    public void setData(int index,double data){
      if((index < 0) || (index > 2)){ 
        throw new IndexOutOfBoundsException();
      }else{
        vector.setData(index,data);
      }//end else
    }//end setData
    //--------------------------------------------------//
    
    //This method draws a vector on the specified graphics
    // context, with the tail of the vector located at a
    // specified point, and with a small circle at the
    // head.
    public void draw(Graphics2D g2D,GM02.Point3D tail){
      
      //Get a 2D projection of the tail
      GM02.ColMatrix2D tail2D = convert3Dto2D(tail.point);
      
      //Get the 3D location of the head
      GM02.ColMatrix3D head = 
                      tail.point.add(this.getColMatrix());
      
      //Get a 2D projection of the head
      GM02.ColMatrix2D head2D = convert3Dto2D(head);
      drawLine(g2D,tail2D.getData(0),
                   tail2D.getData(1),
                   head2D.getData(0),
                   head2D.getData(1));      

      //Draw a small filled circle to identify the head.
      fillOval(g2D,head2D.getData(0)-3,
                   head2D.getData(1)+3,
                   6,
                   6);

    }//end draw
    //--------------------------------------------------//
    
    //Returns a reference to the ColMatrix3D object that
    // defines this Vector3D object.
    public GM02.ColMatrix3D getColMatrix(){
      return vector;
    }//end getColMatrix
    //--------------------------------------------------//

    //This method overrides the equals method inherited
    // from the class named Object. It compares the values
    // stored in the ColMatrix3D objects that define two
    // Vector3D objects and returns true if they are equal
    // and false otherwise. 
    public boolean equals(Object obj){
      if(vector.equals((
                     (GM02.Vector3D)obj).getColMatrix())){
        return true;
      }else{
        return false;
      }//end else
     
    }//end overridden equals method
    //--------------------------------------------------//
    
    //Adds this vector to a vector received as an incoming
    // parameter and returns the sum as a vector.
    public GM02.Vector3D add(GM02.Vector3D vec){
      return new GM02.Vector3D(new ColMatrix3D(
                       vec.getData(0)+vector.getData(0),
                       vec.getData(1)+vector.getData(1),
                       vec.getData(2)+vector.getData(2)));
    }//end add
    //--------------------------------------------------//
    
    //Returns the length of a Vector3D object.
    public double getLength(){
      return Math.sqrt(getData(0)*getData(0) + 
                       getData(1)*getData(1) + 
                       getData(2)*getData(2));
    }//end getLength
    //--------------------------------------------------//
    
    //Multiplies this vector by a scale factor received as
    // an incoming parameter and returns the scaled
    // vector.
    public GM02.Vector3D scale(Double factor){
      return new GM02.Vector3D(new ColMatrix3D(
                                    getData(0) * factor,
                                    getData(1) * factor,
                                    getData(2) * factor));
    }//end scale
    //--------------------------------------------------//
    
    //Changes the sign on each of the vector components
    // and returns the negated vector.
    public GM02.Vector3D negate(){
      return new GM02.Vector3D(new ColMatrix3D(
                                            -getData(0),
                                            -getData(1),
                                            -getData(2)));
    }//end negate
    //--------------------------------------------------//
    
    //Returns a new vector that points in the same
    // direction but has a length of one unit.
    public GM02.Vector3D normalize(){
      double length = getLength();
      return new GM02.Vector3D(new ColMatrix3D(
                                      getData(0)/length,
                                      getData(1)/length,
                                      getData(2)/length));
    }//end normalize
    //--------------------------------------------------//
    
    //Computes the dot product of two Vector3D
    // objects and returns the result as type double.
    public double dot(GM02.Vector3D vec){
      GM02.ColMatrix3D matrixA = getColMatrix();
      GM02.ColMatrix3D matrixB = vec.getColMatrix();
      return matrixA.dot(matrixB);
    }//end dot
    //--------------------------------------------------//
    
    //Computes and returns the angle between two Vector3D
    // objects. The angle is returned in degrees as type
    // double.
    public double angle(GM02.Vector3D vec){
      GM02.Vector3D normA = normalize();
      GM02.Vector3D normB = vec.normalize();
      double normDotProd = normA.dot(normB);
      return Math.toDegrees(Math.acos(normDotProd));
    }//end angle
    //--------------------------------------------------//
  }//end class Vector3D
  //====================================================//
  //====================================================//

  
  //A line is defined by two points. One is called the
  // tail and the other is called the head. Note that this
  // class has the same name as one of the classes in
  // the Graphics2D class. Therefore, if the class from
  // the Graphics2D class is used in some future upgrade
  // to this program, it will have to be fully qualified.
  public static class Line2D{
    GM02.Point2D[] line = new GM02.Point2D[2];
    
    public Line2D(GM02.Point2D tail,GM02.Point2D head){
      //Create and save clones of the points used to
      // define the line to prevent the line from being 
      // corrupted by a later change in the coordinate
      // values of the points.
      this.line[0] = new Point2D(new GM02.ColMatrix2D(
                        tail.getData(0),tail.getData(1)));
      this.line[1] = new Point2D(new GM02.ColMatrix2D(
                        head.getData(0),head.getData(1)));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return "Tail = " + line[0].getData(0) + "," 
             + line[0].getData(1) + "nHead = " 
             + line[1].getData(0) + "," 
             + line[1].getData(1);
    }//end toString
    //--------------------------------------------------//

    public GM02.Point2D getTail(){
      return line[0];
    }//end getTail
    //--------------------------------------------------//
    
    public GM02.Point2D getHead(){
      return line[1];
    }//end getHead
    //--------------------------------------------------//
    
    public void setTail(GM02.Point2D newPoint){
      //Create and save a clone of the new point to
      // prevent the line from being corrupted by a
      // later change in the coordinate values of the
      // point.
      this.line[0] = new Point2D(new GM02.ColMatrix2D(
                newPoint.getData(0),newPoint.getData(1)));
    }//end setTail
    //--------------------------------------------------//
    
    public void setHead(GM02.Point2D newPoint){
      //Create and save a clone of the new point to
      // prevent the line from being corrupted by a
      // later change in the coordinate values of the
      // point.
      this.line[1] = new Point2D(new GM02.ColMatrix2D(
                newPoint.getData(0),newPoint.getData(1)));
    }//end setHead
    //--------------------------------------------------//
    
    public void draw(Graphics2D g2D){
      drawLine(g2D,getTail().getData(0),
                   getTail().getData(1),
                   getHead().getData(0),
                   getHead().getData(1));
    }//end draw
    //--------------------------------------------------//
  }//end class Line2D
  //====================================================//


  //A line is defined by two points. One is called the
  // tail and the other is called the head.
  public static class Line3D{
    GM02.Point3D[] line = new GM02.Point3D[2];
    
    public Line3D(GM02.Point3D tail,GM02.Point3D head){
      //Create and save clones of the points used to
      // define the line to prevent the line from being 
      // corrupted by a later change in the coordinate
      // values of the points.
      this.line[0] = new Point3D(new GM02.ColMatrix3D(
                                        tail.getData(0),
                                        tail.getData(1),
                                        tail.getData(2)));
      this.line[1] = new Point3D(new GM02.ColMatrix3D(
                                        head.getData(0),
                                        head.getData(1),
                                        head.getData(2)));
    }//end constructor
    //--------------------------------------------------//

    public String toString(){
      return "Tail = " + line[0].getData(0) + "," 
                       + line[0].getData(1)  + "," 
                       + line[0].getData(2) 
                       + "nHead = " 
                       + line[1].getData(0) + "," 
                       + line[1].getData(1) + ","      
                       + line[1].getData(2);
    }//end toString
    //--------------------------------------------------//

    public GM02.Point3D getTail(){
      return line[0];
    }//end getTail
    //--------------------------------------------------//
    
    public GM02.Point3D getHead(){
      return line[1];
    }//end getHead
    //--------------------------------------------------//

    public void setTail(GM02.Point3D newPoint){
      //Create and save a clone of the new point to
      // prevent the line from being corrupted by a
      // later change in the coordinate values of the
      // point.
      this.line[0] = new Point3D(new GM02.ColMatrix3D(
                                    newPoint.getData(0),
                                    newPoint.getData(1),
                                    newPoint.getData(2)));
    }//end setTail
    //--------------------------------------------------//
    
    public void setHead(GM02.Point3D newPoint){
      //Create and save a clone of the new point to
      // prevent the line from being corrupted by a
      // later change in the coordinate values of the
      // point.
      this.line[1] = new Point3D(new GM02.ColMatrix3D(
                                    newPoint.getData(0),
                                    newPoint.getData(1),
                                    newPoint.getData(2)));
    }//end setHead
    //--------------------------------------------------//

    public void draw(Graphics2D g2D){
      
      //Get 2D projection coordinates.
      GM02.ColMatrix2D tail = 
                           convert3Dto2D(getTail().point);
      GM02.ColMatrix2D head = 
                           convert3Dto2D(getHead().point);

      drawLine(g2D,tail.getData(0),
                   tail.getData(1),
                   head.getData(0),
                   head.getData(1));
    }//end draw
    //--------------------------------------------------//
  }//end class Line3D
  //====================================================//

}//end class GM02
//======================================================//

 

Listing 10. Source code for the program named DotProd3D05.

/*DotProd3D05.java 
Copyright 2008, R.G.Baldwin
Revised 03/06/08

The purpose of this program is to demonstrate how the dot 
product can be used to compute nine different angles of 
interest that a vector makes with various elements in 3D 
space.

First, the program computes and displays the angle between
a user-specified vector and each of the X, Y, and Z axes.
These values are displayed with the labels Angle X, 
Angle Y, and Angle Z.

Then the program computes and displays the angle between
the vector and each of the XY, YZ, and ZX planes. In
this case, the program computes the smallest possible 
angle by projecting the vector onto the plane and then 
computing the angle between the vector and its projection.
These values are displayed with the labels Angle XY, 
Angle YZ, and Angle ZX.

Finally, the program computes and displays the angle 
between the projection of the vector on each of the three 
planes and one of the axes that defines each plane. 
Obviously, the angle between the projection and the other 
axis that defines the plane is 90 degrees less the 
computed angle.  Specifically the values that are computed
and displayed are:

Projection onto the XY plane relative to the x-axis,
displayed with the label Angle PX.

Projection onto the YZ plane relative to the y-axis,
displayed with the label Angle PY.

Projection onto the ZX plane relative to the z-axis,
displayed with the label Angle PZ.

All angles are reported as positive angles in degrees.
  
Study Kjell through Chapter 10, Angle between 3D Vectors.

A GUI is provided that allows the user to enter three
double values that define a GM02.Vector3D object.  The GUI
also provides an OK button as well as nine text fields 
used to display the computed results described above.

In addition, the GUI provides a 3D drawing area.

When the user clicks the OK button, the program draws the
user-specified vector in black with the tail located at 
the origin in 3D space. It also draws the projection of
that vector in magenta on each of the XY, YZ, AND ZX
planes

Tested using JDK 1.6 under WinXP.
*********************************************************/
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class DotProd3D05{
  public static void main(String[] args){
    GUI guiObj = new GUI();
  }//end main
}//end controlling class DotProd3D05
//======================================================//

class GUI extends JFrame implements ActionListener{
  //Specify the horizontal and vertical size of a JFrame
  // object.
  int hSize = 400;
  int vSize = 400;
  Image osi;//an off-screen image
  int osiWidth;//off-screen image width
  int osiHeight;//off-screen image height
  MyCanvas myCanvas;//a subclass of Canvas 
  Graphics2D g2D;//off-screen graphics context.

  //User input components.
  JTextField vecX = new JTextField("50.0");
  JTextField vecY = new JTextField("100.0");
  JTextField vecZ = new JTextField("0.0");

  JTextField angleX = new JTextField("0");
  JTextField angleY = new JTextField("0");
  JTextField angleZ = new JTextField("0");
  
  JTextField angleXY = new JTextField("0");
  JTextField angleYZ = new JTextField("0");
  JTextField angleZX = new JTextField("0");
  
  JTextField anglePX = new JTextField("0");
  JTextField anglePY = new JTextField("0");
  JTextField anglePZ = new JTextField("0");

  JButton button = new JButton("OK");
  
  //----------------------------------------------------//
  
  GUI(){//constructor

    //Set JFrame size, title, and close operation.
    setSize(hSize,vSize);
    setTitle("Copyright 2008,R.G.Baldwin");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    //Instantiate a JPanel that will house the user input
    // components and set its layout manager.
    JPanel controlPanel = new JPanel();
    controlPanel.setLayout(new GridLayout(0,6));

    //Add the user input components and appropriate labels
    // to the control panel.
    controlPanel.add(new JLabel(" Vec X "));    
    controlPanel.add(vecX);

    controlPanel.add(new JLabel(" Vec Y "));    
    controlPanel.add(vecY);
    
    controlPanel.add(new JLabel(" Vec Z "));    
    controlPanel.add(vecZ);
    
    controlPanel.add(new JLabel(" Angle X "));    
    controlPanel.add(angleX);
    
    controlPanel.add(new JLabel(" Angle Y "));    
    controlPanel.add(angleY);
    
    controlPanel.add(new JLabel(" Angle Z "));    
    controlPanel.add(angleZ);
    
    controlPanel.add(new JLabel(" Angle XY "));    
    controlPanel.add(angleXY);
    
    controlPanel.add(new JLabel(" Angle YZ "));    
    controlPanel.add(angleYZ);
    
    controlPanel.add(new JLabel(" Angle ZX "));
    controlPanel.add(angleZX);
    
    controlPanel.add(new JLabel(" Angle PX "));    
    controlPanel.add(anglePX);
    
    controlPanel.add(new JLabel(" Angle PY "));    
    controlPanel.add(anglePY);
    
    controlPanel.add(new JLabel(" Angle PZ "));
    controlPanel.add(anglePZ);
    
    controlPanel.add(button);

    //Add the control panel to the SOUTH position in the
    // JFrame.
    this.getContentPane().add(
                        BorderLayout.SOUTH,controlPanel);

    
    //Create a new drawing canvas and add it to the
    // CENTER of the JFrame above the control panel.
    myCanvas = new MyCanvas();
    this.getContentPane().add(
                            BorderLayout.CENTER,myCanvas);

    //This object must be visible before you can get an
    // off-screen image.  It must also be visible before
    // you can compute the size of the canvas.
    setVisible(true);
    
    //Make the size of the off-screen image match the
    // size of the canvas.
    osiWidth = myCanvas.getWidth();
    osiHeight = myCanvas.getHeight();
    
    //Create an off-screen image and get a graphics
    // context on it.
    osi = createImage(osiWidth,osiHeight);
    g2D = (Graphics2D)(osi.getGraphics());
    

    //Translate the origin to the center.
    GM02.translate(g2D,0.5*osiWidth,-0.5*osiHeight);

    
    //Register this object as an action listener on the
    // button.
    button.addActionListener(this);

    //Cause the overridden paint method belonging to
    // myCanvas to be executed.
    myCanvas.repaint();
    
  }//end constructor
  //----------------------------------------------------//
  
  //This method is used to draw orthogonal 3D axes on the
  // off-screen image that intersect at the origin.
  private void setCoordinateFrame(Graphics2D g2D){

    //Erase the screen
    g2D.setColor(Color.WHITE);
    GM02.fillRect(g2D,-osiWidth/2,osiHeight/2,
                                      osiWidth,osiHeight);

    //Draw x-axis in RED
    g2D.setColor(Color.RED);
    GM02.Point3D pointA = new GM02.Point3D(
                   new GM02.ColMatrix3D(-osiWidth/2,0,0));
    GM02.Point3D pointB = new GM02.Point3D(
                    new GM02.ColMatrix3D(osiWidth/2,0,0));
    new GM02.Line3D(pointA,pointB).draw(g2D);
    
    //Draw y-axis in GREEN
    g2D.setColor(Color.GREEN);
    pointA = new GM02.Point3D(
                  new GM02.ColMatrix3D(0,-osiHeight/2,0));
    pointB = new GM02.Point3D(
                   new GM02.ColMatrix3D(0,osiHeight/2,0));
    new GM02.Line3D(pointA,pointB).draw(g2D);
    
    //Draw z-axis in BLUE. Make its length the same as the
    // length of the x-axis.
    g2D.setColor(Color.BLUE);
    pointA = new GM02.Point3D(
                   new GM02.ColMatrix3D(0,0,-osiWidth/2));
    pointB = new GM02.Point3D(
                    new GM02.ColMatrix3D(0,0,osiWidth/2));
    new GM02.Line3D(pointA,pointB).draw(g2D);

  }//end setCoordinateFrame method
  //----------------------------------------------------//
  
  //This method is called to respond to a click on the
  // button.
  public void actionPerformed(ActionEvent e){
    
    //Erase the off-screen image and draw the axes.
    setCoordinateFrame(g2D);
    
    //Create one ColMatrix3D object based on the user
    // input values.
    GM02.ColMatrix3D matrixA = new GM02.ColMatrix3D(
                  Double.parseDouble(vecX.getText()),
                  Double.parseDouble(vecY.getText()),
                  Double.parseDouble(vecZ.getText()));
                  
    //Create ColMatrix3D objects that represent each of
    // the three axes.
    GM02.ColMatrix3D matrixX = 
                              new GM02.ColMatrix3D(1,0,0);
    GM02.ColMatrix3D matrixY = 
                              new GM02.ColMatrix3D(0,1,0);
    GM02.ColMatrix3D matrixZ = 
                              new GM02.ColMatrix3D(0,0,1);
    
    //Create ColMatrix3D objects that represent the
    // projection of the user-specified vector onto each
    // of the three planes.
    GM02.ColMatrix3D matrixXY = new GM02.ColMatrix3D(
                      Double.parseDouble(vecX.getText()),
                      Double.parseDouble(vecY.getText()),
                      0);
                  
    GM02.ColMatrix3D matrixYZ = new GM02.ColMatrix3D(
                      0,
                      Double.parseDouble(vecY.getText()),
                      Double.parseDouble(vecZ.getText()));
                  
    GM02.ColMatrix3D matrixZX = new GM02.ColMatrix3D(
                      Double.parseDouble(vecX.getText()),
                      0,
                      Double.parseDouble(vecZ.getText()));
    


    //Use the ColMatrix3D objects to create Vector3D
    // objects representing the user-specified vector and
    // each of the axes.
    GM02.Vector3D vecA = new GM02.Vector3D(matrixA);
    GM02.Vector3D vecX = new GM02.Vector3D(matrixX);
    GM02.Vector3D vecY = new GM02.Vector3D(matrixY);
    GM02.Vector3D vecZ = new GM02.Vector3D(matrixZ);
    
    //Create Vector3D objects that represent the
    // projection of the user-specified vector on each of
    // the planes.
    GM02.Vector3D vecXY = new GM02.Vector3D(matrixXY);
    GM02.Vector3D vecYZ = new GM02.Vector3D(matrixYZ);
    GM02.Vector3D vecZX = new GM02.Vector3D(matrixZX);
    	                    
    //Draw the projection of the user specified vector on
    // each of the three planes.
    g2D.setColor(Color.MAGENTA);
    vecXY.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    vecYZ.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    vecZX.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    
    //Draw the user-specified vector with its tail at the
    // origin.
    g2D.setColor(Color.BLACK);
    vecA.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    
    	                    

    //Compute and display the angle relative to the
    // x-axis.
    double angle = vecA.angle(vecX);
    angleX.setText("" +  prepareForDisplay(angle));

    //Compute and display the angle relative to the
    // y-axis.
    angle = vecA.angle(vecY);
    angleY.setText("" +  prepareForDisplay(angle));
    
    //Compute and display the angle relative to the
    // z-axis.
    angle = vecA.angle(vecZ);
    angleZ.setText("" +  prepareForDisplay(angle));
    
    
    //Compute and display the angle relative to the
    // XY plane
    angle = vecA.angle(vecXY);
    angleXY.setText("" +  prepareForDisplay(angle));
    
    //Compute and display the angle relative to the
    // YZ plane
    angle = vecA.angle(vecYZ);
    angleYZ.setText("" +  prepareForDisplay(angle));
    
    //Compute and display the angle relative to the
    // ZX plane
    angle = vecA.angle(vecZX);
    angleZX.setText("" +  prepareForDisplay(angle));
    
    
    //Compute and display the angle of the projection onto
    // the XY plane relative to the x-axis
    angle = vecXY.angle(vecX);
    anglePX.setText("" +  prepareForDisplay(angle));
    
    //Compute and display the angle of the projection onto
    // the YZ plane relative to the y-axis
    angle = vecYZ.angle(vecY);
    anglePY.setText("" +  prepareForDisplay(angle));
    
    //Compute and display the angle of the projection onto
    // the ZX plane relative to the z-axis
    angle = vecZX.angle(vecZ);
    anglePZ.setText("" +  prepareForDisplay(angle));    
    

    myCanvas.repaint();//Copy off-screen image to canvas.
    
    
  }//end actionPerformed
  //----------------------------------------------------//
  
  //The code in this method prepares a double value for
  // display in a text field by eliminating exponential
  // format for very small values and setting the number
  // of decimal digits to four.
  private double prepareForDisplay(double data){
      //Eliminate exponential notation in the display.
      if(Math.abs(data) < 0.001){
        data = 0.0;
      }//end if
      
      //Convert to four decimal digits.
      return ((int)(10000*data))/10000.0;
  }//end prepareForDisplay
  
  //====================================================//
  
  
  //This is an inner class of the GUI class.
  class MyCanvas extends Canvas{
    //Override the paint() method. This method will be
    // called when the JFrame and the Canvas appear on the
    // screen or when the repaint method is called on the
    // Canvas object.
    //The purpose of this method is to display the
    // off-screen image on the screen.
    public void paint(Graphics g){
      g.drawImage(osi,0,0,this);
    }//end overridden paint()
    
  }//end inner class MyCanvas
    
}//end class GUI
//======================================================//

 

Listing 11. Source code for the program named DotProd3D06.

/*DotProd3D06.java 
Copyright 2008, R.G.Baldwin
Revised 03/09/08

This program demonstrates how the dot product can be used
to find vectors that are perpendicular to a given vector.

The program computes and displays normalized and scaled
versions of six of the infinite set of vectors that are 
perpendicular to a user specified vector.

If the user specifies one of the coordinates to be zero
or close to zero, the program only computes and displays 
four of the possible vectors in order to avoid performing 
division by a near-zero value. For a value of zero, the
orientation of two of the vectors will overlay the 
orientation of the other two. Because they are the same
length, and occupy the same space, you will only see two
vectors.

If the user specifies two of the coordinates to be zero
or close to zero, the program doesn't produce a valid
result.  Instead, it displays the coordinates for a 
perpendicular vector where all of the coordinates are
zero and displays NaN for the angle.

Study Kjell through Chapter 10, Angle between 3D Vectors.

A GUI is provided that allows the user to enter three
double values that define a GM02.Vector3D object.  The GUI
also provides an OK button.

In addition, the GUI provides a 3D drawing area.

When the user clicks the OK button, the program draws the
user-specified vector in black with the tail located at 
the origin in 3D space. It also draws normalized versions
of the perpendicular vectors in magenta with their tails 
located at the origin.  Each normalized vector is scaled
by a factor of 50 before it is drawn.

The program also displays the values of three of the
perpendicular vectors on the command-line screen along
with the angle between the perpendicular vector and the
user-specified vector. The angle should be 90 degrees or
at least very very close to 90 degrees. The other three
perpendicular vectors are simply negated versions of the
three for which the values are displayed.

Tested using JDK 1.6 under WinXP.
*********************************************************/
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class DotProd3D06{
  public static void main(String[] args){
    GUI guiObj = new GUI();
  }//end main
}//end controlling class DotProd3D06
//======================================================//

class GUI extends JFrame implements ActionListener{
  //Specify the horizontal and vertical size of a JFrame
  // object.
  int hSize = 400;
  int vSize = 400;
  Image osi;//an off-screen image
  int osiWidth;//off-screen image width
  int osiHeight;//off-screen image height
  MyCanvas myCanvas;//a subclass of Canvas 
  Graphics2D g2D;//off-screen graphics context.

  //User input components.
  JTextField vecX = new JTextField("50.0");
  JTextField vecY = new JTextField("50.0");
  JTextField vecZ = new JTextField("50.0");

  JButton button = new JButton("OK");
  
  //----------------------------------------------------//
  
  GUI(){//constructor

    //Set JFrame size, title, and close operation.
    setSize(hSize,vSize);
    setTitle("Copyright 2008,R.G.Baldwin");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    //Instantiate a JPanel that will house the user input
    // components and set its layout manager.
    JPanel controlPanel = new JPanel();
    controlPanel.setLayout(new GridLayout(0,6));

    //Add the user input components and appropriate labels
    // to the control panel.
    controlPanel.add(new JLabel(" Vec X "));    
    controlPanel.add(vecX);

    controlPanel.add(new JLabel(" Vec Y "));    
    controlPanel.add(vecY);
    
    controlPanel.add(new JLabel(" Vec Z "));    
    controlPanel.add(vecZ);

    controlPanel.add(button);

    //Add the control panel to the SOUTH position in the
    // JFrame.
    this.getContentPane().add(
                        BorderLayout.SOUTH,controlPanel);

    
    //Create a new drawing canvas and add it to the
    // CENTER of the JFrame above the control panel.
    myCanvas = new MyCanvas();
    this.getContentPane().add(
                            BorderLayout.CENTER,myCanvas);

    //This object must be visible before you can get an
    // off-screen image.  It must also be visible before
    // you can compute the size of the canvas.
    setVisible(true);
    
    //Make the size of the off-screen image match the
    // size of the canvas.
    osiWidth = myCanvas.getWidth();
    osiHeight = myCanvas.getHeight();
    
    //Create an off-screen image and get a graphics
    // context on it.
    osi = createImage(osiWidth,osiHeight);
    g2D = (Graphics2D)(osi.getGraphics());
    

    //Translate the origin to the center.
    GM02.translate(g2D,0.5*osiWidth,-0.5*osiHeight);

    
    //Register this object as an action listener on the
    // button.
    button.addActionListener(this);

    //Cause the overridden paint method belonging to
    // myCanvas to be executed.
    myCanvas.repaint();
    
  }//end constructor
  //----------------------------------------------------//
  
  //This method is used to draw orthogonal 3D axes on the
  // off-screen image that intersect at the origin.
  private void setCoordinateFrame(Graphics2D g2D){

    //Erase the screen
    g2D.setColor(Color.WHITE);
    GM02.fillRect(g2D,-osiWidth/2,osiHeight/2,
                                      osiWidth,osiHeight);

    //Draw x-axis in RED
    g2D.setColor(Color.RED);
    GM02.Point3D pointA = new GM02.Point3D(
                   new GM02.ColMatrix3D(-osiWidth/2,0,0));
    GM02.Point3D pointB = new GM02.Point3D(
                    new GM02.ColMatrix3D(osiWidth/2,0,0));
    new GM02.Line3D(pointA,pointB).draw(g2D);
    
    //Draw y-axis in GREEN
    g2D.setColor(Color.GREEN);
    pointA = new GM02.Point3D(
                  new GM02.ColMatrix3D(0,-osiHeight/2,0));
    pointB = new GM02.Point3D(
                   new GM02.ColMatrix3D(0,osiHeight/2,0));
    new GM02.Line3D(pointA,pointB).draw(g2D);
    
    //Draw z-axis in BLUE. Make its length the same as the
    // length of the x-axis.
    g2D.setColor(Color.BLUE);
    pointA = new GM02.Point3D(
                   new GM02.ColMatrix3D(0,0,-osiWidth/2));
    pointB = new GM02.Point3D(
                    new GM02.ColMatrix3D(0,0,osiWidth/2));
    new GM02.Line3D(pointA,pointB).draw(g2D);

  }//end setCoordinateFrame method
  //----------------------------------------------------//
  
  //This method is called to respond to a click on the
  // button.
  public void actionPerformed(ActionEvent e){
    
    //Erase the off-screen image and draw the axes.
    setCoordinateFrame(g2D);
    
    //Get and save the user specified coordinate values.
    double xCoor = Double.parseDouble(vecX.getText());
    double yCoor = Double.parseDouble(vecY.getText());
    double zCoor = Double.parseDouble(vecZ.getText());
    
    //Create a ColMatrix3D object based on the user input
    // values.
    GM02.ColMatrix3D matrixA = 
                  new GM02.ColMatrix3D(xCoor,yCoor,zCoor);
    
    //Use the ColMatrix3D object to create a Vector3D
    // object representing the user-specified vector.
    GM02.Vector3D vecA = new GM02.Vector3D(matrixA);
    
    //Draw the user-specified vector with its tail at the
    // origin.
    g2D.setColor(Color.BLACK);
    vecA.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
    
    //Create and draw the perpendicular vectors.  However,
    // if a coordinate value is near zero, don't attempt
    // to create and draw the perpendicular vector that
    // would require division by the near-zero value.
    GM02.Vector3D tempVec;
    GM02.ColMatrix3D tempMatrix;
    g2D.setColor(Color.MAGENTA);
    
    if(Math.abs(zCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
          xCoor,yCoor,-(xCoor*xCoor + yCoor*yCoor)/zCoor);
      tempVec = new GM02.Vector3D(tempMatrix);
      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if
    
    if(Math.abs(yCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
          xCoor,-(xCoor*xCoor + zCoor*zCoor)/yCoor,zCoor);
      tempVec = new GM02.Vector3D(tempMatrix);
      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if
    
    if(Math.abs(xCoor) > 0.001){
      tempMatrix = new GM02.ColMatrix3D(
         -(yCoor*yCoor + zCoor*zCoor)/xCoor, yCoor,zCoor);
      tempVec = new GM02.Vector3D(tempMatrix);
      System.out.println(tempVec);
      //Normalize and scale the perpendicular vector.
      tempVec = tempVec.normalize().scale(50.0);
      tempVec.draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      tempVec.negate().draw(g2D,new GM02.Point3D(
                            new GM02.ColMatrix3D(0,0,0)));
      System.out.println(vecA.angle(tempVec));
    }//end if


    myCanvas.repaint();//Copy off-screen image to canvas.
    System.out.println();//blank line
  }//end actionPerformed
  //----------------------------------------------------//
 
  //====================================================//
  
  //This is an inner class of the GUI class.
  class MyCanvas extends Canvas{
    //Override the paint() method. This method will be
    // called when the JFrame and the Canvas appear on the
    // screen or when the repaint method is called on the
    // Canvas object.
    //The purpose of this method is to display the
    // off-screen image on the screen.
    public void paint(Graphics g){
      g.drawImage(osi,0,0,this);
    }//end overridden paint()
    
  }//end inner class MyCanvas
    
}//end class GUI
//======================================================//

 

Listing 12. Source code for the program named
DotProb3D04.

/*DotProd3D04.java 
Copyright 2008, R.G.Baldwin
Revised 03/07/08

The purpose of this program is serve as a counterpoint to 
the program named Prob3D03, which demonstrates backface 
culling.

This program draws the same 3D object as the one drawn in 
DotProd3D03 but without the benefit of backface culling.

Study Kjell through Chapter 10, Angle between 3D Vectors.

The program draws a 3D circular cylinder by stacking 20 
circular disks on the x-z plane. The disks are centered on
the y-axis and are parallel to the x-z plane. The 
thickness of each disk is 5 vertical units.

There is no backface culling in this program, so all of 
the lines that should be hidden show through.


Tested using JDK 1.6 under WinXP.
*********************************************************/
import java.awt.*;
import javax.swing.*;

class DotProd3D04{
  public static void main(String[] args){
    GUI guiObj = new GUI();
  }//end main
}//end controlling class DotProd3D04
//======================================================//

class GUI extends JFrame{
  //Specify the horizontal and vertical size of a JFrame
  // object.
  int hSize = 230;
  int vSize = 250;
  Image osi;//an off-screen image
  int osiWidth;//off-screen image width
  int osiHeight;//off-screen image height
  MyCanvas myCanvas;//a subclass of Canvas 
  Graphics2D g2D;//off-screen graphics context.
  //----------------------------------------------------//

  GUI(){//constructor

    //Set JFrame size, title, and close operation.
    setSize(hSize,vSize);
    setTitle("Copyright 2008,R.G.Baldwin");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create a new drawing canvas and add it to the
    // CENTER of the JFrame above the control panel.
    myCanvas = new MyCanvas();
    this.getContentPane().add(
                            BorderLayout.CENTER,myCanvas);

    //This object must be visible before you can get an
    // off-screen image.  It must also be visible before
    // you can compute the size of the canvas.
    setVisible(true);

    //Make the size of the off-screen image match the
    // size of the canvas.
    osiWidth = myCanvas.getWidth();
    osiHeight = myCanvas.getHeight();

    //Create an off-screen image and get a graphics
    // context on it.
    osi = createImage(osiWidth,osiHeight);
    g2D = (Graphics2D)(osi.getGraphics());

    //Call a method that sets the axes and draws the 
    // cylinder.
    drawTheCylinder(g2D);

    //Cause the overridden paint method belonging to
    // myCanvas to be executed.
    myCanvas.repaint();

  }//end constructor
  //----------------------------------------------------//

  //This method is used to set the axes to the center of
  // the off-screen image and to draw a 3D cylinder that
  // is centered on the y-axis.
  private void drawTheCylinder(Graphics2D g2D){

    //Translate the origin to the center of the off-screen
    // image.
    GM02.translate(g2D,0.5*osiWidth,-0.5*osiHeight);

    //Erase the screen
    g2D.setColor(Color.WHITE);
    GM02.fillRect(g2D,-osiWidth/2,osiHeight/2,
                                      osiWidth,osiHeight);
    //Draw a cylinder by stacking 20 circular disks on
    // the x-z plane. The disks are centered on the
    // y-axis and are parallel to the x-z plane. The
    // thickness of each disk is 5 vertical units.
    GM02.Point3D tempPointA;
    GM02.Point3D tempPointB;
    g2D.setColor(Color.BLACK);

    for(int y = 0;y < 105;y += 5){//iterate on disks
      //Define the starting point on the circle for this
      // disk.
      tempPointA = new GM02.Point3D(
              new GM02.ColMatrix3D(76,y - osiHeight/4,0));
      //Iterate on points on the circle that represents
      // this disk.
      for(int cnt = 0;cnt < 360;cnt++){//360 points
        //Compute the next point on the circle.
        tempPointB = 
            new GM02.Point3D(new GM02.ColMatrix3D(
              76*Math.cos(Math.toRadians(cnt*360/360)),
              y - osiHeight/4,
              76*Math.sin(Math.toRadians(cnt*360/360))));

        //Draw the line in 3D. Note that there is no
        // backface culling in this program.
        new GM02.Line3D(tempPointA,tempPointB).draw(g2D);

        //Save the point for use in drawing the next line.
        tempPointA = tempPointB;
      }//end for loop on points
    }//end for loop on disks

  }//end drawTheCylinder method
  //====================================================//


  //This is an inner class of the GUI class.
  class MyCanvas extends Canvas{
    //Override the paint() method. This method will be
    // called when the JFrame and the Canvas appear on the
    // screen or when the repaint method is called on the
    // Canvas object.
    //The purpose of this method is to display the
    // off-screen image on the screen.
    public void paint(Graphics g){
      g.drawImage(osi,0,0,this);
    }//end overridden paint()

  }//end inner class MyCanvas

}//end class GUI
//======================================================//

 

Listing 13. Source code for the program named DotProb3D03.

/*DotProd3D03.java
Copyright 2008, R.G.Baldwin
Revised 03/07/08

The purpose of this program is to demonstrate a practical
use of the vector dot product - backface culling.

Study Kjell through Chapter 10, Angle between 3D Vectors.

The program draws a 3D circular cylinder by stacking 20
circular disks on the x-z plane. The disks are centered on
the y-axis and are parallel to the x-z plane. The
thickness of each disk is 5 vertical units.

Backface culling is done using the dot product between a
vector that is parallel to the viewpoint of the viewer and
a vector that is perpendicular to the line being drawn to
form the outline of a disk.  The backface culling 
process is good but not perfect.  There is some leakage 
around the back on the right and left sides of the 
cylinder and some short lines segments are visible that 
should not be visible.


Tested using JDK 1.6 under WinXP.
*********************************************************/
import java.awt.*;
import javax.swing.*;

class DotProd3D03{
  public static void main(String[] args){
    GUI guiObj = new GUI();
  }//end main
}//end controlling class DotProd3D03
//======================================================//

class GUI extends JFrame{
  //Specify the horizontal and vertical size of a JFrame
  // object.
  int hSize = 230;
  int vSize = 250;
  Image osi;//an off-screen image
  int osiWidth;//off-screen image width
  int osiHeight;//off-screen image height
  MyCanvas myCanvas;//a subclass of Canvas 
  Graphics2D g2D;//off-screen graphics context.
  //----------------------------------------------------//

  GUI(){//constructor

    //Set JFrame size, title, and close operation.
    setSize(hSize,vSize);
    setTitle("Copyright 2008,R.G.Baldwin");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create a new drawing canvas and add it to the
    // CENTER of the JFrame above the control panel.
    myCanvas = new MyCanvas();
    this.getContentPane().add(
                            BorderLayout.CENTER,myCanvas);

    //This object must be visible before you can get an
    // off-screen image.  It must also be visible before
    // you can compute the size of the canvas.
    setVisible(true);

    //Make the size of the off-screen image match the
    // size of the canvas.
    osiWidth = myCanvas.getWidth();
    osiHeight = myCanvas.getHeight();

    //Create an off-screen image and get a graphics
    // context on it.
    osi = createImage(osiWidth,osiHeight);
    g2D = (Graphics2D)(osi.getGraphics());

    //Call a method that sets the axes and draws the 
    // cylinder.
    drawTheCylinder(g2D);

    //Cause the overridden paint method belonging to
    // myCanvas to be executed.
    myCanvas.repaint();

  }//end constructor
  //----------------------------------------------------//

  //This method is used to set the axes to the center of
  // the off-screen image and to draw a 3D cylinder that
  // is centered on the y-axis.
  private void drawTheCylinder(Graphics2D g2D){

    //Translate the origin to the center of the off-screen
    // image.
    GM02.translate(g2D,0.5*osiWidth,-0.5*osiHeight);

    //Erase the screen
    g2D.setColor(Color.WHITE);
    GM02.fillRect(g2D,-osiWidth/2,osiHeight/2,
                                      osiWidth,osiHeight);

    //Get a vector that is approximately parallel to the
    // viewpoint of the viewer. The best values for this
    // vector were determined experimentally using this
    // program and the earlier program named DotProd3D02.
    GM02.Vector3D viewPoint = 
        new GM02.Vector3D(new GM02.ColMatrix3D(43,5,50));

    //Draw a cylinder by stacking 20 circular disks on
    // the x-z plane. The disks are centered on the
    // y-axis and are parallel to the x-z plane. The
    // thickness of each disk is 5 vertical units.
    GM02.Point3D tempPointA;
    GM02.Point3D tempPointB;
    g2D.setColor(Color.BLACK);

    for(int y = 0;y < 105;y += 5){//iterate on disks
      //Define the starting point on the circle for this
      // disk.
      tempPointA = new GM02.Point3D(new GM02.ColMatrix3D(
                                   76,y - osiHeight/4,0));
      //Iterate on points on the circle that represents
      // this disk.
      for(int cnt = 0;cnt < 360;cnt++){//360 points
        //Compute the next point on the circle.
        tempPointB = 
            new GM02.Point3D(new GM02.ColMatrix3D(
              76*Math.cos(Math.toRadians(cnt*360/360)),
              y - osiHeight/4,
              76*Math.sin(Math.toRadians(cnt*360/360))));

        //Do backface culling using the dot product of the
        // viewpoint vector and a vector that is almost
        // perpendicular to the line being drawn. If the
        // dot product is negative, or if the disk being
        // drawn is the top disk on the stack, draw the
        // line. Otherwise, don't draw the line.
        //The perpendicular vector used in the dot
        // product is the displacement vector from the
        // origin to a point that defines one end of the
        // line being drawn. Note that this vector is not
        // perfectly perpendicular to the line being 
        // drawn. Later in the series, we will learn about
        // and use the cross product to get perpendicular
        // vectors.
        if((tempPointB.getDisplacementVector(
                           new GM02.Point3D(
                             new GM02.ColMatrix3D(0,0,0)))
                                   .dot(viewPoint) < 0.0) 
                                           || (y == 100)){
          //Draw the line in 3D.
          new GM02.Line3D(
                         tempPointA,tempPointB).draw(g2D);
        }//end if

        //Save the point for use in drawing the next line.
        tempPointA = tempPointB;
      }//end for loop on points
    }//end for loop on disks

  }//end drawTheCylinder method
  //====================================================//


  //This is an inner class of the GUI class.
  class MyCanvas extends Canvas{
    //Override the paint() method. This method will be
    // called when the JFrame and the Canvas appear on the
    // screen or when the repaint method is called on the
    // Canvas object.
    //The purpose of this method is to display the
    // off-screen image on the screen.
    public void paint(Graphics g){
      g.drawImage(osi,0,0,this);
    }//end overridden paint()

  }//end inner class MyCanvas

}//end class GUI
//======================================================//

 


Copyright

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin’s Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor’s degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories