/* * Copyright (c) 1998 Bill Venners. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for EVALUATION purposes only * is hereby granted provided that this copyright notice * appears in all copies. "Evaluation purposes" are any uses which * do not constitute the sale, sharing, or redistribution of this * software with or to any other persons in any medium. * * BILL VENNERS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT * THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. ARTIMA SOFTWARE COMPANY * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ /* * TrafficLightApplet.java * * This file contains all the code for the traffic light state machine * applet, shown as Figure 1 in the JavaWorld Design Techniques * article titled,"Designing Object Initialization". * * Bill Venners, January 1998 */ import java.applet.*; import java.awt.*; class TrafficLight { public static final int RED = 0; public static final int YELLOW = 1; public static final int GREEN = 2; private int currentColor = RED; public int change() { switch (currentColor) { case RED: currentColor = GREEN; break; case YELLOW: currentColor = RED; break; case GREEN: currentColor = YELLOW; break; } return currentColor; } public int getCurrentColor() { return currentColor; } } public class TrafficLightApplet extends Applet { private TrafficLightCanvas trafficLightCanvas = new TrafficLightCanvas(); public void init() { setLayout(new GridLayout(1, 1)); add(trafficLightCanvas); } public void start() { trafficLightCanvas.start(); } public void stop() { trafficLightCanvas.stop(); } } class TrafficLightCanvas extends Canvas implements Runnable { private TrafficLight trafficLight = new TrafficLight(); private Thread runner; private final static int RED_SECS = 15; private final static int YELLOW_SECS = 5; private final static int GREEN_SECS = 15; private final String redLabel = "red"; private final String yellowLabel = "yellow"; private final String greenLabel = "green"; private final String changeLabel = "change"; private final String startLabel = "start"; TrafficLightCanvas() { setBackground(Color.white); } public void run() { // Make sure the traffic light starts out as red while (trafficLight.getCurrentColor() != TrafficLight.RED) { trafficLight.change(); } for (;;) { repaint(); try { Thread.sleep(getSecs()); } catch (InterruptedException e) { } trafficLight.change(); } } private int getSecs() { switch (trafficLight.getCurrentColor()) { case TrafficLight.RED: default: return RED_SECS * 1000; case TrafficLight.YELLOW: return YELLOW_SECS * 1000; case TrafficLight.GREEN: return GREEN_SECS * 1000; } } public void start() { runner = new Thread(this); runner.start(); } public void stop() { if (runner != null) { runner.stop(); runner = null; } } public void paint(Graphics g) { Dimension dim = size(); int circleDiameter = dim.height / 4; int circleRadius = dim.height / 8; int quarterCircleDiameter = dim.height / 16; int xCircleStart = (dim.width / 2) - circleRadius; int yRedCircleStart = circleRadius / 2; int yYellowCircleStart = circleDiameter + circleRadius; int yGreenCircleStart = (3 * circleDiameter) - (circleRadius / 2); // Fill in one circle switch (trafficLight.getCurrentColor()) { case TrafficLight.RED: default: g.setColor(Color.red); g.fillOval(xCircleStart, yRedCircleStart, circleDiameter, circleDiameter); break; case TrafficLight.YELLOW: g.setColor(Color.yellow); g.fillOval(xCircleStart, yYellowCircleStart, circleDiameter, circleDiameter); break; case TrafficLight.GREEN: g.setColor(Color.green); g.fillOval(xCircleStart, yGreenCircleStart, circleDiameter, circleDiameter); break; } // Draw outlines of circles g.setColor(Color.black); g.drawOval(xCircleStart, yRedCircleStart, circleDiameter, circleDiameter); g.drawOval(xCircleStart, yYellowCircleStart, circleDiameter, circleDiameter); g.drawOval(xCircleStart, yGreenCircleStart, circleDiameter, circleDiameter); int yCenterRedCircle = (3 * circleDiameter) / 4; int yCenterYellowCircle = 2 * circleDiameter; int yCenterGreenCircle = (3 * circleDiameter) + (circleDiameter / 4); // Draw arcs for arrow bodies g.drawArc(xCircleStart - circleDiameter, yCenterRedCircle, circleDiameter * 2, (2 * circleDiameter) + circleRadius, 90, 180); g.drawArc(xCircleStart, yCenterRedCircle, circleDiameter * 2, circleDiameter + (circleDiameter / 4), 90, -180); g.drawArc(xCircleStart, yCenterYellowCircle, circleDiameter * 2, circleDiameter + (circleDiameter / 4), 90, -180); g.drawArc(xCircleStart - circleDiameter, yRedCircleStart - circleRadius, circleDiameter * 2, circleDiameter, 180, 90); // (the start arrow) // Create polygons for the arrow heads Polygon toRedArrowHead = new Polygon(); toRedArrowHead.addPoint(xCircleStart + circleDiameter, yCenterRedCircle); toRedArrowHead.addPoint(xCircleStart + circleDiameter + quarterCircleDiameter, yCenterRedCircle - (quarterCircleDiameter / 2)); toRedArrowHead.addPoint(xCircleStart + circleDiameter + quarterCircleDiameter, yCenterRedCircle + (quarterCircleDiameter / 2)); Polygon toYellowArrowHead = new Polygon(); toYellowArrowHead.addPoint(xCircleStart + circleDiameter, yCenterYellowCircle); toYellowArrowHead.addPoint(xCircleStart + circleDiameter + quarterCircleDiameter, yCenterYellowCircle - (quarterCircleDiameter / 2)); toYellowArrowHead.addPoint(xCircleStart + circleDiameter + quarterCircleDiameter, yCenterYellowCircle + (quarterCircleDiameter / 2)); Polygon toGreenArrowHead = new Polygon(); toGreenArrowHead.addPoint(xCircleStart, yCenterGreenCircle); toGreenArrowHead.addPoint(xCircleStart - quarterCircleDiameter, yCenterGreenCircle - (quarterCircleDiameter / 2)); toGreenArrowHead.addPoint(xCircleStart - quarterCircleDiameter, yCenterGreenCircle + (quarterCircleDiameter / 2)); Polygon startArrowHead = new Polygon(); startArrowHead.addPoint(xCircleStart, yCenterRedCircle); startArrowHead.addPoint(xCircleStart - quarterCircleDiameter, yCenterRedCircle - (quarterCircleDiameter / 2)); startArrowHead.addPoint(xCircleStart - quarterCircleDiameter, yCenterRedCircle + (quarterCircleDiameter / 2)); // Draw arrowheads g.fillPolygon(toRedArrowHead); g.fillPolygon(toYellowArrowHead); g.fillPolygon(toGreenArrowHead); g.fillPolygon(startArrowHead); Font font = getFont(); FontMetrics fm = getFontMetrics(font); int widthRedString = fm.stringWidth(redLabel); int widthYellowString = fm.stringWidth(yellowLabel); int widthGreenString = fm.stringWidth(greenLabel); int widthChangeString = fm.stringWidth(changeLabel); int widthStartString = fm.stringWidth(startLabel); int labelHeight = fm.getAscent() + fm.getDescent(); // Label the circles g.drawString(redLabel, xCircleStart + circleRadius - (widthRedString / 2), yCenterRedCircle + (labelHeight / 2) - fm.getDescent()); g.drawString(yellowLabel, xCircleStart + circleRadius - (widthYellowString / 2), yCenterYellowCircle + (labelHeight / 2) - fm.getDescent()); g.drawString(greenLabel, xCircleStart + circleRadius - (widthGreenString / 2), yCenterGreenCircle + (labelHeight / 2) - fm.getDescent()); // Write the ch-ch-ch-changes g.drawString(changeLabel, xCircleStart - circleDiameter - quarterCircleDiameter - widthChangeString, yCenterYellowCircle + (labelHeight / 2) - fm.getDescent()); g.drawString(changeLabel, xCircleStart + (2 * circleDiameter) + quarterCircleDiameter, ((yCenterRedCircle + yCenterYellowCircle) / 2) + (labelHeight / 2) - fm.getDescent()); g.drawString(changeLabel, xCircleStart + (2 * circleDiameter) + quarterCircleDiameter, ((yCenterYellowCircle + yCenterGreenCircle) / 2) + (labelHeight / 2) - fm.getDescent()); // Write the start label g.drawString(startLabel, xCircleStart - circleDiameter - quarterCircleDiameter - widthStartString, yCenterRedCircle - (circleRadius / 2) + (labelHeight / 2) - fm.getDescent()); } }