/*
 * File: RotaryAnimation.java
 *
 * Draws the animated Rotary in action!
*/

import java.awt.*;
import java.awt.event.*;

/**
  * This class runs an animated simulation of a Rotary engine.
  * It tracks the engine through stages.
  * It can be used in an Applet (browser) or in Swing (executable).
*/
class RotaryAnimation
{
	// Data state
	int			xSize, ySize,
				xHalf, yHalf,
				stage, lastStage,
				rx, ry,
				a1x, a1y,
				a2x, a2y,
				a3x, a3y,
				seg1, seg2, seg3,
				rot,
				csRadius,
				rotRadius,
				rotSide,
				rotEcc;
	double			aspRatio,
				rrot,
				cosR,
				sinR;
	static final int	jumpFactor = 2;
	static final double	pi = 3.14159265358979323846264338327950288,
				pi180 = pi / 180.0,
				pi2 = pi * 2.0,
				rotorArc1 = 29.0,
				rotorArc2 = 331.0;


	// GUI stuff
	Polygon			rHousing;

	public RotaryAnimation(int xs, int ys)
	{
		xSize = xs;
		ySize = ys;
	}

	public void init()
	{
		int		i;

		stage = lastStage = 0;
		if (xSize == 0)
			xSize = 640;
		if (ySize == 0)
			aspRatio = 1.333;
		else
			aspRatio = xSize / ySize;
		ySize = (int)Math.round(xSize * (1 / aspRatio));
		xHalf = Math.round(xSize / 2);
		yHalf = Math.round(ySize / 2);

		// Set dimensions & constants of drawing
		seg1 = (int)Math.round(yHalf * 0.75);
		seg2 = (int)Math.round(seg1 * 0.181818181818);
		seg3 = (int)Math.round(seg1 * 0.136363636363);
		csRadius = (int)Math.round(seg2 * 1.05);
		rotRadius = (int)Math.round(seg1 * 0.336363636363);
		rotSide = (int)Math.round(seg1 * 1.809090909090);
		rotEcc = (int)Math.round(seg1 * -1.090909090909);

		// Build Housing Polygon
		rHousing = new Polygon();
		for (i = 0; i < 270; i++)
		{
			rrot = i * (pi / 45.0);
			rx = (int)Math.round(seg3 * Math.cos(rrot));
			ry = (int)Math.round(seg3 * Math.sin(rrot));
			a1x = xHalf + rx + (int)Math.round(-seg1 * Math.cos(rrot / 3));
			a1y = yHalf + ry + (int)Math.round(-seg1 * Math.sin(rrot / 3));
			rHousing.addPoint(a1x, a1y);
		}
	}

	public void inc()
	{
		inc(1);
	}

	public void inc(int steps)
	{
		lastStage = stage;
		stage += steps;
		setStage(stage);
	}

	protected void setStage(int stage)
	{
		rot = (stage * jumpFactor);
		// The animation goes through 3 rotations which is 1080 degrees
		rot %= 1080;
		// Convert degrees to radians
		rrot = rot * pi180;
		// Compute constants used for drawing each stage
		cosR = Math.cos(rrot);
		sinR = Math.sin(rrot);
		rx = (int)Math.round(seg3 * cosR);
		ry = (int)Math.round(seg3 * sinR);
	}

	public void nextStage(Graphics g)
	{
		inc();
		draw(g);
	}

	// Draws the animation at the current stage
	public void draw(Graphics g)
	{
		// Draw housing
		g.drawPolygon(rHousing);
		drawArc(g, xHalf, yHalf-seg2, 0, 180, seg1);
		drawArc(g, xHalf, yHalf+seg2, 180, 360, seg1);
		g.drawLine(xHalf-seg1, yHalf+seg2, xHalf-seg1, yHalf-seg2);
		g.drawLine(xHalf+seg1, yHalf+seg2, xHalf+seg1, yHalf-seg2);

		// Draw crankshaft & rotor
		drawArc(g, xHalf, yHalf, 0, 360, csRadius);
		drawRotor(g);
	}

	protected void drawRotor(Graphics g)
	{
		// Draw rotor's eccentric center hole & line through crankshaft
		drawArc(g, xHalf+rx, yHalf-ry, 0, 360, rotRadius);
		g.drawLine(
			xHalf - (int)Math.round(csRadius * cosR),
			yHalf - (int)Math.round(csRadius * sinR),
			xHalf, yHalf);

		// Draw rotor
		a1x = rx + (int)Math.round(rotEcc * Math.cos(rrot / 3.0));
		a1y = ry + (int)Math.round(rotEcc * Math.sin(rrot / 3.0));
		a2x = rx + (int)Math.round(rotEcc * Math.cos((rrot+pi2) / 3.0));
		a2y = ry + (int)Math.round(rotEcc * Math.sin((rrot+pi2) / 3.0));
		a3x = rx + (int)Math.round(rotEcc * Math.cos((rrot-pi2) / 3.0));
		a3y = ry + (int)Math.round(rotEcc * Math.sin((rrot-pi2) / 3.0));

		drawArc(g, xHalf+a1x, yHalf-a1y, rotorArc2+rot/3.0, rotorArc1+rot/3.0, rotSide);
		drawArc(g, xHalf+a2x, yHalf-a2y, rotorArc2+(rot+360.0)/3.0, rotorArc1+(rot+360.0)/3.0, rotSide);
		drawArc(g, xHalf+a3x, yHalf-a3y, rotorArc2+(rot-360.0)/3.0, rotorArc1+(rot-360.0)/3.0, rotSide);
	}

	/* NOTE: the JDK AWT Graphics object provides a method for drawing arcs, but it uses different units
		that are less intuitive to use - the arc is specified by its bounding rectangle.
		This method specifies the center point, start & stop angles, and radius.
		This facilitates the above math drawing the rotary engine.
	*/
	private void drawArc(Graphics g, int xCenter, int yCenter, double angStart, double angEnd, double radius)
	{
		int		i,
				pCount = 64,
				xPoints[] = new int[pCount],
				yPoints[] = new int[pCount];
		double		a, da, aStart, aEnd;

		while (angEnd <= angStart)
			angEnd += 360;
		aStart = angStart * pi180;
		aEnd = angEnd * pi180;
		// Calculate arc degrees for each point in this arc
		da = (aEnd - aStart) / (pCount - 1);
		for (i = 0, a = aStart;
				i < pCount;
				i++, a += da)
		{
			xPoints[i] = xCenter + (int)Math.round(radius * Math.cos(a));
			yPoints[i] = yCenter - (int)Math.round(radius * Math.sin(a));
		}
		// Draw the curve
		g.drawPolyline(xPoints, yPoints, pCount);
	}
}
