Grass Veins

This was my entry to the Horticulture Challenge by DBF. So the task was to make a plant life themed effect. After googling the web a bit I found several vein effects mostly written in actionscript/flash.

This effects reminded me on growing grass so I thought this might fit into the compo's theme. All code was coded 100% from scratch since I did not have access to the .fla files.

There are still some bugs in it - but I ran out of time.
You can press in the applet window to reset the grass root.

In action

Technique & Credits

Code benny!weltenkonstrukteur.de
Music unknown (does anyone know the author?)
Soundsystem JavaMod Daniel "quippy" Becker

Sourcecode

GrassVein.java

import java.applet.Applet;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

public class GrassVeins extends Applet {	
	//
	// Generic
	//	
	private Graphics2D		g2;
	private Graphics2D		g2Back;	
	private BufferedImage	biBack;
	private int appWidth, appHeight;
	
	//
	// Specific
	//
	public static final int IMG_X = 800;
	public static final int IMG_Y = 800;
	
	private Branch branch;
	private Twig twig[] = new Twig[8];
	private Color colBranch;
	private Color colTwig; 
	private Stroke strokeBranch;
	private Stroke strokeTwig;	
	private EasyModPlay modPlayer;	
	//private String modFile = "http://labs.weltenkonstrukteur.de/jar/data/ldrunner.mod";
	private String modFile = "data/ldrunner.mod";
	private URL modFileURL;
		
	public void init() {
		//
		// Init
		//
		appWidth 	= this.getWidth();
		appHeight	= this.getHeight();

		reinit();
		
		try {
			modFileURL = new URL( getCodeBase(), modFile );
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		modPlayer	= new EasyModPlay( modFileURL );
		modPlayer.doStartPlaying();
	}

	private void reinit() {
		
		//
		// create backbuffer
		//
	    if ( biBack==null ) {
	        //bImg	= (BufferedImage)createImage( appWidth, appHeight );
	    	biBack	= new BufferedImage(IMG_X, 
	    								IMG_Y, 
	    								BufferedImage.TYPE_INT_RGB);
/*
	    	try {
	    		biBack = ImageIO.read(new File("wall.jpg"));
	    	} catch (IOException e) {
	    	}
*/
	        try {
	            URL url = new URL(getCodeBase(), "data/wall.jpg");
	            biBack = ImageIO.read(url);
	        } catch (IOException e) {
	        }	    	
	        g2Back	= (Graphics2D)biBack.getGraphics();
	        g2Back.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
	                				RenderingHints.VALUE_ANTIALIAS_ON);
	        strokeBranch	= new BasicStroke(2.0f);
	        strokeTwig		= new BasicStroke(1.0f);
	    }
	    
	    //
	    // Specific
	    //
	    branch = new Branch (IMG_X/2, IMG_Y/2);
		for (int i = 0; i < twig.length; i++) {
			twig[i] = new Twig();
		}
		colBranch	= new Color( 0, 255, 0, 80 );
		colTwig 	= new Color( 0, 255, 0, 60 );		
		
	}
	
    public void update(Graphics g) {
		//
        // Delay ... don't consume too much cpu power
		//
        try {Thread.sleep(25);}
        catch (InterruptedException e) {}

        
        //
        // Specific
        //
        if ( branch.grow() == -1 ) reinit();
        
        //g2Img.setColor( Color.black );
        g2Back.setColor( colBranch );
        g2Back.setStroke( strokeBranch );
    	g2Back.drawLine((int)branch.x,
						(int)branch.y, 
						(int)branch.x + (int)branch.dx, 
						(int)branch.y + (int)branch.dy);
        
    	if ( WKMath.rand_minmax( 0, 30 ) < 5 ) {
    		for ( int i=0; i < twig.length; i++ ) {
    			if ( twig[i].isActive == false ) {
    				twig[i].init( branch.x, branch.y, branch.deg );
    				break;
    			}
    		}    		
    	}
    	g2Back.setColor( colTwig );
    	g2Back.setStroke( strokeTwig );
    	for ( int i=0; i < twig.length; i++ ) {
        	if ( twig[i].isActive ) {
        		twig[i].grow();        		
        		g2Back.drawLine((int)twig[i].x,
    							(int)twig[i].y, 
    							(int)twig[i].x + (int)twig[i].dx, 
    							(int)twig[i].y + (int)twig[i].dy);    		
        	}    		
    	}    	
    	
    	paint(g);    	
    }
	
	
	public void paint (Graphics g){
		int move_x = ((int)branch.x-(IMG_X/2)) / 2;
		if ( move_x > 200 ) move_x = 200;
		if ( move_x < -200 )move_x = -200;
		int move_y = ((int)branch.y-(IMG_Y/2)) / 2;
		if ( move_y > 200 ) move_y = 200;
		if (move_y < -200 ) move_y = -200;
		g2 = (Graphics2D)g;
		g2.drawImage(	biBack, 
				0, 
				0,
				appWidth,
				appHeight,
				200+move_x,		// 200 = (IMG_X/2) - (appWidth/2)
				200+move_y,		// 200 = (IMG_Y/2) - (appHeight/2)
				600+move_x,		// 600 = appWidth + ((IMG_X/2) - (appWidth/2))
				600+move_y,		// 600 = appHeight+ ((IMG_Y/2) - (appHeight/2))
				this );
		repaint();		
	}
    
