Chapter 4
2D Transformation

4.1  Introduction

Geometric 2D and 3D transformations used in computer graphics are mappings from one coordinate system to the other. They play a central role in model construction and viewing. For example, tools such as rotating, zooming, and mirroring an image found in most CAD systems are all based on geometric transformations. Other engineering applications of geometric transformations include creation of animated mechanisms to study their kinematic and dynamic properties. Due to the importance of geometric transformations, we shall study 2D and transformations in this and the following chapters.

Transformation of a point represent the core problem in geometric transformation because it is the basic geometric element of object. For example, a line may be defined by the starting and ending points. Curves, surfaces, and solids are, as we shall see in succeeding chapters, represented by a collection of points. Therefore, transforming a set of points results in transforming a line, curve, surface, or solid. For this reason, our study of a geometric transformation focuses on the transformation of a point that represents an arbitrary point on an object.

Most engineering drawing and design are carried out in the Cartesian coordinate system, which will be referred to as the world coordinate system. It is known that the axes of the Cartesian coordinate system are mutually orthogonal and form a right-handed system. However, most computer monitors use a left-handed screen coordinate system, which means the origin of the coordinates is at the top-left corner of screen and positive x values are to the right and positive y values are downward. In order to draw an object defined in the world coordinate system on a computer screen, we need to transform the world coordinate system to the screen coordinate system. When the design of geometric object comes to be manufactured, it is then necessary to relate the world coordinate system to the numerical controlled (NC) machine axes, so that coordinate transformations are required here also. In computer graphics, the screen and NC machine coordinate systems are often referred to as a device coordinate (DC) system. Based on the study of 2D transformations in this chapter, we shall also discuss the conversion between the world and DC coordinate system.

Geometric transformations are a well-established subject. We have encountered it when we learned the analytic geometry in school and linear algebra in college. Therefore, we shall not discuss the underlying mathematics of geometric transformations in exhaustive detail but emphasize on their implementation and applications.

4.2  Translation

Let (O;x,y) and (O;x,y) denote two coordinate systems with the corresponding axes in these two systems being parallel.

Figure 4.1: Translation

From Figure 4.1 we can immediately see that the coordinate system (O;x,y) is translated by a displacement vector t, which is defined in terms of (O;x,y) system, so as to obtain the new coordinate system (O;x,y). We denote a fixed point in the (O;x,y) and (O;x,y) systems by the position vector r and r respectively. Then, the position vector r is related to r by the equation

r = t+ r.
The above equation enables us to translate a position vector from one coordinate system to the other. Defining the following column vectors as
r =

x
r


,    t =

tx
ty


,     r =

x
y


,
we can rewrite the above translation equation as


x
y


=

tx
ty


+

x
y


,
which is the form used for implementation.

4.3  Scaling

Sometimes, it is necessary to stretch or shrink a geometric object. Such transformation is known as scaling and can be described by
x = sx x,        y = sy y
where, sx and sy are scalar factors in x and y coordinates. The transformation is called a uniform scaling if sx = sy. Applying a uniform scaling to a geometric object will shrink or stretch the object. However, it will not change the proportion of the object. For example, the ratio of the height to the width of a rectangle will not be affected by a uniform scaling. On the other hand, if sx sy, the transformation is called a differential scaling which changes the proportion of a transformed object. Denoting the scaling operation in matrix form as


sx
0
0
sy


,
we may then describe the scaling as


x
y


=

sx
0
0
sy




x
y


.
It should be pointed out that we use post-multiplying matrix in this book (i.e., the position vector is given as a column vector and placed on the right of the transformation matrix). In some literature, a pre-multiplying matrix is used. In this case, a position vector is given as a row vector and placed on the left of transformation matrix. A pre-multiplying matrix is simply a transpose matrix of a post-multiplying matrix.

Applying a scaling transformation to an image usually change the coordinates of an arbitrary point on the image. If we want to preserve the coordinates of a certain point (e.g., the center of the image), we then need to translate the image so that the point coincides with the origin. After scaling, we then translate the image back. This kind of scaling is sometimes called a scaling about a point.

4.4  Rotation

Let r be the position vector of a point and f the angle between r and the x-axis. Then, r can be explicitly written as
r =

