// Ray intersection diagram applet
// David Eppstein, UC Irvine, 18 May 1997

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

public class RayDiagram extends Applet
{
	private int x_down = 0;
	private int y_down = 0;
	private FastPair rays;
	
	// Draw a segment
	private void drawSeg(RayDiagObj o) {
		double dr = o.t;
		if (dr == Double.POSITIVE_INFINITY) {		// if infinite ray
			Rectangle r = this.bounds();				// perform doubling search
			dr = 1.0;										// to find length param outside
			while (true) {									// screen rectangle area
				double ox = o.x + dr * o.dx;
				double oy = o.y + dr * o.dy;
				if (ox < r.x && o.dx <= 0) break;		// off left edge of screen?
				if (ox > r.x + r.width && o.dx >= 0) break;	// off right edge of screen?
				if (oy < r.y && o.dy <= 0) break;		// off top edge of screen?
				if (oy > r.y + r.height && o.dy >= 0) break;	// off bottom edge of screen?
				dr *= 2;										// none of the above, keep growing the ray
			}
		}
	
		Graphics g = this.getGraphics();
		g.setColor(Color.black);
		g.drawLine((int) o.x, (int) o.y,
					  (int) (o.x + dr * o.dx), (int) (o.y + dr * o.dy));
	}
	
	// redisplay
	public void paint(Graphics g) {
		Rectangle r = this.bounds();
		g.setColor(Color.white);
		g.fillRect(r.x, r.y, r.width, r.height);
		Enumeration enum = rays.elements();
		while (enum.hasMoreElements()) {
			RayDiagObj o = (RayDiagObj) enum.nextElement();
			drawSeg(o);
		}
	}
	
	// Found the end of a segment
	private void terminate(RayDiagObj r, double termTime) {
		r.t = termTime;
		rays.update(r);
		drawSeg(r);
	}
	
	// Recompute the ray diagram
	private void restart() {
		// empty the screen
		Graphics g = this.getGraphics();
		Rectangle r = this.bounds();
		g.setColor(Color.white);
		g.fillRect(r.x, r.y, r.width, r.height);
		
		// unterminate all rays
		Enumeration enum = rays.elements();
		if (!enum.hasMoreElements()) return;	// nothing to do
		while (enum.hasMoreElements()) {
			RayDiagObj o = (RayDiagObj) enum.nextElement();
			o.t = Double.POSITIVE_INFINITY;
		}
		rays.restart();
		
		// now find collisions
		while (true) {
			RayDiagObj o = (RayDiagObj) rays.closestPair();
			if (o.distanceToNeighbor == Double.POSITIVE_INFINITY) break;
			RayDiagObj n = (RayDiagObj) o.neighbor;
			double no = n.crosstime(o);
			double on = o.crosstime(n);
			if (no >= on) terminate(n, no);
			if (on >= no) terminate(o, on);
		}
		
		// draw all remaining rays
		enum = rays.elements();
		while (enum.hasMoreElements()) {
			RayDiagObj o = (RayDiagObj) enum.nextElement();
			if (o.t == Double.POSITIVE_INFINITY) drawSeg(o);
		}
	}
	
	// Forget about all our rays
	public void clear() {
		rays = new FastPair();
		restart();
	}

	// Called to initialize the applet
	public void init() {
		this.setBackground(Color.white);
		clear();
	}
	
	// Called on mouse-down events: start making a ray
	public boolean mouseDown(Event e, int x, int y)
	{
		x_down = x;
		y_down = y;
		return true;
	}
	
	// Called on mouse-up events: finish making a ray
	public boolean mouseUp(Event e, int x, int y)
	{
		if (x == x_down && y == y_down) return true;	// ignore single click
		rays.add(new RayDiagObj((double) x_down, (double) y_down,
									   (double) (x - x_down), (double) (y - y_down)));
		restart();
		return true;
	}
}
