Bouncing Circle Scroller

This was my entry to the DBF scroll competition. Didn't win - but it was fun to code since this was my first real JavaApplet I have written.

In action

Technique

The effect itself is written as a standard java applet. The used soundsystem called Ana-Mp is by Torkjel Hongve. Furthermore the music you hear was composed by stargazer/cplx.

Sourcecode

package anakata.modplay.example.applet;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import anakata.modplay.Meta;
import anakata.modplay.ThreadedPlayer;
import anakata.modplay.loader.ModuleLoader;
import anakata.modplay.module.Module;
import anakata.sound.output.JavaSoundOutput;
import anakata.sound.output.SoundDataFormat;


public class CircleScrollAna extends Applet {	
	
   // Module Stuff
    public static final String INFO = Meta.PROJECT_NAME + " " + Meta.VERSION;

    public static final int BITS = 16;
    public static final int RATE = 44100;
    public static final int CHANNELS = 2;
    public static final boolean INTERPOLATE = true;
    public static final int BUFFERSIZE = 500;

    private Module module;
    private URL theUrl;
    private ThreadedPlayer tp;

    private String protocol;
    private String host;
    private int port;
    private List fileList = new ArrayList();
    private int nextModule = 0;	
	
    private int lengthOfModule = 0;
    
	// Applet Stuff
	public int appHeight;
	public int appWidth;
	public Font appFont;

	
	// Circlescroll Stuff
	public CircleLetter[] letters	= new CircleLetter[18];
	public float fallFactor			= 0.5f;
	public float circleCenter_X;
	public float circleCenter_Y;
	public int border_Y;
	public int circleRadius	  		= 100;
	public int circleTextCount		= 0;
	public String circleText		= "... put scrolltext here ...";

    Image	buffer;
    Graphics2D 	gBuffer;
    
    // CircleLetter InnerClass    
    public class CircleLetter {
    private String	letter = "";
    private double	radius;
    private int		x,y;
    private boolean directionDown = true; 
    private boolean directionLeft = true;
    	
    public CircleLetter( double radius ) {
    		this.radius = radius;
    }
    	
    private void rotate( Graphics2D gBuffer ) {

    		// Rotate and change chars
    		radius = radius - 2 % 360;
    		
    		if ( (int)radius == 450 ) {
    			letter = "" + circleText.charAt( circleTextCount );
    			circleTextCount++;
    			if ( circleTextCount == circleText.length() ) circleTextCount = 0; 
    			radius = 810;
    		}
    		
    		// bounce up/down
    		if ( directionDown ){
    			fallFactor = fallFactor + 0.001f;
    			circleCenter_Y = circleCenter_Y + fallFactor;
    			if ( circleCenter_Y > border_Y ) {
    				directionDown = false;
    			}
    		} else {
    			fallFactor = fallFactor - 0.012f;
    			if ( fallFactor < 0 ) {
    				directionDown = true;
    			}
    		}
    		// bounce left/right
    		if ( directionLeft ){
    			circleCenter_X = circleCenter_X + 0.1f;
    			if ( circleCenter_X > 280 ) {
    				directionLeft = false;
    			}    		    			
    		} else {
    			circleCenter_X = circleCenter_X - 0.1f;
    			if ( circleCenter_X < 20 ) {
    				directionLeft = true;
    			}
    		}
    		
    		// rotate
    		x = (int)(circleRadius * Math.cos( radius * ( 2 * Math.PI / 360)) + circleCenter_X);
    		y = (int)(circleRadius * Math.sin( radius * ( 2 * Math.PI / 360)) + circleCenter_Y);
    		
    		if ( y > appHeight ) y = appHeight;    		
    		if ( x > 288 ) x = 288;
    		if ( x < 0 ) x = 0;
    		
    		gBuffer.drawString( letter, x, y);    		
    	}    	
    }
    

   // Applet methods
    
    public void init() {
		
		// App Stuff
		appHeight	   = this.getHeight();
		appWidth	   = this.getWidth();
		appFont		   = new Font( "Courier New", Font.BOLD, 20 );		
		
		// CircleScroll Stuff
		circleCenter_X = this.getWidth() / 2;
		circleCenter_Y = this.getHeight() / 2;
		border_Y	   = (int)(this.getHeight() * 0.95);
		
		circleText = circleText.toUpperCase();
		
		int start = 452;
		for ( int i = 0; i < letters.length; i++ ) {
			letters[i] = new CircleLetter( start );
			start = start + 20;
		}

	// Music Stuff
        protocol = getParameter("protocol");
        host = getParameter("host");
        port = Integer.parseInt(getParameter("port"));
        StringTokenizer st = new StringTokenizer(getParameter("files"), ",");
        while (st.hasMoreTokens())
            fileList.add(st.nextToken());

        module = loadModule(nextModule++);
        // start the thread
        tp = createPlayer(module);
        lengthOfModule = tp.getModule().getNumberOfPositions();
        tp.start();        
        		
	}

    public void start() {
        tp.pause(false);
    }

    public void stop() {
        tp.pause(true);
    }

    public void destroy() {
        tp.stop();
    }	
	
    /**
     * create a player thread for playing the
     * @param module
     * @return
     */
    private ThreadedPlayer createPlayer(Module module) {
        ThreadedPlayer player = new ThreadedPlayer();
        try {
            player.init(
                new JavaSoundOutput(new SoundDataFormat(BITS, RATE, CHANNELS), BUFFERSIZE),
                INTERPOLATE);
            player.load(module);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        player.pause(true);
        return player;
    }

    /**
     * load the nextModule'th file in the file list.
     * @param nextModule
     * @return
     */
    private Module loadModule(int nextModule) {
        int mc = fileList.size();
        
        while (nextModule <= 0) nextModule += mc;
        try {
            theUrl = new URL(protocol, host, port, (String)fileList.get((nextModule + mc) % mc));
            return ModuleLoader.getModuleLoader(theUrl).getModule();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
	
	
	public void paint(Graphics g) {
		
        // Double-Buffering
        if (buffer==null) {
            buffer=createImage(this.getSize().width, this.getSize().height);
            gBuffer=(Graphics2D)buffer.getGraphics();
        }
        gBuffer.setBackground( new Color( 120, 120, 250 ));
        gBuffer.setColor( Color.WHITE );
        gBuffer.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, 
        							RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        gBuffer.clearRect( 0,0,this.getSize().width, this.getSize().height);

        // Antialiasing
        gBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);		
        
        gBuffer.setFont( appFont );
        
        for ( int i=0; i < letters.length; i++ ) {
        	letters[i].rotate( gBuffer );
        }
        g.drawImage (buffer, 0, 0, this);        
        
        // Delay
        try {Thread.sleep(10);}
        catch (InterruptedException e) {}                       

        // Loop Module
        if ( lengthOfModule == tp.getPosition() ) {
        	tp.setPosition( 0 );
        	tp.start();
        }
        
        repaint();
	}
	
    public void update(Graphics g) {
    	paint(g);
    }

}