x
y


=

rcosf
rsinf


,
where, r = ||r||. If the point is rotated about the z-axis by an angle q, the position vector of the rotated point is given by
r =

x
y


=

rcos(f+q)
rsin(f+q)


=

rcosfcosq-rsinfsinq
rcosfsinq+rsinfcosq


.
Since x = rcosf and y = rsinf, we have
r =

x
y


=

xcosq-ysinq
xsinq+ycosq


=

cosq
-sinq
sinq
cosq




x
y


.     (4-1)
The above rotation effect may also be achieved by rotating the coordinate system (O;x,y) about the z-axis by -q so as to obtain the new coordinate system (O;x,y). Accordingly, r is the position vector of the same point r in the new system.

We now look at an example.

Question: Let the length of a major and minor axis of an ellipse be 2a and 2b respectively, and q the angle between the major axis and the x-axis. Then, derive the expression of an ellipse in the (O;x,y) system.
Solution: Let (O;[`x],[`y]) be the coordinate system such that the [`x]- and [`y]-axis are parallel to the major and minor axes of the ellipse. Accordingly, the ellipse can be written as
_
x
 
= a cosf   and    _
y
 
= b sinf.
To derive the x- and y-coordinate of the ellipse, we need to rotate the (O;[`x],[`y]) system about the z-axis by -q such that it coincides with the system (O;x,y). Therefore, by equation (5-1), we have


x
y


=

cosq
-sinq
sinq
cosq







_
x
 
_
y
 





=

a cosfcosq-b sinfsinq
a sinfcosq+b cosfsinq


.

The rotation described previously is used to rotate an object about the origin of the coordinate system. If we want to rotate an object about an arbitrary point p, we need to apply first the translation such that the origin of the new coordinate system coincides with the point p, then the rotation about the origin of the new system (i.e., p), and finally the translation to move the coordinate system back to where it was.

4.5  Reflection

Reflecting (or mirroring) an image about y-axis is achieved by simply changing the sign of the x-coordinate. Therefore, we have


x
y


=

-1
0
0
1




x
y


.
Similarly, reflecting an image about the x-axis is achieved by simply changing the sign of the y-coordinate. However, it is relatively involved to reflect an image about an arbitrary line passing through the origin. Assume the line is represented by a unit vector u. Let f denote the angle between u and the x-axis. Then, the reflection about u is performed as follows:

  1. Rotating the image about the z-axis by -f such that u coincides with the x-axis. The Rotation matrix is given by
    R =

    cos(-f)
    -sin(-f)
    sin(-f)
    cos(-f)


    =

    cosf
    +sinf
    -sinf
    cosf


    .
  2. Reflecting the image about the x-axis. The reflection matrix is
    F =

    1
    0
    0
    -1


    .
  3. Rotating the image such that u is back to its original position. The rotation matrix is R-1 = RT.

Therefore, the combined transformation matrix A is

A = RTFR =

cosf
-sinf
sinf
cosf




1
0
0
-1




cosf
sinf
-sinf
cosf


=

cos2f
sin2f
sin2f
-cos2f


.
Let r be a position vector of a point. Then, the reflected image r is given by r = Ar. We may write A as
A =

cos2f
-sin2f
sin2f
cos2f




1
0
0
-1


.
Therefore, A may also be obtained by reflecting the image about the x-axis and then rotating the image about the z-axis by 2f.

If the mirror line does not pass through the origin, we may apply the translation such that the starting point a (or any other point on the line) coincides with the origin. After the reflection is performed, we shall need to apply the inverse translation to the image again. For example, to reflect r about the mirror line L(l) = a+lu we would have

r = a+A(r-a).

4.6  Transformation in homogeneous coordinates

If a rotation R and scaling S are applied one after another to r, then the transformed image r is given by
r = S(Rr) = (SR)r.
Therefore, we may combine two 2×2 transformation matrices into a single 2×2 transformation matrix. However, if a translation is involved, we can not combine a series of transformations to form a single transformation matrix since translations are treated as additions rather than multiplications. In order to treat all transformations in a consistent way so that they can be easily combined, we need to use a homogeneous coordinate system. A position vector r = (x,y)T in a homogeneous coordinate system is represented by r = (w·x, w·y, w)T, where the third coordinate, w may take any non-zero scalar value. For simplicity, however, we usually choose w = 1. In this case, r is represented as r = (x,y,1)T. Accordingly,

