import java.applet.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;

/* portions of this code were adapted from the DitherTest applet */
/* fractal faulting codes adapted from Usenet and published references
 *   - Basic faulting on a plane adapted from 'faultmap' by Aurel Balmosan
 *     (aurel@xylo.owl.de) -- some significant changes made
 */

public class SpectralSynthesisApplet extends Applet implements Runnable
{
    /************************************************************
     * Boilerplate methods to handle painting and initialization
     ************************************************************/

    private int width = 256;
    private int height = 256;
    private double scalefactor = 1.0;
    private int[][] heightfield;
    private Random rgen;
    private Image img;
    private boolean started;

    public boolean handleEvent(Event evt)
    {
	if (evt.id == Event.WINDOW_DESTROY)  System.exit(0);
	return super.handleEvent(evt);
    }
    public boolean action( Event evt, Object arg)
    {
	return true;
    }
    String calcString = "Calculating...";
    String calcStyle = "planet";
    public void paint(Graphics g)
    {
	int w = size().width;
	int h = size().height;
	if (img == null) {
	    super.paint(g);
	    g.setColor(Color.black);
	    FontMetrics fm = g.getFontMetrics();
	    int x = (w - fm.stringWidth(calcString))/2;
	    int y = h/2;
	    g.drawString(calcString, x, y);
	} else {
	    g.drawImage(img, 0, 0, w, h, this);
	}
    }
    public void init()
    {
	int w = Integer.parseInt(getParameter("width"));
	if (w<=0 || w>256) w=width;
	width=w;
	height=w;
	scalefactor = 256/width;
        rgen = new Random();
	initgauss();
	started = false;
	calcStyle = getParameter("style");
	start();
    }
    synchronized void BuildImage()
    {
        /* build the image for display -- greyscale */
	int pixels[];
	int i, j, a, index = 0, min, max;
	// calculate range of values in heightfield
	min = heightfield[0][0];
	max = heightfield[0][0];
	for (i=0;i max) max = heightfield[i][j];
	    }
	}
	scalefactor = 255.0 / (max-min);
	pixels = new int[width * height];
	for (i=0;i255) a=255;
		/*if (a>255) a=255;*/
		pixels[index++] = (255 << 24) | (a << 16) | (a << 8) | a;
	    }
	}

	img = createImage(new MemoryImageSource(width, height,
						ColorModel.getRGBdefault(),
						pixels, 0, width));
	repaint();
    }

    /************************************************************
     * Thread methods to handle processing in the background
     ************************************************************/

    Thread kicker;

    public /*synchronized*/ void start() {
	if (!started) {
	    started = true;
	    kicker = new Thread(this);
	    kicker.start();
	} else if ((kicker != null) && (kicker.isAlive()))
	    kicker.resume();
    }

    public /*synchronized*/ void stop() {
	try {
	    if ((kicker != null) && (kicker.isAlive())) {
		kicker.suspend();
	    }
	} catch (Exception e) {
	}
    }

    public void restart() {
	try {
	    if (kicker != null) {
		kicker.stop();
	    }
	} catch (Exception e) {
	}
	kicker = null;
	img = null;
	started = false;
	start();
    }

    public void run()
    {
	Thread me = Thread.currentThread();
	me.setPriority(4);
	if (calcStyle.equals("planet"))
	    DoPlanet();
	else
	    DoClouds();
    }

    /*
    
		Fractal forgery generator for the PPM toolkit
    
	    Originally  designed  and  implemented	in December of 1989 by
	    John Walker as a stand-alone program for the  Sun  and	MS-DOS
	    under  Turbo C.  Adapted in September of 1991 for use with Jef
	    Poskanzer's raster toolkit.
    
	    References cited in the comments are:
    
		Foley, J. D., and Van Dam, A., Fundamentals of Interactive
		    Computer  Graphics,  Reading,  Massachusetts:  Addison
		    Wesley, 1984.
    
		Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal
		    Images, New York: Springer Verlag, 1988.
    
		Press, W. H., Flannery, B. P., Teukolsky, S. A., Vetterling,
		    W. T., Numerical Recipes In C, New Rochelle: Cambridge
		    University Press, 1988.
    
	Author:
		John Walker
		Autodesk SA
		Avenue des Champs-Montants 14b
		CH-2074 MARIN
		Switzerland
		Usenet: kelvin@Autodesk.com
		Fax:    038/33 88 15
		Voice:  038/33 76 33
    
	Permission	to  use, copy, modify, and distribute this software and
	its documentation  for  any  purpose  and  without	fee  is  hereby
	granted,  without any conditions or restrictions.  This software is
	provided "as is" without express or implied warranty.
    
				    PLUGWARE!
    
	If you like this kind of stuff, you may also enjoy "James  Gleick's
	Chaos--The  Software"  for  MS-DOS,  available for $59.95 from your
	local software store or directly from Autodesk, Inc., Attn: Science
	Series,  2320  Marinship Way, Sausalito, CA 94965, USA.  Telephone:
	(800) 688-2344 toll-free or, outside the  U.S. (415)  332-2344  Ext
	4886.   Fax: (415) 289-4718.  "Chaos--The Software" includes a more
	comprehensive   fractal    forgery	  generator    which	creates
	three-dimensional  landscapes  as  well as clouds and planets, plus
	five more modules which explore other aspects of Chaos.   The  user
	guide  of  more  than  200	pages includes an introduction by James
	Gleick and detailed explanations by Rudy Rucker of the  mathematics
	and algorithms used by each program.
    
    */
    
    static private final int nrand = 4;   /* Gauss() sample count */
    private double arand, gaussadd, gaussfac; /* Gaussian random parameters */
    private double fracdim;		      /* Fractal dimension */
    private double powscale; 	      /* Power law scaling exponent */
    private int meshsize = 128;	      /* FFT mesh size */
    private int rseed;	      /* Current random seed */
    private boolean seedspec = false;      /* Did the user specify a seed ? */
    private boolean clouds = false;	      /* Just generate clouds */
    private boolean stars = false;	      /* Just generate stars */
    
    /*	FOURN  --  Multi-dimensional fast Fourier transform
    
	    Called with arguments:
    
	       data       A  one-dimensional  array  of  doubles, indexed from one 
			  (NOTE!!!   NOT  ZERO!!),
			  containing  pairs of numbers representing the complex
			  valued samples.  The Fourier transformed results are
			  returned in the same array.
    
	       nn	      An  array specifying the edge size in each dimension.
			  THIS ARRAY IS INDEXED FROM  ONE,	AND  ALL  THE  EDGE
			  SIZES MUST BE POWERS OF TWO!!!
    
	       ndim       Number of dimensions of FFT to perform.  Set to 2 for
			  two dimensional FFT.
    
	       isign      If 1, a Fourier transform is done; if -1 the  inverse
			  transformation is performed.
    
	    This  function  is essentially as given in Press et al., "Numerical
	    Recipes In C", Section 12.11, pp.  467-470.
    */
    
    private void fft2d(double[][][] x, int[] nn, int ndim, int isign)
    {
	// interface to fourn -- copies x to a vector of doubles
	double[] a;
	int idim, ntot=1, i, j, k, n;
    
	// should raise an exception if not exactly two dimensions
	for (idim = 1; idim <= ndim; idim++)
	    ntot *= nn[idim];
	ntot*=2;
	a = new double[ntot+1];
	n=1;
	for (i=0;i= 1; idim--) {
	    n = nn[idim];
	    nrem = ntot / (n * nprev);
	    ip1 = nprev << 1;
	    ip2 = ip1 * n;
	    ip3 = ip2 * nrem;
	    i2rev = 1;
	    for (i2 = 1; i2 <= ip2; i2 += ip1) {
		if (i2 < i2rev) {
		    for (i1 = i2; i1 <= i2 + ip1 - 2; i1 += 2) {
			for (i3 = i1; i3 <= ip3; i3 += ip2) {
			    i3rev = i2rev + i3 - i2;
			    tempr=data[i3]; data[i3] = (data[i3rev]); data[i3rev] = tempr;
			    tempr=data[i3 + 1]; data[i3 + 1] = data[i3rev + 1]; data[i3rev + 1] = tempr;
			}
		    }
		}
		ibit = ip2 >> 1;
		while (ibit >= ip1 && i2rev > ibit) {
		    i2rev -= ibit;
		    ibit >>= 1;
		}
		i2rev += ibit;
	    }
	    ifp1 = ip1;
	    while (ifp1 < ip2) {
		ifp2 = ifp1 << 1;
		theta = isign * (Math.PI * 2) / (ifp2 / ip1);
		wtemp = Math.sin(0.5 * theta);
		wpr = -2.0 * wtemp * wtemp;
		wpi = Math.sin(theta);
		wr = 1.0;
		wi = 0.0;
		for (i3 = 1; i3 <= ifp1; i3 += ip1) {
		    for (i1 = i3; i1 <= i3 + ip1 - 2; i1 += 2) {
			for (i2 = i1; i2 <= ip3; i2 += ifp2) {
			    k1 = i2;
			    k2 = k1 + ifp1;
			    tempr = wr * data[k2] - wi * data[k2 + 1];
			    tempi = wr * data[k2 + 1] + wi * data[k2];
			    data[k2] = data[k1] - tempr;
			    data[k2 + 1] = data[k1 + 1] - tempi;
			    data[k1] += tempr;
			    data[k1 + 1] += tempi;
			}
		    }
		    wr = (wtemp = wr) * wpr - wi * wpi + wr;
		    wi = wi * wpr + wtemp * wpi + wi;
		}
		ifp1 = ifp2;
	    }
	    nprev *= n;
	}
    }
    
    /*  INITGAUSS  --  Initialise random number generators.  As given in
		       Peitgen & Saupe, page 77. */
    
    private void initgauss()
    {
	/* Range of random generator */
	arand = Math.pow(2.0, 15.0) - 1.0;
	gaussadd = Math.sqrt(3.0 * nrand);
	gaussfac = 2 * gaussadd / (nrand * arand);
    }
    
    /*  GAUSS  --  Return a Gaussian random number.  As given in Peitgen
		   & Saupe, page 77. */
    
    private double gauss()
    {
	int i;
	double sum = 0.0;
    
	for (i = 1; i <= nrand; i++) {
	    sum += Math.random();
	}
	return gaussfac * sum - gaussadd;
    }
    
    /*  SPECTRALSYNTH  --  Spectrally  synthesised	fractal  motion in two
			   dimensions.  This algorithm is given under  the
			   name   SpectralSynthesisFM2D  on  page  108  of
			   Peitgen & Saupe. */
    
    private void spectralsynth(double[][][] x, int n, double h)
    {
	int i, j, i0, j0, nsize[];
	double rad, phase, rcos, rsin;
    
	nsize = new int[3];
    
	for (i = 0; i <= n / 2; i++) {
	    for (j = 0; j <= n / 2; j++) {
		phase = 2 * Math.PI * Math.random();
		if (i != 0 || j != 0) {
		    rad = Math.pow((double) (i * i + j * j), -(h + 1) / 2) * gauss();
		} else {
		    rad = 0;
		}
		rcos = rad * Math.cos(phase);
		rsin = rad * Math.sin(phase);
		x[i][j][0] = rcos;
		x[i][j][1] = rsin;
		i0 = (i == 0) ? 0 : n - i;
		j0 = (j == 0) ? 0 : n - j;
		x[i0][j0][0] = rcos;
		x[i0][j0][1] = - rsin;
	    }
	}
	x[n / 2][0][1] = 0;  // imaginary
	x[0][n / 2][1] = 0;
	x[n / 2][n / 2][1] = 0;
	for (i = 1; i <= n / 2 - 1; i++) {
	    for (j = 1; j <= n / 2 - 1; j++) {
		phase = 2 * Math.PI * Math.random();
		rad = Math.pow((double) (i * i + j * j), -(h + 1) / 2) * gauss();
		rcos = rad * Math.cos(phase);
		rsin = rad * Math.sin(phase);
		x[i][n - j][0] = rcos;
		x[i][n - j][1] = rsin;
		x[n - i][j][0] = rcos;
		x[n - i][j][1] = - rsin;
	    }
	}
    
	nsize[0] = 0;
	nsize[1] = nsize[2] = n;	      /* Dimension of frequency domain array */
	fft2d(x, nsize, 2, -1);	      /* Take inverse 2D Fourier transform */
    }
    
	/* The  default  fractal  dimension  and  power  scale depend upon
	   whether we're generating a planet or clouds. */
    
    synchronized public void DoClouds()
    {
	double[][][] x;
	int i,j;
	fracdim = 2.15;
	powscale = 0.75;
	x = new double[width][width][2];  // last dimension is real, imag
	spectralsynth(x, width, 3.0 - fracdim);
	heightfield = new int[width][width];
	for (i=0;i