import java.awt.*;
import java.applet.*;
import java.util.Random;
import java.awt.image.*;

import java.lang.System;

public class Plasma extends Applet implements Runnable{
  boolean stop;  
  int GridMX, GridMY;
  int x, y;
  double MaxC, cScale;
  double RX2, RY2, GX2, GY2, BX2, BY2, MX2, MY2;
  double RXA2, RYA2, GXA2, GYA2, BXA2, BYA2;
  double RX1, RY1, GX1, GY1, BX1, BY1, MX1, MY1;
  double RXA1, RYA1, GXA1, GYA1, BXA1, BYA1;
  double[][] GridR;
  double[][] GridG;
  double[][] GridB;

  Random         rand = new Random();
  DataBuffer     dbPlasma;
  BufferedImage  biPlasma;
  Thread         animator;
 
  public void init() 
  { GridMX=this.getSize().width;
    GridMY=this.getSize().height;
    
    biPlasma = new BufferedImage(GridMX, GridMY, 1);
    dbPlasma = biPlasma.getRaster().getDataBuffer(); 
      
    MaxC =Math.sqrt(((GridMX-1.0)*(GridMX-1.0))+((GridMY-1.0)*(GridMY-1.0)));
    cScale=(MaxC/100.0);
        
    GridR = new double[GridMX+1][GridMY+1];
    GridG = new double[GridMX+1][GridMY+1];
    GridB = new double[GridMX+1][GridMY+1];
    
    RX1=rand.nextInt(GridMX);
    RY1=rand.nextInt(GridMY);
    GX1=rand.nextInt(GridMX);
    GY1=rand.nextInt(GridMY);
    BX1=rand.nextInt(GridMX);
    BY1=rand.nextInt(GridMY);

    RX2=rand.nextInt(GridMX);
    RY2=rand.nextInt(GridMY);
    GX2=rand.nextInt(GridMX);
    GY2=rand.nextInt(GridMY);
    BX2=rand.nextInt(GridMX);
    BY2=rand.nextInt(GridMY);
  
    double xr=GridMX/20.0;
    double yr=GridMY/20.0;
        
    RXA1=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    RYA1=rand.nextInt((int)(yr*10))/yr-(yr/2.0);
    GXA1=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    GYA1=rand.nextInt((int)(yr*10))/yr-(yr/2.0);
    BXA1=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    BYA1=rand.nextInt((int)(yr*10))/yr-(yr/2.0);

    RXA2=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    RYA2=rand.nextInt((int)(yr*10))/yr-(yr/2.0);
    GXA2=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    GYA2=rand.nextInt((int)(yr*10))/yr-(yr/2.0);
    BXA2=rand.nextInt((int)(xr*10))/xr-(xr/2.0);
    BYA2=rand.nextInt((int)(yr*10))/yr-(yr/2.0);

    for(x=0; x<GridMX; x++)
    { for(y=0; y<GridMY; y++)
      { GridR[x][y]=(int)(((float)x/(float)GridMX)*(float)255);
        GridG[x][y]=(int)(((float)y/(float)GridMY)*(float)255);
        GridB[x][y]=127; } } }

  public void paint(Graphics g) 
  { g.drawImage(biPlasma, 0,0, this); }
  
  public void update(Graphics g)
  { paint(g); }

  public double GetShade(double a, double b)
  { return (1.0-(Math.sqrt(a*a + b*b) /MaxC))*cScale; }
  	  
  public void start()
  { if(animator == null)
    { animator=new Thread(this);
      animator.start(); } }
  
  public void stop()
  { stop=true;
    animator = null;  }

  public void run()
  { while(!stop)
    { if(((RX1+RXA1)>=GridMX)||((RX1+RXA1)<0)) RXA1=-RXA1;
      if(((RY1+RYA1)>=GridMY)||((RY1+RYA1)<0)) RYA1=-RYA1;
      if(((GX1+GXA1)>=GridMX)||((GX1+GXA1)<0)) GXA1=-GXA1;
      if(((GY1+GYA1)>=GridMY)||((GY1+GYA1)<0)) GYA1=-GYA1;
      if(((BX1+BXA1)>=GridMX)||((BX1+BXA1)<0)) BXA1=-BXA1;
      if(((BY1+BYA1)>=GridMY)||((BY1+BYA1)<0)) BYA1=-BYA1;

      if(((RX2+RXA2)>=GridMX)||((RX2+RXA2)<0)) RXA2=-RXA2;
      if(((RY2+RYA2)>=GridMY)||((RY2+RYA2)<0)) RYA2=-RYA2;
      if(((GX2+GXA2)>=GridMX)||((GX2+GXA2)<0)) GXA2=-GXA2;
      if(((GY2+GYA2)>=GridMY)||((GY2+GYA2)<0)) GYA2=-GYA2;
      if(((BX2+BXA2)>=GridMX)||((BX2+BXA2)<0)) BXA2=-BXA2;
      if(((BY2+BYA2)>=GridMY)||((BY2+BYA2)<0)) BYA2=-BYA2;

      RX1+=RXA1;
      RY1+=RYA1;
      GX1+=GXA1;
      GY1+=GYA1;
      BX1+=BXA1;
      BY1+=BYA1;

      RX2+=RXA2;
      RY2+=RYA2;
      GX2+=GXA2;
      GY2+=GYA2;
      BX2+=BXA2;
      BY2+=BYA2;

      for(x=0; x<GridMX; x++)
      { for(y=0; y<GridMY; y++)
        { GridR[x][y]+=GetShade(x-RX1, y-RY1);
          GridG[x][y]+=GetShade(x-GX1, y-GY1);
          GridB[x][y]+=GetShade(x-BX1, y-BY1);

          GridR[x][y]-=GetShade(x-RX2, y-RY2);
          GridG[x][y]-=GetShade(x-GX2, y-GY2);
          GridB[x][y]-=GetShade(x-BX2, y-BY2);

          if(GridR[x][y]>255) GridR[x][y]=255;
          if(GridG[x][y]>255) GridG[x][y]=255;
          if(GridB[x][y]>255) GridB[x][y]=255;

          if(GridR[x][y]<0) GridR[x][y]=0;
          if(GridG[x][y]<0) GridG[x][y]=0;
          if(GridB[x][y]<0) GridB[x][y]=0;  

          dbPlasma.setElem(x + (y*GridMX), ((int)GridR[x][y]<<16) | ((int)GridG[x][y]<<8) | (int)GridB[x][y]);
      } }
      repaint();
} } }
