import java.awt.*; import utilities.*; import utilities.gfx.*; public class Lissajous extends Panel implements Runnable { protected int animationSpeed; protected int animationFrame; protected Thread animationThread; protected SineParameters xSineParams, ySineParams; protected Canvas animationCanvas; protected Image lissajousImage; protected Graphics gg; protected final static int NUM_ANGLES = 128; // power of two !! protected final static int LISSA_WIDTH = 300; protected final static int LISSA_HEIGHT = 300; protected final static int LISSA_OX = LISSA_WIDTH/2; protected final static int LISSA_OY = LISSA_HEIGHT/2; protected Color[] colors; protected Color backgroundColor; //------------------------------------------------------------------- // Lissajous Constructor //------------------------------------------------------------------- public Lissajous() { ySineParams = new SineParameters("Y Sine", 75.0, 2.0, 90.0); xSineParams = new SineParameters("X Sine", 50.0, 1.0, 0.0); animationCanvas = new Canvas(); animationCanvas.setSize(LISSA_WIDTH, LISSA_HEIGHT); setLayout(new BorderLayout()); add("Center", animationCanvas); add("North", ySineParams); add("South", xSineParams); initColors(); animationFrame = 0; animationSpeed = 10; start(); // start color animation effect } //------------------------------------------------------------------- // set()/get() for 'AnimationSpeed' property //------------------------------------------------------------------- public void setAnimationSpeed(int value) { this.animationSpeed = value; } public int getAnimationSpeed() { return animationSpeed; } //------------------------------------------------------------------- // set()/get() for 'Xsine' property //------------------------------------------------------------------- public void setXsine(SineParameters value) { xSineParams.setSineParameters(value); } public SineParameters getXsine() { return xSineParams; } //------------------------------------------------------------------- // set()/get() for 'Ysine' property //------------------------------------------------------------------- public void setYsine(SineParameters value) { ySineParams.setSineParameters(value); } public SineParameters getYsine() { return ySineParams; } //------------------------------------------------------------------- // Animation thread body //------------------------------------------------------------------- public void run() { while ( animationThread != null ) { renderLissajous(); MiscKit.delay(250 - animationSpeed*10); animationFrame++; } } //------------------------------------------------------------------- // Animation thread control methods //------------------------------------------------------------------- public void start() { if ( animationThread == null ) { animationThread = new Thread(this); animationThread.start(); } } public void stop() { animationThread = null; } //------------------------------------------------------------------- // Lissajous renderer // A lissajous figure consists of two sine waves combining at right angles. // One sine wave is determined completely by the formula: s.sin(fa+p) // Where s=amplitude, f=frequency, a=angle, p= phase shift //------------------------------------------------------------------- protected void renderLissajous() { double freq1, freq2; // frequencies double phas1, phas2; // phases double ampl1, ampl2; // amplitudes int x,y, lastX, lastY; if ( lissajousImage == null ) { lissajousImage = createImage(LISSA_WIDTH,LISSA_HEIGHT); if ( lissajousImage == null ) { return; } gg = lissajousImage.getGraphics(); } GfxKit.wipeImage(lissajousImage, backgroundColor, this); // sample sine wave parameters from the two SineParameters panels ampl1 = xSineParams.getAmplitude(); freq1 = xSineParams.getFrequency(); phas1 = xSineParams.getPhase(); ampl2 = ySineParams.getAmplitude(); freq2 = ySineParams.getFrequency(); phas2 = ySineParams.getPhase(); phas1 = toRadians(phas1); phas2 = toRadians(phas2); // now we are ready to render the Lissajous curve int colorIndex = (animationFrame*7) & (NUM_ANGLES-1); lastX = (int) (LISSA_OX + ampl1*Math.sin(phas1)); lastY = (int) (LISSA_OY + ampl2*Math.sin(phas2)); for (double alpha=0; alpha < 2*Math.PI ; alpha += Math.PI/NUM_ANGLES) { x = (int) (LISSA_OX + ampl1*Math.sin(freq1*alpha + phas1)); y = (int) (LISSA_OY + ampl2*Math.sin(freq2*alpha + phas2)); gg.setColor(colors[colorIndex]); gg.drawLine(lastX,lastY, x,y); colorIndex ++; colorIndex &= NUM_ANGLES-1; lastX = x; lastY = y; } // blast the off-screen generated Image onto the screen animationCanvas.getGraphics().drawImage(lissajousImage, 0,0, this); } //------------------------------------------------------------------- // Initialize set of colors. This avoids producing Color objects on-the-fly //------------------------------------------------------------------- protected void initColors() { float r,g,b; float step = 1.0F/NUM_ANGLES; colors = new Color[NUM_ANGLES]; r = b = 1.0F; g = 0.0F; for (int i=0; i < NUM_ANGLES/2; i++) { colors[i] = new Color(r,g,b); r -= step*2; g += step*2; b -= step*2; } for (int i=NUM_ANGLES/2; i < NUM_ANGLES; i++) { colors[i] = new Color(r,g,b); r += step*2; g -= step*2; b += step*2; } backgroundColor = new Color(0.3F, 0.3F, 0.3F); } //------------------------------------------------------------------- // convert degrees to radians //------------------------------------------------------------------- public final static double toRadians(double degreesAngle) { return degreesAngle*Math.PI/180.0; } //------------------------------------------------------------------- // Self-test code. //------------------------------------------------------------------- public static void main (String[] args) { Frame window = new Frame("Lissajous Test"); Lissajous lisa = new Lissajous(); window.add("Center", lisa); window.pack(); window.setVisible(true); } } // End of Class Lissajous