/*
 * MogensenSolidNoiseGenerator.java  1.0  98/06/03  Carl Burke
 *
 * Encapsulates Mogensen's method for solid noise generation.
 *
 * Copyright (c) 1998 Carl Burke.
 *
 * Derived from code in planet.c Copyright 1998 Torben AE. Mogensen
 */

public class MogensenSolidNoiseGenerator implements SolidNoiseGenerator
{
    private double log2(double x)
    {
	return(Math.log(x)/Math.log(2.0));
    }

    ///** SUBDIVISION CONSTANTS ***

    public static final double Dda0 = .13; // initial altitude variation (suggest 0.15)
    public static final double Ddda = .9;  // rate of decrease of altitude variation (suggest .9)

    public static final double M  = -.015;  // initial altitude (slightly below sea level) (suggest -.015)

    ///** SUBDIVISION VARIABLES ***

    int Depth; // depth of subdivisions

    ///*** VALUES CONTROLLING WORLD CHARACTERISTICS ***

    public static final double Da0 = 2.0; 	 // initial scale (size of box)

    double t1,t2,t3;

    public MogensenSolidNoiseGenerator()
    {
	latic = false;
	land = 0;
	water = 0;
	t1 = Math.exp(Math.log(2.)/3.);	   // dimension of box = (scale,t1*scale,t2*scale)
	t2 = Math.exp(2.*Math.log(2.)/3.); // so halving will keep the same relative scale
	t3 = t2/2.;		   // like A4 paper (but in 3 dimensions)
    }

    ///** RANDOM NUMBER INITIALIZATION AND GENERATION ***

    private double rseed;
    private double r1;
    private double r2;
    private double r3;
    private double r4;
    private double r5;
    private double r6;
    private double r7;
    private double r8;

    // random number generator taking two seeds
    // rand2(p,q) = rand2(q,p) is important
    private double rand2(double p, double q)
    {
	double r = (p+3.14159265)*(q+3.14159265);
	return(2.*(r-(int)r)-1.);
    }
    public void setSeed(double s)
    {
	r1 = rseed = s;
	
	r1 = rand2(r1,r1);
	r2 = rand2(r1,r1);
	r3 = rand2(r1,r2);
	r4 = rand2(r2,r3);
	r5 = rand2(r4,r3);
	r6 = rand2(r4,r5);
	r7 = rand2(r6,r5);
	r8 = rand2(r6,r7);
    }
    public double getSeed() {return rseed;}

    ///** FRACTAL PLANET CALCULATION ROUTINES ***

    private double ta,tb,tc,td,te,tf,tg,th;
    private double tas,tbs,tcs,tds,tes,tfs,tgs,ths;
    private double tax,tay,taz, tscale, tdalt;
    
