import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class IFS extends Applet implements Runnable, MouseListener, MouseMotionListener {
	//private Vector points = new Vector();
	private Image image = null;
	private Graphics g  = null;
	private float x, y, w, h;
	private Point start = null;
	private Point end   = null;
	
	public void init() {
		Dimension size = getSize();
		image = createImage( size.width, size.height );
		g     = image.getGraphics();
		addMouseListener( this );
		addMouseMotionListener( this );
	}
	
	public synchronized void mousePressed( MouseEvent me ) {
		start = new Point( me.getX(), me.getY() );
		end   = new Point( me.getX(), me.getY() );
	}
	
	public synchronized void mouseReleased( MouseEvent me ) {
		end = new Point( me.getX(), me.getY() );
		if ( start.equals( end ) )
			initCoords();
		else {
			Dimension size = getSize();
			// on-screen rectangle
			int x = Math.min( start.x, end.x );
			int y = Math.max( start.y, end.y );
			int w = Math.abs( start.x - end.x );
			int h = Math.abs( start.y - end.y );
			
			// alter so y goes up screen
			y = size.height - y;
			
			float nx = this.x + (this.w*x)/size.width;
			float ny = this.y + (this.h*y)/size.height;
			float nw = (this.w*w)/size.width;
			float nh = (this.h*h)/size.height;
			
			changeCoords( nx, ny, nw, nh );
			
		}
		start = end = null;
	}
	
	public void mouseClicked( MouseEvent me ) {}
	
	public synchronized void mouseExited( MouseEvent me ) {
		start = end = null;
	}
	
	public synchronized void mouseEntered( MouseEvent me ) {
		start = end = null;
	}
	
	public synchronized void mouseMoved( MouseEvent me ) {
		start = end = null;
	}
	
	public synchronized void mouseDragged( MouseEvent me ) {
		end = new Point( me.getX(), me.getY() );
	}
	
	public synchronized void start() {
		(new Thread( this )).start();
	}
	
	private void initCoords() {
		changeCoords( -5.5f, 0, 11, 11 );
	}
	
	private synchronized void changeCoords( float x, float y, float w, float h ) {
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
		Dimension size = getSize();
		g.setColor( Color.white );
		g.fillRect( 0, 0, size.width, size.height );
	}
	
	public void run() {
		Pt p = new Pt( 0.5f, 0.5f );
		Dimension size = getSize();
		
		//g.setColor( new Color( 0, 0, 0, 16 ) );
		initCoords();
		while ( true ) {
			synchronized( this ) {
				g.setColor( Color.black );
				for ( int i = 0; i < 1000; i++ ) {				
					int x = (int)(size.width*(p.x-this.x)/this.w);
					int y = (int)(size.height - size.height*(p.y-this.y)/this.h);
				
					g.drawLine( x, y, x, y );
					p = apply( p );
				}
			}
			//System.out.println( p.x + " " + p.y );
			repaint();
			try {
				Thread.sleep( 40 );
			}
			catch( Exception e ) {}
		}
	}
	
	public Dimension getPreferredSize() {
		return new Dimension( 256, 256 );
	}
	
	public Pt apply( Pt p, float a, float b, float c, float d, float e, float f ) {
		float x = p.x, y = p.y;
		p.x = a*x + b*y + e;
		p.y = c*x + d*y + f;
		return p;
	}
	
	public Pt apply( Pt p ) {
		double r = Math.random();
	
		if ( r < 0.85 ) {
			return apply( p, 0.85f, 0.04f, -0.04f, 0.85f, 0, 1.6f );
		}
		else if ( r < 0.92 ) {
			return apply( p, 0.2f, -0.26f, 0.23f, 0.22f, 0, 1.6f );
		}
		else if ( r < 0.99 ) {
			return apply( p, -0.15f, 0.28f, 0.26f, 0.24f, 0, 0.44f );
		}
		else {
			return apply( p, 0, 0, 0, 0.16f, 0, 0 );
		}
	}
	
	public void update( Graphics g ) {
		paint( g );
	}
	
	public synchronized void paint( Graphics g ) {
		if ( image != null ) {
			g.drawImage( image, 0, 0, this );
			if ( start != null && end != null ) {
				int x = Math.min( start.x, end.x );
				int y = Math.min( start.y, end.y );
				int w = Math.abs( start.x - end.x );
				int h = Math.abs( start.y - end.y );
				g.setColor( Color.black );
				g.setXORMode( Color.white );
				g.drawRect( x, y, w, h );
			}
		}
	}
	
	public static class Pt {
		public float x, y;
		
		public Pt( float x, float y ) {
			this.x = x;
			this.y = y;
		}
	}
	
	public static void main( String[] args ) {
		Frame f = new Frame( "IFS" );
		f.addWindowListener(
			new WindowAdapter() {
				public void windowClosing( WindowEvent we ) {
					we.getWindow().dispose();
				}
				
				public void windowClosed( WindowEvent we ) {
					System.exit( 0 );
				}
			}
		);
		IFS ifs = new IFS();
		f.add( ifs );
		f.pack();
		f.show();
		ifs.init();
		ifs.start();
	}
	
}
