Return-Path: <env_42676079-1624309795@hermes.sun.com>
Received: from po10.mit.edu (po10.mit.edu [18.7.21.66])
	by po10.mit.edu (Cyrus v2.1.5) with LMTP; Tue, 09 Sep 2003 21:15:41 -0400
X-Sieve: CMU Sieve 2.2
Received: from fort-point-station.mit.edu by po10.mit.edu (8.12.4/4.7) id h8A1FYp2022676; Tue, 9 Sep 2003 21:15:34 -0400 (EDT)
Received: from hermes.sun.com (hermes.sun.com [64.124.140.169])
	by fort-point-station.mit.edu (8.12.4/8.9.2) with SMTP id h8A1FVU8002880
	for <alexp@mit.edu>; Tue, 9 Sep 2003 21:15:31 -0400 (EDT)
Date: 9 Sep 2003 16:23:27 -0800
From: "SDN - Core Java Technologies Tech Tips" <sunmail@hermes.sun.com>
To: alexp@mit.edu
Message-Id: <42676079-1624309795@hermes.sun.com>
Subject: Core Java Technologies Tech Tips, September 9, 2003 (SocketChannels, AffineTransform)
Mime-Version: 1.0
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Mailer: SunMail 1.0
X-Spam-Score: 1.6
X-Spam-Level: * (1.6)
X-Spam-Flag: NO
X-Scanned-By: MIMEDefang 2.28 (www . roaringpenguin . com / mimedefang)

<!DOCtype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Core Java Technologies Technical Tips</title>

<style type="text/css">

