package COM.furryandfrisky.fractal;
/*
* Improved fractal applet, originally by C Thornborrow (Silicon Graphics
* (UK))
*/
import java.awt.*;
import java.applet.Applet;
import java.awt.image.MemoryImageSource;
import COM.furryandfrisky.util.FastRandom;
//----------------------------------------------------------------------------
public class Landscape extends Applet implements Runnable
{
/**
* Reset random number, clear height arrays
*/
public void init()
{
seed = new FastRandom().nextLong();
rng = new FastRandom( seed );
for( int i = 0; i < size; i++ )
{
for( int j = 0; j < size; j++ )
scape[i][j] = 0;
}
top_view = false;
}
public String getAppletInfo()
{
return "Random seed = " + seed;
}
/**
* The start routine for the landscape thread
*/
public void start()
{
if( land_thread == null )
{
land_thread = new Thread( this );
land_thread.start();
}
}
/**
* Stop calculation of the landscape thread
*/
public void stop()
{
if( land_thread != null )
{
land_thread.stop();
land_thread = null;
}
}
/**
* Creates the height array
*/
public void run()
{
int step = size;
int hstep = step / 2;
double delta = delta_start;
showStatus( "calculating landscape..." );
// set the four corner points
scape[0][0] = (int) ((rng.nextGaussian() + 1) * delta);
scape[0][size] = (int) ((rng.nextGaussian() + 1) * delta);
scape[size][0] = (int) ((rng.nextGaussian() + 1) * delta);
scape[size][size] = (int) ((rng.nextGaussian() + 1) * delta);
// create the landscape
for( int scale = 1; scale <= 8; scale++ )
{
int max = size - hstep;
delta *= fracdim;
// set the internal point in square
for( int i = hstep; i <= max; i += step )
{
for( int j = hstep; j <= max; j += step )
{
set4point( i, j,
scape[i + hstep][j + hstep],
scape[i + hstep][j - hstep],
scape[i - hstep][j + hstep],
scape[i - hstep][j - hstep],
delta );
}
}
delta *= fracdim;
// set boundary points
for( int i = hstep; i <= max; i += step )
{
set3point( i, 0,
scape[i + hstep][0],
scape[i - hstep][0],
scape[i][hstep],
delta );
set3point( i, size,
scape[i + hstep][size],
scape[i - hstep][size],
scape[i][max],
delta );
set3point( 0, i,
scape[0][i + hstep],
scape[0][i - hstep],
scape[hstep][i],
delta );
set3point( size, i,
scape[size][i + hstep],
scape[size][i - hstep],
scape[max][i],
delta );
}
// set interior grid points
for( int i = hstep; i <= max; i += step )
{
for( int j = step; j <= max; j += step )
{
set4point( i, j,
scape[i][j + hstep],
scape[i][j - hstep],
scape[i + hstep][j],
scape[i - hstep][j],
delta );
}
}
for( int i = step; i <= max; i += step )
{
for( int j = hstep; j <= max; j += step )
{
set4point( i, j,
scape[i][j + hstep],
scape[i][j - hstep],
scape[i + hstep][j],
scape[i - hstep][j],
delta );
}
}
// change the size of the grid we are working on
step >>= 1;
hstep >>= 1;
}
// level out sea level
for( int i = 0; i <= size; i ++ )
{
for( int j = 0; j <= size; j++ )
{
if( scape[i][j] < 0 )
scape[i][j] = 0;
}
}
create_landscape();
}
/**
* Draw image if completed
*/
public void paint(Graphics g)
{
if( image != null )
g.drawImage( image, 0, 0, this );
}
/**
* Create new landscape on right mouse click
*/
public boolean mouseDown(Event evt, int x, int y)
{
if( evt.metaDown() )
{
stop();
init();
start();
return true;
}
return false;
}
/**
* Rotate landscape on arrow keys
*/
public boolean keyDown( Event evt, int key )
{
if( (key == Event.RIGHT ) || (key == Event.LEFT) )
{
int new_view[][] = new int[size+1][size+1];
if( key == Event.RIGHT )
{
for( int i = 0; i <= size; i++ )
{
for( int j = 0; j <= size; j++ )
new_view[i][j] = scape[size - j][i];
}
}
else if( key == Event.LEFT )
{
for( int i = 0; i <= size; i++ )
{
for( int j = 0; j <= size; j++ )
new_view[i][j] = scape[j][size - i];
}
}
scape = new_view;
image = null;
}
else if( key == Event.UP )
{
if( top_view )
return true;
top_view = true;
}
else if( key == Event.DOWN )
{
if( !top_view )
return true;
top_view = false;
}
else
return true;
if( top_view )
create_top_view();
else
create_landscape();
return true;
}
/**
* Render height array on image
*/
private void create_landscape()
{
showStatus( "rendering landscape" );
for( int i = 0; i < shadow.length; i++ )
shadow[i] = (float) 0.0;
for( int i = 0; i < offscreen.length; i++ )
offscreen[i] = sky;
for( int i = 0; i < size; i++ )
{
int x = i * 2;
int highest = screen_height;
int next_highest = screen_height;
for( int j = 0; j < size; j++ )
{
int y = screen_height - j / 2;
// draw one vertical line
int scrheight = scape[i][j];
if( (y - scrheight) < highest )
{
draw_line( x, highest - 1, y - scrheight,
calc_color( j, scrheight ));
highest = y - scrheight;
}
calc_shadow( j, scrheight );
// draw an interpolated line to give twice width
scrheight = (scape[i + 1][j] + scrheight) / 2;
if( (y - scrheight) < next_highest )
{
draw_line( x + 1, next_highest - 1, y - scrheight,
calc_color( j, scrheight ));
next_highest = y - scrheight;
}
calc_shadow( j, scrheight );
}
}
image = createImage( new MemoryImageSource(screen_width,
screen_height, offscreen, 0, screen_width ));
repaint();
showStatus( "landscape completed" );
}
private static final int calc_color( int j, int h )
{
int color = 0;
if( (j == 0) && (!top_view) )
color = cut_away;
else if( h <= 0 )
color = sea;
else if( h < 5 )
color = sand;
else if( h < 110 )
{
// green trees
int red = (h > 30) ? h - 10 : 0;
int green = 50 + h / 2;
color = 0xFF000000 | (red << 16) | (green << 8 );
}
else
{
// grey mountain tops
int red = h + 100;
if( red > 255 )
red = 255;
color = 0xFF000000 | ((red - 10) << 16 ) | ((red - 10) << 8 )
| red;
}
if( shadow[j] > h )
{
int red = (int)(((color >> 16) & 0xFF) * darken_color);
int green = (int)(((color >> 8) & 0xFF) * darken_color);
int blue = (int)((color & 0xFF) * darken_color);
color = 0xFF000000 | (red << 16) | (green << 8) | blue;
}
return color;
}
private static final void calc_shadow( int j, int h )
{
if( shadow[j] > h )
shadow[j] -= shadow_fade;
else
shadow[j] = (float) h;
}
private static final void draw_line( int x, int from_y, int to_y,
int color )
{
for( int i = to_y; i <= from_y; i++ )
offscreen[ x + (i * screen_width)] = color;
}
/**
* Render height array on image
*/
private void create_top_view()
{
showStatus( "rendering landscape" );
for( int i = 0; i < top_shadow.length; i++ )
top_shadow[i] = (float) 0.0;
for( int i = 0; i < size; i++ )
{
int x = i * 2;
for( int j = 1; j <= size; j++ )
{
int y = screen_height - (j*2);
int height = scape[i][j];
offscreen[x + (y * screen_width)] = top_calc_color( y,
height );
top_calc_shadow( y, height );
int height2 = (height + scape[i][j-1])/2;
offscreen[x + ((y+1) * screen_width)] = top_calc_color( y+1,
height2 );
top_calc_shadow( y+1, height2 );
int height3 = (height + scape[i+1][j])/2;
offscreen[(x+1) + (y * screen_width)] = top_calc_color( y,
height3 );
top_calc_shadow( y, height3 );
int height4 = (height + scape[i+1][j] + scape[i][j-1]
+ scape[i+1][j-1] )/4;
offscreen[(x+1) + ((y+1) * screen_width)] = top_calc_color(
y+1, height4 );
top_calc_shadow( y+1, height4 );
}
}
image = createImage( new MemoryImageSource(screen_width,
screen_height, offscreen, 0, screen_width ));
repaint();
showStatus( "landscape completed" );
}
private static final int top_calc_color( int y, int h )
{
int color = 0;
if( h <= 0 )
color = sea;
else if( h < 5 )
color = sand;
else if( h < 110 )
{
// green trees
int red = (h > 30) ? h - 10 : 0;
int green = 50 + h / 2;
color = 0xFF000000 | (red << 16) | (green << 8 );
}
else
{
// grey mountain tops
int red = h + 100;
if( red > 255 )
red = 255;
color = 0xFF000000 | ((red - 10) << 16 ) | ((red - 10) << 8 )
| red;
}
if( top_shadow[y] > h )
{
int red = (int)(((color >> 16) & 0xFF) * darken_color);
int green = (int)(((color >> 8) & 0xFF) * darken_color);
int blue = (int)((color & 0xFF) * darken_color);
color = 0xFF000000 | (red << 16) | (green << 8) | blue;
}
return color;
}
private static final void top_calc_shadow( int y, int h )
{
if( top_shadow[y] > h )
top_shadow[y] -= shadow_fade;
else
top_shadow[y] = (float) h;
}
private static final void set4point( int i, int j, int a, int b, int c,
int d, double delta )
{
scape[i][j] = (a + b + c + d) / 4;
scape[i][j] += (int)(rng.nextGaussian() * delta);
}
private static final void set3point (int i, int j, int a, int b, int c,
double delta)
{
scape[i][j] = (a + b + c) / 3;
scape[i][j] += (int)(rng.nextGaussian() * delta);
}
//-------------------------------------------------------------------------
private static final int screen_width = 512;
private static final int screen_height = 512;
private static final int size = 256;
private static final double delta_start = 64.0;
private static final double fracdim = 0.5 ^ (0.5 * 0.8)
private static final double shadow_fade = 0.3;
private static final double darken_color = 0.7;
private static final int sky = 0xFFA2D3FF;
private static final int cut_away = 0xFF000000;
private static final int sea = 0xFF0000C9;
private static final int sand = 0xFFDAC370;
private Thread land_thread = null;
private static float shadow[] = new float[size+1];
private static float top_shadow[] = new float[screen_height+1];
private static int scape[][] = new int[size+1][size+1];
private static FastRandom rng = null;
private static Image image = null;
private static int[] offscreen = new int[screen_width
* screen_height];
private static boolean top_view = false;
private static long seed = 0;
}