/*
*  Copyright (C) 2002 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.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, so 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();
    
    /* 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);
  }
};