Space Life (Java Applet)

Download for Symbian UIQ based Mobile phone, e.g. Motorola A1000

Usage

<applet code="SpaceLife.class" width="300" height="300"></applet>

Download for Symbian UIQ Mobile Devices

SpaceLife.SIS (tested with Motorola A1000)

Source Code

// Space Life
// Copyright (C) 2002 Charles Foster
// Please visit www.cfoster.net

import java.awt.*;
import java.awt.image.*;
import java.applet.*;

public final class SpaceLife extends Applet implements Runnable
{
  Thread thread;

  Image total_image;
  int total_pixels[];
  public static int w, h, cx, cy;

  int oldx,oldy;
  boolean touchy=false;

  // Constants
  public static final int MAX_PARTICLES = 30;
  public static final int MAX_STARS = 1000;
  public static final int STAR_COLOUR = 0xFFB4B4B4;
  public static final int RANDOM_JUNK = 4;

  public static final float DEG2RAD=(float)3.14159265/180;

  public static final Matrix SRM =
  Matrix.rotationMatrix(.0015f,.0015f,.0015f);

  // objects (3d space)
  obj[] parts = new obj[MAX_PARTICLES];
  obj[] stars = new obj[MAX_STARS];
  obj[] paths = new obj[MAX_PARTICLES];
  obj[] rndob = new obj[RANDOM_JUNK];
  obj star;

  // MIS and CM
  MemoryImageSource mis;
  ColorModel cm=ColorModel.getRGBdefault();

  float size=100; // Particle distance away from (0,0,0)

  public void init()
  {
    cx=(int)((w=size().width)/2);
    cy=(int)((h=size().width)/2);

    initImages();

    setupStars();
    setupParticles();

    initBG();
    setupParticlePaths();
    getRandomJunk();
    setupRandomJunk();

    mis=new MemoryImageSource(w,h,cm,total_pixels,0,w);
    mis.setAnimated(true);
    mis.setFullBufferUpdates(true);
    total_image=createImage(mis);

    Matrix.buildLUT();
  }

  public void run()
  {
    while(true)
    {
      repaint();
      try { thread.sleep(15); } catch(InterruptedException e) { }
    }
  }

  public boolean imageUpdate(Image image,
    int i1, int j1, int k, int i2, int j2)
  {
    return true;
  }

  public void start()
  {
    if(thread==null)
    {
      thread=new Thread(this);
      thread.start();
    }
  }
  public void stop()
  {
    if(thread!=null)
    {
      thread.stop();
      thread=null;
    }
  }

  public void paint(Graphics g) {}

  public void update(Graphics g)
  {
    for(int i=w*h-1;i>=0;i--)
      total_pixels[i]=0xFF000000;

    // JUNK
    for(int i=0;i<RANDOM_JUNK;i++)
    {
      rndob[i].transform(SRM);
      if(rndob[i].z<-30)
        placeOnImage(rndob[i].data,
        rndob[i].xx, rndob[i].yy, rndob[i].w, rndob[i].h);
    }


    // STARS
    for(int i=0;i<MAX_STARS;i++)
    {
      stars[i].transform(SRM);
      //rotateStars(.0015f,.0015f,.0015f);
      if(stars[i].z<0)
        placeOnImage(star.data, stars[i].xx, stars[i].yy,
        star.w, star.h);
    }

    // PARTICLES
    for(int i=0;i<MAX_PARTICLES;i++)
    {
      rotateParticle(i,paths[i].x,paths[i].y,paths[i].z);
      placeOnImage(parts[i].data, parts[i].xx, parts[i].yy,
      parts[i].w, parts[i].h);
    }
    mis.newPixels(0,0,w,h);
    g.drawImage(total_image,0,0,this);
  }

  public void getRandomJunk()
  {
    Image im;
    for(int i=0;i<RANDOM_JUNK;i++)
    {
      rndob[i] = new obj();
      im = getImage(getCodeBase(),"junk"+i+".jpg");
      while((rndob[i].w=im.getWidth(this))<0
             || (rndob[i].h=im.getHeight(this))<0);

      rndob[i].data = new int[rndob[i].w*rndob[i].h];
      rndob[i].data = getPixels(im,rndob[i].w,rndob[i].h);
    }
  }

