/** * Paramecium * Copyright (C) 2003 Michael Birken * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * http://www.meatfighter.com */ package paramecium; import gamingtools.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; public class Paramecium extends FrameBuilder { private class Cell { private double angle; private double x; private double y; private int rotateCounter; private double angleDelta; private int translateCounter; private double xDelta; private double yDelta; public Cell() { angle = Math.random() * Math.PI * 2.0; x = random.nextInt(WIDTH - cellWidth) + cellWidth / 2; y = random.nextInt(HEIGHT - cellHeight) + cellHeight / 2; } public void move() { if (rotateCounter <= 0 && translateCounter <= 0) { rotateCounter = MIN_ROTATE_STEPS + random.nextInt(MAX_ROTATE_STEPS - MIN_ROTATE_STEPS); double newAngle = Math.random() * Math.PI * 2.0; angleDelta = (newAngle - angle) / rotateCounter; translateCounter = MIN_TRANSLATE_STEPS + random.nextInt(MAX_TRANSLATE_STEPS - MIN_TRANSLATE_STEPS); double newX, newY; int k = 0; do { newX = x + Math.cos(newAngle) * (MIN_DISTANCE + random.nextInt( (int)(MAX_DISTANCE - MIN_DISTANCE))); newY = y + Math.sin(newAngle) * (MIN_DISTANCE + random.nextInt( (int)(MAX_DISTANCE - MIN_DISTANCE))); k++; } while((newX < cellWidthOver2 || newX > WIDTH - cellWidthOver2 || newY < cellHeightOver2 || newY > HEIGHT - cellHeightOver2) && k < 20); if (k == 20) translateCounter = 0; xDelta = (newX - x) / translateCounter; yDelta = (newY - y) / translateCounter; } else if (rotateCounter > 0) { rotateCounter--; angle += angleDelta; } else { translateCounter--; x += xDelta; y += yDelta; } } public void drawCell(Graphics2D g) { affineTransform.setToIdentity(); affineTransform.translate(x, y); affineTransform.rotate(angle); affineTransform.translate(-cellWidthOver2, -cellHeightOver2); affineTransform.transform(dirtyRegion1, 0, dirtyRegion2, 0, 4); double minX, minY, maxX, maxY; minX = maxX = dirtyRegion2[0]; minY = maxY = dirtyRegion2[1]; for(int i = 2; i < 8; i += 2) { minX = Math.min(minX, dirtyRegion2[i]); maxX = Math.max(maxX, dirtyRegion2[i]); minY = Math.min(minY, dirtyRegion2[i+1]); maxY = Math.max(maxY, dirtyRegion2[i+1]); } g.drawImage(paramecium, affineTransform, null); markDirtyRegion((int)minX - 1, (int)minY - 1, (int)(maxX-minX) + 2, (int)(maxY-minY) + 2); } } public static final int WIDTH = 640; public static final int HEIGHT = 480; public static final int CELLS = 10; public static final int FPS = 60; public static final int MIN_ROTATE_STEPS = FPS / 2; public static final int MAX_ROTATE_STEPS = FPS * 2; public static final int MIN_TRANSLATE_STEPS = FPS * 2; public static final int MAX_TRANSLATE_STEPS = FPS * 6; public static final double MIN_DISTANCE = WIDTH / 10.0; public static final double MAX_DISTANCE = WIDTH / 2.0; private Image paramecium; private Image background; private AffineTransform affineTransform; private Cell[] cells; private double cellWidthOver2; private double cellHeightOver2; private int cellWidth; private int cellHeight; private double[] dirtyRegion1; private double[] dirtyRegion2; private Random random; public Paramecium() { GraphicsSource.enterDisplayMode(WIDTH, HEIGHT); Frame frame = GraphicsSource.getFrame(); frame.setTitle("Paramecium"); frame.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { GraphicsSource.stopAnimation(); GraphicsSource.exitDisplayMode(); System.exit(0); } } }); GraphicsSource.setRenderListener(this); GraphicsSource.startAnimation(FPS); } public void init() { affineTransform = new AffineTransform(); random = new Random(); paramecium = ImageSource.getImage("paramecium.gif"); cellWidthOver2 = (cellWidth = paramecium.getWidth(null)) / 2.0; cellHeightOver2 = (cellHeight = paramecium.getHeight(null)) / 2.0; cells = new Cell[CELLS]; for(int i = CELLS - 1; i >= 0; i--) cells[i] = new Cell(); dirtyRegion1 = new double[] { 0, 0, paramecium.getWidth(null), 0, 0, paramecium.getHeight(null), paramecium.getWidth(null), paramecium.getHeight(null) }; dirtyRegion2 = new double[8]; background = createBackgroundImage(); } public void renderBackground( Graphics g, int x, int y, int width, int height) { renderBackground(g); } public void renderBackground(Graphics g) { g.drawImage(background, 0, 0, null); } public boolean isBackgroundSame() { return true; } public void updateState() { for(int i = CELLS - 1; i >= 0; i--) cells[i].move(); } public void renderForeground(Graphics g) { for(int i = CELLS - 1; i >= 0; i--) cells[i].drawCell((Graphics2D)g); } public Image createBackgroundImage() { GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment .getLocalGraphicsEnvironment().getDefaultScreenDevice() .getDefaultConfiguration(); Image image = graphicsConfiguration.createCompatibleImage( WIDTH, HEIGHT, Transparency.BITMASK); Image background = ImageSource.getImage("background.gif"); Graphics g = null; try { g = image.getGraphics(); g.setColor(new Color(180, 175, 172)); g.fillRect(0, 0, WIDTH, HEIGHT); for(int i = 60; i >= 0; i--) g.drawImage( background, random.nextInt(WIDTH), random.nextInt(HEIGHT), null); } finally { if (g != null) g.dispose(); } return image; } public static void main(String[] args) { Paramecium paramecium = new Paramecium(); } }