/*********************************************************************************************/
/*                                                                                           */
/*  Java Image Demo - Generating Marble Texture                                              */
/*  Auth: bkenwright@xbdev.net                                                               */
/*  URL: www.xbdev.net                                                                       */
/*                                                                                           */
/*********************************************************************************************/

import java.awt.image.*;
import java.awt.*;
import java.applet.*;
import java.util.Random;

public class perlin_noise_pattern extends Applet implements Runnable
{
	private Thread m_thread        = null;
  public Screen scr;

  Noise n;
  
	public synchronized void update(Graphics g)
	{
	    int[] pixels = scr.GetPixels();
	    int w=scr.GetWidth();    int h=scr.GetHeight();
			PerlinNoisePattern(pixels, w, h );
			scr.Render(g);
	}// End update(..)

  /*
  double trunc( double aa )
  {
     // Math.trunc(x) =
    return  Math.round(Math.floor(aa));
  }
  */

	
	//Random ran = new Random();
	double w = 0;
	void PerlinNoisePattern(int pix[], int width, int height)
	{
			int x, y;
			//double w = ran.nextDouble();
			double u, v;
			w+=0.01;
			
			//generate a pretty colour background on the screen
      for (y=0; y<height; y++)
      {
            for (x=0; x<width; x++)
            {
                u = x;   v = y;
                int r = (int)(Noise.noise(u/100, v/100, w) * 1000);
                int g = (int)(Noise.noise(u/500, v/500, w) * 1000);
                int b = (int)(Noise.noise(u/800, v/800, w) * 1000);
                r=Math.abs(r);    g=Math.abs(g);   b=Math.abs(b);
                int cc = ( 0xff<<24 | 
                       r << 16 |
                       g << 8 |
                       b );
                
                pix[y*width + x] = cc; 
            }// End inner loop
      }// End outer loop

	}// End Pattern(..)
	
  
  public void run()
	{
	    scr = new Screen(320,320);
	    
			// Remember the starting time
			long thenTime = System.currentTimeMillis();
			while (true) 
			{
			    long nowTime = System.currentTimeMillis();
			    long difTime = nowTime - thenTime;
			    
			    // I've set the delay to 0, so its pushing for the MAX fps, you would
			    // set a value in there to limit for example to 25-50 fps refresh rate
			    if( difTime > 25 )
			    {
			       thenTime = nowTime;
			       // This method provides better updates, instead of filling the
			       // message queue up
			       Graphics g=this.getGraphics();
			       update(g);
			    }
			}// end while loop
	}// End of run() method
	
	
	public void start()
	{
		if(m_thread == null)
    {
			m_thread = new Thread(this);
			m_thread.setPriority(Thread.MAX_PRIORITY);
			m_thread.start();
		}
		System.out.println( "Starting Graphics Demo\n" );
	}// End of start()
	
}// End of Applet


/*********************************************************************************************/
/*                                                                                           */
/*  class Screen                                                                             */
/*  Well its the fastest method to plot pixels using MemoryImageSource...and to access your  */
/*  array of pixel data directly...but it sure makes your setup applet code look complicated */
/*  and hides the real reason!  So I've done a sort of back buffer screen class, which we    */
/*  can use to render pixels fastly....for our cool effects...and its all in there...settup  */
/*  and rendering.                                                                           */
/*                                                                                           */
/*********************************************************************************************/
// Few tiny comments
// We need to extend from Applet so we can use, two api's.  createImage(..) and drawImage(..)

class Screen extends Applet
{
   Image m_image;
   int m_pixels[];
   
   int m_width=0;
   int m_height=0;

   MemoryImageSource m_p; 
   public Screen(int width, int height)
   {
     m_pixels = new int[width*height];
     m_width = width;
     m_height = height;
     // Init MemoryImageSource - pixels will be in 32bit ARGB format
		 DirectColorModel dcm=new DirectColorModel(32, 0xff0000, 0xff00, 0xff);
     m_p = new MemoryImageSource(/*width*/m_width,/*height*/m_height,dcm,m_pixels,0,/*width*/m_width);
     m_p.setAnimated(true);
     m_p.setFullBufferUpdates(true);
     m_image = createImage(m_p);
   }// End CScreen(..) constuctor

   public void Render(Graphics g)
   {
      m_p.newPixels(0,0,/*width*/m_width,/*height*/m_height);
      g.drawImage(m_image,0,0, this);
   }// End Render(..)
   