  public void setupRandomJunk()
  {
    for(int i=0;i<RANDOM_JUNK;i++)
    {
      rndob[i].x=(float)(Math.random()-.5);
      rndob[i].y=(float)(Math.random()-.5);
      rndob[i].z=(float)(Math.random()-.5);
      float l =
      (float)(1/Math.sqrt(rndob[i].x*rndob[i].x +
                          rndob[i].y*rndob[i].y +
                          rndob[i].z*rndob[i].z));

      rndob[i].x*=l*250; rndob[i].y*=l*250; rndob[i].z*=l*250;
    }
  }

  public void initBG()
  {
    total_pixels=new int[w*h];
    for(int i=w*h-1;i>=0;i--)
      total_pixels[i]=0xFF000000;
    star = new obj();
    star.w=1;
    star.h=1;
    star.data = new int[] { STAR_COLOUR };
  }

  public void initImages()
  {
    for(int i=0;i<MAX_PARTICLES;i++)
    {
      int rand=(int)Math.floor(Math.random()*6);
      parts[i]=new obj();
      Image im = getImage(getCodeBase(),"particle"+rand+".jpg");
      while((parts[i].w=im.getWidth(this))<0 ||
            (parts[i].h=im.getHeight(this))<0);
      parts[i].data = new int[parts[i].w*parts[i].h];
      parts[i].data = getPixels(im,parts[i].w,parts[i].h);
    }
  }

  public void setupStars()
  {
    for(int i=0;i<MAX_STARS;i++)
    {
      stars[i]=new obj();
      stars[i].x=(float)(Math.random()-.5);
      stars[i].y=(float)(Math.random()-.5);
      stars[i].z=(float)(Math.random()-.5);
      float l = (float)(1/Math.sqrt(stars[i].x*stars[i].x +
                                    stars[i].y*stars[i].y +
                                     stars[i].z*stars[i].z));
      stars[i].x*=l*250; stars[i].y*=l*250; stars[i].z*=l*250;
    }
  }

  public void setupParticles()
  {
    for(int i=0;i<MAX_PARTICLES;i++)
    {
      parts[i].x=(float)(Math.random()-.5);
      parts[i].y=(float)(Math.random()-.5);
      parts[i].z=(float)(Math.random()-.5);
      float l = (float)(1/Math.sqrt(parts[i].x*parts[i].x +
                                    parts[i].y*parts[i].y +
                                    parts[i].z*parts[i].z));
      parts[i].x*=l*size; parts[i].y*=l*size; parts[i].z*=l*size;
    }
  }

  public void setupParticlePaths()
  {
    for(int i=0;i<MAX_PARTICLES;i++)
    {
      paths[i]=new obj();
      paths[i].x=(float)((Math.random()-.5)/10);
      paths[i].y=(float)((Math.random()-.5)/10);
      paths[i].z=(float)((Math.random()-.5)/10);
    }
  }

  public int[] getPixels(Image i, int w, int h)
  {
    int[] t=new int[w*h];
    PixelGrabber pg = new PixelGrabber(i,0,0,w,h,t,0,w);
    try { pg.grabPixels(); }
    catch(InterruptedException e){}
    return t;
  }


  int xEnd, yBase, ixBase, ixC, iyC, temp_y, temp_x;
  int overflow,pixel;
  final void placeOnImage(int[] image, int _x, int _y, int _w, int _h)
  {
    _h += _y;
    _h = Math.min(_h,h);
    xEnd = Math.min(_x+_w,w);
    ixBase = -Math.min(0,_x);
    ixC=ixBase;
    iyC=-Math.min(0,_y);
    _x = Math.max(_x,0);
    _y = Math.max(_y,0);

    for(temp_y=_y;temp_y<_h;temp_y++)
    {
      ixC=ixBase;
      yBase = temp_y * w;
      for(temp_x=_x;temp_x<xEnd;temp_x++)
      {
        pixel=(total_pixels[temp_x+yBase]&0xFEFEFF) +
              (image[iyC*_w + ixC++]&0xFEFEFF);

        overflow=pixel&0x1010100;
        overflow=overflow-(overflow>>8);
        total_pixels[temp_x+yBase]=0xFF000000|pixel|overflow;
      }
      iyC++;
    }
  }

