// -*-Java-*- import java.applet.Applet; import java.awt.Graphics; import java.awt.Font; import java.awt.Color; import java.awt.Shape; import com.koniaris.astronomy.Moon; import com.koniaris.astronomy.JulianDay; // Look at the source of the web page "http://www.koniaris.com/lunar" to see how this applet is called public class ShowMoon extends Applet implements Runnable { // ---------------------------------------------------------------------- // local variables private int ip; // the illuminated percentage private boolean waning; // is the moon getting darker? private Thread t; private boolean exit = false; private boolean pause = false; private Font myFont; private Color black; // the background private Color yellow; // the foreground private Color white; // the percentage private int sleep; // how many ms we sleep // ---------------------------------------------------------------------- private int parameterAsInt(String p) { return Integer.parseInt(getParameter(p)); } // ---------------------------------------------------------------------- private Color getColor(String rgb) { return new Color(Integer.parseInt(getParameter(rgb), 16)); } // ---------------------------------------------------------------------- public void init() { try { update_moon(); // set the sleep time sleep = parameterAsInt("sleep"); if (sleep < 1) sleep = 1; sleep *= 1000; // to put it into units of ms, what is required // launch the thread, and it will block t = new Thread(this); pause = true; t.start(); // the thread will block until a notify() // get the font myFont = new Font(getParameter("font"), Font.BOLD, parameterAsInt("fontSize")); // get the colors black = getColor("black"); yellow = getColor("yellow"); white = getColor("white"); } catch(Exception e) { // oops, something went wrong.... ip = -1; } } // ---------------------------------------------------------------------- public void start() { pause = false; synchronized(this) { this.notify(); // let the thread resume } } // ---------------------------------------------------------------------- public void stop() { pause = true; } // ---------------------------------------------------------------------- public void destroy() { exit = true; pause = false; synchronized(this) { this.notify(); // wake it up so it can decide to return in case it is sleeping } t = null; // cut the pointer to the thread loose for easier GC } // ---------------------------------------------------------------------- private void update_moon() throws Exception { // this sets the illuminated percentage of the moon JulianDay j = new JulianDay(); Moon m = new Moon(j); ip = m.illuminatedPercentage(); waning = m.isWaning(); } // ---------------------------------------------------------------------- // ----|---- waxing 0% // ----|---* // ----|--** black circle, clip R, yellow circle, black oval // ----|-*** // ----|**** 50% // ---*|**** // --**|**** yellow circle, clip L, black circle, yellow oval // -***|**** // ****|**** waning 100% // ****|***- // ****|**-- yellow circle, clip R, black circle, yellow oval // ****|*--- // ****|---- 50% // ***-|---- // **--|---- black circle, clip L, yellow circle, black oval // *---|---- public void paint(Graphics g) { // I was too lazy to double-buffer here, as it's unnecessary // It's an interesting question as to if I really have to grab // a lock here.... // And do I keep on getting the same g? It would be extremely // convenient to know that, as I could transform the // coordinate system, only install my font one time, etc.! synchronized(g) { // ??? // get the sizes int w = getSize().width; int h = getSize().height; // set the center to something reasonable int x = w/2; int y = h/2; // get the radius of the circle int radius = (int) (0.40 * ((w < h) ? w : h)); int diameter = 2 * radius; // determine the majority and minority colors Color majority, minority; boolean clipRight; if (ip > 50) { majority = yellow; minority = black; clipRight = waning; } else { majority = black; minority = yellow; clipRight = ! waning; } // draw the big background g.setColor(majority); g.fillOval(x - radius, y - radius, // the starting point diameter, diameter); // the sides of the bounding box // save the clipping path Shape clip = g.getClip(); // clip.... if (clipRight) { g.clipRect(x, y - radius, radius, diameter); } else { // Sun did the wrong thing with a negative width, IMHO g.clipRect(x - radius, y - radius, radius, diameter); } // draw the semi-circle g.setColor(minority); g.fillOval(x - radius, y - radius, // the starting point diameter, diameter); // the sides of the bounding box // draw the majority g.setColor(majority); int xxx = (int) Math.abs((ip - 50) * radius / 50.0); // the width of the oval (0 when 50%/50%) g.fillOval(x - xxx, y - radius, 2 * xxx, diameter); // restore the clipping path g.setClip(clip); // install my font g.setFont(myFont); // draw the percentage in the middle g.setColor(white); g.drawString(ip + "%" + (waning ? "-" : "+"), x, y); } } // ---------------------------------------------------------------------- public void run() { try { try { while (! exit) { while (pause) synchronized(this) { this.wait(); // wait until the start() method wakes us up.... } // has the moon changed? int old_ip = ip; // get the old illuminated portion update_moon(); // update the phase of the moon if (old_ip != ip) { // COOL, IT CHANGED! :) repaint(); } synchronized(this) { this.wait(sleep); } } } catch(InterruptedException e) { // we'll just fall thru.... } } catch(Exception e) { // alas, a bug on my part? } } // ---------------------------------------------------------------------- }