Chapter 1
Understanding Java

1.1 Introduction

It is unnecessary to cover all Java syntax and flavors in this book since many good text books on Java programming are available in libraries and bookstores. In this chapter, instead, we shall introduce you some essential Java ingredients through several tutorials. The examples we choose for the tutorials are about points and vectors - the basic geometric entities of computer graphics. After completing these tutorials, you will learn not only the fundamental mathematics on geometric points and vectors but also basic techniques of object-oriented Java programming required for understanding the programs presented in this book.

If you are experienced Java programmers, you will already know most of the information in this chapter. However, you might still want to go through tutorials 2, 6, and 7 so that you can review your mathematics on vector algebra and also get familiar with our program style and the way we organize our source and class files. If you are experienced C/C++ programmers, you will find the syntax of Java language is very similar to C/C++ a nd feel fairly easy to understand our tutorials. This is because Java is derived from C++. If you know nothing about Java and C/C++ but one of other programming languages such as FORTRAN, PASCAL, BASICS, you will still be able to understand our tutorials since necessary explanations and comments are given in each tutorial. However, if you want to be a good Java programmer you are recommended to read specialized Java programming books as well.

1.2 Tutorial 1 - Why Java

Java is a programming language developed in 1991 by Sun Microsystems, a company best known for its high-end UNIX workstations. Sun describes Java as a ``simple, object-oriented, distributed, interpreted, and dynamic language." Well, this is quite mouse-full and abstract. In what follows, we shall give our reasons why we choose Java as a programming language for this book.

Firstly, Java is an object-oriented programming language. Derived from C++, Java's object-oriented concepts are inherited from C++ but it borrows many concepts from other object-oriented languages as well. Except for some simple data type such as integers, characters, and floating point numbers, Java deals with everything else as objects. This enables us to take full advantage of object-oriented methodology and its capabilities for creating flexible, modular programs and reusing code.

Secondly, Java is simple to learn as it removed or streamlined the area where programmers have had difficulty or that have been the most sources of bugs. For example, we will not find pointers in Java, nor will we find overloaded operators. To an experienced C/C++ programmer, these omissions may be difficult to get used to, but to beginners or programmers who have worked in other languages, they make the Java language far easier to learn. Furthermore, memory management in Java is automatic, which avoids a ``memory leaking'' bug that is common to all levels of programmers.

Thirdly, Java is a platform independent language, which is one of the most significant advantages that Java has gained popularity over other programming languages. To understand the importance of platform independence, let us consider writing software for the World Wide Web. Since your programs will be accessed virtually by any computer system through the Internet, it is hence crucial that your programs are platform independent. Most computer languages are either compiled (e.g., FORTRAN, C, and C++) or interpreted (BASIC, Perl, and other script languages). When using a compiled language, we need to run a compiler to translate the human-readable source files of our programs into a machine-readable object code that is tailored for execution on a specific type of processor such as the Intel Pentium processor. Compiled applications, referred to as binary executables, can be run only on the same type of computers they were compiled for since they consist of actual machine language instructions understood by the processor. On the other hand, interpret languages usually exist only as source code. When they are run, an interpreter takes the source file and performs the action indicated by the commands in the file. A program written in an interpreted language essentially consist of a series of instructions for the interpreter to perform. The interpreter is the only real application that is running. Among the benefits of interpreted languages is that programs can be run easily on a variety of platforms because they exist only in source file form. However, interpreter languages run much slower than compiled languages. Java is both compiled and interpreted. After writing a Java program in a text editor, we save it as a source file. We then compile the Java source file to produce a type of binary object file called a class file. These files are not directly executable because they do not contain instructions directly understandable by the processor. Java programs are compiled to a byte-code format. This byte-code represents machine language instructions for a virtual microprocessor called Java Virtual Machine. The Java interpreter implements the Java Virtual Machine in software so that it can execute Java class binary files. Because Java byte-codes are very similar to the native machine code instructions that the computer understands, they can be executed with performance that approaches that of native compiled languages.