With the use of homogeneous coordinates, we are able to give a single transformation matrix representing a combination of several transformations. For example, a rotation of an object about an arbitrary point p = (px,py) is a combination of translation, rotation, and translation:





1
0
px
0
1
py
0
0
1








cosq
-sinq
0
sinq
cosq
0
0
0
1








1
0
-px
0
1
-py
0
0
1




.
By the rule of multiplication of matrices we may write the above transformations in a single transformation matrix as




cosq
-sinq
px(1-cosq)+pysinq
sinq
cosq
py(1-cosq)-pxsinq
0
0
1




.
Similarly, a scaling about an arbitrary point p is




1
0
px
0
1
py
0
0
1








sx
0
0
0
sy
0
0
0
1








1
0
-px
0
1
-py
0
0
1




=



sx
0
px(1-sx)
0
sy
py(1-sy)
0
0
1




.
As it is seen, the use of homogeneous coordinates enables us to combine several transformations into a single transformation matrix by multiplication. In general, a composition of rotation, scale, reflection, and translation will produce a transformation matrix of the following form:




a00
a01
tx
a10
a11
ty
0
0
1




.
The upper-left 2×2 is a composite rotation, scale, and reflection matrix, while the tx and ty are composite translations. To transform a position vector r = (w·x, w·y, w)T we have




x
y
1




=



a00
a01
tx
a10
a11
ty
0
0
1








x
y
1




,
which involves nine multiplications and six additions. Since the last row of the matrix is mostly zero elements, in the real world computation we may rearrange the transformation as follows to reduce the number of operations
x = a00x+a01y+tx,
y = a10x+a11y+ty.
It is noted that the transformation process is reduced to four multiplications and two additions, which is a significant speedup in consideration of transforming hundreds and thousands points that represent your drawing picture.

Finally, we should point out that, since matrix multiplication is in general not commutative, it is of important to keep the correct sequence of multiplications in terms of the order of transformations applied to an image. Only some special cases can we mix the order of multiplications. For example, given a uniform scaling S and a rotation R, we have SR = RS.

4.7  World and device coordinates transformation

As the title indicates, this section concerns the conversion between the world coordinate system and the device coordinate (DC) system. In practice, a DC system can be either your computer screen, printer, or NC machine coordinate system. However, the device coordinate system in this book usually means the coordinate system of your applet's drawing area. As we have mentioned in the previous chapter, the origin (0,0) of your applet's drawing area is, by default, at the top-left corner. Positive x values are to the right and positive y values are downward. Units are measured in pixel values. Therefore, the x- and y-coordinate are integers. Such coordinate system arrangement agrees with the coordinate system of your computer screen but disagrees with the world (i.e., the Cartesian) coordinate system we usually work with. For this reason, geometric objects designed in the world coordinate system may not be displayed directly in your applet's drawing area. In this section, we shall show you how to derive the required transformations so that you will be able to display in your applet's drawing area the geometric objects defined in the world coordinate system.

We assume that the DC and world coordinate systems are denoted by (O;x,y) and (O;x,y). Since the origin of the DC system is at the top-left corner, the first thing we need to do is to translate the origin to the left-bottom corner, which is denoted by O as shown in Figure 4.2.

Figure 4.2: Translating the origin to the left-bottom corner.

Writing this translation explicitly gives x = x-Ox and y = y-Oy. The next thing we want to do is to change the left-handed system to the right-handed system. This can be achieved by reflection about the x-axis. Consequently, the relation between the world and DC coordinate systems is

