
/* 
    A KSCanvas object is a drawing area on which the user can
    sketch by dragging  the mouse.  It has methods for
    filling the canvas with a specified color and for setting
    the color to be used when the user draws something.  It has
    two modes, which are controlled by the "kaleidascopic" variable.
    If this variable is false, then a simple curve is drawn as
    the user drags the mouse.  However, if kaleidascopic is true,
    then an eight-way symmetric curve is drawn.  This curve is
    made up of the horizontal, vertical, and diagonal reflections
    of the curve drawn by the user.
       Note that a copy of the sketch is kept in an off-screen
    Image.  Whatever the user draws on the screen is drawn to
    the off-screen Image as well.  The off-screen canvas is
    used in the paint() method to redraw the image as necessary.
    It does not have any other purpose.
*/


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

class KSCanvas extends Canvas {

   int width  = -1;  // The width and height of the canvas and of the
   int height = -1;  //   off-screen Image.  This is set in the method
                     //   checkSize().  (Note that the size of a canvas
                     //   is unknown when the constuctor is called.  So,
                     //   unfortunately, you can't just set the size in
                     //   the constructor.  Instead, checkSize() is called
                     //   in the paint() method.  By the time this is
                     //   called, the size of the canvas is known.  The
                     //   initial values of width and height act as signals
                     //   to checkSize() that the size has not yet been set.)

   boolean kaleidascopic = false;  // When this is true, 8-way symmetric
                                   //    drawing is done.

   Color drawingColor = Color.black;    // Current color used for user's drawing.
   Color backgroundColor = Color.white; // Color with which canvas has been filled.

   Image OSC;    // The off-screen canvas.  (This is created in checkSize().)
   Graphics osg; // A graphics object for drawing to OSC.


   void clear(Color fillColor) {
         // Fills the canvas -- and the off-screen Image -- with the specified color.
      checkSize();
      backgroundColor = fillColor;
      osg.setColor(backgroundColor);
      osg.fillRect(0,0,width,height);
      repaint();
   }

   void setDrawingColor(Color color) {
         // Set color to be used when user draws something with the mouse.
      drawingColor = color;
   }

   void setKaleidascopic(boolean k) {
         // Turn symmetric drawing on or off by setting the value of "kaleidascopic"
      kaleidascopic = k;
   }

   void doDrawLine(int x1, int y1, int x2, int y2) {
         // User has dragged mouse from (x1,y1) to (x2,y2).  If kaleidascopic
         // is false, this just draws a line from (x1,y1) to (x2,y2).  If
         // kaleidascopic is false, it draws that line plus all its horizontal,
         // vertical, and diagonal reflections.  (Each line is drawn by a
         // call to basicPutLine(), which is defined below.)
      basicPutLine(x1,y1,x2,y2);
   }

   //------------------------ size/painting stuff ------------------------

   void checkSize() {
         // This is called before using the off-screen Image.
         // It creates the canvas (OSC) and a graphics object for 
         // drawing to the canvas.  It should also be called if
         // the canvas's size might have changed.  When the size
         // changes, it will allocate a new off-screen Image.
         // (However, since the new image will be blank, any
         // drawing that was done will be lost.)
      if (width == size().width && height == size().height)
         return;
      width = size().width;
      height = size().height;
      OSC = null;
      OSC = createImage(width,height);
      osg = OSC.getGraphics();
      osg.setColor(backgroundColor);
      osg.fillRect(0,0,width,height);
   }

   public void paint(Graphics g) {
        // Paint() redraws canvas by copying the off-screen Image onto the screen
      checkSize();
      g.drawImage(OSC,0,0,this);
   }

   public void update(Graphics g) {
        // Modify update so it doesn't erase the canvas before calling paint.
      paint(g);
   }


   //--------------------- basic line drawing routine -------------------

   void basicPutLine(int x1, int y1, int x2, int y2) {
         // This routine draws a line in the current drawingColor from
         // the point (x1,y1) to the point (x2,y2).  The line is drawn
         // on the screen, and a copy of the line is drawn on the canvas.
         // The screenGraphics variable should be set for drawing to
         // the screen before this routine is called.  (But just to be
         // paranoid, if screenGraphics is null, a graphics object is
         // obtained for drawing to the screen.)
      osg.setColor(drawingColor);
      osg.drawLine(x1,y1,x2,y2);
      if (screenGraphics == null) {
         Graphics g = getGraphics();
         g.setColor(drawingColor);
         g.drawLine(x1,y1,x2,y2);
         g.dispose();
      }
      else {
         screenGraphics.setColor(drawingColor);
         screenGraphics.drawLine(x1,y1,x2,y2);
      }
   }


   //---------------------------- mouse-handling -------------------------------

   int last_x, last_y; // Keeps track of the previous (x,y) point that came
                       //   came in as parameters to mouseDown or mouseDrag.
                       //   These are used for drawing a line from the previous
                       //   mouse location to the current mouse location.

   Graphics screenGraphics;  // Used for drawing to the screen while the
                             //   user is drawing the mouse.

   public boolean mouseDown(Event evt, int x, int y) {
         // When the user first presses the mouse, just record the
         // location and get a graphics object for drawing on the screen.
      last_x = x;
      last_y = y;
      screenGraphics = getGraphics();
      return true;
   }

   public boolean mouseDrag(Event evt, int x, int y) {
         // When the mouse moves, draw a line from its previous position
         // to its new position, and record the position for use in the
         // next call to mouseDrag.
      if (last_x != x || last_y != y) {
         doDrawLine(last_x,last_y,x,y);
         last_x = x;
         last_y = y;
      }
      return true;
   }

   public boolean mouseUp(Event evt, int x, int y) {
         // When the mouse button is released, dispose of the screen
         // graphics object (since it's good form not to keep such things
         // around when they are not being used.
      screenGraphics.dispose();
      screenGraphics = null;
      return true;
   }


} // end class KSCanvas