Fourthly, Java is a secured programming language. As it is known, Java is both compiled and interpreted language. That the language is interpreted means the interpreter can check the code for questionable activity before excusing it. In this way, the interpreter can prevent a Java application from accessing sensitive areas of the system and memory. Due to this security feature, many Internet service providers will allow you to post your Java applets to their web sites.

Finally, the Java Development Kit, abbreviated as JDK, is free. This is obviously very appealing if you are a college student or a person who learns Java programming for fun.

1.3 Tutorial 2 - Mathematics on points and vectors

The simplest graphics element is a point. In Cartesian space, it is defined by distances from the origin along the three mutually orthogonal axes, namely the x-, y-, and z-axis. Throughout this book, points are denoted by boldface letters as p, q, R, etc. Assume that the distances of a point p from the origin along the three axes are px, py, and pz as shown in Figure 1.1. Then, the distance from the origin to the point p is [(px2+py2+pz2)].

Figure 1.1: The point p in Cartesian space.

A vector is the quantity having magnitude and direction that obey certain laws. It is usually represented by a directed line segment whose length is the magnitude of the vector. Similar to the notation of points, vectors are also denoted by both lower and upper case letters in bold print, for example, a, b, and P. Some fundamental laws associated with vectors are:

Based on the addition and scalar multiplication laws of vectors, it is readily derived that an arbitrary vector a in a plane can be represented by any two non-collinear (or non-parallel) vectors e1 and e2 as a = l1e1+l2e2. Similarly, a vector b in space can be represented by any three non-coplanar vectors e1, e2, and e3 as b = l1e1+l2e2+l3e3. In the linear and vector algebra, e1, e2, and e3 are known as the vector bases. For the convenience of measurement, e1, e2, and e3 are often chosen to be a unit vector, i.e., the magnitude (or length) of the vector is one unit. With the notation of vector bases, a vector a in space may hence be represented as a = a1e1+a2e2 +a3e3, where a1, a2 and a3 are the components of the vector a for the vector bases chosen. If e1, e2, and e3 are mutually orthogonal, they furnish the vector bases for Cartesian space (In this case, they are often written as i, j, and k to denote respectively the positive direction of the x-, y-, and z-axis). Since mutually-orthogonal vector bases are most familiar to us, from now on we shall use them exclusively in this book.

The laws of addition, negation and scalar multiplication of vectors enables us to describe any vector in terms of other vectors. In many applications, however, we often wish to go further to consider the angle between two vectors, projections of points onto the given lines and planes, the shortest distance between any two lines, and so on. In this case, we will certainly need to perform the dot product or cross product of two vectors.

The dot or scalar product of two vectors a and b is defined as

a·b = b·a = ||a|| ||b||cosa,
where ||a|| and ||b|| denote the magnitude of a and b; a the angle between a and b. If b is a unit vector, it is readily seen that a·b = ||a||cosa, which is the projection length of a onto b. If a is orthogonal to b, a = p/2 and cosa = 0. Accordingly, a·b = 0, which is one the criterion to check whether two vectors are orthogonal. Assume that e1, e2, and e3 are the vector bases for a and b. We may then represent them as a = a1e1+a2e2+a3e3 and b = b1e1+b2e2+b3e3. In this situation, the dot product of these two vectors is defined as
a·b = b·a = a1b1+a2b2+a3b3,
noting that e1, e2, and e3 are unit vectors and mutually orthogonal so that ei·ei = 1 and ei·ej = 0 if i j. Based on the knowledge of dot product, we look at how to compute the magnitude of a vector. It is noted that a·a = ||a||2cosa = ||a||2 since a = 0. Therefore,
||a|| =   


a·a
 
=   ___________
a12+a22+a32
 
.

The cross or vector product of two non-collinear vectors a and b defines a new vector c that is orthogonal to both a and b and is denoted by c = a×b as shown in Figure 1.4.

Figure 1.4: The cross product of a and b.

The direction of c follows the right-hand rule. The magnitude of c is given by