x = x-Ox,    y = -(y-Oy) = Oy-y.
As we mentioned, units in your applet's drawing area are measured in pixel values so that the x- and y-coordinate are integers. To match the units used in the world coordinate system (e.g., millimeters or inches), we need to scale the DC system. A choice of scaling factor depends on the size and resolution of your computer monitor. An experimental method to determine a right scale factor is to draw a line whose length is d in pixels. Then, you measure this line with a ruler. If the length of this line is d in millimeters, the scale factor is s=d/d which denotes s pixels per millimeter. Therefore, the final transformation between the DC and world coordinate systems is given by
x = x-Ox
s
,    y = Oy-y
s
.
It should be pointed out that the above transformation does not take into consideration the so-called aspect ratio, which is the ratio between the width and height of each pixel. Since the pixels of most monitors are taller than they are wide, the aspect ratio may be critical when you try to draw a figure with symmetric shape such as circles and squares. For instance, if you use the CGA 320×200 mode, each pixel is approximately twice as high as it is wide. Therefore, if you draw a 4×4 block of pixels you will not get a square on the screen. To actually draw a square you must adjust for the aspect ratio of the mode you are working with. Many graphics packages provide a method to get the aspect ratio of your monitor. If such a method is not available, you can get it experimentally by drawing a horizontal and vertical line with length equal to, say, 1000 pixels, then measuring these two lines in millimeters. The length (in millimeters) of the horizontal line to that of the vertical line is the aspect ratio of your monitor. Taking the aspect ratio into account, the transformation from the DC to the world coordinates is given by
x = x-Ox
s
,    y = Oy-y
s×AspectRatio
.
Conversely, the conversion from the world to DC coordinates is
x = Ox+s×x,    y = Oy-s×AspectRatio×y.
Having derived the transformation between the DC and world coordinate systems, we now consider implementation of transformations between two systems. First, let us create a new class GService in the GeomLib directory. The GService class will provide common geometric services to our applets such as conversion between the DC and world coordinate systems. We implement the GService class as follows.

package GeomLib;

import java.awt.*;

public class GService
{
   public int xor, yor;       // origin of world coordinate system
   public double scale;       // pixels per millimeter
   public double AspectRatio; // width : height

   // Constructor initializes origin of world system to top-left corner:
   public GService()
   {
      xor = yor = 0;
      scale = 2.631578947368;       // machine dependent
      AspectRatio = 0.974358974359; // machine dependent
   }

   // Set the origin of world system to user specified position:
   public void SetOrigin(int xor, int yor)
   {
      this.xor = xor;
      this.yor = yor;
   }

   // Set the world system with respect to user specified information:
   public void SetWorldSystem(int xor, int yor, double scale, double AspectRatio)
   {
      this.xor = xor;
      this.yor = yor;
      this.scale = scale;
      this.AspectRatio = AspectRatio;
   }

   // Convert the DC to world coordinates:
   public void DCToWorld(int x_dc, int y_dc, GPoint pt_world)
   {
      pt_world.x = (x_dc - xor) / scale;
      pt_world.y = (yor - y_dc) / (AspectRatio * scale);
      pt_world.z = 0.0;
   }

   // Convert the world to DC coordinates:
   public void WorldToDC(GPoint pt_world, Point pt_dc)
   {
      pt_dc.x = (int)(xor + scale * pt_world.x);
      pt_dc.y = (int)(yor - AspectRatio * scale * pt_world.y);
   }

   // Convert the world to DC coordinates:
   public void WorldToDC(double x, double y, Point pt_dc)
   {
      pt_dc.x = (int)(xor + scale * x);
      pt_dc.y = (int)(yor - AspectRatio * scale * y);
   }
}

It should be pointed out that the constructor of GService initializes the scale factor and aspect ratio with respect to our 21-inch monitor with resolution being set to 1024×768. You may need to modify them to match the mode of your monitor. Save the above code under the name GService.java in the directory GeomLib and compile it to generate GService.class.

Let us now create an applet to illustrate the conversion of two coordinate systems. In particular, we want to print out both the DC and world coordinates of the current mouse position while moving the mouse cursor within the applet's drawing area. We call this applet the WorldToDC and type the following code:

import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import GeomLib.*;

