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];
}
}