code {color: #333333; font-family: verdana, courier, monospace; font-size: 10pt}
pre {color: #333333; font-family: verdana, courier, monospace; font-size: 10pt}
body, div, span {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
td, th {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
tr {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
table {font-family: arial, helvetica, sans-serif; font-size: 10pt}
p {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
li {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
br {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
div {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
sup {font-family: arial, helvetica, sans-serif; font-size: 5pt}
h3 {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 11pt}
h4, b {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
blockquote, input, select {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
ul, ol {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
a:link {font-size: 10pt; font-family: arial, helvetica, sans-serif; color:#666699 }
span.purple {font-weight: bold; color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
span.small {font-size: 8pt; font-family: arial, helvetica, sans-serif; color:#333333 }
span.link {font-size: 8pt; font-family: arial, helvetica, sans-serif; color:#666699 }
</style>

</head>

<body bgcolor="#ffffff">
<a name="top"></a>
<table border="0" cellpadding="0" cellspacing="0" width="611">
<tr>
<td colspan="3" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>

<tr>
<td bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>

<td>
<table border="0" cellpadding="0" cellspacing="0" width="611">
<tr>
<td colspan="2"><a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287541"><img src="http://developer.java.sun.com/images/headers/core_ttips_hdr.jpg" width="611" height="160" alt="Core Java Technologies Technical Tips" border="0"></a></td>
</tr>

<tr>
<td colspan="2" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>

<!-- ================== -->
<!-- Start Main Content -->
<!-- ================== -->

<tr>
<td height="20">&nbsp;&nbsp;&nbsp;<a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287560" style="text-decoration:none;">View this issue as simple text</a></td>
<td align="right" height="20"><span class="purple">September 9, 2003</span>&nbsp;&nbsp;&nbsp;&nbsp;</td>
</tr>

<tr><td colspan="2">

<table border="0" cellpadding="10" cellspacing="0" width="100%">
<tr>
<td>
     
<form method="get" action="http://search.java.sun.com/search/java/">   
<table border="0" cellpadding="3" cellspacing="0" width="100%">
<tr> 
<td align="left" valign="middle" width="20%"><h3>In this Issue</h3></td>
              
<td valign="middle" align="right" width="70%">
<font size="2">
<input type="text" size="15" maxlength="128" name="qt"></font></td>

<td valign="middle" width="55">
<input type="image" src="http://developer.java.sun.com/images/v4a_search.gif" alt="Search" value="search" border="0" width="55"></td>
</tr>
</table>
</form>

<p>
Welcome to the Core Java Technologies Tech Tips, September 9, 2003. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
</p>

<p>
This issue covers:
</p>

<p>
<a href="#1"><img src="http://developer.java.sun.com/images/anchor.gif" border="0" alt="-">Working with SocketChannels</a><br>    
<a href="#2"><img src="http://developer.java.sun.com/images/anchor.gif" border="0" alt="-">Understanding AffineTransform</a>
</p>

<p>
These tips were developed using Java 2 SDK, Standard Edition, v 1.4. 
</p>

<p>
This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Dim Sum Thinking, Inc, and editor-in-chief for <a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287471">java.net</a>.
</p>

<p>
See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<a name="1"></a>

<h3>WORKING WITH SOCKETCHANNELS</h3>

<p>
The next time you have a networking task that has you reaching for the Web Services API or some other high level API, consider whether you can accomplish the same task more simply and with less overhead using Sockets. In this Tech Tip you will use the <code>SocketChannel</code> and <code>ServerSocketChannel</code> classes in the <code>java.nio</code> package to create a simple networked application.
</p>

<p>
When you use a browser to view a particular web page, the details are hidden from you. Open a browser and type in:
</p>

<p>
http://developer.java.sun.com/developer/JDCTechTips/
</p>

<p>
As a result, this entry connects you to port 80 of the web site. It also sends a particular HTTP request that contains the name of the page you want to view. 
</p>

<p>
Now let's take these actions explicitly with a <code>SocketChannel</code>. The following program, <code>TechTipReader</code>, uses a <code>SocketChannel</code> to retrieve the page. Notice the <code>getTips()</code> method in the program. The method first instantiates a <code>SocketChannel</code>. It then uses the instance to connect to port 80 of <code>developer.java.sun.com</code>. Then the method sends a standard HTTP &quot;<code>GET</code>&quot; request to retrieve the JDCTechTips page. 
</p>

<p>
The response from the server is printed to standard out.
</p>

<pre>
   import java.nio.channels.SocketChannel;
   import java.nio.charset.Charset;
   import java.nio.ByteBuffer;
   import java.net.InetSocketAddress;
   import java.io.IOException;

   public class TechTipReader {

      private Charset charset = 
        Charset.forName(&quot;UTF-8&quot;);
      private SocketChannel channel;

      public void getTips() {
        try {
          connect();
          sendRequest();
          readResponse();
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          if (channel != null) {
            try {
              channel.close();
            } catch (IOException e) {}
          }
       }
      }

      private void connect() throws IOException {
         InetSocketAddress socketAddress =
           new InetSocketAddress(
             &quot;developer.java.sun.com&quot;, 80);
         channel = SocketChannel.open(socketAddress);
       }

      private void sendRequest() throws IOException {
          channel.write(charset.encode("GET "
                            + &quot;/developer/JDCTechTips/&quot;
                            + &quot;\r\n\r\n&quot;));
        }

      private void readResponse() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while ((channel.read(buffer)) != -1) {
          buffer.flip();
          System.out.println(charset.decode(buffer));
          buffer.clear();
        }
      }

      public static void main(String[] args) {
        new TechTipReader().getTips();
      }
   }
</pre>

<p>
Looking further into the <code>TechTipReader</code> program, notice that the <code>connect()</code> method creates an <code>InetSocketAddress</code> by passing in two parameters: a <code>String</code> that represents the domain, and the standard HTTP port 80. The call to the <code>static</code> method <code>SocketChannel.open()</code> takes the <code>InetSocketAddress</code> as a parameter. That call is equivalent to the following two steps:
</p>

<pre>
   channel = SocketChannel.open();
   channel.connect(socketAddress);
</pre>

<p>
The <code>sendRequest()</code> method sends the three-line request. The first line is &quot;<code>GET /developer/JDCTechTips/</code>&quot; and the second and third lines are empty. The call to <code>channel.write()</code> takes a <code>ByteBuffer</code> as its argument so you have to transform the <code>String</code>. Much of the time in working with <code>SocketChannels</code> involves converting to and from <code>ByteBuffers</code>. There are many ways to do the conversion, this example uses the <code>encode()</code> method of the <code>java.nio.charset.Charset</code> 
class.
</p>

<p>
The <code>readResponse()</code> method is responsible for handling the response that is returned to the <code>SocketChannel</code>. The method reads from the <code>SocketChannel</code> into a <code>ByteBuffer</code>. As long as it hasn't reached the end of the file being transferred, the method flips the buffer from reading to being ready to write. The contents of the <code>ByteBuffer</code> are transformed by the <code>charset.decode()</code> method to a <code>String</code> that is sent to standard out.
</p>

<p>
When you run the <code>TechTipReader</code> program, you should see the HTML for the page displayed.
</p>

<p>
Now let's look at a second example. In this example, two numbers are added. The example involves a client and a server. When the client has two numbers to add, it sends the numbers to a server. The server then performs the addition and returns the sum. This example is based on the <code>SocketChannel</code> API. Other possible solutions could use Web services, RMI, or servlets. 
</p>

<p>
In the example, the contents of the <code>ByteBuffer</code> are two ints. This allows you to restrict the <code>ByteBuffer</code> to eight bytes. It also allows you to create an <code>IntBuffer</code> that serves as a view into the <code>ByteBuffer</code>, as follows:
</p>

<pre>
  private ByteBuffer buffer = ByteBuffer.allocate(8);
  private IntBuffer intBuffer = buffer.asIntBuffer();
</pre>

<p>
The following program, <code>SumClient</code>, provides the client part of the example. In order to run <code>SumClient</code>, you first have start up the server part of the example, <code>SumServer</code>, which you can find later in this tip. 
</p>

<pre>
   import java.nio.channels.SocketChannel;
   import java.nio.ByteBuffer;
   import java.nio.IntBuffer;
   import java.io.IOException;
   import java.net.InetSocketAddress;

   public class SumClient {

      private SocketChannel channel;
      private ByteBuffer buffer = ByteBuffer.allocate(8);
      private IntBuffer intBuffer = buffer.asIntBuffer();

      public void getSum(int i, int j) {
        try {
          channel = connect();
          sendSumRequest(i, j);
          receiveResponse();
        } catch (IOException e) {
          // add exception handling code here
          e.printStackTrace();
        } finally {
          if (channel != null) {
            try {
              channel.close();
            } catch (IOException e) {
              // add exception handling code here
              e.printStackTrace();
            }
          }
        }
      }

      private SocketChannel connect() 
                                   throws IOException {
        InetSocketAddress socketAddress =
          new InetSocketAddress(&quot;localhost&quot;, 9099);
        return SocketChannel.open(socketAddress);
      }

      private void sendSumRequest(int i, int j)
                                   throws IOException {
        buffer.clear();
        intBuffer.put(0, i);
        intBuffer.put(1, j);
        channel.write(buffer);
        System.out.println(&quot;Sent request for sum of &quot;
                           + i + &quot; and &quot; + j + &quot;...&quot;);
      }

      private void receiveResponse() 
                                   throws IOException {
        buffer.clear();
        channel.read(buffer);
        System.out.println(
                       &quot;Received response that sum is &quot;
                       + intBuffer.get(0) + &quot;.&quot;);
      }

      public static void main(String[] args) {
        new SumClient().getSum(14, 23);
      }
   }
</pre>

<p>
The <code>getSum()</code> method in <code>SumClient</code> connects a <code>SocketChannel</code> to a predetermined address. The <code>connect()</code> method is essentially the same as the one in the <code>TechTipReader</code> example. After calling <code>connect()</code>, the <code>getSum()</code> method passes two ints (in the example, 14 and 23) to the <code>sendSumRequest()</code> method, which, in turn, loads the <code>ByteBuffer</code> and writes the contents to the <code>SocketChannel</code>. The <code>receiveResponse()</code> method then takes the contents of the returned method and writes them to standard out.
</p>

<p>
When you run <code>SumClient</code> (after starting <code>SumServer</code>), it should display the following:
</p>

<pre>
   Sent request for sum of 14 and 23...
   Received response that sum is 37.
</pre>

<p>
Looking further at <code>SumClient</code>, notice that <code>sendSumRequest()</code> uses two different views of the same <code>ByteBuffer</code>. First, <code>buffer.clear()</code> discards the mark in the buffer, sets the position back to zero, and sets the limit back to its capacity. In effect it clears the buffer. Next, the <code>ByteBuffer</code> is viewed as a buffer that holds two ints using the <code>intBuffer</code> handle. The int passed in as the first parameter to <code>sendSumRequest()</code> is put in the first slot using <code>intBuffer.put(0,i)</code>, and the second parameter is similarly put in the second slot. The <code>write()</code> method of <code>SocketChannel</code> takes a <code>ByteBuffer</code>, so the first view of the buffer is required.
</p>

<p>
The <code>receiveResponse()</code> method parallels <code>sendSumRequest()</code>. The buffer is again cleared. Then the contents of the <code>SocketChannel</code> are read into the <code>ByteBuffer</code> view of the buffer. Next, the <code>IntBuffer</code> is used to return the int in the first position.
</p>

<p>
Before moving on to the <code>SumServer</code>, the server part of the solution, you might want to put an additional safeguard into the places where you are calling the <code>read()</code> and <code>write()</code> methods on the <code>SocketChannel</code> object. These methods return a long with the number of bytes written or read. In this case you are expecting eight bytes. Find the following line inside of <code>sendSumRequest()</code>:
</p>

<pre>
   channel.write(buffer);
</pre>

<p>
Replace it with this check on the size of the buffer being written.
</p>

<pre>
   if (channel.write(buffer)!= 8){
       throw new IOException(&quot;Expected 8 bytes.&quot;);
}
</pre>

<p>
Put a similar guard around the <code>channel.read()</code> call in the <code>receiveResponse()</code> method.
</p>

<p>
Here's <code>SumServer</code>. To make the example work, you need to set up a server that listens for incoming requests in an agreed upon location. In the <code>openChannel()</code> method below the port 9099 is used. A call is made to the static method <code>open()</code> in <code>ServerSocketChannel</code>, the next line binds the socket to the specified port. As soon as <code>channel.isOpen()</code> returns true, a confirmation message is sent to standard out.
</p>

<pre>
   import java.nio.ByteBuffer;
   import java.nio.IntBuffer;
   import java.nio.channels.ServerSocketChannel;
   import java.nio.channels.SocketChannel;
   import java.io.IOException;
   import java.net.InetSocketAddress;

   public class SumServer {

      ByteBuffer buffer = ByteBuffer.allocate(8);
      IntBuffer intBuffer = buffer.asIntBuffer();
      ServerSocketChannel channel = null;
      SocketChannel sc = null;

      public void start() {
        try {
          openChannel();
          waitForConnection();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }

      private void openChannel() throws IOException {
        channel = ServerSocketChannel.open();
        channel.socket().bind(
                          new InetSocketAddress(9099));
        while (!channel.isOpen()) {
        }
        System.out.println(&quot;Channel is open...&quot;);
      }

      private void waitForConnection() 
      throws IOException {
        while (true) {
          sc = channel.accept();
          if (sc != null) {
            System.out.println(
                           &quot;A connection is added...&quot;);
            processRequest();
            sc.close();
          }
        }
      }

      private void processRequest() throws IOException {
        buffer.clear();
        sc.read(buffer);
        int i = intBuffer.get(0);
        int j = intBuffer.get(1);
        buffer.flip();
        System.out.println(&quot;Received request to add &quot;
                           + i + &quot;  and &quot; + j);
        buffer.clear();
        intBuffer.put(0, i + j);
        sc.write(buffer);
        System.out.println(&quot;Returned sum of &quot; +
                           intBuffer.get(0) + &quot;.&quot;);
      }

      public static void main(String[] args) {
        new SumServer().start();
      }

   }
</pre>

<p> 
When you start <code>SumServer</code>, it should display the following:
</p>

<pre>
   Channel is open...

   Sent request for sum of 14 and 23...
   Received response that sum is 37.  
</pre>

<p>
Then, when you run <code>SumClient</code>, <code>SumServer</code> should display the following:
</p>

<pre>   
   A connection is added...
   Received request to add 14  and 23
   Returned sum of 37. 
</pre>

<p>
In the <code>waitForConnection()</code> method, the application cycles until a request to connect to the <code>ServerSocketChannel</code> arrives. When a request does arrive, <code>channel.accept()</code> returns a <code>SocketChannel</code>, so the variable <code>sc</code> is no longer <code>null</code>. The incoming request is processed and then the <code>SocketChannel</code> is closed. The server then awaits another connection request. Note that in this simple example requests are processed fully one at a time. A different structure would be required for a more sophisticated server that handles many concurrent requests but that is beyond the scope of this tip.
</p>

<p>
The <code>processRequest()</code> method is much like the corresponding methods in the <code>SumClient</code>. The buffer is cleared, the channel is read into the buffer. Then the <code>IntBuffer</code> view is used to retrieve the two ints from the buffer. The buffer is then cleared and the sum is loaded back in using the <code>IntBuffer</code> view. The buffer is then written back to the <code>SocketChannel</code>.
</p>

<p>
For more information about <code>SocketChannels</code>, see the technical 
article &quot;<a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287513">New I/0 Functionality for Java 2 Standard Edition 1.4</a>&quot;. 
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td>
</tr>

<tr>
<td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<a name="2"></a>

<h3>UNDERSTANDING AFFINETRANSFORM</h3>

<p>
Included in the Java 2D classes is the class <code>java.awt.geom.AffineTransform</code> which allows you to perform affine transformations. An affine transformation transforms the coordinates of a two-dimensional image in such a way that parallel lines remain parallel. For example, if you start with a rectangle, an affine transformation might produce a parallelogram in a different location, but lines remain lines and parallelism is preserved. Affine transformations can be translations, rotations, scaling (which includes flips), and shears. 
</p>

<p>
The documentation for the <code>AffineTransform</code> class explains that:
</p>

<pre> 
  Such a coordinate transformation can be represented by a 3 row 
  by 3 column matrix with implied last row of [0 0 1]. This 
  matrix transforms source coordinates (x,y) into destination 
  coordinates (x',y') by considering them to be a column vector 
  and multiplying the coordinate vector by the matrix according 
  to the following process:

   [ x']   [ m00  m01  m02 ]  [ x ]   [ m00x + m01y + m02 ]
   [ y'] = [ m10  m11  m12 ]  [ y ] = [ m10x + m11y + m12 ]
   [ 1 ]   [  0    0    1  ]  [ 1 ]   [         1         ]
</pre>

<p>
This Tech Tip explains why you need a 3 by 3 matrix to express the affine transformations of two-dimensional space. It relates the matrices for the fundamental transformations to the method calls in the <code>AffineTransform</code> class.
</p>

<p>
In this tip, you'll run the following program, <code>AffineChecker</code>, to compare <code>AffineTransform</code> transformations to matrix multiplications. Notice the three instance variables of type <code>Point2D.Double</code> in the program. These correspond to a starting point that has coordinate (2,3), and the points that result from each of the transformations, respectively.
</p>

<pre>
   import java.awt.geom.Point2D;
   import java.awt.geom.AffineTransform;

   public class AffineChecker {
      private static Point2D startingPoint
                             = new Point2D.Double(2,3);
      private static Point2D affineResult
                               = new Point2D.Double();
      private static Point2D matrixResult
                               = new Point2D.Double();

      public static void matrixMultiply(
                               double m00, double m01,
                               double m10, double m11){
        double x = m00 * startingPoint.getX() +
                   m01 * startingPoint.getY();
        double y = m10 * startingPoint.getX() +
                   m11 * startingPoint.getY();
        matrixResult.setLocation(x,y);
      }

      public static void perform(AffineTransform at){
        at.transform(startingPoint,affineResult);
      }

      public static void report(){
        System.out.println(&quot;Affine Result = (&quot;
                          + affineResult.getX() + &quot; , &quot;
                          + affineResult.getY() + &quot;)&quot;);
        System.out.println(&quot;Matrix Result = (&quot;
                          + matrixResult.getX() + &quot; , &quot;
                          + matrixResult.getY() + &quot;)&quot;);
        if (affineResult.distance(matrixResult)&lt;.0001){
          System.out.println(&quot;    No real difference&quot;);
        }
      }
   }
</pre>

<p>
The <code>matrixMultiply()</code> method in <code>AffineChecker</code> provides the standard matrix multiplication:
</p>

<pre>
   [ m00 m01 ] [ x ]   [ m00x + m01y]
   [ m10 m11 ] [ y ] = [ m10x + m11y]
</pre>

<p>
The <code>report()</code> method in the program prints the results for the transformation and the matrix multiplication, and indicates whether the results are close enough to be essentially the same.
</p>

<p>
As a first test, let's do nothing to the starting point and then make comparisons. To do this, run the following program:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class NothingChecker {
      public static void main(String[] args){
        System.out.println(
                       "The result of doing nothing:");
        AffineChecker.perform(
          AffineTransform.getRotateInstance(0));
        AffineChecker.matrixMultiply(1,0,
                                     0,1);
        AffineChecker.report();
      }
   }
</pre>

<p>   
<code>NothingChecker</code> creates a new rotation transformation and passes in the degrees to be rotated (in this case, 0) using the static method call:
</p>

<pre>
   AffineTransform.getRotateInstance(0)
</pre>

<p>
<code>NothingChecker</code> then calls the <code>transform()</code> method (through <code>AffineChecker's perform()</code> method) on the returned <code>AffineTransform</code>. Note that the <code>transform()</code> method is passed the <code>startingPoint</code> and <code>affineResult</code> to transform the point (2,3) and store the resulting point in <code>affineResult</code>. Next, <code>NothingChecker</code> compares the Affine Result with the result of multiplying by the 2 by 2 identity matrix:
</p>

<pre>
  [ 1 0 ]
  [ 0 1 ]
</pre>

<p>   
When you run <code>NothingChecker</code>, you should see the following:
</p>

<pre>
   The result of doing nothing:
   Affine Result = (2.0 , 3.0)
   Matrix Result = (2.0 , 3.0)
       No real difference
</pre>

<p>
In other words, you should find that the result of both transforms is the point (2,3).
</p>

<p>
You can scale in the x and/or y directions using the <code>getScaleInstance()</code> static method of <code>AffineTransform</code>. Try to scale the x direction by a factor of four and the y direction by a factor of five. Here is the matrix multiplication that corresponds to this scaling.
</p>

<pre>
  [ 4 0 ]  [ x ]   [ 4x ]
  [ 0 5 ]  [ y ] = [ 5y ].
</pre>

<p>
The following program shows the call to <code>getScaleInstance()</code>. You can run the program to compare the transformation to the matrix multiplication:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class Scale4Xand5YChecker {
      public static void main(String[] args) {
        System.out.println(
                       &quot;The results of scaling four &quot; +
                       &quot;times in the x direction &quot; +
                       &quot;and five times in the y:&quot;);
        AffineChecker.perform(
          AffineTransform.getScaleInstance(4,5));
        AffineChecker.matrixMultiply(4,0,
                                     0,5);
        AffineChecker.report();
      }
   }
</pre>

<p>  
The results should look like this:
</p>

<pre> 
   The results of scaling four times in the x direction and five 
   times in the y:
   Affine Result = (8.0 , 15.0)
   Matrix Result = (8.0 , 15.0)
       No real difference
</pre>

<p>   
Again, the results are the same.
</p>

<p>
You can reflect about the x and/or y axes by entering a negative number into one of the scaling coefficients. For example,
</p>

<pre>
  [ -1 0 ]  [ x ]   [ -x ]
  [  0 1 ]  [ y ] = [  y ]
</pre>

<p>
is a reflection in the x direction (reflecting about the y axis). Here is a program that performs the reflection and compares it to the matrix multiplication:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class FlipChecker {
      public static void main(String[] args) {
         System.out.println(
                       &quot;The results of flipping over&quot; +
                       &quot;the y-axis:&quot;);
       AffineChecker.perform(
          AffineTransform.getScaleInstance(-1,1));
        AffineChecker.matrixMultiply(-1, 0,
                                     0, 1);
        AffineChecker.report();
      }
   }
</pre>

<p>   
and the results are:
</p>

<pre>
   The results of flipping over the y-axis:
   Affine Result = (-2.0 , 3.0)
   Matrix Result = (-2.0 , 3.0)
       No real difference   
</pre>

<p>
A rotation expects its argument to be given in radians and not degrees. As a reminder, you can easily convert from degrees to radians by multiplying by pi and dividing by 180 degrees. For example, thirty degrees corresponds to pi/6. To rotate through pi/6 radians you multiply by the rotation matrix:
</p>

<pre>
  [ cos pi/6  -sin pi/6 ]
  [ sin pi/6   cos pi/6 ]
</pre>

<p>
You might recall that one characteristic of rotation matrices is that their determinant is 1. In code, you check the results like this:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class RotateThirtyChecker {
      public static void main(String[] args) {
        System.out.println(
            &quot;The results of a thirty degree&quot;
            + &quot; counter clockwise rotation:&quot;);
        AffineChecker.perform(
          AffineTransform.getRotateInstance(
                                           Math.PI/6));
        AffineChecker.matrixMultiply(
            Math.cos(Math.PI/6), -Math.sin(Math.PI/6),
            Math.sin(Math.PI/6),  Math.cos(Math.PI/6));
        AffineChecker.report();
      }
   }
</pre>

<p>   
Run <code>RotateThirtyChecker</code> and get:
</p>

<pre>
   The results of a thirty degree counter clockwise 
   rotation:
   Affine Result = 
              (0.23205080756887764 , 3.598076211353316)
   Matrix Result = 
              (0.23205080756887764 , 3.598076211353316)
       No real difference   
</pre>

<p>
A shearing transform changes the shape of the transformed object. The coefficients shx and shy indicate how much the x direction is transformed as a factor of the y coordinate, and how much the y direction is transformed as a factor of the x coordinate. This is the transformation that turns a rectangle into a parallelogram. The matrix representation is
</p>

<pre>
[  1  shx ]  [ x ]   [ x + y (shx) ]
[ shy  1  ]  [ y ] = [ y + x (shy) ].
</pre>

<p>
The code to do the transform and compare it to the matrix multiplication is this:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class ShearChecker {
      public static void main(String[] args) {
         System.out.println(
            &quot;The results of shearing:&quot;);
          AffineChecker.perform(
            AffineTransform.getShearInstance(5,6));
          AffineChecker.matrixMultiply(1, 5,
                                       6, 1);
          AffineChecker.report();
      }
   }
</pre>

<p>   
Run <code>ShearChecker</code> and get:   
</p>

<pre>   
   The results of shearing:
   Affine Result = (17.0 , 15.0)
   Matrix Result = (17.0 , 15.0)
       No real difference
</pre>

<p>
What remains is, in some sense, the easiest transform. It's a translation that just moves the shape over by some fixed amount. To move tx in the x direction and ty in the y direction you perform the following addition.
</p>

<pre>
  [ tx ]   [ x ]   [ tx + x ]
  [ ty ] + [ y ] = [ ty + y ].
</pre>

<p>
The problem is that until now, every transformation has been expressable in terms of multiplication with a 2 by 2 matrix. One way to combine these ideas is that all transformations can be written using the following.
</p>

<pre>
  [ m00 m01 ] [ x ]   [ tx ]   [ m00x + m01y + tx ]
  [ m10 m11 ] [ y ] + [ ty ] = [ m10x + m11y + ty ]
</pre>

<p>
You can write the left side more simply if you add a third row like this.
</p>

<pre>
  [ m00  m01  tx ]  [ x ]   [ m00x + m01y + tx ]
  [ m10  m11  ty ]  [ y ] = [ m10x + m11y + ty ]
  [  0    0    1 ]  [ 1 ]   [         1        ]
</pre>

<p>
Now all of these building block transformations can be written in terms of the six coefficients m00, m01, m10, m11, tx, and ty. Note that the order matters. This corresponds to applying the top left 2 by 2 matrix and then performing the translation. Add these two methods to <code>AffineChecker</code>.
</p>

<pre>
   private static void translate(double tx, double ty){
        matrixResult.setLocation(
                             matrixResult.getX() + tx,
                             matrixResult.getY() + ty);
   }

   public static void matrixTransform(
                                double m00, double m01,
                                double m10, double m11,
                                double tx,  double ty){
      matrixMultiply(m00,m01,m10,m11);
      translate(tx,ty);
   }
</pre>

<p>
You can now call any of the basic transformations using the <code>matrixTransform()</code> method. For example, you can replace the call in <code>ShearChecker</code> to <code>matrixMultiply(1,5,6,1)</code> with a call to the new method <code>matrixTransform(1,5,6,1,0,0)</code>. A translation can also be called using the <code>matrixTransform()</code> method as is shown in the following program:
</p>

<pre>
   import java.awt.geom.AffineTransform;

   public class TranslationChecker {
      public static void main(String[] args) {
         System.out.println(&quot;The results of translating &quot; +
                           &quot;by (-2,5):&quot;);
        AffineChecker.perform(
          AffineTransform.getTranslateInstance(-2,5));
        AffineChecker.matrixTransform(1,0,0,1,-2,5);
        AffineChecker.report();
      }
   }
</pre>

<p>   
Run <code>TranslationChecker</code> (remember to first update <code>AffineChecker</code> with the two additional methods) and get:
</p>

<pre>
   The results of translating by (-2,5):
   Affine Result = (0.0 , 8.0)
   Matrix Result = (0.0 , 8.0)
       No real difference
</pre>

<p>
These basic operations can be combined to get sophisticated results many of which are captured in convenience methods in the <code>AffineTransform</code> class.
</p>

<p>
For more information about <code>AffineTransform</code>, see &quot;<a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287526">Transforming Shapes, Text, and Images</a>&quot; in the Java Tutorial. 
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td></tr>

<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<form method="POST" action="http://developer.java.sun.com/servlet/jdc.survey.TabulationServlet">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<tr><td>
<h3>Reader Feedback</h3> 

<input type="hidden" name="survey_id" value="2600">
<input type="hidden" name="anonymous" value="True">
<input type="hidden" name="answer_count" value="3">
<input type="hidden" name="answer1" value="SeptCore_tt090903">
<input name="answer2" type="radio" value="2">&nbsp; Very worth reading&nbsp; 
<input name="answer2" type="radio" value="1">&nbsp; Worth reading&nbsp; 
<input name="answer2" type="radio" value="0">&nbsp; Not worth reading&nbsp; 
 
<p>
If you have other comments or ideas for future technical tips, please type them  here: 
</p>
 
<p>
<textarea name="answer3" rows="6" cols="50"></textarea> 
</p>
 
<p>
<input type="Submit" value="Submit"> &nbsp; <input type="Reset"> 
</p>

<p> 
Have a question about Java programming? Use  
<a href="http://developer.java.sun.com/developer/support/">Java Online Support</a>. 
</p>

</td></tr></table>
</form> 

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td></tr>

<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<br>

<span class="small">IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:<br>
<a href="http://www.sun.com/share/text/termsofuse.html"><span class="link">http://www.sun.com/share/text/termsofuse.html</span></a><br>
<a href="http://www.sun.com/privacy/"><span class="link">http://www.sun.com/privacy/</span></a><br> 
<a href="http://developer.java.sun.com/berkeley_license.html"><span class="link">http://developer.java.sun.com/berkeley_license.html</span></a>
</span><br><br>


<span class="small">
Comments? Send your feedback on the Core Java Technologies Tech Tips to: <a href="http://developers.sun.com/contact/feedback.jsp?category=sdn"><span class="link">http://developers.sun.com/contact/feedback.jsp?category=sdn</span></a>
</span><br><br>

<span class="small">
Subscribe to other Java developer Tech Tips:
</span><br><br>

<span class="small">
- Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).<br>
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).<br><br>
</span>
   
<span class="small">
To subscribe to these and other JDC publications:<br>
- Go to the JDC Newsletters and Publications page, choose the newsletters you want to <a href="http://developer.java.sun.com/subscription/"><span class="link">subscribe</span></a> to and click &quot;Update&quot;.<br>
  - To unsubscribe, go to the <a href="http://developer.java.sun.com/subscription/"><span class="link">subscriptions page</span></a>, uncheck the appropriate checkbox, and click &quot;Update&quot;.
</span><br><br>

<span class="small">ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:<br>
<a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287583"><span class="link">http://java.sun.com/jdc/TechTips/index.html</span></a>
</span><br><br>

<span class="small">Copyright 2003 <a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287597"><span class="link">Sun Microsystems, Inc.</span></a> All rights reserved.
<br>4150 Network Circle, Santa Clara, CA 95054 USA.
</span><br><br>

<span class="small">This document is protected by copyright. For more information, see:
<br><a href="http://java.sun.com/jdc/copyright.html"><span class="link">http://java.sun.com/jdc/copyright.html</span></a>
</span><br><br>

<span class="small">Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks (<a href="http://www.sun.com/suntrademarks/"><span class="link">http://www.sun.com/suntrademarks/</span></a>) of Sun Microsystems, Inc. in the United States and other countries.</span>
<br><br>

<!-- ================ -->
<!-- End Main Content -->
<!-- ================ -->

<center>
<a href="http://bulkmail2.sun.com/CTServlet?id=42676079-1624309795:1063149287597"><img src="http://developer.java.sun.com/images/lgsun.gif" border="0" alt="Sun Microsystems, Inc."></a>
</center>

</td>
</tr>
</table>

</td></tr>
</table>
</td>

<td bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>
<tr>
<td colspan="3" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>
</table>

<br><br><table bgcolor = "#efefef"><tr><td><a href="http://subscriptions.sun.com/unsubscribe?42676079-1624309795">Please unsubscribe me from this newsletter.</a></td></tr></table><br><br><img src="http://bulkmail2.sun.com/OTServlet?id=42676079-1624309795" width=1 height=1></body>
</html>