||c|| = ||a|| ||b||sina,
where, again, a denotes the angle between a and b. If a is parallel to b, a = 0 and hence sina = 0. Accordingly, a×b = 0, which is one of the criterion to determine if two vectors are parallel. If a and b are represented in terms of vector bases as a = a1e1+a2e2+a3e3 and b = b1e1+b2e2+b3e3, the cross product may be written conveniently in the form of a determinant:
a×b =




e1
e2
e3
a1
a2
a3
b1
b2
b3





or explicitly as
a×b = (a2b3-a3b2)e1+(a3b1-a1b3)e2+(a1b2-a2b1)e3.
Having understood vectors and vector bases, we now examine a point from a different aspect. As a point is represented by three components along the three axes (i.e., the vector bases), it can also be considered as an instance of a vector with fixed position whose starting point is at the origin. For this reason, a point is sometimes called a position vector.

Finally, we should pointed out that both points and vectors in Cartesian space are simply written as row (or column) vectors in literature when there is no confusion about the vector bases. Therefore, a = a1e1+a2e2+a3e3 may simply written as a = (ax, ay, az), where ax, ay, and az are the three components on the three axes. In this book, we also use this simple expression when there is no confusion.

1.4 Tutorial 3 - Classes and objects

If you have programmed for sometime, you may have encountered user-defined data structures in your area of interest. For example, if you are writing a program that deals with a point in the Cartesian coordinate system, you may define a structure called GPoint that holds three variables x, y, and z. In C, this GPoint may be defined as
typedef struct
{
   double x;
   double y;
   double z;
}GPoint;

Once this GPoint structure is defined, it can declare variables of that particular type as demonstrated in the following simple C program:

#include <stdio.h>
int main()
{
   // Define GPoint structure:
   typedef struct
   {
      double x;
      double y;
      double z;
   }GPoint;

   /* Comment: Declare a variable of the type of GPoint: */
   GPoint p;

   /* Set the coordinates of the point p: */
   p.x = 3.0;
   p.y = 2.0;
   p.z = 0.0;

   /* Print out the coordinates of p: */
   printf("p=(%lf, %lf, %lf)\n", p.x, p.y, p.z);
   return 0;
}

In object-oriented language such as C++ and Java, this GPoint data type is usually defined as a class of the following form:

public class GPoint
{
   double x;
   double y;
   double z;
}

The word public is used to define the scope of the class that can be accessed by other classes, which will be discussed in detail in the section of encapsulation. In Java, x, y, and z are called member variables or member data. When this GPoint class is defined, it can create an object of this particular class. An object created from a class is often referred to as an instance of the class, or it is said to have been instantiated from a class.

Working with objects, one of the tools you will certainly need is constructor which is a function called when an object is created. It is used to perform any initialization you needed. Taking the GPoint class as an example, a constructor is implemented as follows:

public class GPoint
{
   double x;
   double y;
   double z;

   // Comment: Constructor:
   GPoint()
   {
      x = y = z = 0.0;
   }
}

Accordingly, when an object is created from the GPoint class, its three member variables, x, y, and z will be initialized to 0.0. It should be noted that both Java and C++ allow you to use ``//'' or a pair of ``/* ... */'' to add comments in your code.

In a traditional procedural language (e.g., PASCAL and C) you deal with procedures and functions and, for the most part, where you put this code in your program is a matter of your choice. In an object-oriented language, however, you put functions, known as methods in Java, associated with a particular class within the definition of the class. For example, if we want to set the x, y, and z coordinates of a GPoint object, we may then add a method called SetPoint in the definition of the class:

public class GPoint
{
   double x;
   double y;
   double z;
   
   // Constructor:
   GPoint()
   {
      x = y = z = 0.0;
   }
     
   // Set a point with respect to three coordinates
   public void SetPoint(double px, double py, double pz)
   {
      x = px;
      y = py;
      z = pz;
   }
}

We declare SetPoint method as void since we do not expect it to return any information. When you are done entering the above code, save the file under the name GPoint.java in your working directory (In our case, it is C:\JBook\Ch1). Before you can run this class, you need to compile it by running the Java compiler, called javac. For Windows 95 and NT users, you may compile the file as:

C:\JBook\Ch1>javac GPoint.java
Once you have compiled your source file, you will see the GPoint.class file in your working directory.

At this stage, you cannot run this program yet since neither an object of the type GPoint nor a method called main() has been created. We now write a code that creates an instance of GPoint class and also the main() method.

class TestGPoint
{
   public static void main(String args[])
   {
      GPoint p;   // Declare variable
      p = new GPoint();  // Create an instance
      p.SetPoint(3, 2, 0);
      System.out.println("p = ("+p.x+", "+p.y+", "+p.y+")");
   }
}

The above code defines a class named TestGPoint, which contains a single method called main(). The method main() is always needed in real-world Java applications. When TestGPoint class is run, the Java interpreter will send a main message to the TestGPoint class, which will in turn cause the code in the main() method to be executed.

You save this code in the file under the name TestGPoint.java in the same directory where GPoint.class file resides (In our case, it is C:\JBook\Ch1). To compile this program, you type

C:\JBook\Ch1>javac TestGPoint.java
After compiling, you will see a file named TestGPoint.class in your working directory. To run it, you type
C:\JBook\Ch1>java TestGPoint
noting that we omitted the .class since the interpreter knows to add .class when looking for a class file. At this point, if everything goes right, you should see the following output:
p = (3.0, 2.0, 2.0)
We now discuss the input variable args[] which is an array containing Java String objects. Similar to C/C++ DOS application programs, the args[] are used to store any command-line arguments you want to input. For example, if you want the TestPoint to print out the result repeatedly to some specific times, you then modify the TestPoint.java as follows:
class TestPoint
{
   public static void main(String args[])
   {
      int    i, n = 1;
      GPoint p;

      p = new GPoint();
      p.SetPoint(3, 2, 0);
      if (args.length != 0)
      {
         // Convert String to integer:
         n = Integer.parseInt(args[0]);
      }
      for (i=0; i<n; i++)
         System.out.println("p = ("+p.x+", "+p.y+", "+p.y+")");
   }
}

After compiling the code, you type

C:\JBook\ch2>java TestPoint 2
Accordingly, you will see the same result is printed twice.

Before moving to the next section, we would like to discuss briefly the way to refer to the variables and methods of the current object or to pass the current object as an argument to another method. To understand what we mean by that, let us refer to the SetPoint() method defined in GPoint class. As it is noted, we declare on purpose the input variables as px, py, and pz. If we now call these three variables as x, y, and z, we may have a problem to distinguish the input variables from the member variables of the current object. Fortunately, Java (and C++ as well) provides you with a special read-only variable that contains a reference to the current object appropriately named this. You can use the this variable to get a reference to the current object. Therefore, the problem of telling the input variables from the member variables can be solved as follows:

public class GPoint
{
   double x;
   double y;
   double z;
   
   // Constructor:
   GPoint()
   {
      this.x = this.y = this.z = 0.0;
   }
     
   // Set a point with respect to three coordinates
   public void SetPoint(double x, double y, double z)
   {
      this.x = x;
      this.y = y;
      this.z = z;
   }
}

Although you may omit the this keyword in your program and refer to both member variables and methods by names, you should be aware that the this is implicit in those references.

1.5 Tutorial 4 - Inheritance

Inheritance is the process by which one object can acquire the properties of another. Inheritance is one of the most important benefits of object-oriented programming since it allows an object to support the concept of hierarchical classification. In practice, you may use the inheritance technique by developing a base class that addresses your current issues and, when there is a need, deriving a subclasses to deal with new problems.

In this section, we shall create the GVector class that defines a vector and provides some methods to perform vector computations. Then, we shall use the GVector as an example to demonstrate how we can benefit the inheritance technique. The source code of the GVector class is listed below.

// Define geometry vector class:
public class GVector
{
   public double x;
   public double y;
   public double z;

   // Constructor:
   public GVector()
   {
      x = y = z = 0.0;
   }
   
