/** File: AnalogClockApplet.java Author: Angus McIntyre Date: 22.05.96 Updated: 17.03.98 Simple Java applet to display an analog clock and update it continuously. HISTORY ------- 20.02.99 SLAM Added call to 'verifyAppletLocation'. 17.03.98 SLAM Added documentation for 'time-zone' parameter. 29.11.96 SLAM Split out into separate files 27.05.96 SLAM Classes merged, bugs fixed 26.05.96 SLAM Analog clock implemented 22.05.96 SLAM First version implemented TO-DO ----- * Add tick-marks (optionally) to outer edge of clock frame * Add numbers (optionally) to outer edge of clock frame * Implement better scheme for calculating hand position; one of the problems is that the hour hand points directly to the current hour, rather than pointing to somewhere intermediate between it and the next hour, the way that a real analog clock does. This makes the display unrealistic and hard to read. LEGAL ----- This software is free. It can be used and modified in any way you choose, but it may not be sold, either separately or as part of a collection without explicit prior permission from the author. The author assumes no liability for any loss, damage or mental or physical trauma you may incur through use of or inability to use this software. This disclaimer must appear on any modified or unmodified version of the software in which the name of the author also appears. **/ /* ---------------------------------------------------------------------- * IMPORTS * ---------------------------------------------------------------------- */ import java.applet.*; import java.awt.*; import java.util.*; import ClockApplet; /* ---------------------------------------------------------------------- * CLASSES * ---------------------------------------------------------------------- */ // Class: AnalogClockApplet // // An alternative clock type, used for analog clocks. This actually // caches the last hour, minute and second drawn, so as to reduce the // amount of redrawing required. public class AnalogClockApplet extends ClockApplet { // Constants static final int margin = 5; // Defaults static final Color default_hand_color = Color.black; static final Color default_sweep_color = Color.red; static final Color default_face_color = Color.white; static final Color default_frame_color = Color.black; static final Color default_background_color = Color.lightGray; // Instance variables Color hand_color; Color sweep_color; Color face_color; Color frame_color; Color background_color; Point center; int diameter = 0; int last_hour, last_minute, last_second; int hour_range = 24; int hour_hand_length; int minute_hand_length; int second_hand_length; // Method: getAppletInfo() // // Return information about the applet. public String getAppletInfo() { return "AnalogClockApplet 1.2 by Angus McIntyre "; } // Method: getParameterInfo() // // Return information about the applet's parameter options. public String[][] getParameterInfo() { String[][] info = { {"time-zone", "int", "TZ offset in hours from Greenwich"}, {"hand-color", "Color", "color used for clock hands"}, {"second-hand-color", "Color", "color used for second hand"}, {"face-color", "Color", "color used for clockface"}, {"frame-color", "Color", "color used for frame of clock"}, {"background-color", "Color", "color used for background"}, {"twenty-four-hour", "boolean", "use 24-hour clock - default to false"}, {"show-seconds", "boolean", "show second hand - default to true"} }; return info; } // Method: init() // // Call the superclass method, read in some parameters, and then set // up the information that we need to draw the clock. Note that for // analog clocks, the default is a twelve-hour display, because // twenty-four-hour analog clocks look strange. // // The call to 'verifyAppletLocation' has been put in to guard against // people who link to classfiles stored on other people's servers // (intentionally or unintentionally stealing bandwidth). The argument // it takes is the URL of a place where the source and class files can // be downloaded. If you create your own modified version of this applet, // change this to point to your own page. If you don't intend to share // your code, then simply pass an empty string. public void init() { super.init(); hand_color = parseColorParameter("hand-color",default_hand_color); sweep_color = parseColorParameter("second-hand-color",default_sweep_color); face_color = parseColorParameter("face-color",default_face_color); frame_color = parseColorParameter("frame-color",default_frame_color); background_color = parseColorParameter("background-color",default_background_color); twenty_four_hour_p = parseBooleanParameter("twenty-four-hour",false); // Set up internal numeric variables. Calculate the diameter of the // clockface, and its center. The lengths of the various hands are // also calculated here; change the decimal constants if you want. diameter = Math.min(bounds().width-(2*margin), bounds().height-(2*margin)); center = new Point(bounds().x + (diameter / 2) + margin, bounds().y + (diameter / 2) + margin); hour_hand_length = (int) (diameter * 0.40); minute_hand_length = (int) (diameter * 0.45); second_hand_length = (int) (diameter * 0.45); // Set up the internal variables used to track time last_hour = date.getHours(); last_minute = date.getMinutes(); last_second = date.getSeconds(); if (!twenty_four_hour_p) { hour_range = 12; if (last_hour > 12) last_hour -= 12; } } // Method: drawClock(Graphics) // // Draw the clock in the graphics environment provided. This involves // drawing the face and frame of the clock, and then drawing the hands // for the first time. public void drawClock (Graphics g) { super.drawClock(g); Rectangle bounds = bounds(); // Fill in with the background color. I'd really like to be able // to get this from the browser, but they don't seem to be too // intelligent about this as a rule (for example, NetScape always // gives browser gray as the background color of the applet and // its parent, even if the BGCOLOR of the page is specified to be // something else). g.setColor(background_color); g.fillRect(bounds.x,bounds.y,bounds.width,bounds.height); // Draw the clock face g.setColor(face_color); g.fillOval(bounds.x+margin,bounds.y+margin,diameter,diameter); g.setColor(frame_color); g.drawOval(bounds.x+margin,bounds.y+margin,diameter,diameter); // Draw the hands drawClockHand(g,hand_color,hour_hand_length,last_hour,hour_range); drawClockHand(g,hand_color,minute_hand_length,last_minute,60); if (show_seconds_p) drawClockHand(g,sweep_color,second_hand_length,last_second,60); } // Method: updateClock(Graphics) // // Call the superclass method to make sure that everything is ready // for drawing, then get the time information we need to draw. public void updateClock(Graphics g) { super.updateClock(g); int hour = date.getHours(); int minute = date.getMinutes(); int second = date.getSeconds(); if (hour > hour_range) hour -= hour_range; // Drawing is actually done in 'reverse' order - second hand (if // shown) first, then minute hand, then hour hand. The reason for // this is that the second hand is more likely to wipe out the // hour hand as it moves on. If it does, the hour hand doesn't // get redrawn for a full second, which looks ugly. So we draw // the second hand first, and then draw the other hands over it. // The same applies with the minute hand. // // Each hand is checked against the last recorded position of the // hand. If it has moved on since it was last drawn, the old hand // is drawn over in the background color, and the hand is then // redrawn in the new position. This would actually be a prime // candidate for use of XOR drawing mode: unfortunately, NetScape // seems to do something a bit weird with XOR, so we simply use // standard mode and paint out the preceding item with the // background color. It seems to work equally well. if (show_seconds_p) { if (second != last_second) { drawClockHand(g,face_color,second_hand_length,last_second,60); last_second = second; } drawClockHand(g,sweep_color,second_hand_length,second,60); } if (minute != last_minute) { drawClockHand(g,face_color,minute_hand_length,last_minute,60); last_minute = minute; } drawClockHand(g,hand_color,minute_hand_length,minute,60); if (hour != last_hour) { drawClockHand(g,face_color,hour_hand_length,last_hour,hour_range); last_hour = hour; } drawClockHand(g,hand_color,hour_hand_length,hour,hour_range); } // Method: drawClockHand(Graphics,Color,int,int,int) // // Draw one of the hands of the clock. The method is passed not only // a graphics environment and a color, but also the length of the hand // to draw, and two other values called 'value' and 'range'. 'value' is // the time for that hand, e.g. if it were 8:45 and we were drawing // the hour hand, it would be '8'. 'range' is the range of possible // values: this will always be 60 for minutes and seconds, and either // 12 or 24 for hours, depending on whether the 24-hour clock is in // use. The combination of the value and range determines the angle // of the hand. protected void drawClockHand(Graphics g, Color color, int length, int value, int range) { Point tip = calculateHandTip(center.x,center.y, length,value,range); g.setColor(color); g.drawLine(center.x,center.y,tip.x,tip.y); } // Method: calculateHandTip(int,int,int,int,int) // // Work out where the tip of the hand should be drawn, given the // position of the center of the clock, the length of the hand, and // the value and range (as explained above). The calculation of the // angle involves an implicit conversion from degrees (which I like // thinking in) to radians (which Java expects). This could probably // be optimised. Ideally, in fact, we'd calculate everything in // advance and store it in an array for maximum speed - but this // amount of trigonometry probably isn't straining modern processors // too much. protected Point calculateHandTip(int x, int y, int length, int value, int range) { double angle = (Math.PI * (((value * 360) / range) - 90)) / 180; return new Point(x + (int) (length * Math.cos(angle)), y + (int) (length * Math.sin(angle))); } }