/*
	Project: LineDraw
	Author: Simon Gayton
	Copyright 2005 (C) Simon Gayton
*/

import java.awt.*;
import java.applet.Applet;

/*
	This applet displays a rasterized line segment using
	the Bresenham line drawing algorithm. The line is
	also colored from green to red in a smooth blend to
	show the ability to blend vertex attributes.
*/
public class LineDraw extends Applet
{
	int pixelSize = 16;
	int x1 = 50;
	int y1 = 60;
	int x2 = 200;
	int y2 = 175;
	boolean ptOne = true;
	boolean showGrid = true;
	
	/*
		Initialize the applet.
	*/
	public void init() {
		repaint();
	}
	
	/*
		Respond to a keyDown event.
	*/
	public boolean keyDown(Event e, int key) {
		if (key == 'w') {
			// Shrink the rendered pixel size.
			pixelSize -= 1;
			if (pixelSize < 1)
				pixelSize = 1;
		} else if (key == 's') {
			// Increase the rendered pixel size.
			pixelSize += 1;
			if (pixelSize > 128)
				pixelSize = 128;
		} else if (key == 'g') {
			// Toggle the display of the grid.
			showGrid = !showGrid;
		}
		
		// Redraw the image.
		repaint();
		
		return true;
	}
	
	/*
		Respond to a mouseDown event.
	*/
	public boolean mouseDown(Event e, int x, int y) {
		// Toggle the currently active point.
		ptOne = !ptOne;
		
		// Redraw the image.
		repaint();
		
		return true;
	}
	
	/*
		Respond to a mouseMove event.
	*/
	public boolean mouseMove(Event e, int x, int y) {
		// Update the position of the currently active point.
		if (ptOne) {
			x1 = x;
			y1 = y;
		} else {
			x2 = x;
			y2 = y;
		}
		
		// Redraw the image.
		repaint();
		
		return true;
	}
	/*
		Overridding this method allows me to redraw the image without
		erasing the image to a background color. This prevents the
		flicker you would get without overridding this method.
	*/
	public void update(Graphics g) {
		// Simply call paint without erasing the background.
		paint(g);
	}
	
	/*
		Display the image on screen.
	*/
	public void paint(Graphics g) {
		int i;
		
		// Erase the background.
		Color saveColor = g.getColor();
		
		g.setColor(new Color(150, 150, 150));
		g.fillRect(0, 0, 256, 256);
		g.setColor(saveColor);
		
		// Draw the rasterized line.
		drawRasterLine(g);
		
		// Draw the grid.
		if (showGrid) {
			for (i = 0; i < 256/pixelSize; i++) {
				g.drawLine(0, i*pixelSize, 256, i*pixelSize);
				g.drawLine(i*pixelSize, 0, i*pixelSize, 256);
			}
		
			// Draw the true line that was rasterized.
			g.setColor(new Color(50, 128, 255));
			
			// Calculate the screen coordinates of the true line that was rasterized.
			int u1 = (x1 / pixelSize) * pixelSize + (pixelSize / 2);
			int v1 = (y1 / pixelSize) * pixelSize + (pixelSize / 2);
			int u2 = (x2 / pixelSize) * pixelSize + (pixelSize / 2);
			int v2 = (y2 / pixelSize) * pixelSize + (pixelSize / 2);
			
			// Draw the true line.
			g.drawLine(u1, v1, u2, v2);
			
			g.setColor(saveColor);
		}
	}
	
	/*
		This draws a single pixel, it may be larger than a screen pixel.
		This is part of how the zooming effect is done.
	*/
	void drawRasterPixel(Graphics g, int x, int y, Color c) {
		Color saveColor = g.getColor();
		
		g.setColor(c);
		g.fillRect(x*pixelSize, y*pixelSize, pixelSize, pixelSize);
		g.setColor(saveColor);
	}
	
	/*
		This is the implementation of the Bresenham line drawing algorithm.
		It has been written to handle pixels larger than the screen pixels.
	*/
	void drawRasterLine(Graphics g) {
		// Line interpolation data.
		int dx = x2/pixelSize - x1/pixelSize;
		int dy = y2/pixelSize - y1/pixelSize;
		int xStep = 1;
		int yStep = 1;
		int d;
		int incrE;
		int incrNE;
		int x = x1 / pixelSize;
		int y = y1 / pixelSize;
		// Color interpolation data.
		int red, green, blue;
		int r1 = 0;
		int g1 = 255;
		int b1 = 0;
		int r2 = 255;
		int g2 = 0;
		int b2 = 0;
		
		if (dx < 0) {
			dx = -dx;
			xStep = -1;
		}
		
		if (dy < 0) {
			dy = -dy;
			yStep = -1;
		}
		
		// Draw the first pixel.
		drawRasterPixel(g, x, y, new Color(r1, g1, b1));
		
		// Iterate over the line differently depending on its slope.
		if (dx < dy) {
			d = 2*dx-dy;
			incrE = 2*dx;
			incrNE = 2*(dx-dy);
			
			// Loop over the height of the line.
			while ((dy--) > 0) {
				if (d <= 0) {
					d += incrE;
					y += yStep;
				} else {
					d += incrNE;
					y += yStep;
					x += xStep;
				}
				
				// Interpolate the vertex attributes.
				red = (y - y1/pixelSize) * (r2 - r1) / (y2/pixelSize - y1/pixelSize) + r1;
				green = (y - y1/pixelSize) * (g2 - g1) / (y2/pixelSize - y1/pixelSize) + g1;
				blue = (y - y1/pixelSize) * (b2 - b1) / (y2/pixelSize - y1/pixelSize) + b1;
				
				drawRasterPixel(g, x, y, new Color(red, green, blue));
			}
		} else {
			d = 2*dy-dx;
			incrE = 2*dy;
			incrNE = 2*(dy-dx);
			
			// Loop over the width of the line.
			while ((dx--) > 0) {
				if (d <= 0) {
					d += incrE;
					x += xStep;
				} else {
					d += incrNE;
					x += xStep;
					y += yStep;
				}
				
				// Interpolate the vertex attributes.
				red = (x - x1/pixelSize) * (r2 - r1) / (x2/pixelSize - x1/pixelSize) + r1;
				green = (x - x1/pixelSize) * (g2 - g1) / (x2/pixelSize - x1/pixelSize) + g1;
				blue = (x - x1/pixelSize) * (b2 - b1) / (x2/pixelSize - x1/pixelSize) + b1;
				
				drawRasterPixel(g, x, y, new Color(red, green, blue));
			}
		}
	}
}