  public void rotateParticle(int P, float dx, float dy, float dz)
  {
    float qx,qy,qz;
  // Rotation arround x-axis
    qy=parts[P].y;
    qz=parts[P].z;
    parts[P].y = qy*Matrix.cos(dx) - qz*Matrix.sin(dx);
    parts[P].z = qz*Matrix.cos(dx) + qy*Matrix.sin(dx);
  // Rotation arround y-axis
    qz=parts[P].z;
    qx=parts[P].x;
    parts[P].z = qz*Matrix.cos(dy) - qx*Matrix.sin(dy);
    parts[P].x = qx*Matrix.cos(dy) + qz*Matrix.sin(dy);
  // Rotation arround z-axis
    qx=parts[P].x;
    qy=parts[P].y;
    parts[P].x = qx*Matrix.cos(dz) - qy*Matrix.sin(dz);
    parts[P].y = qy*Matrix.cos(dz) + qx*Matrix.sin(dz);
  // Calculate Points (3d -> 2d)
    parts[P].xx = (int)(parts[P].x+cx*.8);
    parts[P].yy = (int)(parts[P].y+cy*.8); // speed impediment?
  }

  public boolean mouseDown(Event evt,int x,int y)
  {
    touchy=true;
    for(int i=0;i<MAX_PARTICLES;i++)
      rotateParticle(i,(float)(y-oldy)*DEG2RAD,
                     (float)(oldx-x)*DEG2RAD,(float)0.0);
    oldx=x;
    oldy=y;

    return true;
  }

  public boolean mouseDrag(Event e,int x,int y)
  {
    touchy=true;
    for(int i=0;i<MAX_PARTICLES;i++)
      rotateParticle(i,(float)(y-oldy)*DEG2RAD,
                     (float)(oldx-x)*DEG2RAD,(float)0.0);
    oldx=x;
    oldy=y;
    return true;
  }
  public boolean mouseUp(Event e,int x,int y)
  {
    touchy=false;
    return true;
  }
  public boolean gotFocus(Event e, Object o)
  {
    return true;
  }
  public boolean lostFocus(Event e, Object o)
  {
    return true;
  }
}
class obj
{
  float x,y,z;
  int xx,yy,w,h;
  int[] data;

  public void transform(Matrix m)
  {
    x = x*m.m00 + y*m.m10 + z*m.m20+ m.m30;
    y = x*m.m01 + y*m.m11 + z*m.m21+ m.m31;
    z = x*m.m02 + y*m.m12 + z*m.m22+ m.m32;
    xx = (int)(x + SpaceLife.cx);
    yy = (int)(y + SpaceLife.cy);
  }
}
class Matrix
{
  float m00=1,m01=0,m02=0,m03=0,
        m10=0,m11=1,m12=0,m13=0,
        m20=0,m21=0,m22=1,m23=0,
        m30=0,m31=0,m32=0,m33=1;

  static float[] sin;
  static float[] cos;
  static boolean builtLUT=false;

  static final float rad2scale=4096f/3.14159265f/2f;
  static final float pad=256*3.14159265f;

  public Matrix()
  {
    buildLUT();
  }
  public static void buildLUT()
  {
    if(builtLUT)
      return;
    sin=new float[4096];
    cos=new float[4096];
    for(int i=0;i<4096;i++)
    {
      sin[i]=(float)Math.sin((float)i/rad2scale);
      cos[i]=(float)Math.cos((float)i/rad2scale);
    }
    builtLUT=true;
  }