public class WorldToDC extends Applet 
implements MouseMotionListener
{
   String   str;
   Point    pt_dc = new Point();
   GPoint   pt_world = new GPoint();
   GService gs = new GService();

   public void init()
   {
      // Register event listener:
      addMouseMotionListener(this);

      // Set origin to the bottom-left corner:
      gs.SetOrigin(0, getSize().height);
   }

   // Required to declare listener interfaces:
   public void mouseDragged(MouseEvent evt){}

   // Dynamically update pt_dc and pt_world to mouse position:
   public void mouseMoved(MouseEvent evt)
   {
      pt_dc.x = evt.getX();
      pt_dc.y = evt.getY();
      gs.DCToWorld(pt_dc.x, pt_dc.y, pt_world);
      repaint();
   }

   public void paint(Graphics g)
   {
      str = String.valueOf(pt_dc.x)+", "+String.valueOf(pt_dc.y);
      g.drawString("cursor position in screen coorinates="+str, 5, 15);

      str = String.valueOf((float)pt_world.x)+", "+String.valueOf((float)pt_world.y);
      g.drawString("cursor position in world coorinates="+str, 5, 285);
   }
}

Save the code under the name WorldToDC.java in the directory Ch4 and compile it to obtain WorldToDC.class. Then, write a WorldToDC.html web page to invoke the applet which looks similar to that in Figure 4.3.

Before we move to the next section, let us add a method drawLine() in GService that draws a straight line defined in the world coordinate system on the screen. This drawLine() method is the essential tool for us to draw a sophisticated graphics. The code fragment of this method is given below.

   public void drawLine(Graphics g, double x1, double y1, double x2, double y2)
   {
      Point p1_dc = new Point();
      Point p2_dc = new Point();
      WorldToDC(x1, y1, p1_dc);
      WorldToDC(x2, y2, p2_dc);
      g.drawLine(p1_dc.x, p1_dc.y, p2_dc.x, p2_dc.y);
   }

4.8  Applications of 2D transformation

We have discussed basic 2D transformations and a conversion between the DC and world coordinate systems. In this section, we shall put all information together to create two applets that have practical applications in 2D engineering drawing and simulation.

Our first applet is called the Mechanism that simulates a four-bar linkage commonly seen in the manufacturing industry. The source code of Mechanism is listed below.

import java.awt.*;
import java.applet.Applet;
import GeomLib.*;

public class Mechanism extends Applet
{
   int      width, height;
   double   r, R;
   GService gs = new GService();

   public void init()
   {
      setBackground(Color.lightGray);
      r = 20.0;
      R = 50.0;
      width = getSize().width;
      height = getSize().height;
      gs.SetOrigin((int)(5*r), height/2);
   }

   void pause(int time)
   {
      try{Thread.sleep(time);}
      catch(InterruptedException e){}
   }

   // display the image in the applet then sleep and call repaint
   public void paint(Graphics g)
   {
      double p1[]={0,0}, p2[]={0,0};
      double theta=Math.PI/2.0, delta_theta=0.2;

      while (true)
      {
         // Draw axes in red:
         g.setColor(Color.red);
         gs.drawLine(g, -20.0, 0.0, 80.0, 0.0);
         gs.drawLine(g, 0.0, -20.0, 0.0, 20.0);

         // Draw linkages:
         g.setColor(Color.blue);
         p1[0] = r * Math.cos(theta);
         p1[1] = r * Math.sin(theta);
         gs.drawLine(g, 0.0, 0.0, p1[0], p1[1]);
         p2[0] = p1[0] + Math.sqrt(R * R - p1[1] * p1[1]);
         gs.drawLine(g, p1[0], p1[1], p2[0], p2[1]);
  
         // Draw sliding bar:
         gs.drawLine(g, p2[0]-5, p2[1]-3, p2[0]+5, p2[1]-3);
         gs.drawLine(g, p2[0]+5, p2[1]-3, p2[0]+5, p2[1]+3);
         gs.drawLine(g, p2[0]+5, p2[1]+3, p2[0]-5, p2[1]+3);
         gs.drawLine(g, p2[0]-5, p2[1]+3, p2[0]-5, p2[1]-3);

         pause(100);
         theta += delta_theta;
         if (theta > Math.PI * 2.0)
            theta = 0.0;

         // Erase drawings:
         g.setColor(Color.lightGray);
         g.fillRect(0, 0, width, height);
      }
   }
}

Save the above code under the name Mechanism.java in the directory ch4 and compile it to obtain Mechanism.class. We then write a HTML web page to invoke the applet. If everything works, you should see an applet similar to that in Figure 4.4.