   // Set a vector with respect to three displacements (dx, dy, dz)
   public void SetVector(double dx, double dy, double dz)
   {
      x = dx;
      y = dy;
      z = dz;
   }
   
   // Normalize a vector such that the vector has a unit length:
   public void Normalize()
   {
      double length = Math.sqrt(x * x + y * y + z * z);
      if (length > 1.0-10)
      {
         x /= length;
         y /= length;
         z /= length;
      }
   }
   
   // Add two vectors
   public GVector Add(GVector V2)
   {
      GVector V = new GVector();
      V.SetVector(x+V2.x, y+V2.y, z+V2.z);
      return V;
   }
   
   // Subtract one vector from another
   public GVector Subtract(GVector V2)
   {
      GVector V = new GVector();
      V.SetVector(x-V2.x, y-V2.y,, z-V2.z);
      return V;
   }
}

The GVector class has four methods: SetVector, Normalize, Add, and Subtract. The SetVector method assigns the given values to the three coordinates of a vector. The Normalize method is used to normalize the vector to a unit length. The Add and Subtract simply perform addition and subtraction of two vectors. Obviously, several other vector operations such as dot and cross product (i.e., v1·v2 and v1×v2) are not defined in the GVector class. If such operations are required in the future, you may either modify the GVector class to add the two methods or derive a subclass from the base class GVector. The latter is in particular preferred if you do not have the GVector source file.

Let us call the derived class as MyVector and define it as

public class MyVector extends GVector
{
   // Compute the dot product of two vectors
   public double DotProduct(MyVector V2)
   {
      return (x * V2.x + y * V2.y + z * V2.z);
   }
   
   // Compute the cross product of two vectors
   public MyVector CrossProduct(MyVector V2)
   {
      MyVector V = new MyVector();
      V.x = y * V2.z - z * V2.y;
      V.y = z * V2.x - x * V2.z;
      V.x = x * V2.y - y * V2.x;
      return V;
   }
}

Note that the keywords extends indicates that MyVector is a subclass of GVector. We now write a code to test this subclass.

class TestMyVector
{
   public static void main(String args[])
   {
      MyVector v1, v2;
      // Create instances:
      v1 = new MyVector();
      v2 = new MyVector();

      // Set two vectors:
      v1.SetVector(3, 2, 1);
      v2.SetVector(1, 2, 3);

      // Test dot product:
      double   dot_product;
      dot_product = v1.DotProduct(v2);
      System.out.println("v1.v2 = "+dot_product);

      // Test cross product:
      MyVector v;
      v = v1.CrossProduct(v2);
      System.out.println("v = ("+v.x+", "+v.y+", "+v.z+")");
   }
}

Running this program gives you the following output:

v1.v2 = 10.0
v = (4.0, -8.0, 0.0)

1.6 Tutorial 5 - Encapsulation

In this section, we shall discuss an important concept in computer science: data hiding. In an object-oriented language, data hiding is known as encapsulation which is the mechanism that binds together code and the data it manipulates and keeps both safe from outside interface and misuse. Within an object, data variables and methods may be public, private, or protected. These access modifier keywords limit the ability for other objects and classes to access certain variables and methods.

Variables and methods marked as public can be used from any other method in your program. In our examples we have looked so far, we have not marked variables as public, yet they were still accessible by other classes (e.g., TestGPoint class) in the program. In Java, if you do not precede a variable or method with an modifier, it defaults to a limited version of public accessibility, where it is usable by other classes in the same package, but not by classes of other packages.

On another hand, variables and methods marked as private can be used only from inside their class, i.e., they are invisible to methods in any other class, including subclasses. To understand this point, let us modify the GPoint class as follows:

public class GPoint
{
   private double x;
   private double y;
   private double z;
   
   // Constructor:
   GPoint()
   {
      x = y = z = 0.0;
   }
     
   // Set a point with respect to three coordinates
   public void SetPoint(double x, double y, double z)
   {
      this.x = x;
      this.y = y;
      this.z = z;
   }
}

