import java.awt.*;
import java.applet.*;

public class Galaxy2 extends Applet implements Runnable
{
  Thread runThread = null;
  Image im = null;
  Graphics img = null;

  int imw, imh;

  //---- Galaxy data

  int rad = 5;

  static int gw, gh;

  double[][] m0, m1, dx, dy;
  int frame = 0;
  int time = 0;

  double[][] potx, poty;

  Color[] greyscale;


  public void init()
  {
    int i, j;

    imw = size().width;
    imh = size().height;
    gw = imw/2;
    gh = imh/2;

    m0 = new double[gh][gw];
    m1 = new double[gh][gw];
    dx = new double[gh][gw];
    dy = new double[gh][gw];

    for(j=0; j<gh; j++) for(i=0; i<gw; i++)
    {
      m0[j][i] = 0;
      m1[j][i] = 0;
      dx[j][i] = 0;
      dy[j][i] = 0;
    }

    potx = new double[rad*2+1][rad*2+1];
    poty = new double[rad*2+1][rad*2+1];

    for(j=-rad; j<=rad; j++) for(i=-rad; i<=rad; i++)
    {
      double d = Math.sqrt(i*i+j*j);
      if(d < 0.01) d = 1.0;
      
      potx[j+rad][i+rad] = ((double)i)/d;
      poty[j+rad][i+rad] = ((double)j)/d;

      d = d*5.0/((double)rad);
      d = 1.0/(d*d);
      d *= 0.1;

      potx[j+rad][i+rad] *= d;
      poty[j+rad][i+rad] *= d;
    }

    greyscale = new Color[256];
    for(i=0; i<256; i++) greyscale[i] = new Color(i, i, i);
  }

  public void start()
  {
    if(runThread == null)
    {
      runThread = new Thread(this);
      runThread.start();
    }
  }

  public void stop()
  {
    if(runThread != null)
    {
      runThread.stop();
      runThread = null;
    }
  }

  public void run()
  {
    im = createImage(imw, imh);
    img = im.getGraphics();

    while(true)
    {
      recalc();
      repaint();
      try { runThread.sleep(20); } catch (InterruptedException e) { return; }
    }

  }

  public void update(Graphics g)
  {
    paint(g);
  }

	public void paint(Graphics g)
	{
    if(img != null)
    {
      //- Draw image

      int mc;

      double[][] b0, b1;
      if(frame == 0) { b0 = m0; b1 = m1; } else { b0 = m1; b1 = m0; }

      for(int j=0; j<gh; j++) for(int i=0; i<gw; i++)
      {
        mc = (int)(b0[j][i]*256.0); if(mc > 255) mc = 255;
        img.setColor(greyscale[mc]);
        img.fillRect(i+i, j+j, 2, 2);
      }

      g.drawImage(im, 0, 0, this);
    }
    else
    {
      g.drawString("Loading...", 5, 15);
    }
	}

  int mouse_x = 0, mouse_y = 0;
  int mouse_oldx = 0, mouse_oldy = 0;
  boolean mouse_drag = true;

  int mouse_dx = 0, mouse_dy = 0;

  public boolean mouseDown(Event evt, int x, int y)
  {
    mouse_oldx = x;
    mouse_oldy = y;
    mouse_x = x;
    mouse_y = y;
    mouse_drag = true;
    
    //.. Mouse button pressed

    return true;
  }
  
  public boolean mouseDrag(Event e, int x, int y)
  {
    if(mouse_x != x || mouse_y != y)
    {
      mouse_dx = x-mouse_x;
      mouse_dy = y-mouse_y;
      mouse_x = x;
      mouse_y = y;
      
      //.. Mouse is dragging
    }

    return true;
  }

  public boolean mouseUp(Event evt, int x, int y)
  {
    mouse_drag = false;

    if(mouse_oldx == mouse_x && mouse_oldy == mouse_y)
    {
      //.. The mouse has been released on the same spot (might have moved while dragging tho)
    }
    else
    {
      //.. Mouse released in new position
    }

    return true;
  }
  
  public void recalc()
  {
    int i, j;

    int ii, jj;
    double newi, newj;
    double mm;
    double px, py;
    
    //- Decide which buffer is primary and which is secondary

    double[][] b0, b1;
    if(frame == 0) { b0 = m0; b1 = m1; } else { b0 = m1; b1 = m0; }

    //- Add mouse generated matter

    if(mouse_drag)
    {
      int x, y;
      x = mouse_x/2;
      y = mouse_y/2;
      for(j=y-2; j<=y+2; j++) for(i=x-2; i<=x+2; i++)
        if(i >= 0 && i < gw && j >= 0 && j < gh)
          {
            b0[j][i] += 0.5;
            dx[j][i] += mouse_dx*0.1;
            dy[j][i] += mouse_dy*0.1;
          }
    }

    //- Calculate new speeds

    for(j=0; j<gh; j++) for(i=0; i<gw; i++)
    {
      px =  0.003621826;
      py = -0.008876421;

      for(jj = -rad; jj <= rad; jj++) for(ii = -rad; ii <= rad; ii++)
      {
        px += potx[jj+rad][ii+rad] * b0[(j+jj+gh)%gh][(i+ii+gw)%gw];
        py += poty[jj+rad][ii+rad] * b0[(j+jj+gh)%gh][(i+ii+gw)%gw];
      }

      dx[j][i] += px*b0[j][i];
      dy[j][i] += py*b0[j][i];
      
/*
      if(dx[j][i] < -2) dx[j][i] = -2;
      if(dx[j][i] >  2) dx[j][i] =  2;
      if(dy[j][i] < -2) dy[j][i] = -2;
      if(dy[j][i] >  2) dy[j][i] =  2;
*/
    }

    //- Move matter

    for(j=0; j<gh; j++) for(i=0; i<gw; i++) b1[j][i] = 0;
    
    for(j=0; j<gh; j++) for(i=0; i<gw; i++)
    {
      mm = b0[j][i];

      newi = (double)(i) + dx[j][i] + (double)(gw);
      newj = (double)(j) + dy[j][i] + (double)(gh);
      
      ii = (int)newi; jj = (int)newj;
      
      px = newi-ii; py = newj-jj;

      b1[(jj  )%gh][(ii  )%gw] += mm*(1-px)*(1-py);
      b1[(jj  )%gh][(ii+1)%gw] += mm*(  px)*(1-py);
      b1[(jj+1)%gh][(ii  )%gw] += mm*(1-px)*(  py);
      b1[(jj+1)%gh][(ii+1)%gw] += mm*(  px)*(  py);
    }    

    frame ^= 1;
    time ++;
  }

}	