Our next applet is to mirror (i.e., to reflect) a drawing object about an arbitrary reflection line so as to create a symmetric image about the mirror line. Recalling from the previous section, a reflection about an arbitrary line involves the following three steps:

  1. Translating the coordinate system such that the origin of the new system coincides with the starting point of the mirror line. Denoting the starting point of the line by p0, the translation matrix in homogeneous coordinate is




    1
    0
    -x0
    0
    1
    -y0
    0
    0
    1




    .
  2. Reflection about the line that passes through the origin. Assume the angle between the mirror line and the x-axis is f. Then, the reflection matrix is




    cos2f
    sin2f
    0
    sin2f
    -cos2f
    0
    0
    0
    1




    .
  3. Translating the coordinate system back to where it was:




    1
    0
    x0
    0
    1
    y0
    0
    0
    1




    .

Based on the sequence of transformation applied to an object, we have





1
0
x0
0
1
y0
0
0
1








cos2f
sin2f
0
sin2f
-cos2f
0
0
0
1








1
0
-x0
0
1
-y0
0
0
1




.
By the rule of product of matrices we can derive the following combined transformation matrix:




cos2f
sin2f
x0(1-cos2f)-y0sin2f
sin2f
-cos2f
y0(1+cos2f)-x0sin2f
0
0
1




.
Let r = (x,y) denotes an arbitrary point on an image and r = (x,y) the corresponding point on the mirrored image. The relation between r and r is given by:


x
y


=

cos2f
sin2f
sin2f
-cos2f




x
y


+

x0(1-cos2f)-y0sin2f
y0(1+cos2f)-x0sin2f


.
We call this applet the Mirror2d and type the following source code.

import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import GeomLib.*;

public class Mirror2d extends Applet
implements ActionListener, MouseListener, MouseMotionListener
{
   final int max_num_pts = 20;
   int       num_pts;
   GService  gs = new GService();
   GPoint    line[] = new GPoint[2];
   GPoint    obj[] = new GPoint[max_num_pts];
   GPoint    newObj[] = new GPoint[max_num_pts];
   boolean   DrawObj;

   public void init()
   {
      // Register event listeners:
      addMouseListener(this);
      addMouseMotionListener(this);

      setLayout(new FlowLayout());
      Button b;
      b= new Button("Draw Object");
      b.addActionListener(this);
      add(b);

      b= new Button("Draw Mirror Line");
      b.addActionListener(this);
      add(b);

      // Inializing other variables:
      num_pts = 0;
      DrawObj = true;
      line[0] = new GPoint();
      line[1] = new GPoint();
      for (int i=0; i<max_num_pts; i++)
      {
         obj[i] = new GPoint();
         newObj[i] = new GPoint();
      }
      gs.SetOrigin(0, getSize().height);
      setBackground(Color.lightGray);
   }

   // Set variables with respect to an activated button:
   public void actionPerformed(ActionEvent evt)
   {
      String buttonName = evt.getActionCommand();
      if (buttonName.equals("Draw Object"))
      {
         num_pts = 0;
         DrawObj = true;
         line[0].SetPoint(0, 0, 0);
         line[1].SetPoint(0, 0, 0);
      }
      else
      {
         DrawObj = false;
      }
      repaint();
   }

   // Required to declare listener interfaces:
   public void mouseClicked(MouseEvent evt){}
   public void mouseEntered(MouseEvent evt){}
   public void mouseExited(MouseEvent evt){}
   public void mouseMoved(MouseEvent evt){}

   public void mousePressed(MouseEvent evt)
   {
      if (DrawObj)
      {
         if (num_pts == 0)
         {
            gs.DCToWorld(evt.getX(), evt.getY(), obj[num_pts]);
            num_pts += 1;
         }
         if (num_pts < max_num_pts)
         {
            gs.DCToWorld(evt.getX(), evt.getY(), obj[num_pts]);
            num_pts += 1;
         }
      }
      else
      {
         // Draw mirror line mode:
         gs.DCToWorld(evt.getX(), evt.getY(), line[0]);
      }
   }

   public void mouseDragged(MouseEvent evt)
   {
      if (DrawObj)
         gs.DCToWorld(evt.getX(), evt.getY(), obj[num_pts-1]);
      else
         gs.DCToWorld(evt.getX(), evt.getY(), line[1]);
      repaint();
   }

   public void mouseReleased(MouseEvent evt)
   {

      if (DrawObj)
         gs.DCToWorld(evt.getX(), evt.getY(), obj[num_pts-1]);
      else
         gs.DCToWorld(evt.getX(), evt.getY(), line[1]);
   }

   public void paint(Graphics g)
   {
      int    i;
      double dist;

      // Draw the geometric object in blue:
      g.setColor(Color.blue);
      for (i=1; i<num_pts; i++)
         gs.drawLine(g, obj[i-1].x, obj[i-1].y, obj[i].x, obj[i].y);

      // Check if the mirror line is valid:
      dist = Math.abs(line[1].x - line[0].x) + Math.abs(line[1].y - line[0].y);
      if (dist > 1.0)
      {
         // Draw mirror line in red:
         g.setColor(Color.red);
         gs.drawLine(g, line[0].x, line[0].y, line[1].x, line[1].y);

         // Draw mirrored object in green:
         g.setColor(Color.green);
         Transform();
         for (i=1; i<num_pts; i++)
            gs.drawLine(g, newObj[i-1].x, newObj[i-1].y, newObj[i].x, newObj[i].y);
      }
   }

   void Transform()
   {
      int i;
      double phi, translation_x, translation_y, A[] = new double[4];

      // Construct reflection transformation matrix:
      phi = Math.atan2(line[1].y-line[0].y, line[1].x-line[0].x);
      A[0] = Math.cos(2 * phi);
      A[3] = -A[0];
      A[1] = A[2] = Math.sin(2 * phi);
      translation_x = line[0].x * (1 - A[0]) - line[0].y * A[1];
      translation_y = line[0].y * (1 - A[3]) - line[0].x * A[2];

      // Compute mirrored vertices:
      for (i=0; i<num_pts; i++)
      {
         newObj[i].x = A[0]*obj[i].x + A[1]*obj[i].y + translation_x;
         newObj[i].y = A[2]*obj[i].x + A[3]*obj[i].y + translation_y;
      }
   }
}