Save and compile the above code to get the GPoint.class. As it is seen, we precede member variables with the access modifier keyword private. Accordingly, if we recompile TestGPoint.java given in the previous section, we shall encounter a compiling error similar to ``Variables in class GPoint not accessible from class TestGPoint". We now delete the access modifier keyword private and save the GPoint.java file as it was.

To understand why data hiding is important, we create a MyLine class that has the startPoint and endPoint to define a straight line. The MyLine class also has a member variable midPoint that records the middle point of the line.

public class MyLine
{
   public GPoint startPoint;
   public GPoint midPoint;
   public GPoint endPoint;
   
   // Constructor that creates three instances of GPoint:
   MyLine()
   {
      startPoint = new GPoint();
      midPoint = new GPoint();
      endPoint = new GPoint();
   }
}

To test MyLine we write a program called TestMyLine.java as shown below:

class TestMyLine
{
   public static void main(String args[])
   {
      MyLine line = new MyLine();
      line.startPoint.SetPoint(1, 2, 0);
      line.endPoint.SetPoint(6, 4, 0);

      // You now need to set the middle point correctly:
      line.midPoint.SetPoint((6.0+1.0)/2.0, (4.0+2.0)/2.0, 0.0);

      System.out.println("middle point = ("+line.midPoint.x+", "
                      +line.midPoint.y+", "+line.midPoint.z+")");
   }
}

An experienced programmer will note that this program has the following shortcomings with respect to Myline class:

  • Users of MyLine class have to understand the formula to compute the middle point of a line.
  • Even though users of MyLine class know how to compute middle point of a line, they may forget to do so while changing either the start point or the end point.

To overcome the problems, we may hide start, middle, and end points from users and provide some methods to let users set and get the information of these points. We modify the code as follows:

public class MyLine
{
   private GPoint startPoint;
   private GPoint midPoint;
   private GPoint endPoint;
   
   // Constructor that creates three instances of GPoint:
   MyLine()
   {
      startPoint = new GPoint();
      midPoint = new GPoint();
      endPoint = new GPoint();
   }

   // Method to set start point and re-compute middle point:
   public void SetStartPoint(double x, double y, double z)
   {
      startPoint.SetPoint(x, y, z);
      SetMiddlePoint();
   }

   // Method to set end point and re-compute middle point:
   public void SetEndPoint(double x, double y, double z)
   {
      endPoint.SetPoint(x, y, z);
      SetMiddlePoint();
   }

   // Method to set middle point:
   public void SetMiddlePoint()
   {
      double x_mid = (startPoint.x + endPoint.x) / 2.0;
      double y_mid = (startPoint.y + endPoint.y) / 2.0;
      double z_mid = (startPoint.z + endPoint.z) / 2.0;
      midPoint.SetPoint(x_mid, y_mid, z_mid);
   }

   // Method to get start point:
   public void GetStartPoint(GPoint p)
   {
      p.x = startPoint.x;
      p.y = startPoint.y;
      p.z = startPoint.z;
   }

   // Method to get end point:
   public void GetEndPoint(GPoint p)
   {
      p.x = endPoint.x;
      p.y = endPoint.y;
      p.z = endPoint.z;
   }

   // Method to get middle point:
   public void GetMiddlePoint(GPoint p)
   {
      p.x = midPoint.x;
      p.y = midPoint.y;
      p.z = midPoint.z;
   }
} 

After such modifications, users can no longer access the member variables directly but through the methods provided by the MyLine class. Accordingly, the problems of getting incorrect middle point is avoided.

We finally discuss protected variables and methods. In many applications, you may want to hid variables and methods from other classes but let the subclasses be able to use these variables and methods. In this case, you can use the access modifier keyword protected.

1.7 Tutorial 6 - Polymorphism

Object-oriented programming languages support polymorphism, which is characterized by the phrase ``one interface, multiple methods". One way that Java achieves polymorphism is through the feature method overloading. In Java, two or more methods can share the same name as long as their parameter declarations are different. In this situation, the methods which share the same name are said to be overloaded.