    public boolean mouseDown(Event ev, int xDown, int yDown) {
    	
    	reinit();
    	
    	return true;
    }
	
	public void destroy() {
		modPlayer.doStopPlaying();
	}
}

Branch.java

/*
 * GrassVein
 * by benny!weltenkonstrukteur.de
 * (c) 2oo8
 *
 * Class: Branch.java
 *
 * This program is provided 'as is', without warranty of any kind, 
 * express or implied. The author shall not be liable for any damages 
 * caused by the use of or inability to use this program. This means 
 * that the user must assume the entire risk of using this program. 
 *
 * Use of this program commercially without prior consent of the 
 * author is prohibited. 
 * 
 * This program may be freely distributed, but may not be sold. 
 * 
 * Use of this program indicates you understand and agree to the 
 * conditions of this license agreement.
 */

public class Branch {
	
	public	int 	deg;
	public float	x, y, dx, dy;
	private static final float PI2 		= 6.28f;
	private static final float DEG_RAD 	= 360 / PI2;
	private static final int STEP 		= 5; 
	private boolean isOutOfBounce;
	
	public Branch( int x, int y) {
		this.x = x;
		this.y = y;
		this.dx = 0;
		this.dy = 0;
		this.deg = 90;
		this.isOutOfBounce = false;
	}
	
	public int grow() {
		this.x = this.x + this.dx;
		this.y = this.y + this.dy;		
		this.dx = (float)Math.cos( (this.deg/DEG_RAD ) ) * STEP;
		this.dy = (float)-Math.sin( (this.deg/DEG_RAD) ) * STEP;
		
		if ( ( (this.x < 50) || (this.y < 50 ) 
				|| (this.x > (GrassVeins.IMG_X-50) ) 
				|| (this.y > (GrassVeins.IMG_Y-50) ) ) 
				&& isOutOfBounce == false ) {
			isOutOfBounce = true;
			this.deg = this.deg + WKMath.rand_minmax(145, 160);
		} 

		if ( ( (this.x > 50) && (this.y > 50 ) 
				&& (this.x < (GrassVeins.IMG_X-50) ) 
				&& (this.y < (GrassVeins.IMG_Y-50) ) ) 
				&& isOutOfBounce == true ) {
			isOutOfBounce = false;
		}
			
		if ( isOutOfBounce == false ) {
			this.deg = this.deg + WKMath.rand_minmax( -25, +25 );
		}
		
		if ( (this.x < 0) || ( this.y < 0 ) 
				|| (this.x > GrassVeins.IMG_X)
				|| (this.y > GrassVeins.IMG_Y) ) {
			return -1;
		} else {
			return 0;
		}
	}

}

Twig.java

/*
 * GrassVein
 * by benny!weltenkonstrukteur.de
 * (c) 2oo8
 *
 * Class: Twig.java
 *
 * This program is provided 'as is', without warranty of any kind, 
 * express or implied. The author shall not be liable for any damages 
 * caused by the use of or inability to use this program. This means 
 * that the user must assume the entire risk of using this program. 
 *
 * Use of this program commercially without prior consent of the 
 * author is prohibited. 
 * 
 * This program may be freely distributed, but may not be sold. 
 * 
 * Use of this program indicates you understand and agree to the 
 * conditions of this license agreement.
 */

public class Twig {
	public	int 	deg;
	public float	x, y, dx, dy;
	public boolean	isActive;
	private static final float PI2 		= 6.28f;
	private static final float DEG_RAD 	= 360 / PI2;
	private static final int STEP 		= 3;
	private int lifetime;
	
	
	public Twig() {
		isActive = false;
	}
	
	public void init( float x, float y, int deg) {
		lifetime = WKMath.rand_minmax(0, 20) + 20;
		this.x = x;
		this.y = y;
		this.deg = deg;
		this.isActive = true;
	}
	
	public void grow() {
		this.x = this.x + this.dx;
		this.y = this.y + this.dy;		
		this.dx = (float)Math.cos( (this.deg/DEG_RAD ) ) * STEP;
		this.dy = (float)-Math.sin( (this.deg/DEG_RAD) ) * STEP;
		

		if ( (this.x < 50) || (this.y < 50 ) 
				|| (this.x > (GrassVeins.IMG_X-50) ) 
				|| (this.y > (GrassVeins.IMG_Y-50) ) ) {
			this.deg = this.deg + WKMath.rand_minmax( 5, 25 );
		} else {
			this.deg = this.deg + WKMath.rand_minmax( -25, +25 );
		}
		lifetime--;
		if ( lifetime == 0 )	isActive = false;

	}	
}

EasyModPlay.java

