// Genetic art applet // // Author Matthew Caryl // Created 16.10.96 package AutomaticArt; import java.awt.*; import java.awt.image.*; import java.applet.*; import ADT.*; import java.io.*; public final class Artist extends Applet implements Runnable { private static final Color backgroundColor = Color.black; private static final ColorVector r = new ColorVector(1f, 0f, 0f); private static final ColorVector g = new ColorVector(0f, 1f, 0f); private static final ColorVector b = new ColorVector(0f, 0f, 1f); private Thread animation; private Graphics graphics; private Random random = new Random(); private Image[] images; private int widthInPictures, heightInPictures; public void init() { setBackground(backgroundColor); // number of images to fit in width String widthString = this.getParameter("widthInPictures"); try { widthInPictures = Integer.parseInt(widthString); if (widthInPictures < 1) throw new NumberFormatException("Picture width must be postive"); } catch (NumberFormatException e) { widthInPictures = 1; } // number of images to fit in height String heightString = this.getParameter("heightInPictures"); try { heightInPictures = Integer.parseInt(heightString); if (heightInPictures < 1) throw new NumberFormatException("Picture height must be postive"); } catch (NumberFormatException e) { heightInPictures = 1; } // Save graphics context for later // Note: this seems to reduce garbage collection problems graphics = this.getGraphics(); } public void destroy() { stop(); } public void start() { if (animation == null) { animation = new Thread(this); animation.start(); } } public void stop() { if (animation != null && animation.isAlive()) animation.stop(); animation = null; } public void run() { images = new Image[widthInPictures * heightInPictures]; paint(graphics); ColorTree t; switch (random.range(3)) { case 0: t = r; break; case 1: t = b; break; default: t = g; break; } int last = -1; for (int y = 0; y < heightInPictures; y++) for (int x = 0; x < widthInPictures; x++) { images[xy2i(x, y)] = newImage(t, width(), height()); paintImage(graphics, x, y); int current = random.range(3); while (current == last) current = random.range(3); switch (current) { case 0: t = new ColorNode(t, ColorNode.YROTATE); break; case 1: t = new ColorNode(t, ColorNode.XROTATE); break; default: t = new ColorNode(t, ColorNode.ZROTATE); break; } last = current; } } public void paint(Graphics g) { g.setColor(backgroundColor); g.fillRect(0, 0, width(), height()); for (int x = widthInPictures - 1; x >= 0; x--) for (int y = heightInPictures - 1; y >= 0; y--) paintImage(g, x, y); } public boolean mouseDown(Event e, int x, int y) { stop(); start(); return true; } private void paintImage(Graphics g, int x, int y) { int i = xy2i(x, y); int w = width() / widthInPictures; int h = height() / heightInPictures; if (images[i] != null) synchronized (images[i]) { g.drawImage(images[i], x * w, y * h, w, h, this); } } private int xy2i(int x, int y) { return y * widthInPictures + x; } int width() { return size().width; } int height() { return size().height; } private Image newImage(ColorTree n, int w, int h) { int[] pixels; int index = 0; ColorVector vector = new ColorVector(); pixels = new int[w * h]; for (int y = h - 1; y >= 0; y--) { float real_y = (float) y / (float) h; for (int x = w - 1; x >= 0; x--) { float real_x = (float) x / (float) w; n.vector(vector, real_x, real_y); pixels[index++] = vector.color(); } } return createImage(new MemoryImageSource(w, h, ColorModel.getRGBdefault(), pixels, 0, w)); } } abstract class ColorTree { abstract public void vector(ColorVector v, float x, float y); } class ColorVector extends ColorTree { Vector vector; public ColorVector() { vector = new Vector(); } public ColorVector(float x, float y, float z) { vector = new Vector(x, y, z); } public ColorVector(ColorVector v) { vector = new Vector(vector); } public void vector(ColorVector v, float x, float y) { v.vector.set(vector); } public int color() { int r = (int) (127f * vector.x) + 127; // into range [0, 254] int g = (int) (127f * vector.y) + 127; int b = (int) (127f * vector.z) + 127; return (255 << 24) | (r << 16) | (g << 8) | (b << 0); } } class ColorNode extends ColorTree { public static final int XROTATE = 1; public static final int YROTATE = 2; public static final int ZROTATE = 3; Matrix m; ColorVector i; ColorTree t; int operand; public ColorNode(ColorTree T, int O) { m = new Matrix(); i = new ColorVector(); t = T; operand = O; switch (operand) { case XROTATE: m.setRotateX(0f); break; case YROTATE: m.setRotateY(0f); break; case ZROTATE: m.setRotateZ(0f); break; } } public void vector(ColorVector v, float x, float y) { switch (operand) { case XROTATE: m.resetRotateX(2f * x * (float) (Math.PI)); break; case YROTATE: m.resetRotateY(2f * x * (float) (Math.PI)); break; case ZROTATE: m.resetRotateZ(2f * y * (float) (Math.PI)); break; } t.vector(i, x, y); m.transform(i.vector, v.vector); } }