package COM.furryandfrisky.landscape;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.IOException;
import COM.furryandfrisky.util.FastRandom;
/**
* Landscape generator.
*/
public class FractalLandscapeGenerator implements Serializable
{
private static final long serialVersionUID = -3434666601902115106L;
// landscape colors
public static final int SKY = 0xFFA2D3FF;
public static final int CUT_AWAY = 0xFF000000;
public static final int SEA = 0xFF0000C9;
public static final int SAND = 0xFFDAC370;
// color level constants
private static final int SAND_LEVEL = 652;
private static final int TREE_LEVEL = 17031;
private static final int delta_start = 10000;
private static final double fracdim = Math.pow( 0.5, 0.5 * 0.8 );
private transient int size;
private transient int[][] scape;
private transient FastRandom rng;
private int scale = 0;
private long seed = 0;
/**
* Create a generator. The initial corner points are randomly generated.
*
* @param scale scale of the area
* @param seed random number seed
*/
public FractalLandscapeGenerator( int scale, long seed )
{
this.scale = scale;
this.seed = seed;
init();
}
private void init()
{
size = (int) Math.pow( 2, scale );
scape = new int[size+1][size+1];
rng = new FastRandom( seed );
}
/**
* Stream in a generator.
*/
private void readObject(ObjectInputStream stream) throws IOException
{
try
{
stream.defaultReadObject();
init();
}
catch( ClassNotFoundException e )
{
throw new IOException( e.getMessage() );
}
}
/**
* Get the random seed.
*
* @return the random seed
*/
public final long getSeed()
{
return seed;
}
/**
* Set the generator's random seed.
*
* @param seed the new seed value
*/
public void setSeed( long seed )
{
this.seed = seed;
rng = new FastRandom( seed );
}
/**
* Get the size of the generated grid.
*
* @return the size of the grid
*/
public final int getSize()
{
return size;
}
/**
* Get the scale used to create the landscape grid.
*
* @return the scale of the landscape
*/
public final int getScale()
{
return scale;
}
/**
* Sets the height all points below sea level to 0.
*/
public void flattenSeaLevel()
{
// 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 the landscape grid.
*/
public void generate()
{
int step = size;
int hstep = step / 2;
int delta = delta_start;
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 s = 1; s <= scale; s++ )
{
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 = hstep;
hstep /= 2;
}
}
private final void set4point( int i, int j, int a, int b, int c, int d, int delta )
{
scape[i][j] = (a + b + c + d) / 4;
scape[i][j] += (int)(rng.nextGaussian() * delta);
}
private final void set3point (int i, int j, int a, int b, int c, int delta)
{
scape[i][j] = (a + b + c) / 3;
scape[i][j] += (int)(rng.nextGaussian() * delta);
}
/**
* Get the height of a cell in the landscape grid.
*
* @param x the x coord
* @param y the y coord
* @return the height at the coord
*/
public final int getHeightAt( int x, int y )
{
return scape[x][y];
}
/**
* Get the color for a particular height in the array.
*
* @param h the height
* @return the appropriate color for the height
*/
public final int getColorAt( int h )
{
int color = 0;
if( h <= 0 )
color = SEA;
else if( h < SAND_LEVEL )
color = SAND;
else if( h < TREE_LEVEL )
{
// green trees
int red = (h > 4688) ? (h/156) - 10 : 0;
int green = 50 + (h/156) / 2;
color = 0xFF000000 | (red << 16) | (green << 8 );
}
else
{
// grey mountain tops
int grey = (h/156) + 100;
if( grey > 255 )
grey = 255;
color = 0xFF000000 | ((grey - 10) << 16 ) | ((grey - 10) << 8 ) | grey;
}
return color;
}
/**
* Rotate the landscape to the right.
*/
public void rotateRight()
{
int new_view[][] = new int[size+1][size+1];
for( int i = 0; i <= size; i++ )
{
for( int j = 0; j <= size; j++ )
new_view[i][j] = scape[size - j][i];
}
scape = new_view;
}
/**
* Rotate the landscape to the left.
*/
public void rotateLeft()
{
int new_view[][] = new int[size+1][size+1];
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;
}
}