We take our GVector class as an example to see how we can achieve and benefit the method overloading feature. We modify the GVector class as follows:

// Define geometry vector class:
public class GVector
{
   public double x;
   public double y;
   public double z;

   // Constructor 1:
   public GVector()
   {
      x = y = z = 0.0;
   }
   
   // Constructor 2:
   public GVector(double dx, double dy, double dz)
   {
      x = dx;
      y = dy;
      z = dz;
   }
   
   // Set a vector with respect to three displacements (dx, dy, dz)
   public void SetVector(double dx, double dy, double dz)
   {
      x = dx;
      y = dy;
      z = dz;
   }
   
   // Set a vector that equals p2-p1
   public void SetVector(GPoint p1, GPoint p2)
   {
      x = p2.x - p1.x;
      y = p2.y - p1.y;
      z = p2.z - p1.z;
   }

   // Copy a vector:
   public void CopyVector(GVector v)
   {
      x = v.x;
      y = v.y;
      z = v.z;
   }

   // Compute the length of a vector:
   public double GetLength()
   {
      return Math.sqrt(x * x + y * y + z * z);
   }

   // Normalize a vector such that the vector has a unit length:
   public void Normalize()
   {
      double length = GetLength();
      if (length > 1.0e-10)
      {
         x /= length;
         y /= length;
         z /= length;
      }
   }
   
   // Add two vectors
   public GVector Add(GVector V2)
   {
      GVector V = new GVector(x+V2.x, y+V2.y, z+V2.z);
      return V;
   }
   
   // Subtract one vector from another
   public GVector Subtract(GVector V2)
   {
      GVector V = new GVector(x-V2.x, y-V2.y, z-V2.z);
      return V;
   }
   
   // Compute the dot-product of two vectors
   public double DotProduct(GVector V2)
   {
      return (x * V2.x + y * V2.y + z * V2.z);
   }
   
   // Compute the cross-product of two vectors
   public GVector CrossProduct(GVector V)
   {
      GVector crossV = new GVector();
      crossV.x = y * V.z - z * V.y;
      crossV.y = z * V.x - x * V.z;
      crossV.z = x * V.y - y * V.x;
      return crossV;
   }
}

In the modified GVector class, we have two overloaded constructors: one initialize the coordinates to zero and the other to the given values. Similarly, we have two overloaded methods to set a vector. Due to method overloading, we can use one method name to set GVector in different ways. It is noted that we also added a few new methods into the GVector class, namely the CopyVector(), GetLength(), DotProduct() and CrossProduct().

1.8 Tutorial 7 - Package

Recalling from Tutorial 3, we asked you to save the GPoint.class and TestGPoint.java in the same directory. This is because you need to declare and create an instance of a GPoint object in TestGPoint class. Only you have saved the GPoint.class file in the same directory where TestGPoint.java resides are you able to compile TestGPoint.java. Otherwise, you may encounter a compiling error similar to ``Class GPoint not found in type declaration.'' To save all classes relevant to your project in the same directory have the following short comings:

  • If your project has hundreds or even thousands classes, it is difficult to manage all files in a single directory .
  • If some classes are used by multiple projects, you then need to copy these classes into the directories where your projects reside and keep them updated in each directory whenever you modified these classes.

It would be painful if we had to work like that in the real world. Fortunately, there is a solution to these problems. That is the use of class libraries or what Java refers to packages. They are the collections of classes, usually grouped according to functionality. In fact, all the functionality that Java gives you in the form of system classes for working with data, graphics, etc. is provided to you in packages that you can import into your program when there is a need.

 

In this section, we shall take a look at the steps for creating a package and then see how to import it in another class. First, let us create a subdirectory under C:\JBook and call it GeomLib that stands for ``Geometry Library''. Then, we move GPoint.java into this subdirectory and add one line of statement ``package GeomLib'' at the top of the file so that the GPoint.java looks like:

package GeomLib;

public class GPoint
{
   double x;
   double y;
   double z;
   
