Java Take-Off Step Six: Geometry of Interaction (Rendering Shapes, Text, Images - Part Two)

Here's a first program.

```import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;

public class CollisionTest extends Applet implements MouseListener,
MouseMotionListener
{
// the number of rectangles contained in our scene
private final int NUM_RECTS = 10;

// a list of rectangles

// an AlphaCompisite to show semi-transparent rectangles
private AlphaComposite alpha;

// the rectangle that is currently selected
private Rectangle2D pick;

public void init()
{
pick = null;

// create an AlphaComposite with 50% transparency
alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);

// create NUM_RECTS rectangles at random positions and add them
// to the list
Random r = new Random();
int width = (int)getSize().getWidth();
int height = (int)getSize().getHeight();

for(int i = 0; i < NUM_RECTS; i++)
{
(
(double)(   Math.abs(r.nextInt())%width),
(double)(   Math.abs(r.nextInt())%height),
(double)(20+Math.abs(r.nextInt())%50),
(double)(20+Math.abs(r.nextInt())%50))
);
}

// don't forget to register the applet to listen for mouse events
}

public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;

// tell our Graphics2D context to use transparency
g2d.setComposite(alpha);

// draw the rectangles
g2d.setPaint(Color.black);
for(int i = 0; i < NUM_RECTS; i++)
{
g2d.draw((Rectangle2D.Double)rectangles.get(i));
}

// if pick refers to a valid rectangle, test it for collisions
if(pick != null)
{
Rectangle2D rect;
g2d.setPaint(Color.red.darker());
for(int i = 0; i < NUM_RECTS; i++)
{
// get the ith rectangle in the list
rect = (Rectangle2D)rectangles.get(i);

// test for intersection-- note we shouldn't test
// pick against itself
if(pick != rect && pick.intersects(rect))
{
// fill collisions
g2d.fill(rect);
}
}

// fill the pick rectangle
g2d.setPaint(Color.blue.brighter());
g2d.fill(pick);
}
}

// methods inherited from the MouseListener interface

public void mouseClicked(MouseEvent e) { }

public void mouseEntered(MouseEvent e) { }

public void mouseExited(MouseEvent e)  { }

public void mousePressed(MouseEvent e)
{
// attempt to pick up a rectangle
if(pick == null)
{
Rectangle2D rect;
for(int i = 0; i < NUM_RECTS; i++)
{
rect = (Rectangle2D)rectangles.get(i);

// if the rectangle contains the mouse position,
// 'pick' it up
if(rect.contains(e.getPoint()))
{
pick = rect;
return;
}
}
}
}

public void mouseReleased(MouseEvent e)
{
// release the pick rectangle and repaint the scene
pick = null;
repaint();
}

// methods inherited from the MouseMotionListener interface

public void mouseDragged(MouseEvent e)
{
// if we have a picked rectangle, set its position to the
// current mouse position and repaint
if(pick != null)
{
pick.setRect(e.getX(),
e.getY(),
pick.getWidth(),
pick.getHeight());
repaint();
}
}

public void mouseMoved(MouseEvent e)  { }

} // CollisionTest```
What does it do? (Compile and run it).

Here's another one:

```import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;

class BoundedImage extends Object
{
// the Image and bounding data for this container
private Image       image;
private Rectangle2D bounds;

public BoundedImage(Image img, ImageObserver obs)
{
image = img;

// set the bounds to (0,0) with the width and height of
// the image data
bounds = new Rectangle2D.Double(0, 0,
image.getWidth(obs),
image.getHeight(obs));
}

public Rectangle2D getBounds2D()
{     return bounds;
}

public Image getImage()
{     return image;
}

public AffineTransform getTransform()
{     return AffineTransform.getTranslateInstance
(bounds.getX(),
bounds.getY());
}

public void moveTo(Point p)
{
bounds.setRect((double)p.x, (double)p.y,
bounds.getWidth(),
bounds.getHeight());
}
}    // BoundedImage

public class BoundedImageTest extends Applet implements MouseListener,
MouseMotionListener
{
// global reference to the image filename for easy editing
private final String FILENAME = "simon.gif";

// the number of images contained in our scene
private final int NUM_IMAGES = 3;

// a list of BoundedImages

// the BoundedImage that is currently selected
private BoundedImage pick;

// an AlphaCompisite to highlight collisions
private AlphaComposite alpha;

public void init()
{
pick = null;

// create NUM_IMAGES images at random positions and add them
// to the list

Random r = new Random();
int width = (int)getSize().getWidth();
int height = (int)getSize().getHeight();

// create a MediaTracker object so our images are fully loaded
// before being passed to the BoundedImage class
MediaTracker mt = new MediaTracker(this);

BoundedImage bi;     // BoundedImage to add to the list
Image img;           // a single Image

for(int i = 0; i < NUM_IMAGES; i++)
{
img = getImage(getCodeBase(), FILENAME);
try
{     mt.waitForID(i);
}
catch(InterruptedException e) { /* do nothing */ }

bi = new BoundedImage(img, this);
bi.moveTo
(new Point
(Math.abs
(r.nextInt())%width,
Math.abs(r.nextInt())%height)
);

}

// create an AlphaComposite with 40% transparency
alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);

// don't forget to register the applet to listen for mouse events
}

public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;

// draw the images
BoundedImage bi;
for(int i = 0; i < NUM_IMAGES; i++)
{
bi = (BoundedImage)images.get(i);
g2d.drawImage(bi.getImage(), bi.getTransform(), this);
}

// if pick refers to a valid image, test it for collisions
if(pick != null)
{
for(int i = 0; i < NUM_IMAGES; i++)
{
// get the ith image in the list
bi = (BoundedImage)images.get(i);

// test for intersection
if(imageCollision(pick, bi))
{
// fill the bounding rectangle to highlight
// the collision
g2d.setComposite(alpha);
g2d.setPaint(Color.red.darker());
g2d.fill(bi.getBounds2D());
}
}

// draw and fill the pick rectangle
g2d.setPaint(Color.blue.brighter());
g2d.setComposite(alpha);
g2d.drawImage(pick.getImage(), pick.getTransform(), this);
g2d.fill(pick.getBounds2D());
}
}

// determines if two BoundedImages intersect (collide). this method
// will return false if i1 and i2 refer to the same object
private boolean imageCollision(BoundedImage i1, BoundedImage i2)
{
return (i1 == i2) ? false :
i1.getBounds2D().intersects(i2.getBounds2D());
}

// methods inherited from the MouseListener interface

public void mouseClicked(MouseEvent e) { }

public void mouseEntered(MouseEvent e) { }

public void mouseExited(MouseEvent e)  { }

public void mousePressed(MouseEvent e)
{
// attempt to pick up an image
if(pick == null)
{
BoundedImage bi;
for(int i = 0; i < NUM_IMAGES; i++)
{
bi = (BoundedImage)images.get(i);

// if the BoundedImage contains the mouse position,
// 'pick' it up
if(bi.getBounds2D().contains(e.getPoint()))
{
pick = bi;
return;
}
}
}
}

public void mouseReleased(MouseEvent e)
{
// release the pick image and repaint the scene
pick = null;
repaint();
}

// methods inherited from the MouseMotionListener interface

public void mouseDragged(MouseEvent e)
{
// if we have a picked image, set its position to the
// current mouse position and repaint
if(pick != null)
{
pick.moveTo(e.getPoint());
repaint();
}
}

public void mouseMoved(MouseEvent e) { }

} // BoundedImageTest```
What does it do?

(Please notice you need this: simon.gif)

How does it do what it does?

Here's a third program:

```import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;

public class AreaTest extends Applet
{
// an array of Area objects and an associated array of String
// geometry descriptions
private Area[] shapes;
private String[] ops;

public void init()
{
// create 4 Areas and Strings
shapes = new Area[4];
ops = new String[4];

// create two circles that overlap about the origin
Ellipse2D e1 = new Ellipse2D.Double(-0.125, 0.0, 0.5, 0.5);
Ellipse2D e2 = new Ellipse2D.Double(+0.125, 0.0, 0.5, 0.5);

// create a Union between the shapes
shapes[0] = new Area(e1);
ops[0] = "Union";

// Substract e2 from e1
shapes[1] = new Area(e1);
shapes[1].subtract(new Area(e2));
ops[1] = "Subtraction";

// create an Intersection between the shapes
shapes[2] = new Area(e1);
shapes[2].intersect(new Area(e2));
ops[2] = "Intersection";

// use the Exclusive OR operation between the shapes
shapes[3] = new Area(e1);
shapes[3].exclusiveOr(new Area(e2));
ops[3] = "XOR";
}

public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;

// create a stroke to outline the shapes
g2d.setStroke(new BasicStroke(2.0f/100.0f));

// a Random object for creating random colors
Random r = new Random();

// render the shapes and the operation descriptions
for(int i = 0; i < 4; i++)
{
g2d.setTransform(new AffineTransform());
g2d.translate(50+(i*100), 40);

g2d.drawString(ops[i], 0, 70);

g2d.scale(100, 100);

g2d.setPaint(new Color(r.nextInt()));
g2d.fill(shapes[i]);
g2d.setPaint(Color.black);
g2d.draw(shapes[i]);
}
}

} // AreaTest```
That's called additive geometry and I hope you like it.

Here's something related:

```import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;

public class ClipTest extends Applet implements ItemListener
{
// the Image and clip region to render
private Image image;
private Polygon clip;

// a Choice to display the Image and clip region
private Choice dropBox;

// indices for our drop box
private final int SHOW_IMAGE_ONLY    = 0;
private final int SHOW_CLIP_ONLY     = 1;
private final int SHOW_CLIPPED_IMAGE = 2;

public void init()
{
// load an image from file
MediaTracker mt = new MediaTracker(this);
image = getImage(getCodeBase(), "blabber.gif");
try
{     mt.waitForID(0);
}
catch(InterruptedException e) { /* do nothing */ }

// create an 8-sided clip region about an anchor point; the
// anchor point will be located at the center of our Image

clip = new Polygon();
int anchor = image.getWidth(this)/2;
for(int i = 0; i < 8; i++)
{
}

// add the drop box to the bottom of the container, along with the
// three render choices
setLayout(new BorderLayout());
dropBox = new Choice();
}

public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;

// set the transform to the identity transform
final AffineTransform at = new AffineTransform();

// render based on the current selection
switch(dropBox.getSelectedIndex())
{
case SHOW_IMAGE_ONLY:
{
// just draw the Image
g2d.drawImage(image, at, this);
break;
}

case SHOW_CLIP_ONLY:
{
// just draw the clipping region
g2d.setTransform(at);
g2d.draw(clip);
break;
}

case SHOW_CLIPPED_IMAGE:
{
// draw the Image with respect to the clipping region
g2d.setClip(clip);
g2d.drawImage(image, at, this);
break;
}

default:
{
// invalid index
System.out.println("Invalid Choice: " +
dropBox.getSelectedIndex());
break;
}
}
}

public void itemStateChanged(ItemEvent e)
{
// our drop box has changed-- redraw the scene
repaint();
}

} // ClipTest
```
Note that you need this (blabber.gif)

Hope you like this stuff.

Here's another program:

```import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;

public class BufferedImageTest extends Applet
{
// a BufferedImage, along with its creation width and height
private BufferedImage image;
private final int BI_WIDTH  = 100;
private final int BI_HEIGHT = 100;

// for generating random colors and screen positions
private java.util.Random random;

public void init()
{
random = new java.util.Random();

// create a (BI_WIDTH x BI_HEIGHT) buffered image
image = new BufferedImage(BI_WIDTH,
BI_HEIGHT,
BufferedImage.TYPE_INT_RGB);

// create a Graphics2D context for our BufferedImage.  Remember
// this has nothing to do with our Applet's Graphics2D context
Graphics2D g2d = image.createGraphics();

// we will render some stripes to the BufferedImage
// the width and height of our stripes will be one-tenth
// the total width and height of the image
final int stripWidth  = BI_WIDTH  / 10;
final int stripHeight = BI_HEIGHT / 10;

// fill the image with a random color
g2d.setPaint(new Color(random.nextInt()));
g2d.fill(new Rectangle(0, 0, BI_WIDTH, BI_HEIGHT));

// render the vertical strips using a random color
g2d.setPaint(new Color(random.nextInt()));
int x = stripWidth / 2;
while(x < BI_WIDTH)
{
g2d.fill(new Rectangle(x, 0, stripWidth, BI_HEIGHT));
x += 2 * stripWidth;
}

// set a transparency channel for our stripes
g2d.setComposite
(AlphaComposite.getInstance
(AlphaComposite.SRC_OVER, 0.5f));

// render the horizontal strips using a random color
g2d.setPaint(new Color(random.nextInt()));
int y = stripHeight / 2;
while(y < BI_HEIGHT)
{
g2d.fill(new Rectangle(0, y, BI_WIDTH, stripHeight));
y += 2 * stripHeight;
}

// render a dark opaque outline around the perimeter of the image
g2d.setStroke(new BasicStroke(2.0f));
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g2d.setPaint(Color.black);
g2d.draw(new Rectangle(0, 0, BI_WIDTH, BI_HEIGHT));
}

public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;

// draw a bunch of images at random locations
for(int i = 0; i < 20; i++)
{
g2d.drawImage
(image,

AffineTransform.getTranslateInstance
(
random.nextInt()%getSize().getWidth(),
random.nextInt()%getSize().getHeight()
),

this
);
}
}

} // BITextureTest```
The focus should be here on the internal mechanism of the program.

Here's how we cann use image enhancement operations:

```import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;

public class BlurTest extends Applet implements ActionListener
{
// the original and blurred BufferedImages
private BufferedImage sourceImage;
private BufferedImage destImage;

// field for accepting a filename
private TextField input;

public void init()
{
// create a layout and add the textfield and "Ok" button
setLayout(new BorderLayout());
Panel p = new Panel();
input = new TextField("", 20);
Button ok = new Button("Ok");
}

public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;

// draw a friendly reminder to input a filename if the text
// field just contains white-space
if("".equals(input.getText().trim()))
{
g2d.drawString("Enter a filename to blur", 10, 50);
return;
}

MediaTracker mt = new MediaTracker(this);
Image img = getImage(getCodeBase(), input.getText());
try
{
// wait until the image has loaded completely
// before continuing
mt.waitForID(0);
}
catch(InterruptedException e) { /* do nothing */ }

// our blur operation will require a BufferedImage object, so render
// the input image to a new BufferedImage object
sourceImage = new BufferedImage(img.getWidth(this),
img.getHeight(this),
BufferedImage.TYPE_INT_RGB);
sourceImage.createGraphics().drawImage(img, null, this);

// create the destination image
destImage = new BufferedImage(sourceImage.getWidth(this),
sourceImage.getHeight(this),
BufferedImage.TYPE_INT_RGB);

// blur the input image
blur(sourceImage, destImage, 0.1f, 3, 3);

// draw both the source and destination images
AffineTransform at = new AffineTransform();
int width = sourceImage.getWidth(this);
g2d.drawImage(sourceImage, at, this);
at.translate(width+20, 0);
g2d.drawImage(destImage, at, this);
}

// blurs an image using a ConvolveOp operation
public void blur(
BufferedImage sourceImage,  // the input image data
BufferedImage destImage,    // the output image data
float weight,               // weight of the Kernel data
int width,                  // width of the Kernel
int height                  // height of the Kernel
)
{
// this will be the data array for our Kernel
float[] elements = new float[width*height];

// fill the array with the weight
java.util.Arrays.fill(elements, weight);

// use the array of elements and the
// width and height to create a Kernel
Kernel k = new Kernel(width, height, elements);
ConvolveOp blurOp = new ConvolveOp(k);

// blur the image
blurOp.filter(sourceImage, destImage);
}

public void actionPerformed(ActionEvent e)
{
// the "Ok" was pressed; update the changes
repaint();
}

} // BlurTest
```
Here are some more images:

You may or may not need these images now.

Can you use images from the web, directly, in the program above?

Last updated: Apr 11, 2002 by Adrian German for `T540`