/* * AnalogClock.java v1.0 Jan 8, 1997 * * Copyright (c) 1997 H.J. Tsai, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for any purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. * * H.J. Tsai 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. H.J. Tsai SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * * Version 1.0 Jan 8 1996 Initial version */ import java.lang.Math; import java.util.*; import java.awt.*; import java.applet.*; public class AnalogClock extends Clock { static final private String clockname = "AnalogClock 97"; static final private String clocksmith = "Clocksmith: H.J. Tsai (c) 1997"; static final private int SHOW_MARK_ALL = 1; static final private int SHOW_MARK_LARGE = 2; static final private int SHOW_MARK_NONE = 3; private int showMark = SHOW_MARK_ALL; private Color clrMark = Color.black; private boolean showRoundFrame = true; private int roundFrameWidth = 5; private Color clrRoundFrame = Color.red; private Color clrHandHour = Color.blue; private Color clrHandMinute = Color.blue; private Color clrHandSecond = Color.red; private Color clrCenterDot = Color.black; private Color clrClockFace = Color.yellow; private Color clrClockBackground = Color.lightGray; // the lengths of tails of the hands // tails are the other ends of the clock hands (closer to the center) private int lenHourTail = 8; private int lenMinuteTail = 10; private int lenSecondTail = 16; // widths of the clock hands (distance from the center to the tip of the side) private int widthHour = 5; private int widthMinute = 4; private int widthSecond = 3; private int radiusCenterDot = 2; private boolean counterClockwise = false; private boolean showFrame = true; private Color clrFrame = Color.black; private int frameThickness = 5; private int frameStyle = Rect.RS_NORMAL; private int outBevel = 0; private int outBevelStyle = Rect.RS_3DRAISED; private int inBevel = 0; private int inBevelStyle = Rect.RS_3DINSET; // program options protected boolean showZoneTime = true; /// false; protected long tzdiff = 0L; // diff between local and target zone in ms protected int tztarget; // diff between GMT and target zone in minutes // internal variables private RectFrame rectFrame; private Rect rectClock; private int radius; private int xcenter; private int ycenter; private double xtrigtable[] = new double [61]; // work around netscape 3.0 bug private double ytrigtable[] = new double [60]; private int lasts = -1; private int lastm = -1; private int lasth = -1; private int xpoints[] = new int[4]; private int ypoints[] = new int[4]; // last hand positions on the screen private Point slastp[] = new Point[4]; private Point mlastp[] = new Point[4]; private Point hlastp[] = new Point[4]; private Point p0 = new Point(0,0); private Point p1 = new Point(0,0); private Point p2 = new Point(0,0); private Point p3 = new Point(0,0); private Image memImage; private boolean showCopyright = true; private Point marks[] = new Point[60]; private Point marks2[] = new Point[60]; public String getAppletInfo() { String info = clockname + " " + clocksmith; return info; } public String [][] getParamterInfo() { String info[][] = { {"ShowFrame", "boolean", "option to display the frame"}, {"OutBevel", "integer", "Outer frame bevel width"}, {"OutBevelStyle", "String", "Outer frame bevel style"}, {"InBevel", "integer", "Inner Frame bevel width"}, {"InBevelStyle", "String", "Inner frame bevel style"}, {"FrameStyle", "String", "various styles for the frame"}, {"FrameThickness", "integer", "the thickness of the clock frame"}, {"FrameColor", "String", "color for the clock frame"}, }; return info; } // // formula for ellipses // x**2/a**2 + y**2/b**2 = 1; // b = sqrt(a**2 - c**2); // // if (b > a) // swap(a,b) a = b, b = a; // x2/a2 = 1 - y2/b2 // public void init() { super.init(); double sintable[] = new double [8]; double costable[] = new double [8]; double delta = Math.PI * 2.0 / 60.0; double angle = 0f; for (int i = 0; i < 8; i++) { sintable[i] = Math.sin(angle); costable[i] = Math.cos(angle); angle += delta; } // setup the trig table to have 0-59 // clock marker positions] xtrigtable[0] = 0; ytrigtable[0] = 1; // 0-15 for (int i = 1; i <= 7; i++) { xtrigtable[i] = sintable[i]; xtrigtable[7+i] = costable[8-i]; ytrigtable[i] = costable[i]; ytrigtable[7+i] = sintable[8-i]; } // 16-59 for (int i=0; i < 15; i++) { xtrigtable[15+i] = ytrigtable[i]; ytrigtable[15+i] = -xtrigtable[i]; xtrigtable[30+i] = -xtrigtable[i]; ytrigtable[30+i] = -ytrigtable[i]; xtrigtable[45+i] = -ytrigtable[i]; ytrigtable[45+i] = xtrigtable[i]; } // xtrigtable[] and ytrigtable[] now has the standard // (x,y) coordinates in the cartensian space // where (0,0) is the origin and the radius is 1 // On the screen <0,0> is upper left hand corner, // y values are the exact oppsite to what we have // so negate them for (int i = 0; i < 60; i++) { ytrigtable[i] = -ytrigtable[i]; } Rectangle r = bounds(); memImage = createImage(r.width, r.height); // get program options getProgramParams(); if (showZoneTime) { super.setTimezoneDiff(tztarget); } // for the couter clock wise if (counterClockwise) for (int i = 0; i < 60; i++) xtrigtable[i] = -xtrigtable[i]; // set up the size for the clock and its frame if (showFrame) { rectFrame = new RectFrame(r, outBevel, outBevelStyle, frameThickness, frameStyle, clrFrame, inBevel, inBevelStyle); rectClock = rectFrame.getInsideRect(); } else { rectClock = new Rect(r); } radius = Math.min(rectClock.width, rectClock.height); radius /= 2; // leave some room radius -= roundFrameWidth; xcenter = rectClock.x + rectClock.width / 2; ycenter = rectClock.y + rectClock.height / 2; for (int i = 0 ; i < 4; i++) { slastp[i] = new Point(0,0); mlastp[i] = new Point(0,0); hlastp[i] = new Point(0,0); } for (int i = 0; i < 60; i++) { marks[i] = new Point(0,0); marks2[i] = new Point(0,0); } double a, b; a = (rectClock.width-roundFrameWidth)/2.0; b = (rectClock.height-roundFrameWidth)/2.0; // make a,b smaller, so the marks will be inside // the elipse, not on it a -= 2; b -= 2; int x, y; int x2 = 0; int y2 = 0; int r2; //System.out.println("RectClock=" + rectClock); //System.out.println("a="+a+",b="+b); double longmark; double shortmark; if (a == b) { longmark = 0.85; shortmark = 0.95; } else { longmark = 0.85; shortmark = 0.95; } for (int i = 0; i <= 15; i++) { r2 = radius; // find the closest distance (radius) // where is still inside the elipse do { x = (int)(xtrigtable[i] * r2); y = (int)(ytrigtable[i] * r2); r2 += 2; } while((x*x)/(a*a) + (y*y)/(b*b) < 0.95); // we found it, // find the coordinates for the marker if (i % 5 == 0) { x2 = (int) (xtrigtable[i] * r2 * longmark); y2 = (int) (ytrigtable[i] * r2 * longmark); } else { x2 = (int) (xtrigtable[i] * r2 * shortmark); y2 = (int) (ytrigtable[i] * r2 * shortmark); } marks[i].x = x; marks[i].y = y; marks[30-i].x = x; marks[30-i].y = -y; marks[30+i].x = -x; marks[30+i].y = -y; marks[(60-i)%60].x = -x; marks[(60-i)%60].y = y; marks2[i].x = x2; marks2[i].y = y2; marks2[30-i].x = x2; marks2[30-i].y = -y2; marks2[30+i].x = -x2; marks2[30+i].y = -y2; marks2[(60-i)%60].x = -x2; marks2[(60-i)%60].y = y2; } for (int i = 0; i <= 59; i++) { marks[i].y += ycenter; marks[i].x += xcenter; marks2[i].x += xcenter; marks2[i].y += ycenter; } } /** * Applet starts running */ public void start() { show(); super.start(); } /** * Applet stops running */ public void stop() { super.stop(); } public void tick() { repaint(); } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if (showCopyright) { showCopyright(g, clockname, clocksmith); showCopyright = false; return; } // update the clock in offscreen memory image Graphics memg = memImage.getGraphics(); // clear old time memg.setPaintMode(); Rectangle r = g.getClipRect(); memg.clipRect(r.x, r.y, r.width, r.height); memg.setColor(clrClockBackground); memg.fillRect(r.x, r.y, r.width, r.height); if (showFrame) drawFrame(memg); if (showRoundFrame) { drawRoundFrame(memg); } if (showMark != SHOW_MARK_NONE) { drawMarks(memg); } int s = getDisplaySeconds(); int m = getDisplayMinutes(); int h = getDisplayHours(); //System.err.println("h="+h + " m="+m + " s=" + s); // hour positions: 1-12 only if (h >= 12) h -= 12; drawHand(memg, h*5 + m/12, hlastp, clrHandHour, radius/2, lenHourTail, widthHour); drawHand(memg, m, mlastp, clrHandMinute, radius*3/4, lenMinuteTail, widthMinute); drawHand(memg, s, slastp, clrHandSecond, radius, lenSecondTail, widthSecond); memg.setColor(clrCenterDot); int circlesize = radiusCenterDot + radiusCenterDot; // * 2; memg.fillOval(xcenter-radiusCenterDot, ycenter-radiusCenterDot, circlesize, circlesize); // copy the offscreen image to the real screen g.drawImage(memImage, 0, 0, null); } public void drawHand(Graphics g, int t, Point lastp[], Color c, int radius, int tail, int width) { // new coordinate for the hand's tip Point tip = new Point(0,0); tip.x = (int)(xtrigtable[t] * radius + xcenter); tip.y = (int)(ytrigtable[t] * radius + ycenter); // new coordinate for the second's rear end and side points p0.x = xcenter + (int)(xtrigtable[(t+30)%60] * tail); p0.y = ycenter + (int)(ytrigtable[(t+30)%60] * tail); p1.x = xcenter + (int)(xtrigtable[(t+15)%60] * width); p1.y = ycenter + (int)(ytrigtable[(t+15)%60] * width); p2.x = xcenter + (int)(xtrigtable[(t+45)%60] * width); p2.y = ycenter + (int)(ytrigtable[(t+45)%60] * width); g.setColor(c); xpoints[0] = p0.x; ypoints[0] = p0.y; xpoints[1] = p1.x; ypoints[1] = p1.y; xpoints[2] = tip.x; ypoints[2] = tip.y; xpoints[3] = p2.x; ypoints[3] = p2.y; g.fillPolygon(xpoints, ypoints, 4); } public void drawFrame(Graphics g) { rectFrame.paint(g); } public void drawRoundFrame(Graphics g) { g.setColor(clrRoundFrame); for (int i = 0; i < roundFrameWidth; i++) g.drawArc(rectClock.x+i, rectClock.y+i, rectClock.width-i*2, rectClock.height-i*2, 0, 360); g.setColor(clrClockFace); g.fillArc(rectClock.x+roundFrameWidth, rectClock.y+roundFrameWidth, rectClock.width-roundFrameWidth*2, rectClock.height-roundFrameWidth*2, 0, 360); } public void drawMarks(Graphics g) { int x0, y0; int x1, y1; double d; g.setColor(clrMark); if (showMark == SHOW_MARK_NONE) return; if (showMark == SHOW_MARK_ALL) { for (int i = 0; i < 60; i++) { g.drawLine(marks[i].x, marks[i].y, marks2[i].x, marks2[i].y); } } else { // show large marks only for (int i = 0; i < 60; i += 5) { g.drawLine(marks[i].x, marks[i].y, marks2[i].x, marks2[i].y); } } } /** * Gets program pamameters */ protected void getProgramParams() { // get program parameters String arg; arg = getParameter("Author"); if (arg != null && arg.equalsIgnoreCase("hjtsai@cargobay.com")) showCopyright = false; else showCopyright = true; // TimeZone desired in minutes arg = getParameter("TimeZone"); if (arg == null) showZoneTime = false; else { try { tztarget = Integer.parseInt(arg); // System.err.println("param timezone=" + tztarget); showZoneTime = true; } catch (NumberFormatException e) { showZoneTime = false; } } // show second arg = getParameter("CounterClockwise"); if (arg != null) counterClockwise = arg.equalsIgnoreCase("yes") || arg.equalsIgnoreCase("true"); arg = getParameter("ShowMark"); if (arg.equalsIgnoreCase("none")) showMark = SHOW_MARK_NONE; else if (arg.equalsIgnoreCase("large")) showMark = SHOW_MARK_LARGE; else showMark = SHOW_MARK_ALL; // Round Frame Color arg = getParameter("MarkColor"); if (arg != null) clrMark = ColorValue.s2color(arg, Color.black); arg = getParameter("ShowRoundFrame"); if (arg != null) showRoundFrame = arg.equalsIgnoreCase("yes") || arg.equalsIgnoreCase("true"); roundFrameWidth = getNumParam("roundFrameWidth", 4); if (roundFrameWidth < 1) roundFrameWidth = 1; // Round Frame Color arg = getParameter("RoundFrameColor"); if (arg != null) clrRoundFrame = ColorValue.s2color(arg, Color.black); // clock face color arg = getParameter("ClockFaceColor"); if (arg != null) clrClockFace = ColorValue.s2color(arg, Color.yellow); // BackgroundColor arg = getParameter("BackgroundColor"); if (arg != null) clrClockBackground = ColorValue.s2color(arg, Color.lightGray); // Hand Colors arg = getParameter("HourHandColor"); if (arg != null) clrHandHour = ColorValue.s2color(arg, Color.blue); arg = getParameter("MinuteHandColor"); if (arg != null) clrHandMinute = ColorValue.s2color(arg, Color.blue); arg = getParameter("SecondHandColor"); if (arg != null) clrHandSecond = ColorValue.s2color(arg, Color.red); // hand sizes lenHourTail = getNumParam("LengthHourTail", 8); if (lenHourTail < 1) lenHourTail = 8; lenMinuteTail = getNumParam("LengthMinuteTail", 12); if (lenMinuteTail < 1) lenMinuteTail = 12; lenSecondTail = getNumParam("LengthSecondTail", 16); if (lenSecondTail < 1) lenSecondTail = 16; // hand fatness widthHour = getNumParam("HourWaistSize", 5); if (widthHour < 1) widthHour = 5; widthMinute = getNumParam("MinuteWaistSize", 4); if (widthMinute < 1) widthMinute = 4; widthSecond = getNumParam("SecondWaistSize", 3); if (widthSecond < 1) widthSecond = 3; // center circle color arg = getParameter("CenterDotColor"); if (arg != null) clrCenterDot = ColorValue.s2color(arg, Color.black); // center dot size radiusCenterDot = getNumParam("CenterDotRadius", 2); if (radiusCenterDot < 0) radiusCenterDot = 2; // show clock frame arg = getParameter("ShowFrame"); if (arg != null) showFrame = arg.equalsIgnoreCase("yes") || arg.equalsIgnoreCase("true"); // clock frame color arg = getParameter("FrameColor"); if (arg != null) clrFrame = ColorValue.s2color(arg, Color.black); // get clock frame thickness frameThickness = getNumParam("FrameThickness", 4); if (frameThickness < 1) frameThickness = 1; } /** * get the specified numeric programeter param, negative number now allowed */ public int getNumParam(String param, int defaultvalue) { String arg = getParameter(param); int i = 0; if (arg == null) i = defaultvalue; else { try { i = Integer.parseInt(arg); } catch (NumberFormatException e) { i = defaultvalue; } } if (i < 0) i = 0; return i; } /** * Displays a copyright statement */ public void showCopyright(Graphics g, String clockname, String author) { Rectangle r = bounds(); // clear background Color c = Color.lightGray; g.setColor(c); g.fillRect(r.x, r.y, r.width, r.height); // draw a frame RectFrame rectFrame = new RectFrame(r, 2, Rect.RS_3DRAISED, 3, Rect.RS_NORMAL, Color.lightGray, 2, Rect.RS_ETCHEDIN); rectFrame.paint(g); // create the marquee for displaying copyright Rectangle newr = new Rectangle(r.x+7,r.y+7, r.width-14, r.height-14); g.clipRect(newr.x, newr.y, newr.width, newr.height); // the amount=1 and delay=5 generate fast and smooth scrolling // unfortuntely, Netscape Navigator V3.0 Java VM seems unable // to handle short delay (delay=4 is too fast) Marquee marquee = new Marquee( newr, 1, // loop 5,// 1, // amount 100, // 5, // delay Marquee.LEFT, // direction Marquee.SCROLL, // behavior clockname+" "+author, Color.lightGray, // background Color.darkGray, // foreground this); Thread mthread = new Thread(marquee); mthread.start(); // wait until the marquee thread ends while (mthread.isAlive()) { try { Thread.sleep(100); } catch (InterruptedException e) { } } mthread.stop(); mthread = null; } }