/* * @(#)SpiroAnim.java 1.0 97/11/04 Jos van den Oever * * based on: Spiro.java by Anu Garg * * Copyright (c) 1997 Jos van den Oever * */ import java.awt.AWTEvent; import java.awt.Canvas; import java.awt.Color; import java.awt.Container; import java.awt.Graphics; import java.awt.Image; import java.awt.Window; import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; /** * SpiroAnim - a class that produces an animated spirograph. * * SpiroAnim is an ideal component for entertaining users waiting for * downloading applications. You can add it to any Container and an animation of a * spirograph starts. It's thread has minimum priority. You can configure the * component to stop and start or to reset when clicked. *
* If you like this class, feel free to use it. I'd like to hear how you like * it, so don't hesitate to inform me if you use it. *
* @author Jos van den Oever * @version 1.00 */ public class SpiroAnim extends Canvas implements Runnable, WindowListener { private int I = 100; // Number of Iterations private float Rvar = 0.03f, rvar = 0.03f, ovar = 0.03f, // variation in radii R, r, O, // radii per frame Rnew, rnew, Onew, // new radii Rold, rold, Oold; // old radii private int cvar=200, // variation in color svar = 10, // variation in #frames redBits, blueBits, greenBits, // current color rednew, greennew, bluenew, // new colors redold, blueold, greenold, // old colors step, // current frame steps; // # of frames private Thread t; // the animation thread private boolean go, // if paint has drawn new frame, go == true mouseUpStopsAnimation = true, // this boolean determines wheter mouseUp stops // the animation or reset it. stopped = false; private Image osi; // yes, the double buffering image again /** * Constructs a SpiroAnim with 100 lines per frame. */ public SpiroAnim() { this(100); } /** * Constructs a SpiroAnim with 100 lines per frame. * @param mouseUpStopsAnimation if true the animation stops when clicked else resets when clicked */ public SpiroAnim(boolean mouseUpStopsAnimation) { this(100, mouseUpStopsAnimation); } /** * Constructs a SpiroAnim with the specified number of lines per frame. * @param I the number of lines per frame */ public SpiroAnim(int I) { this(I, true); } /** * Constructs a SpiroAnim with the specified number of lines per frame. * @param I the number of lines per frame * @param mouseUpStopsAnimation if true the animation stops when clicked else resets when clicked */ public SpiroAnim(int I, boolean mouseUpStopsAnimation) { super(); this.I = (I > 0) ? I : this.I; this.mouseUpStopsAnimation = mouseUpStopsAnimation; reset(); enableEvents(AWTEvent.COMPONENT_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); } /** * Starts the animation. */ public void start() { if (t == null && !stopped) { t = new Thread(this); t.setPriority(Thread.MIN_PRIORITY); t.start(); } } /** * Stops the animation. */ public void stop() { if (t != null) { t.stop(); t = null; } } /** * Called when the animation is started. Shouldn't be called by user. */ public void run() { while (true) { try { t.sleep(40); // 25 frames per second at maximum speed } catch (InterruptedException e) { System.out.println(e); } if (go && isShowing()) { nextFrame(); repaint(); } } } private void nextFrame() { if (step == 0) { Rold = Rnew; rold = rnew; Oold = Onew; redold = rednew; blueold = bluenew; greenold = greennew; Rnew = (float)Math.max(0.1, Math.min(1, Rold+((Math.random()-0.5)*Rvar))); rnew = (float)Math.max(0.1, Math.min(1, rold+((Math.random()-0.5)*rvar))); Onew = (float)Math.max(0, Math.min(1, Oold+((Math.random()-0.5)*rvar))); rednew = Math.max(0, Math.min(255, redold+(int)((Math.random()-0.5)*cvar))); bluenew = Math.max(0, Math.min(255, blueold+(int)((Math.random()-0.5)*cvar))); greennew = Math.max(0, Math.min(255, greenold+(int)((Math.random()-0.5)*cvar))); steps = Math.max(50, Math.min(100, steps+(int)((Math.random()-0.5)*svar))); } R = Rold + (Rnew-Rold)*step/steps; r = rold + (rnew-rold)*step/steps; O = Oold + (Onew-Oold)*step/steps; redBits = redold + (rednew-redold)*step/steps; blueBits = blueold + (bluenew-blueold)*step/steps; greenBits = greenold + (greennew-greenold)*step/steps; go = false; step++; if (step == steps) { step = 0; } } /** * Update method, calls paint(). * @param g the graphics object on which is to be drawn */ public void update(Graphics g) { paint(g); } /** * The paint method draws the spirograph using double buffering. * @param g the graphics object on which is to be drawn */ public void paint(Graphics g) { if(osi == null) { osi = createImage(getSize().width, getSize().height); } Graphics osg = osi.getGraphics(); osg.setColor(getBackground()); osg.fillRect(0,0,getSize().width, getSize().height); osg.setColor(g.getColor()); osg.setClip(0, 0, getSize().width, getSize().height); int x = 0, xpos = getSize().width/2; int y = 0, ypos = getSize().height/2; int prevx = 0; int prevy = 0; int t; float scalex = getSize().width / (2*(R + O + 2 * r)); float scaley = getSize().height/ (2*(R + O + 2 * r)); osg.setColor(new Color(redBits, greenBits, blueBits)); for(t=0; t<=I; t++){ prevx = x; prevy = y; x = (int)(scalex*((R+r)*Math.cos((double)t) - (r+O)*Math.cos(((R+r)/r)*t))); y = (int)(scaley*((R+r)*Math.sin((double)t) - (r+O)*Math.sin(((R+r)/r)*t))); if (t > 0){ osg.drawLine(prevx+xpos, prevy+ypos, x+xpos, y+ypos); } } go = true; g.drawImage(osi, 0, 0, null); osg.dispose(); } /** * Resets the animation. */ public void reset() { Rnew = (float)Math.random(); rnew = (float)Math.random(); Onew = (float)Math.random(); rednew = (int)(Math.random()*255); greennew = (int)(Math.random()*255); bluenew = (int)(Math.random()*255); nextFrame(); repaint(); step = 0; } /** * Invalidate the component. Shouldn't be called by user. */ public void invalidate() { super.invalidate(); osi = null; stop(); } /** * Validate the component. Is called by the system before the component is painted, * if it has been invalidated. Shouldn't be called by user. */ public void validate() { super.validate(); start(); } /** * Notifies the component it has been added to a Container. Shouldn't be called by user. */ public void addNotify() { super.addNotify(); try { // in Hotjava a securityerror can occur. Container p = getParent(); while (p != null && !(p instanceof Window)) { p = p.getParent(); } if (p instanceof Window) { ((Window)p).addWindowListener(this); } start(); } catch(Exception e) { System.out.println(e); } } /** * Notifies the component is has been removed from a component. Shouldn't be called by * user. */ public void removeNotify() { super.removeNotify(); try { // in Hotjava a securityerror can occur. Container p = getParent(); while (p != null && !(p instanceof Window)) { p = p.getParent(); } if (p instanceof Window) { ((Window)p).removeWindowListener(this); } } catch(Exception e) { System.out.println(e); } stop(); } public void setBounds(int x, int y, int w, int h) { super.setBounds(x,y,w,h); validate(); } /** * Processes ComponentEvents concerning this component. Deals with showing and hiding * this component. The SpiroAnim doesn't animate when hidden to save cpu time. * @param e The ComponentEvent to be handled. */ protected void processComponentEvent(ComponentEvent e) { if (e.getID() == ComponentEvent.COMPONENT_HIDDEN) { stop(); } else if (e.getID() == ComponentEvent.COMPONENT_SHOWN) { start(); } } /** * Processes MouseEvents concerning this component. Deals with mouse clicking on this * component. The animation starts/stops or resets on mouse click according to * configuration. * @param e The MouseEvent to be handled. */ protected void processMouseEvent(MouseEvent e) { if (e.getID() == MouseEvent.MOUSE_CLICKED) { if (mouseUpStopsAnimation) { if (stopped) { stopped = false; start(); } else { stopped = true; stop(); } } else { reset(); } } } /** * Unused WindowListener interface function. * @param e The WindowEvent to be handled. */ public void windowOpened(WindowEvent e) { } /** * Unused WindowListener interface function. * @param e The WindowEvent to be handled. */ public void windowClosing(WindowEvent e) { } /** * Unused WindowListener interface function. * @param e The WindowEvent to be handled. */ public void windowClosed(WindowEvent e) { } /** * Function handles iconification of parent window to stop animation. * This save cpu time. * @param e The WindowEvent to be handled. */ public void windowIconified(WindowEvent e) { stop(); } /** * Function handles deiconification of parent window to start animation. * @param e The WindowEvent to be handled. */ public void windowDeiconified(WindowEvent e) { start(); } /** * Unused WindowListener interface function. * @param e The WindowEvent to be handled. */ public void windowActivated(WindowEvent e) { } /** * Unused WindowListener interface function. * @param e The WindowEvent to be handled. */ public void windowDeactivated(WindowEvent e) { } }