/*
 * GrassVein
 * by benny!weltenkonstrukteur.de
 * (c) 2oo8
 *
 * Class: EasyModPlay.java
 *
 * This program is provided 'as is', without warranty of any kind, 
 * express or implied. The author shall not be liable for any damages 
 * caused by the use of or inability to use this program. This means 
 * that the user must assume the entire risk of using this program. 
 *
 * Use of this program commercially without prior consent of the 
 * author is prohibited. 
 * 
 * This program may be freely distributed, but may not be sold. 
 * 
 * Use of this program indicates you understand and agree to the 
 * conditions of this license agreement.
 */


import java.awt.HeadlessException;
import java.net.URL;

import javax.sound.sampled.UnsupportedAudioFileException;

import de.quippy.javamod.loader.Module;
import de.quippy.javamod.loader.ModuleFactory;
import de.quippy.javamod.main.gui.PlayThread;
import de.quippy.javamod.main.gui.PlayThreadEventListener;
import de.quippy.javamod.mixer.Mixer;
import de.quippy.javamod.multimedia.mod.ModMixer;
import de.quippy.javamod.system.Helpers;
import de.quippy.javamod.system.Log;

public class EasyModPlay implements PlayThreadEventListener {
	
	private URL modFileName;
	private int sampleSizeInBits;
	private int channel;
	private int sampleRate;
	private int doISP;
	private boolean doWideStereoMix;
	private boolean doNoiseReduction;
	private boolean doMegaBass;
		
	private PlayThread playerThread = null;
/*
	static // THIS IS FROM JAVAMODMAINBASE FROM WICH THIS CLASS DOES NOT EXTEND
	{
		// Now load and initialize all classes, that should not be
		// initialized during play!
		try
		{
			Helpers.registerAllClasses();
		}
		catch (ClassNotFoundException ex)
		{
			Log.error("JavaModMainBase: a class moved?!", ex);
		}
	}
	*/
	public EasyModPlay( URL modFile ) throws HeadlessException
	{
		super();
		sampleSizeInBits=16;
		channel=2;
		sampleRate=44100;
		doISP=2;
		doWideStereoMix=false;
		doNoiseReduction=true;
		doMegaBass=false;
		modFileName = modFile;
		/*
		try {
			modFileName = new URL( modFile );
		} catch (Exception ex) {
			ex.printStackTrace();
		}*/
	}
	
	
	/**
	 * @param thread
	 * @see de.quippy.javamod.main.gui.PlayThreadEventListener
	 * #playThreadEventOccured(de.quippy.javamod.main.gui.PlayThread)
	 */
	public void playThreadEventOccured(PlayThread thread)
	{
		if (thread.isRunning())
		{
			//
		}
		else
		{
			//
		}
		
		Mixer mixer = thread.getCurrentMixer();
		if (mixer!=null)
		{
			/*
			if (mixer.isPaused())
				//
			else
				//
			*/
		}
	}
	/**
	 * start playback of a mod
	 * @since 01.07.2006
	 */
	public void doStartPlaying()
	{
		if (modFileName!=null)
		{
			doStopPlaying();
			Module mod = null;
			try
			{
				mod = ModuleFactory.getInstance(modFileName);
			}
			catch (UnsupportedAudioFileException ex)
			{
				/*NOOP*/
			}
			if (mod!=null)
			{
				ModMixer mixer = new ModMixer(mod, 
					sampleSizeInBits, 
					channel, 
					sampleRate, 
					doISP, 
					doWideStereoMix, 
					doNoiseReduction, 
					doMegaBass);
				playerThread = new PlayThread(mixer, null, null, this);
				playerThread.start();
			}
		}
	}
	/**
	 * stop playback of a mod
	 * @since 01.07.2006
	 */
	public void doStopPlaying()
	{
		if (playerThread!=null)
		{
			playerThread.stopMod();
			playerThread = null;
		}
	}
	/**
	 * pause the playing of a mod
	 * @since 01.07.2006
	 */
	public void doPausePlaying()
	{
		if (playerThread!=null)
		{
			playerThread.pausePlay();
		}
	}

}

WKMath.java

/*
 * GrassVein
 * by benny!weltenkonstrukteur.de
 * (c) 2oo8
 *
 * Class: WKMath.java
 *
 * This program is provided 'as is', without warranty of any kind, 
 * express or implied. The author shall not be liable for any damages 
 * caused by the use of or inability to use this program. This means 
 * that the user must assume the entire risk of using this program. 
 *
 * Use of this program commercially without prior consent of the 
 * author is prohibited. 
 * 
 * This program may be freely distributed, but may not be sold. 
 * 
 * Use of this program indicates you understand and agree to the 
 * conditions of this license agreement.
 */

public class WKMath {

	public static int rand_minmax( int min, int max ) {
		long range = (long)max - (long)min + 1;
		int value = (int)(min + (long)( Math.random() * range ) );
		
		if ( value > max ) {
			return max;
		} else {
			return value;
		}
	}
	
}