Using the MISApplet and scan-converting a triangle

The assignment due Wednesday March 25 will consist of two parts:

  1. Implement an applet that does some sort of pixel-by-pixel manipulation of an image. This is really just to get you started using applets that involve extending the MISApplet class (rather than the BufferedApplet class we've been extending up till now).

  2. The first steps of rendering - scan convert a single triangle.

The first part of the assignment is mostly fun, and shouldn't be much of a challenge. Of course you can go to town with it if you'd like, and produce something really wild and interesting. I know I would. :-)

In the notes below, first I duplicate what we covered in class on Wednesday, showing the two simple examples of using the MISApplet class. Then I go over our discussion of how to scan-convert a triangle by breaking it up into two trapezoids.

(1) Extending the MISApplet class

The following applet, which we implemented in class, extends MISApplet.java:


import java.awt.*;

public class test1 extends MISApplet
{
   double t = 0;

   public void initFrame(double time) {
      t = 4 * time;
   }

   public void setPixel(int x, int y, int rgb[]) {
      x -= W / 2;
      y -= H / 2;

      double r = Math.sqrt(x * x + y * y) / W;

      rgb[0] = (int)(255 * (0.5 + 0.5 * Math.sin( 50 * r - t)));
      rgb[1] = (int)(255 * (0.5 + 0.5 * Math.sin( 75 * r - t)));
      rgb[2] = (int)(255 * (0.5 + 0.5 * Math.sin(100 * r - t)));
   }
}
As you can see if you launch it, it produces cool looking expanding rings of color. It also runs a little slowly, because there are so many floating point operations for each pixel. Clearly, we want to avoid lots of floating point operations per pixel, if we're going to do real-time rendering in Java applets.

The second applet we implemented in class tries harder to be efficient - doing only integer operations in the inner loop. I've added some comments, to make it easier for you to follow the code. Unlike the first applet, this one is interactive:


import java.awt.*;

public class test2 extends MISApplet
{
   int mx = 0, my = 0;
   int white = pack(255,255,255);
   int black = pack(  0,  0,  0);
   int gray  = pack(128,128,128);
   int red   = pack(255,  0,  0);
   boolean isMouseDown = false;
   int radius = 30;

   int myColor = pack(200, 0, 0);

   public void initialize() {
      mx = W / 2;
      my = H / 2;
   }

   public void computeImage(double time) {

      // PAINT A BLACK AND WHITE CHECKBOARD AS THE BACKGROUND PATTERN

      int i = 0; 
      for (int y = 0 ; y < H ; y++)
      for (int x = 0 ; x < W ; x++) {
         int _i = x / 50;
         int _j = y / 50;
         pix[i++] = (_i + _j) % 2 == 0 ? black : white;
      }

      // DRAW THE MOVING SPOT

      for(int y = my - radius ; y < my + radius ; y++)
      for(int x = mx - radius ; x < mx + radius ; x++) {

         // LOOP THROUGH A SQUARE REGION, BUT ONLY DRAW THOSE
         // PIXELS THAT FALL INSIDE A CIRCULAR DISK

         int rr = (x - mx) * (x - mx) + (y - my) * (y - my);
         if (rr < radius * radius) {
            i = xy2i(x, y);
            if (i >= 0 && i < pix.length) {
               if (isMouseDown) {

                  // OPACITY DROPS OFF WITH RADIUS

                  int c = Math.max(0, 255 * rr / (radius * radius) - 128);

                  // OPAQUE PIXELS JUST GET WRITTEN TO THE FRAME BUFFER

                  if (c == 0)
                     pix[i] = myColor;

                  // TRANSLUCENT PIXELS NEED TO BE BLENDED IN

                  else {
                     int bg = pix[i];

                     int red = ilerp(c, 200, unpack(bg, 0));
                     int grn = ilerp(c,   0, unpack(bg, 1));
                     int blu = ilerp(c,   0, unpack(bg, 2));

                     pix[i] = pack(red, grn, blu);
                  } 
               } 
               else
                  pix[i] = gray;
            }
         }
      }
   }

   // TRACK WHERE THE USER DRAGS, AND IF MOUSE BUTTON IS PRESSED

   public boolean mouseDown(Event e, int x, int y) {
      isMouseDown = true;
      return true;
   }

   public boolean mouseUp(Event e, int x, int y) {
      isMouseDown = false;
      return true;
   }

   public boolean mouseDrag(Event e, int x, int y) {
      mx = x;
      my = y;
      return true;
   }

   // FIXED POINT VERSION OF LINEAR INTERPOLATION, WITH 256 AS UNITY

   int ilerp(int t, int a, int b) { return a + (t * (b - a) >> 8); }
}
You can launch this applet by clicking here. What I would like you to do is try your hand at making your own 2D applet this way. See if you can create interesting shapes, or even entire scenes. You can make it interactive or non-interactive, us you wish. Try different things, experiment, have fun.

(2) Scan converting a triangle

Notes coming soon - watch this space...