Site hosted by Angelfire.com: Build your free website today!
/* An applet to design hexagonal Celtic knots (Judeo-celtic knots?) */
/* Copyright 2005, by John Berglund, inspired by Andrew D. Birrell */
/* This program and source code are available free, under the terms of the GNU
   general public license.  Use at your own risk!  The GNU general public
   license is available at http://www.gnu.org/copyleft/gpl.html */

/* Requires an image with the knot fragments.  The URL for the image is
   provided as the "image" applet parameter. */

import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.net.*;

public class Hexa extends Applet {

  /* All of these are normally overridden by applet parameters. */


  int gridHeight = 12;
  int gridWidth = 24;
  int hexHeight = 4*gridHeight;
  int hexWidth = 2*gridWidth;
  int xHexes = 10;			/* Hexes wide, must be even */
  int yHexes = 10;			/* Hexes high, must be even */
  String limited = null;

  private Polygon poly0 = new Polygon();
  private Polygon poly60 = new Polygon();
  private Polygon poly120 = new Polygon();
 
  Image[] images = null;		/* component images for hexes */
  Image theImage = null;		/* the downloaded image */
  MediaTracker tracker = null;
  int[][][] shapes = null;			

  /*  The images have pictures of hexagons. We clip them by  */                      
  /*  rhombii as seen below. We click on a rhombus to change its shape. */
  /*              __                              */
  /*         __---  ---__                         */
  /*        -__    0   __-                          */
  /*        |  ---__---   |                          */
  /*        |      |      |                          */
  /*        |  2   |  1   |                          */
  /*        |_     |     _|                          */
  /*          ---__|__---                            */
  /*                                                 */
  /*   The shapes arestored in the shapes array - with x,y first,     */
  /*  then which rhombus. Alternate rows of hexagons are offset so that     */
  /* they tile. everything is shifted up and left enough that we can't      */
  /*  see the blank space around the hexagons.           */
  /*                                                 */
  /*                                                 */
  /*                                                 */
  /*                                                 */
  
  
  private void initKnot() {
   
    // these are used to clip out the needed rhombus.   
    poly0.addPoint(0,gridHeight);
    poly0.addPoint(gridWidth,0);
    poly0.addPoint(2*gridWidth,gridHeight);
    poly0.addPoint(gridWidth,2*gridHeight);
    
    poly60.addPoint(2*gridWidth,gridHeight);
    poly60.addPoint(2*gridWidth,3*gridHeight);
    poly60.addPoint(gridWidth,4*gridHeight);
    poly60.addPoint(gridWidth,2*gridHeight);
    
    poly120.addPoint(0,gridHeight);
    poly120.addPoint(gridWidth,2*gridHeight);
    poly120.addPoint(gridWidth,4*gridHeight);
    poly120.addPoint(0,3*gridHeight);
  
    /* Compute the initial shapes */
    for (int y = 0; y < yHexes; y++) {
      for (int x = 0; x < xHexes; x++) {
         for (int a = 0; a<3; a++) {
            shapes[x][y][a]=0;
         }
      }
    }
    
    // top and bottom
    for (int x = 0; x < xHexes; x++) {
       for (int a = 0; a<3; a++) {
          shapes[x][0][a]=7;   
       }
       shapes[x][1][0]=3;
       shapes[x][yHexes-1][0]=4;
       shapes[x][yHexes-1][1]=7;
       shapes[x][yHexes-1][2]=7;
    }
    
    for (int y = 1; y < yHexes-1; y++) {
      if (y%2 == 1) {
         shapes[0][y][0]=7;
         shapes[0][y][1]=4;
         shapes[0][y][2]=7;
         shapes[xHexes-1][y][0]=7;
         shapes[xHexes-1][y][1]=7;
         shapes[xHexes-1][y][2]=4;
      } else {
         shapes[0][y][2]=3;
         shapes[xHexes-1][y][0]=7;
         shapes[xHexes-1][y][1]=7;
         shapes[xHexes-1][y][2]=7;
         shapes[xHexes-2][y][1]=3;
      }
    }
    
    shapes[0][yHexes-1][0]=7;
    shapes[xHexes-1][yHexes-1][0]=7;
         
  }
  
  
  private void paintHex(Graphics g, int nx, int ny) {
    /* Paint a hexagon of the knot */
    int xTrans = nx*hexWidth;
    int yTrans = ny*3*gridHeight-3*gridHeight;
    if (ny%2 == 1) {xTrans = xTrans - hexWidth/2;}
    
    int shape = shapes[nx][ny][0];
    
    Dimension d = size();
    
    poly0.translate(xTrans, yTrans);
    g.setClip(poly0);
    poly0.translate(-xTrans, -yTrans);
    g.drawImage(images[shape], xTrans, yTrans, this);

    shape = shapes[nx][ny][1];
    
    poly60.translate(xTrans, yTrans);
    g.setClip(poly60);
    poly60.translate(-xTrans, -yTrans);
    g.drawImage(images[shape], xTrans, yTrans, this);
   
    shape = shapes[nx][ny][2];
    
    poly120.translate(xTrans, yTrans);
    g.setClip(poly120);
    poly120.translate(-xTrans, -yTrans);
    g.drawImage(images[shape], xTrans, yTrans, this);
   
  }
  
 
  public synchronized void paint(Graphics g) {
    Dimension d = size();
    java.awt.Rectangle clip = g.getClipBounds();
    if (tracker==null || !tracker.checkAll(true)) {
      String s = tracker==null ? "No image!" : "Working on it ...";
      int sWidth = (g.getFontMetrics()).stringWidth(s);
      g.drawString(s, (d.width-sWidth)/2, d.height/2);
      if (tracker!=null) repaint(250, 0, 0, d.width, d.height);
    } else {
      if (images == null) {
        images = new Image[32];
      
          for (int i = 0; i<16; i++) {
            images[i] = createImage(hexWidth, hexHeight);
            Graphics imageG = images[i].getGraphics();
            imageG.drawImage(theImage, -i*hexWidth, 0, this);
          }
   
      }
      /* For efficiency, paint only hexes that intersect with the cliprect */
   
      g.drawString(limited, 80,80);
      int firstX = Math.max((clip.x) / hexWidth, 0);
      int endX = Math.min((clip.x+clip.width+2*hexWidth)/hexWidth, xHexes);
      int firstY = Math.max((clip.y) / hexHeight, 0);
      int endY = Math.min((clip.y+clip.height+4*hexHeight)/hexHeight, yHexes);
      for (int y = firstY; y < endY; y++) {
        for (int x = firstX; x < endX; x++) {
           paintHex(g, x, y);
        }
      }
    
    }
  }