Two buttons, Draw Object and Draw Mirror Line, are implemented in the applet. Clicking the Draw Object button enables you to draw a polygon that represents a geometric object. The mirrored image is generated when you click the Draw Mirror Line button and start to draw the mirror line. It is noted that the ``rubber-banding'' technique discussed in the previous chapter is used here to create both the polygon and mirror line. Save the above code under the name Mirror2d.java in the directory ch4 and compile it to obtain Mirror2d.class. We then write a HTML web page to invoke the applet which looks similar to that in Figure 4.5.

4.9  Summary and references

Two-dimensional translation, scaling, rotation, and reflection are not only the core math for 2D graphics programming but also the foundation of 3D transformations. Since most of us are at some extent familiar with basic 2D transformations, we did not discuss the underlying concepts and mathematics of 2D transformations in exhaustive detail but emphasized on their implementation and applications.

Most engineers design their models in the Cartesian coordinate system, which is a subject of Euclidean geometry. Although we can restrict ourselves in the Euclidean geometry, it is preferred to extend the Euclidean geometry to the projective geometry by introducing the so-called homogeneous coordinates. With the use of homogeneous coordinates, it is possible to represent any transformation in the matrix form and hence to combine a number of transformations into a single transformation. For this reason, we also discussed in this chapter transformations in homogeneous coordinates.

As an example to illustrate the use of 2D transformations in computer graphics programming, we also discussed how to apply the 2D transformations to derive a system that convert the world coordinates to the device coordinates so that images created in the Cartesian coordinate can be displayed on your computer screen or your applet's drawing area.

Suggested readings for this chapter are:

  1. Anand, V.B., Computer Graphics and Geometric Modeling for Engineers, 1993, John Wiley & Sons, Inc.
  2. Ammeraal, L., Computer Graphics for Java Programmers, 1998, John Wiley & Sons, Inc.
  3. Foley, J.D. et al., Computer Graphics: Principles and Practice, 2nd ed., 1995, Addison-Wesley.
  4. Stephens, R., Visual Basic Graphics Programming, 1997, John Wiley & Sons, Inc.