  public static Matrix rotationMatrix(float dx, float dy, float dz)
  {
    Matrix out=new Matrix();
    float SIN;
    float COS;

    if(dx!=0)
    {
      Matrix m=new Matrix();
      SIN=sin(dx);
      COS=cos(dx);
      m.m11=COS;  m.m12=SIN;
      m.m21=-SIN; m.m22=COS;
      //out.postTransform(m);
      out.preTransform(m);
    }
    if(dy!=0)
    {
      Matrix m=new Matrix();
      SIN=sin(dy);
      COS=cos(dy);
      m.m00=COS;  m.m02=SIN;
      m.m20=-SIN; m.m22=COS;
      //out.postTransform(m);
      out.preTransform(m);
    }
    if(dz!=0)
    {
      Matrix m=new Matrix();
      SIN=sin(dz);
      COS=cos(dz);
      m.m00=COS;  m.m01=SIN;
      m.m10=-SIN; m.m11=COS;
      //out.postTransform(m);
      out.preTransform(m);
    }
    return out;
  }
  public void rotate(float dx, float dy, float dz)
  {
    postTransform(rotationMatrix(dx,dy,dz));
  }
  public void postTransform(Matrix n) // this * n
  {
    Matrix m=this.gimmieClone();
    m00=m.m00*n.m00+m.m01*n.m10+m.m02*n.m20;
    m01=m.m00*n.m01+m.m01*n.m11+m.m02*n.m21;
    m02=m.m00*n.m02+m.m01*n.m12+m.m02*n.m22;
    m03=m.m00*n.m03+m.m01*n.m13+m.m02*n.m23+m.m03;
    m10=m.m10*n.m00+m.m11*n.m10+m.m12*n.m20;
    m11=m.m10*n.m01+m.m11*n.m11+m.m12*n.m21;
    m12=m.m10*n.m02+m.m11*n.m12+m.m12*n.m22;
    m13=m.m10*n.m03+m.m11*n.m13+m.m12*n.m23+m.m13;
    m20=m.m20*n.m00+m.m21*n.m10+m.m22*n.m20;
    m21=m.m20*n.m01+m.m21*n.m11+m.m22*n.m21;
    m22=m.m20*n.m02+m.m21*n.m12+m.m22*n.m22;
    m23=m.m20*n.m03+m.m21*n.m13+m.m22*n.m23+m.m23;
  }
  public void preTransform(Matrix n) // n * this
  {
    Matrix m = this.gimmieClone();
    m00=n.m00*m.m00+n.m01*m.m10+n.m02*m.m20;
    m01=n.m00*m.m01+n.m01*m.m11+n.m02*m.m21;
    m02=n.m00*m.m02+n.m01*m.m12+n.m02*m.m22;
    m03=n.m00*m.m03+n.m01*m.m13+n.m02*m.m23+n.m03;
    m10=n.m10*m.m00+n.m11*m.m10+n.m12*m.m20;
    m11=n.m10*m.m01+n.m11*m.m11+n.m12*m.m21;
    m12=n.m10*m.m02+n.m11*m.m12+n.m12*m.m22;
    m13=n.m10*m.m03+n.m11*m.m13+n.m12*m.m23+n.m13;
    m20=n.m20*m.m00+n.m21*m.m10+n.m22*m.m20;
    m21=n.m20*m.m01+n.m21*m.m11+n.m22*m.m21;
    m22=n.m20*m.m02+n.m21*m.m12+n.m22*m.m22;
    m23=n.m20*m.m03+n.m21*m.m13+n.m22*m.m23+n.m23;
  }
  public Matrix gimmieClone()
  {
    Matrix m=new Matrix();
    m.m00=m00; m.m01=m01; m.m02=m02; m.m03=m03;
    m.m10=m10; m.m11=m11; m.m12=m12; m.m13=m13;
    m.m20=m20; m.m21=m21; m.m22=m22; m.m23=m23;
    m.m30=m30; m.m31=m31; m.m32=m32; m.m33=m33;
    return m;
  }
  public static final float sin(float a)
  {
    return sin[(int)((a+pad)*rad2scale)&0xFFF];
  }
  public static final float cos(float a)
  {
    return cos[(int)((a+pad)*rad2scale)&0xFFF];
  }
}