   // Constructor:
   GPoint()
   {
      x = y = z = 0.0;
   }
     
   // Set a point with respect to three coordinates
   public void SetPoint(double x, double y, double z)
   {
      this.x = x;
      this.y = y;
      this.z = z;
   }
}

The statement ``package GeomLib" indicates that the GPoint class is part of the GeomLib package. For source files that are part of a package, the package declaration must be present at the beginning of the file, before any other statement, although it can be preceded by comments or empty lines. Next, we type

C:\JBook\GeomLib>javac GPoint.java
to compile it. The compiled file GPoint.class is stored in the same directory as the source file. If you prefer, you may store the source file somewhere else. However, all the class files (i.e., files with .class) that belong to the GeomLib must be saved in the directory GeomLib.

We now look at how to use this package. First, let us delete GPoint.class file from the directory C:\JBook\Ch1. Then, we open TestGPoint.java and add one line of statement ``import GeomLib.GPoint'' at the top of the file as shown below:

import GeomLib.GPoint;

class TestGPoint
{
   public static void main(String args[])
   {
      GPoint p;
      p = new GPoint();
      p.SetPoint(3, 2, 0);
      System.out.println("p = ("+p.x+", "+p.y+", "+p.y+")");
   }
}

If you compile TestGPoint.java, you are likely to get a compiling error similar to ``Class GeomLib.GPoint not found in import GeomLib.GPoint.'' When the compiler encounters a package name, it looks in the current directory as well as in a list of directories stored in an environment variable called the CLASSPATH. If it finds the package directory, it look inside it for the .class file of the class it needs to load. Since our GeomLib is neither a subdirectory of C:\JBook\Ch1 nor in the list of CLASSPATH, we hence encountered a compiling error. How to modify the CLASSPATH variable is little different from platform to platform. If your operating system is Windows 95 or NT, you can add the following line in your autoexe.bat file:

SET CLASSPATH=.;C:\JBook
This tells Java compiler to look in the current directory and C:\JBook directory for the package name. After restarting your computer to let the new modification take effect, you can then compile and run the TestGPoint program.

In our example, only one class file (i.e., GPoint.class) is added in the package GeomLib. With the progress of your study, you will see that all routines beginning with G (e.g., GPoint, GVector, etc.) will be saved in GeomLib directory as our geometry class library.

1.9 Summary and references

In this chapter, we briefly reviewed the mathematics on geometric points and vectors which are the foundations of other mathematical topics covered in subsequent chapters. However, you should be aware that the information we could provide in one section is very limited. Comprehensive materials on vectors and vector spaces can be found in many text books on vector and linear algebra.

We also introduced you the Java syntax, flavors, and object-oriented Java programming through several examples in our tutorials. Such knowledge is essential to understand all the programs in this book. However, since this chapter is an accelerated introduction to Java language and object-oriented programming concepts, the knowledge you have gained is inadequate. Although you will learn more about Java programming in the subsequent chapters, the programming examples covered in this book are very specialized in graphics programming. To broaden your knowledge and skills in Java programming, you should also read other generic Java programming books. Here are some books we found useful:

  1. Arnold, K and Gosling, J., The Java Programming Language, 2nd ed., 1997, Addison-Wesley.
  2. Deitel, H.M. and Deitel, P.J., Java: How to Program, 2nd ed., 1998, Prentice Hall.
  3. Hausner, M., A Vector Space Approach to Geometry, 1998, Dover Pub. (originally published by Prentice-Hall in 1965)
  4. Hubbard, J.H. and Hubbard, B.B., Vector Calculus, Linear Algebra and Differential Forms : A Unified Approach, 1998, Prentice Hall.
  5. Jackson, J. and McClellan, A., Java by Example 1.2, 3rd ed., 1998, Prentice Hall.
  6. Lemay, L. and Cadenhead, R., Teach Yourself Java 1.2 in 21 Days, 3rd ed., 1998, Sams.
  7. Riddle, Analytical Geometry with Vectors, 1972, Cole Publishing Company.