   public int[] GetPixels()
   {
      return m_pixels;
   }// End GetPixels()
   
   
   public int GetWidth() { return m_width; };
   public int GetHeight(){ return m_height; };

}// End Screen(..)


  // Overwrite imageUpdate
  /*
  public boolean imageUpdate(Image image, int i1, int j1, int k, int i2, int j2) {
    return true;
  }
  */
  
  
  
  
  
  // Reference:
  // http://www.cs.ucsb.edu/~cs280/JavaRender/render/
  final class Noise {

   /** 
   Initialization seed used to start the random number generator.
   */
   public static int seed = 100;

   private static final int P = 8;
   private static final int B = 1 << P;
   private static final int M = B - 1;

   private static final int NP = 8;
   private static final int N = 1 << NP;
   private static final int NM = N - 1;

   private static int p[] = new int[B + B + 2];
   private static double g2[][] = new double[B + B + 2][2];
   private static double g1[] = new double[B + B + 2];
   private static int start = 1;
   private static double[][] points = new double[32][3];

 
 static {
      init();
   }



   private static double lerp(double t, double a, double b) {
      return a + t * (b - a);
   }

   private static double s_curve(double t) {
      return t * t * (3 - t - t);
   }

   /** 
   Computes noise function for one dimension at x.
   @param x 1 dimensional parameter
   @return the noise value at x 
   */
   public static double noise(double x) {

      int bx0, bx1;
      double rx0, rx1, sx, t, u, v;
      t = x + N;
      bx0 = ((int) t) & M;
      bx1 = (bx0 + 1) & M;
      rx0 = t - (int) t;
      rx1 = rx0 - 1;

      sx = s_curve(rx0);
      u = rx0 * g1[p[bx0]];
      v = rx1 * g1[p[bx1]];

      return lerp(sx, u, v);
   }

   /** 
   Computes noise function for two dimensions at the point (x,y).
   @param x x dimension parameter
   @param y y dimension parameter
   @return the value of noise at the point (x,y)
   */
   public static double noise(double x, double y) {

      int bx0, bx1, by0, by1, b00, b10, b01, b11;
      double rx0, rx1, ry0, ry1, sx, sy, a, b, t, u, v, q[];
      int i, j;

      t = x + N;
      bx0 = ((int) t) & M;
      bx1 = (bx0 + 1) & M;
      rx0 = t - (int) t;
      rx1 = rx0 - 1;

      t = y + N;
      by0 = ((int) t) & M;
      by1 = (by0 + 1) & M;
      ry0 = t - (int) t;
      ry1 = ry0 - 1;

      i = p[bx0];
      j = p[bx1];

      b00 = p[i + by0];
      b10 = p[j + by0];
      b01 = p[i + by1];
      b11 = p[j + by1];

      sx = s_curve(rx0);
      sy = s_curve(ry0);

      q = g2[b00];
      u = rx0 * q[0] + ry0 * q[1];
      q = g2[b10];
      v = rx1 * q[0] + ry0 * q[1];
      a = lerp(sx, u, v);

      q = g2[b01];
      u = rx0 * q[0] + ry1 * q[1];
      q = g2[b11];
      v = rx1 * q[0] + ry1 * q[1];
      b = lerp(sx, u, v);

      return lerp(sy, a, b);
   }

   /** 
   Computes noise function for three dimensions at the point (x,y,z).
   @param x x dimension parameter
   @param y y dimension parameter
   @param z z dimension parameter
   @return the noise value at the point (x, y, z)
   */
   static public double noise(double x, double y, double z) {

      int bx, by, bz, b0, b1, b00, b10, b01, b11;
      double rx0, rx1, ry0, ry1, rz, sx, sy, sz, a, b, c, d, u, v, q[];

      bx = (int) Math.IEEEremainder(Math.floor(x), B);
      if (bx < 0)
         bx += B;
      rx0 = x - Math.floor(x);
      rx1 = rx0 - 1;

      by = (int) Math.IEEEremainder(Math.floor(y), B);
      if (by < 0)
         by += B;
      ry0 = y - Math.floor(y);
      ry1 = ry0 - 1;

      bz = (int) Math.IEEEremainder(Math.floor(z), B);
      if (bz < 0)
         bz += B;
      rz = z - Math.floor(z);

      //if (bx < 0 || bx >= B + B + 2)
      //System.out.println(bx);

      b0 = p[bx];

      bx++;

      b1 = p[bx];

      b00 = p[b0 + by];
      b10 = p[b1 + by];

      by++;

      b01 = p[b0 + by];
      b11 = p[b1 + by];

      sx = s_curve(rx0);
      sy = s_curve(ry0);
      sz = s_curve(rz);

      q = G(b00 + bz);
      u = rx0 * q[0] + ry0 * q[1] + rz * q[2];
      q = G(b10 + bz);
      v = rx1 * q[0] + ry0 * q[1] + rz * q[2];
      a = lerp(sx, u, v);
      q = G(b01 + bz);
      u = rx0 * q[0] + ry1 * q[1] + rz * q[2];
      q = G(b11 + bz);
      v = rx1 * q[0] + ry1 * q[1] + rz * q[2];
      b = lerp(sx, u, v);
      c = lerp(sy, a, b);
      bz++;
      rz--;
      q = G(b00 + bz);
      u = rx0 * q[0] + ry0 * q[1] + rz * q[2];
      q = G(b10 + bz);
      v = rx1 * q[0] + ry0 * q[1] + rz * q[2];
      a = lerp(sx, u, v);
      q = G(b01 + bz);
      u = rx0 * q[0] + ry1 * q[1] + rz * q[2];
      q = G(b11 + bz);
      v = rx1 * q[0] + ry1 * q[1] + rz * q[2];
      b = lerp(sx, u, v);
      d = lerp(sy, a, b);

      return lerp(sz, c, d);
   }

   private static double[] G(int i) {
      return points[i % 32];
   }

   public static void init() {
      int i, j, k;
      double u, v, w, U, V, W, Hi, Lo;
      java.util.Random r = new java.util.Random(seed);
      for (i = 0; i < B; i++) {
         p[i] = i;
         g1[i] = 2 * r.nextDouble() - 1;

         do {
            u = 2 * r.nextDouble() - 1;
            v = 2 * r.nextDouble() - 1;
         } while (u * u + v * v > 1 || Math.abs(u) > 2.5 * Math.abs(v) || Math.abs(v) > 2.5 * Math.abs(u) || Math.abs(Math.abs(u) - Math.abs(v)) < .4);
         g2[i][0] = u;
         g2[i][1] = v;
         normalize2(g2[i]);

         do {
            u = 2 * r.nextDouble() - 1;
            v = 2 * r.nextDouble() - 1;
            w = 2 * r.nextDouble() - 1;
            U = Math.abs(u);
            V = Math.abs(v);
            W = Math.abs(w);
            Lo = Math.min(U, Math.min(V, W));
            Hi = Math.max(U, Math.max(V, W));
         } while (u * u + v * v + w * w > 1 || Hi > 4 * Lo || Math.min(Math.abs(U - V), Math.min(Math.abs(U - W), Math.abs(V - W))) < .2);
      }

      while (--i > 0) {
         k = p[i];
         j = (int) (r.nextLong() & M);
         p[i] = p[j];
         p[j] = k;
      }
      for (i = 0; i < B + 2; i++) {
         p[B + i] = p[i];
         g1[B + i] = g1[i];
         for (j = 0; j < 2; j++) {
            g2[B + i][j] = g2[i][j];
         }
      }

      points[3][0] = points[3][1] = points[3][2] = Math.sqrt(1. / 3);
      double r2 = Math.sqrt(1. / 2);
      double s = Math.sqrt(2 + r2 + r2);

      for (i = 0; i < 3; i++)
         for (j = 0; j < 3; j++)
            points[i][j] = (i == j ? 1 + r2 + r2 : r2) / s;
      for (i = 0; i <= 1; i++)
         for (j = 0; j <= 1; j++)
            for (k = 0; k <= 1; k++) {
               int n = i + j * 2 + k * 4;
               if (n > 0)
                  for (int m = 0; m < 4; m++) {
                     points[4 * n + m][0] = (i == 0 ? 1 : -1) * points[m][0];
                     points[4 * n + m][1] = (j == 0 ? 1 : -1) * points[m][1];
                     points[4 * n + m][2] = (k == 0 ? 1 : -1) * points[m][2];
                  }
            }
   }

   private static void normalize2(double v[]) {
      double s;
      s = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
      v[0] = v[0] / s;
      v[1] = v[1] / s;
   }
}

  
  


