import java.awt.*; import java.awt.geom.*; import java.awt.image.*; import java.awt.event.*; import java.applet.Applet; import java.util.*; import javax.swing.*; public class Galaxy extends JApplet implements Runnable { static int topy = 500; static int topx = 500; public Galaxy(){ init_galaxy(); startover(); } static Random rnd = new Random(); // used in Ball static int fps = 30; int frameNumber = -1; int delay; boolean frozen = false; Thread animatorThread; private BufferedImage bimg; // taken from gal.c static double FLOATRAND () { return rnd.nextFloat(); } static boolean WRAP = true; /* Warp around edges */ static boolean BOUNCE = true; /* Bounce from borders */ static int MINSIZE= 1; static int MINGALAXIES = 1; static int MAX_STARS = 300; static int MAX_IDELTAT =50; /* These come originally from the Cluster-version */ static int DEFAULT_GALAXIES =5; static int DEFAULT_STARS =1000; static int DEFAULT_HITITERATIONS =7500; static int DEFAULT_IDELTAT =200; /* 0.02 */ static double EPSILON =0.00000001; static double sqrt_EPSILON =0.0001; static double DELTAT = MAX_IDELTAT * 0.0001; static double GALAXYRANGESIZE =0.1; static double GALAXYMINSIZE =0.1; static double QCONS =0.001; static int COLORBASE = 312234; static int NUMCOLORS = 256*256*256; static int COLORSTEP = (int)NUMCOLORS / COLORBASE; void drawStar(int x,int y,int size, Graphics2D g) { g.fill(new Ellipse2D.Double((double)x, y, size, size)); } static Universe universes = new Universe(); static void startover() { Universe gp = universes; int size = 1; int i, j; /* more tmp */ double w1, w2; /* more tmp */ double d, v, w, h; /* yet more tmp */ gp.step = 0; //if (MI_BATCHCOUNT(mi) < -MINGALAXIES) // free_galaxies(gp); // gp.ngalaxies = MI_BATCHCOUNT(mi); gp.ngalaxies = 5; if (gp.ngalaxies < -MINGALAXIES) gp.ngalaxies = rnd.nextInt(-gp.ngalaxies - MINGALAXIES + 1) + MINGALAXIES; else if (gp.ngalaxies < MINGALAXIES) gp.ngalaxies = MINGALAXIES; for (i = 0; i < gp.ngalaxies; ++i) { GalaxyObject gt = new GalaxyObject(); double sinw1, sinw2, cosw1, cosw2; gt.galcol = new Color(rnd.nextInt(COLORBASE - 2)); if (gt.galcol.getRGB() > 1) gt.galcol = new Color(gt.galcol.getRGB() + 2); /* Mult 8; 16..31 no green stars */ /* Galaxies still may have some green stars but are not all green. */ if(gt.stars.size() > 0) { gt.stars = new Vector(); } gt.nstars = 10; //(rnd.nextInt(MAX_STARS / 2)) + MAX_STARS / 2; w1 = 2.0 * Math.PI * FLOATRAND(); w2 = 2.0 * Math.PI * FLOATRAND(); sinw1 = Math.sin(w1); sinw2 = Math.sin(w2); cosw1 = Math.cos(w1); cosw2 = Math.cos(w2); gp.mat[0][0] = cosw2; gp.mat[0][1] = -sinw1 * sinw2; gp.mat[0][2] = cosw1 * sinw2; gp.mat[1][0] = 0.0; gp.mat[1][1] = cosw1; gp.mat[1][2] = sinw1; gp.mat[2][0] = -sinw2; gp.mat[2][1] = -sinw1 * cosw2; gp.mat[2][2] = cosw1 * cosw2; gt.vel[0] = FLOATRAND() * 2.0 - 1.0; gt.vel[1] = FLOATRAND()* 2.0 - 1.0; gt.vel[2] = FLOATRAND()* 2.0 - 1.0; gt.pos[0] = -gt.vel[0] * DELTAT * gp.f_hititerations + FLOATRAND()- 0.5; gt.pos[1] = -gt.vel[1] * DELTAT * gp.f_hititerations + FLOATRAND()- 0.5; gt.pos[2] = -gt.vel[2] * DELTAT * gp.f_hititerations + FLOATRAND()- 0.5; gt.mass = (int) (FLOATRAND()* 1000.0) + 1; gp.size = GALAXYRANGESIZE * FLOATRAND()+ GALAXYMINSIZE; for (j = 0; j < gt.nstars; ++j) { Star st = new Star(); double sinw, cosw; w = 2.0 * Math.PI * FLOATRAND(); sinw = Math.sin(w); cosw = Math.cos(w); d = FLOATRAND()* gp.size; h = FLOATRAND()* Math.exp(-2.0 * (d / gp.size)) / 5.0 * gp.size; if (FLOATRAND()< 0.5) h = -h; st.pos[0] = gp.mat[0][0] * d * cosw + gp.mat[1][0] * d * sinw + gp.mat[2][0] * h + gt.pos[0]; st.pos[1] = gp.mat[0][1] * d * cosw + gp.mat[1][1] * d * sinw + gp.mat[2][1] * h + gt.pos[1]; st.pos[2] = gp.mat[0][2] * d * cosw + gp.mat[1][2] * d * sinw + gp.mat[2][2] * h + gt.pos[2]; v = Math.sqrt(gt.mass * QCONS / Math.sqrt(d * d + h * h)); st.vel[0] = -gp.mat[0][0] * v * sinw + gp.mat[1][0] * v * cosw + gt.vel[0]; st.vel[1] = -gp.mat[0][1] * v * sinw + gp.mat[1][1] * v * cosw + gt.vel[1]; st.vel[2] = -gp.mat[0][2] * v * sinw + gp.mat[1][2] * v * cosw + gt.vel[2]; st.vel[0] *= DELTAT; st.vel[1] *= DELTAT; st.vel[2] *= DELTAT; st.px = 0; st.py = 0; if (size < -MINSIZE) st.size = rnd.nextInt((-size - MINSIZE + 1) + MINSIZE); else if (size < MINSIZE) st.size = MINSIZE; else st.size = size; gt.stars.insertElementAt(st, j); } gp.galaxies.insertElementAt(gt, i); } } public static void init_galaxy() { if (universes == null) { universes = new Universe(); } universes.f_hititerations = 100; universes.clip.left = 0; universes.clip.top = 0; universes.clip.right = topx; universes.clip.bottom = topy; universes.scale = (double) (universes.clip.right + universes.clip.bottom) / 8.0; universes.midx = universes.clip.right / 2; universes.midy = universes.clip.bottom / 2; } void draw_galaxy(Graphics2D g2) { Universe gp = universes; double d; /* tmp */ int i, j, k; /* more tmp */ for (i = 0; i < gp.ngalaxies; ++i) { GalaxyObject gt = (GalaxyObject)(gp.galaxies.elementAt(i)); for (j = 0; j < ((GalaxyObject)gp.galaxies.elementAt(i)).nstars; ++j) { Star st = (Star)gt.stars.elementAt(j); double v0 = st.vel[0]; double v1 = st.vel[1]; double v2 = st.vel[2]; for (k = 0; k < gp.ngalaxies; ++k) { GalaxyObject gtk = (GalaxyObject)gp.galaxies.elementAt(k); double d0 = gtk.pos[0] - st.pos[0]; double d1 = gtk.pos[1] - st.pos[1]; double d2 = gtk.pos[2] - st.pos[2]; d = d0 * d0 + d1 * d1 + d2 * d2; if (d > EPSILON) d = gt.mass / (d * Math.sqrt(d)) * DELTAT * DELTAT * QCONS; else d = gt.mass / (EPSILON * sqrt_EPSILON) * DELTAT * DELTAT * QCONS; v0 += d0 * d; v1 += d1 * d; v2 += d2 * d; } st.vel[0] = v0; st.vel[1] = v1; st.vel[2] = v2; d = (v0 * v0 + v1 * v1 + v2 * v2) / (3.0 * DELTAT * DELTAT); if (d > (double) COLORSTEP) st.color = new Color(gt.galcol.getRGB() + COLORSTEP - 1); else st.color = new Color(gt.galcol.getRGB() + ((int) d) % COLORSTEP); st.pos[0] += v0; st.pos[1] += v1; st.pos[2] += v2; if (st.px >= gp.clip.left && st.px <= gp.clip.right - st.size && st.py >= gp.clip.top && st.py <= gp.clip.bottom - st.size) { //XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi)); g2.setColor(Color.black); drawStar(st.px, st.py, st.size, g2); } st.px = (int) (st.pos[0] * gp.scale) + gp.midx; st.py = (int) (st.pos[1] * gp.scale) + gp.midy; if (st.px >= gp.clip.left && st.px <= gp.clip.right - st.size && st.py >= gp.clip.top && st.py <= gp.clip.bottom - st.size) { // XSetForeground(display, gc, MI_PIXEL(mi, st.color)); g2.setColor(st.color); drawStar(st.px, st.py, st.size, g2); } } for (k = i + 1; k < gp.ngalaxies; ++k) { GalaxyObject gtk = (GalaxyObject)gp.galaxies.elementAt(k); double d0 = gtk.pos[0] - gt.pos[0]; double d1 = gtk.pos[1] - gt.pos[1]; double d2 = gtk.pos[2] - gt.pos[2]; d = d0 * d0 + d1 * d1 + d2 * d2; if (d > EPSILON) d = gt.mass * gt.mass / (d * Math.sqrt(d)) * DELTAT * QCONS; else d = gt.mass * gt.mass / (EPSILON * sqrt_EPSILON) * DELTAT * QCONS; d0 *= d; d1 *= d; d2 *= d; gt.vel[0] += d0 / gt.mass; gt.vel[1] += d1 / gt.mass; gt.vel[2] += d2 / gt.mass; gtk.vel[0] -= d0 / gtk.mass; gtk.vel[1] -= d1 / gtk.mass; gtk.vel[2] -= d2 / gtk.mass; } gt.pos[0] += gt.vel[0] * DELTAT; gt.pos[1] += gt.vel[1] * DELTAT; gt.pos[2] += gt.vel[2] * DELTAT; } gp.step++; if (gp.step > gp.f_hititerations * 4) startover(); } void release_galaxy() { if (universes != null) { universes = null; } } void refresh_galaxy() { /* Do nothing, it will refresh by itself */ } public void init() { delay = (fps > 500) ? (500 / fps) : 100; } public void start() { if (frozen) { //Do nothing. The user has requested that we //stop changing the image. } else { //Start animating! if (animatorThread == null) { animatorThread = new Thread(this); } animatorThread.start(); } } public void stop() { //Stop the animating thread. animatorThread = null; } public void reset(int x, int y) { topx = x; topy = y; universes.clip.left = 0; universes.clip.right = topx; universes.clip.top = 0; universes.clip.bottom = topy; } public boolean mouseDown(Event e, int x, int y) { if (frozen) { frozen = false; start(); } else { frozen = true; stop(); } return true; } public void run() { //Just to be nice, lower this thread's priority //so it can't interfere with other processing going on. //Thread.currentThread().setPriority(Thread.MIN_PRIORITY); //Remember the starting time. long startTime = System.currentTimeMillis(); //This is the animation loop. while (Thread.currentThread() == animatorThread) { //Advance the animation frame. frameNumber++; //Display it. repaint(); //Delay depending on how far we are behind. try { startTime += delay; Thread.sleep(Math.max(0, startTime-System.currentTimeMillis())); } catch (InterruptedException e) { break; } } } public Graphics2D createGraphics2D(int topx, int topy) { Graphics2D g2 = null; if (bimg == null || bimg.getWidth() != topx || bimg.getHeight() != topy) { bimg = (BufferedImage) createImage(topx, topy); reset(topx, topy); } g2 = bimg.createGraphics(); g2.setBackground(Color.black); g2.clearRect(0, 0, topx, topy); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); return g2; } public void paint(Graphics g) { Dimension d = getSize(); Graphics2D g2 = createGraphics2D(d.width, d.height); // update(topx, topy, g2); draw_galaxy(g2); g2.dispose(); g.drawImage(bimg, 0, 0, this); } public static void main(String argv[]) { final Galaxy galaxy = new Galaxy(); JFrame f = new JFrame("Galaxy"); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} public void windowDeiconified(WindowEvent e) { galaxy.start(); } public void windowIconified(WindowEvent e) { galaxy.stop(); } }); f.getContentPane().add("Center", galaxy); f.pack(); f.setSize(new Dimension(topx,topy)); f.show(); galaxy.start(); } }