  public void update(Graphics g) {
    paint(g);
  }
  
  public boolean mouseDown(Event evt, int xCoord, int yCoord) {
    
    int y = ((yCoord)*4 + 10*gridHeight) / (6*gridHeight);
    int x;
    if ( y%2 == 1 ) {
        x = (xCoord - 0*gridWidth) / gridWidth;
    } else{
        x = (xCoord - gridWidth/2) / gridWidth;
    }
    // offset alternate rows.
    if ( y%4 > 1 ) {
      x++;   
    }
    // don't try to change the gaps when y is even and x is odd.
    if ( y%2 != 0 || x%2 == 0) {
        int yToChange = y/2; 
        int xToChange = x/2;
        int angle = 0;
        if (y%2 == 1 && x%2 == 1) {
            xToChange = (x-1)/2;
            angle = 1;
        }
        if (y%2 == 1 && x%2 == 0) {
            angle = 2;
        }
        // this is where we put the new shape in the shapes array
        int currentShape = shapes[xToChange][yToChange][angle];
        if (limited == "yes") {
            if (currentShape%8 <= 2) {
                currentShape++;
                if (currentShape%8 == 3) {
                    currentShape = currentShape - 3;
                }
            shapes[xToChange][yToChange][angle]=currentShape;
            }
         } else {
            // this is if we don't limit the sides - just cycle through all shapes. Doesn't seem to work
            currentShape = (currentShape+1)%8;
            shapes[xToChange][yToChange][angle]=currentShape;
        }
        repaint();
    }
    
    return true;
  }

  public synchronized void init() {
    /* Initialize, and arrange to fetch the image */
    try {
      String cStr = getParameter("COLUMNS");
      String rStr = getParameter("ROWS");
      String wStr = getParameter("GRIDWIDTH");
      String hStr = getParameter("GRIDHEIGHT");
      limited = getParameter("LIMITS");
      if (cStr != null) xHexes = Integer.parseInt(cStr);
      if (rStr != null) yHexes = Integer.parseInt(rStr);
      if (wStr != null) { 
          gridWidth = Integer.parseInt(wStr);
          hexWidth = 2*gridWidth;
      }
      if (hStr != null) { 
          gridHeight = Integer.parseInt(hStr);
          hexHeight = 4*gridHeight;
      }
    } catch (NumberFormatException e) {
      System.err.println("Missing or malformed applet parameter");
    }
    URL u = null;
    String urlStr = getParameter("IMAGE");
    if (urlStr != null) {
      try {
        u = new URL(getDocumentBase(), urlStr);
      } catch ( MalformedURLException e) {
      }
    }
 
    shapes = new int[xHexes][yHexes][3];
 
    setBackground(Color.white);
    setForeground(new Color(153,153,153));
    if (u!=null) {
      tracker = new MediaTracker(this);
      theImage = getAppletContext().getImage(u);
      tracker.addImage(theImage, 0);
    }
    initKnot();
  }
  
  public synchronized void start() {
  }

  public synchronized void stop() {
  }

  public synchronized void destroy() {
  }

}