| ||||||||||||||
| ||||||||||||||
|
||||||||||||||
|
This tutorial will briefly look at programming with the Generation5 JDK. The Generation5 JDK is broad and covers a wide-range of material, so this tutorial will focus on create a customized cellular automata renderer to create Conway's Life diagrams (see below). A separate tutorial will look at creating your own cellular automata examples.
For writeImage we can use ImageHelper's writeVisualizedImage method (org.generation5.util package) that takes the same parameters as writeImage with an additional parameter, an instance of Visualizable. The method then uses the render method to draw onto a graphics context, then saves that graphics context to a file:
Understanding the ProblemSo now with a simple understanding of how the JDK renders images, how do we create diagrams like the one shown above? We have two initial problems: firstly, we need to change how the cellular automata are rendered to create the 'celled' effect (adding the spacing and borders for each cell) and secondly, we need to solve the problem of rendering multiple instances of the same CA on the same graphics context.We will be creating a class called CellDiagram to solve this problem. Since we will be wanting to write the diagrams, the class has to implement Visualizable as discussed. Rendering the CellsThe best method of rendering the cells is to derive from the Conway's Life demo (org.generation5.demos.ConwaysLifeCA) and override the render method. Since the class has little advantage outside this application, so we will implement it as an inner class. Our class currently looks like:public class CellDiagram implements Visualizable { public void render(java.awt.Graphics graphics, int pw, int ph) { // Diagram implementation here } public void writeImage(String s, int width, int height) { try { ImageHelper.writeVisualizedImage(s, width, height, this); } catch (IOException e) { System.err.println(e); } } public static class CellLife extends ConwaysLifeCA { public void render(java.awt.Graphics graphics, int pw, int ph) { // Cell implementation here } } }The actual implementation details of drawing the cells is irrelevant (see the source code), but when overriding the render method, there are important considerations to remember. Principally, you must remember that the image dimensions can be much larger than the area your data will be drawn in. You will often need to calculate the necessary pixel positions to centre image within the graphics context.
Rendering Multiple InstancesRendering multiple instances of the cellular automata can be achieved very easily using a similar trick to ImageHelper.writeVisualizedImage. Here we create an array of BufferedImage equal to the number of steps we want to render. Then we calculate the necessary dimensions for the images, taking into account border sizes.Next, we render the various steps on buffered images, advancing the CA one time-step by called ca.doStep. Finally, we copy the images on to the final graphics context using drawImage and then drawing a border. To expand the usefulness of the class further, we will allow the images to be drawn across multiple rows and columns. BufferedImage[] shots = new BufferedImage[steps]; int dw = ca.getSizeX() * ca.getCASize() + (ca.getSizeX() - 1) * ca.cellSpace; int dy = ca.getSizeY() * ca.getCASize() + (ca.getSizeY() - 1) * ca.cellSpace; dw += borderSize; dy += borderSize; for (int i=0; i<shots.length; i++) shots[i] = new BufferedImage(dw, dy, 1); for (int i=0; i<shots.length; i++) { ca.render(shots[i].getGraphics(), dw, dy); ca.doStep(); } g.setColor(ca.getBackgroundColor()); g.fillRect(0, 0, width, height); for (int i=0; i<shots.length; i++) g.drawImage(shots[i], (dw) * (i % columns), (dy) * (i / columns), dw, dy, null); g.setColor(java.awt.Color.BLACK); g.drawRect(0, 0, width-1, height-1); Using the ClassThe core of the class is in place, all that is needed is to add functionality to customize your diagrams. Most of this can be done using the CellularAutomata methods, but additional methods are added to set the number of steps to render, the size of the image borders and CA sizes. See the source code for the completed version of the class.Now for some examples of how to use the class:
Glider - Type 1ca.setBackgroundColor(java.awt.Color.lightGray); ca.setWorldSize(6, 6); ca.setWorldAtEx(1, 1, "0,0,1;1,0,1;0,1,1"); ca.init(); diagram.writeImage("glider01.png", diagram.getDiagramWidth(), diagram.getDiagramHeight());
Glider - Type 2diagram.setDimensions(2,3); ca.setWorldSize(9, 7); ca.setWorldAtEx(1, 2, "0,0,1,1;1,1,0,1,1;1,1,1,1;0,1,1"); ca.init(); diagram.writeImage("glider02.png", diagram.getDiagramWidth(), diagram.getDiagramHeight());
Static Objectdiagram.setDimensions(2,2); diagram.setSteps(4); ca.setWorldSize(17, 17); ca.setWorldAtEx(2, 2, "0,0,1,1,1;0;1,0,0,0,0,1;" + "1,0,0,0,0,1;1,0,0,0,0,1;0,0,1,1,1;"); ca.setWorldAtEx(9, 2, "0,1,1,1;0;1,0,0,0,0,1;" + "1,0,0,0,0,1;1,0,0,0,0,1;0,1,1,1;"); ca.setWorldAtEx(2, 9, "0,0,1,1,1;1,0,0,0,0,1;" + "1,0,0,0,0,1;1,0,0,0,0,1;0;0,0,1,1,1;"); ca.setWorldAtEx(9, 9, "0,1,1,1;1,0,0,0,0,1;1,0,0,0,0,1;" + "1,0,0,0,0,1;0;0,1,1,1;"); ca.setBackgroundColor(java.awt.Color.WHITE); ca.setCASize(8); ca.init(); diagram.writeImage("static01.png", diagram.getDiagramWidth(), diagram.getDiagramHeight());
Future ImprovementsThere are a variety of improvements you might want to make to this class. For example, instead of hard-coding diagram parameters into main, read them from an XML or text file. Furthermore, the current class is geared toward Conway's Life, generalize the class for Visualizable classes, as well as expanding the 'celled' format to all CellularAutomata classes.
Submitted: 19/08/2004 Article content copyright © James Matthews, 2004.
|
|
|||||||||||||
All content copyright © 1998-2007, Generation5 unless otherwise noted.
- Privacy Policy - Legal - Terms of Use -