    private double planet(
	// altitudes of the eight corners
	double a, double b, double c, double d,
	double e, double f, double g, double h,
	// seeds for the eight corners
	double as, double bs, double cs, double ds,
	double es, double fs, double gs, double hs,
	// bottom left closest corner
	double ax, double ay, double az,
	// goal point, scale, and variance
	double x, double y, double z,
	int level, double scale, double dalt)
    {
	double ae,bf,cg,dh,aes,bfs,cgs,dhs,tmp;

	while (level>0)
	{
	    if (level == 12)
	    { // save information for shortcut
		ta=a; tb=b; tc=c; td=d; te=e; tf=f; tg=g; th=h;
		tas=as; tbs=bs; tcs=cs; tds=ds; tes=es; tfs=fs; tgs=gs; ths=hs;
		tax=ax; tay=ay; taz=az; tscale=scale; tdalt=dalt;
	    }
    
	    // subdivide over longest axis 
	    aes = rand2(as,es); bfs = rand2(bs,fs); // find seeds for new points 
	    cgs = rand2(cs,gs); dhs = rand2(ds,hs);
	    ae = (a+e+dalt*aes)/2.; bf = (b+f+dalt*bfs)/2.; // find altitudes 
	    cg = (c+g+dalt*cgs)/2.; dh = (d+h+dalt*dhs)/2.; // as mean+variation 
	    // find which halfbox goal point is in and continue in that 
	    if (az+scale*t3 >= z)
	    {
	      h=c; g=cg; f=dh; e=d; d=b; c=bf; b=ae;
	      hs=cs; gs=cgs; fs=dhs; es=ds; ds=bs; cs=bfs; bs=aes;
	      tmp = ax; ax=az; az=ay; ay=tmp;
	    }
	    else
	    {
	      a=ae; b=e; c=f; d=bf; e=dh; f=h; h=cg;
	      as=aes; bs=es; cs=fs; ds=bfs; es=dhs; fs=hs; hs=cgs;
	      tmp = ax; ax=az+scale*t3; az=ay; ay=tmp;
	    }
	    tmp = x; x=z; z=y; y=tmp;
	    scale=scale*t3; dalt=dalt*Ddda;
    
	    // subdivide over longest axis 
	    aes = rand2(as,es); bfs = rand2(bs,fs); // find seeds for new points 
	    cgs = rand2(cs,gs); dhs = rand2(ds,hs);
	    ae = (a+e+dalt*aes)/2.; bf = (b+f+dalt*bfs)/2.; // find altitudes 
	    cg = (c+g+dalt*cgs)/2.; dh = (d+h+dalt*dhs)/2.; // as mean+variation 
	    // find which halfbox goal point is in and continue in that 
	    if (az+scale*t3 >= z)
	    {
	      h=c; g=cg; f=dh; e=d; d=b; c=bf; b=ae;
	      hs=cs; gs=cgs; fs=dhs; es=ds; ds=bs; cs=bfs; bs=aes;
	      tmp = ax; ax=az; az=ay; ay=tmp;
	    }
	    else
	    {
	      a=ae; b=e; c=f; d=bf; e=dh; f=h; h=cg;
	      as=aes; bs=es; cs=fs; ds=bfs; es=dhs; fs=hs; hs=cgs;
	      tmp = ax; ax=az+scale*t3; az=ay; ay=tmp;
	    }
	    tmp = x; x=z; z=y; y=tmp;
	    scale=scale*t3; dalt=dalt*Ddda;
    
	    // subdivide over longest axis 
	    aes = rand2(as,es); bfs = rand2(bs,fs); // find seeds for new points 
	    cgs = rand2(cs,gs); dhs = rand2(ds,hs);
	    ae = (a+e+dalt*aes)/2.; bf = (b+f+dalt*bfs)/2.; // find altitudes 
	    cg = (c+g+dalt*cgs)/2.; dh = (d+h+dalt*dhs)/2.; // as mean+variation 
	    // find which halfbox goal point is in and continue in that 
	    if (az+scale*t3 >= z)
	    {
	      h=c; g=cg; f=dh; e=d; d=b; c=bf; b=ae;
	      hs=cs; gs=cgs; fs=dhs; es=ds; ds=bs; cs=bfs; bs=aes;
	      tmp = ax; ax=az; az=ay; ay=tmp;
	    }
	    else
	    {
	      a=ae; b=e; c=f; d=bf; e=dh; f=h; h=cg;
	      as=aes; bs=es; cs=fs; ds=bfs; es=dhs; fs=hs; hs=cgs;
	      tmp = ax; ax=az+scale*t3; az=ay; ay=tmp;
	    }
	    tmp = x; x=z; z=y; y=tmp;
	    scale=scale*t3; dalt=dalt*Ddda;
    
	    level = level-3;
	}
	return((a+b+c+d+e+f+g+h)/8);
    }

    ///** COLOR INDEX CONSTANTS ***

    public static final int BLACK = 0;
    public static final int BLUE0 = 1;
    public static final int BLUE1 = 9;
    public static final int LAND0 = 10;
    public static final int LAND1 = 18;
    public static final int WHITE = 19;

    static int[] rtable =
	{0,   0,	 0,  0,	 0,  0,	 0,  0,	 0,  0,	  0, 16, 32,
	 48, 64, 80, 96,112,128, 255};
    static int[] gtable =
	{0,   0, 16, 32, 48, 64, 80, 96,112,128, 255,240,224,208,192,
	 176,160,144,128, 255};
    static int[] btable =
	{0, 255,255,255,255,255,255,255,255,255,	  0,  4,  8,
	 12, 16, 20, 24, 28, 32, 255};

    public boolean latic; // flag for latitude based colour

    public double land;	// percentage of surface covered by land
    public double water;// percentage of surface covered by water

    ///*** ROUTINES REQUIRED BY INTERFACE ***

    public void setScaling(double M, double W, double H)
    {
	Depth = 3*((int)(log2(M*H)))+3;
    }
    public double value(double x, double y, double z)
    {
	if (tax= 0.98)
	    {	// white if close to poles
		colour = WHITE;
	    }
	    else
	    {	// blue scale otherwise
		colour = BLUE1+(int)((BLUE1-BLUE0+1)*(10*alt));
		if (colour= 0.1)
	    {	// if high then white
		colour = WHITE;
	    }
	    else
	    {	// else green to brown scale
		colour = LAND0+(int)((LAND1-LAND0+1)*(10*alt));
	    }
	}
	return(255<<24 | rtable[colour]<<16 | gtable[colour]<<8 | btable[colour]);
    }

    public int background()
    {
	return 0xFF000000;
    }
}