/*
*
*  Copyright (C) 2002 - 2005 Stephen F. Booth
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
import java.awt.Color;
import java.awt.Point;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

class ColorPoint
{
  public Point p;
  public Color c;

  public ColorPoint(Point p, Color c)
  { this.p = p; this.c = c; }
};

public class Sierpinski extends Panel implements MouseListener
{

  public static void main(String[] args)
  {
    Frame frame = new Frame("Sierpinski's Gasket");
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {System.exit(0);}
    });

    Sierpinski s = new Sierpinski(10000);

    frame.setSize(800, 800);
    frame.add(s);
        //frame.addMouseListener(s);
    frame.setVisible(true);
  }

    // ============================================================

  Polygon mTriangle = null;
  ColorPoint [] mPoints = null;
  int mIterations = 0;
  int mCount = 0;

  public Sierpinski()
  {
    this(10000);
  }

  public Sierpinski(int iterations)
  {
    mIterations = iterations;
    addMouseListener(this);
  }

  private void init()
  {
    generate_triangle();
    mPoints = new ColorPoint [mIterations];
    while(mCount < mIterations) {
      generate_point();
    }
  }

  private void destroy()
  {
    mTriangle = null;
    mPoints = null;
    mCount = 0;
  }

  public void mouseClicked(MouseEvent e)
  {
    destroy();
    init();
    repaint();
  }
  public void mouseEntered(MouseEvent e)
  {}
  public void mouseExited(MouseEvent e)
  {}
  public void mousePressed(MouseEvent e)
  {}
  public void mouseReleased(MouseEvent e)
  {}

  public void paint(Graphics g)
  {
    Color restoreColor = g.getColor();

    /* If we haven't inited, do so now */
        /* Since initally the frame's size is (0,0), to do this from
    the constructor will cause an infinite loop */
    if(mTriangle == null)
      init();

    /* Fill the background with white and draw a black border */
    Rectangle bounds = getBounds();
    g.setColor(Color.WHITE);
    g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
    g.setColor(Color.BLACK);
    g.drawRect(bounds.x + 1, bounds.y + 1, bounds.width - 2, bounds.height - 2);

    /* First draw our triangle that is the basis for the whole thing */
    g.setColor(Color.BLACK);
    g.drawPolygon(mTriangle);

    /* Now draw the points */
    for(int i = 0; i < mCount; ++i) {
      g.setColor(mPoints[i].c);
      g.drawLine(mPoints[i].p.x, mPoints[i].p.y,
                 mPoints[i].p.x, mPoints[i].p.y);
    }

    g.setColor(restoreColor);
  }

  public void generate_triangle()
  {
    double x1, x2, x3;
    double y1, y2, y3;

    int width = getSize().width;
    int height = getSize().height;

    /* Generate three points */
    x1 = width * Math.random();
    x2 = width * Math.random();
    x3 = width * Math.random();

    y1 = height * Math.random();
    y2 = height * Math.random();
    y3 = height * Math.random();


    /* Three points make a triangle iff the third point is not on
     * the line created by the first two points. */
    /* y = mx + b, m = dy/dx, and b = y - mx */
    /* We can calculate m and b from the two points on the line,
     * and use them to determine if the third point lies on the
     * line */

    double m = (y2 - y1) / (x2 - x1);
    double b = y1 - m * x1;

    /* Determine if the third point is on the line */
        /* Since we are using floating-point arithmetic, nothing will
    * match up exactly.  Use a small delta to check for
        * equality - 0.001 or so */
    double delta = 0.001;

    /* y = mx + b will rearrange in this case to |y - mx - b| < delta */
    while(Math.abs(y3 - m * x3 + b) < delta) {
      x3 = width * Math.random();
      y3 = height * Math.random();
    }

    /* Now we have three points that form a triangle */
    mTriangle = new Polygon();
    mTriangle.addPoint((int) Math.floor(x1 + 0.5),
                        (int) Math.floor(y1 + 0.5));
    mTriangle.addPoint((int) Math.floor(x2 + 0.5),
                        (int) Math.floor(y2 + 0.5));
    mTriangle.addPoint((int) Math.floor(x3 + 0.5),
                        (int) Math.floor(y3 + 0.5));
  }

  public void generate_point()
  {
    double x, y;
    Color c;
    Point p;

    /* If this is the first iteration, generate a starting point */
    if(mCount == 0) {
      int height = getSize().height;
      int width = getSize().width;

      /* Generate a point inside our original triangle */
      do {
        x = width * Math.random();
        y = height * Math.random();
      } while(mTriangle.contains(x, y) == false);
    }
    /* Otherwise, use the previous point as our starter */
    else {
      x = mPoints[mCount - 1].p.x;
      y = mPoints[mCount - 1].p.y;
    }

    /* Generate a number between 1 and 6 */
    int roll = ((int) Math.floor(6 * Math.random())) + 1;

    double xcorner = 0, ycorner = 0;
    double newx = 0, newy = 0;
    double xdiff = 0, ydiff = 0;
    double xorigin = 0, yorigin = 0;

    switch(roll) {

      default:
        throw new RuntimeException();
            //break;

      case 1:
      case 2:
        xcorner = mTriangle.xpoints[0];
        ycorner = mTriangle.ypoints[0];
        c = Color.RED;
        break;

      case 3:
      case 4:
        xcorner = mTriangle.xpoints[1];
        ycorner = mTriangle.ypoints[1];
        c = Color.GREEN;
        break;

      case 5:
      case 6:
        xcorner = mTriangle.xpoints[2];
        ycorner = mTriangle.ypoints[2];
        c = Color.BLUE;
        break;
    };

    /* Calculate half the distance between the points */
    xdiff = Math.abs(xcorner - x)/2;
    ydiff = Math.abs(ycorner - y)/2;

    /* Add the diffs to the lesser value */
    xorigin = Math.min(xcorner, x);
    yorigin = Math.min(ycorner, y);

    newx = xorigin + xdiff;
    newy = yorigin + ydiff;

    p = new Point((int) Math.floor(newx + 0.5),
                   (int) Math.floor(newy + 0.5));
    mPoints[mCount++] = new ColorPoint(p, c);
  }
};
