/* * AutomatonCanvas.java * Copyright (C) 2002 Frank Buß (fb@frank-buss.de) * * 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 can get the GNU General Public License at * http://www.gnu.org/licenses/gpl.html */ import java.awt.*; import java.math.*; public class AutomatonCanvas extends Canvas { private Automaton parent; private int automatonWidth = 250; private boolean automaton[][]; private boolean automaton2[][]; private Image buffer; private Graphics gBuffer; private int cornerCount; private boolean rules[] = new boolean[512]; private int baseGridWidth = 30; private int gridWidth; private int baseGridHeight = 60; private int gridHeight; private int baseEdge = 4; private int edge; private int hexagonWidth = 8; private int ofs = 20; private BigInteger rule; private Color white = new Color(255, 255, 255); private Color black = new Color(0, 0, 0); public AutomatonCanvas(Automaton parent) { this.parent = parent; // Double-Buffering buffer = parent.createImage(2 * automatonWidth, 2 * automatonWidth); gBuffer = buffer.getGraphics(); init(); } private void drawSquare(int x0, int y0, boolean fill) { if (fill) { gBuffer.fillRect(x0, y0, edge + 1, edge + 1); } else { gBuffer.drawRect(x0, y0, edge, edge); } } private void updateSquareResultDraw(int x, int y, boolean fill) { drawSquare(x * gridWidth + edge + ofs, y * gridHeight + 7 * edge + ofs, fill); } private void updateSquareResult(int x, int y, boolean set) { if (set) { updateSquareResultDraw(x, y, true); } else { gBuffer.setColor(white); updateSquareResultDraw(x, y, true); gBuffer.setColor(black); updateSquareResultDraw(x, y, false); } } private void drawHexagon(int x0, int y0, boolean fill) { Polygon p = new Polygon(); p.addPoint(x0 + hexagonWidth / 2, y0); p.addPoint(x0 + hexagonWidth, y0 + edge / 2); p.addPoint(x0 + hexagonWidth, y0 + edge / 2 + edge); p.addPoint(x0 + hexagonWidth / 2, y0 + 2 * edge); p.addPoint(x0, y0 + edge / 2 + edge); p.addPoint(x0, y0 + edge / 2); if (fill) { gBuffer.fillPolygon(p); } else { gBuffer.drawPolygon(p); } } private void updateHexagonResultDraw(int x, int y, boolean fill) { drawHexagon( x * gridWidth + hexagonWidth + ofs, y * gridHeight + edge * 7 + ofs, fill); } private void updateHexagonResult(int x, int y, boolean set) { if (set) { updateHexagonResultDraw(x, y, true); } else { gBuffer.setColor(white); updateHexagonResultDraw(x, y, true); gBuffer.setColor(black); updateHexagonResultDraw(x, y, false); } } public void init() { automaton = new boolean[automatonWidth][automatonWidth]; automaton2 = new boolean[automatonWidth][automatonWidth]; if (parent.isInitPoint()) automaton[automatonWidth / 2][automatonWidth / 2] = true; gBuffer.setColor(white); gBuffer.fillRect(0, 0, 2 * automatonWidth, 2 * automatonWidth); gBuffer.setColor(black); cornerCount = parent.getCornerCount(); rule = parent.getRule(); for (int i = 0; i < 512; i++) { rules[i] = rule.testBit(i); } if (parent.isRuleMode()) { int bit = 0; if (cornerCount == 4) { gridWidth = baseGridWidth * 2; gridHeight = baseGridHeight * 2; edge = baseEdge * 2; for (int y = 0; y < 4; y++) { for (int x = 0; x < 8; x++) { updateSquareResult(x, y, rule.testBit(bit)); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 4 + ofs, x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs, x * gridWidth + edge * 3 / 2 - edge / 2 + ofs, y * gridHeight + edge * 5 + ofs); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs, x * gridWidth + edge * 3 / 2 + edge / 2 + ofs, y * gridHeight + edge * 5 + ofs); drawSquare( x * gridWidth + edge + ofs, y * gridHeight + edge * 2 + ofs, (bit & 1) > 0); drawSquare( x * gridWidth + edge * 2 + ofs, y * gridHeight + edge + ofs, (bit & 2) > 0); drawSquare(x * gridWidth + edge + ofs, y * gridHeight + ofs, (bit & 4) > 0); drawSquare(x * gridWidth + ofs, y * gridHeight + edge + ofs, (bit & 8) > 0); drawSquare( x * gridWidth + edge + ofs, y * gridHeight + edge + ofs, (bit & 16) > 0); bit++; } } } else if (cornerCount == 6) { gridWidth = baseGridWidth; gridHeight = baseGridHeight; edge = baseEdge; for (int y = 0; y < 8; y++) { for (int x = 0; x < 16; x++) { updateHexagonResult(x, y, rule.testBit(bit)); gBuffer.drawLine( x * gridWidth + hexagonWidth * 3 / 2 + ofs, y * gridHeight + edge * 5 + ofs, x * gridWidth + hexagonWidth * 3 / 2 + ofs, y * gridHeight + edge * 7 + ofs); gBuffer.drawLine( x * gridWidth + hexagonWidth * 3 / 2 + ofs, y * gridHeight + edge * 7 + ofs, x * gridWidth + hexagonWidth * 3 / 2 - edge / 2 + ofs, y * gridHeight + edge * 6 + ofs); gBuffer.drawLine( x * gridWidth + hexagonWidth * 3 / 2 + ofs, y * gridHeight + edge * 7 + ofs, x * gridWidth + hexagonWidth * 3 / 2 + edge / 2 + ofs, y * gridHeight + edge * 6 + ofs); drawHexagon( x * gridWidth + hexagonWidth * 3 / 2 + ofs + 1, y * gridHeight + edge * 3 + ofs + 1, (bit & 1) > 0); drawHexagon( x * gridWidth + 2 * hexagonWidth + ofs + 1, y * gridHeight + edge * 3 / 2 + ofs, (bit & 2) > 0); drawHexagon( x * gridWidth + hexagonWidth * 3 / 2 + ofs + 1, y * gridHeight + ofs - 2, (bit & 4) > 0); drawHexagon( x * gridWidth + hexagonWidth / 2 + ofs - 1, y * gridHeight + ofs - 2, (bit & 8) > 0); drawHexagon( x * gridWidth + ofs - 1, y * gridHeight + edge * 3 / 2 + ofs, (bit & 16) > 0); drawHexagon( x * gridWidth + hexagonWidth / 2 + ofs - 1, y * gridHeight + edge * 3 + ofs + 2, (bit & 32) > 0); drawHexagon( x * gridWidth + hexagonWidth + ofs, y * gridHeight + edge * 3 / 2 + ofs, (bit & 64) > 0); bit++; } } } else if (cornerCount == 8) { gridWidth = baseGridWidth / 2; gridHeight = baseGridHeight / 2; edge = baseEdge / 2; for (int y = 0; y < 16; y++) { for (int x = 0; x < 32; x++) { updateSquareResult(x, y, rule.testBit(bit)); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 4 + ofs, x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs, x * gridWidth + edge * 3 / 2 - edge / 2 + ofs, y * gridHeight + edge * 5 + ofs); gBuffer.drawLine( x * gridWidth + edge * 3 / 2 + ofs, y * gridHeight + edge * 6 + ofs, x * gridWidth + edge * 3 / 2 + edge / 2 + ofs, y * gridHeight + edge * 5 + ofs); drawSquare( x * gridWidth + edge + ofs, y * gridHeight + edge * 2 + ofs + 1, (bit & 1) > 0); drawSquare( x * gridWidth + edge * 2 + ofs + 1, y * gridHeight + edge * 2 + ofs + 1, (bit & 2) > 0); drawSquare( x * gridWidth + edge * 2 + ofs + 1, y * gridHeight + edge + ofs, (bit & 4) > 0); drawSquare( x * gridWidth + edge * 2 + ofs + 1, y * gridHeight + ofs - 1, (bit & 8) > 0); drawSquare( x * gridWidth + edge + ofs, y * gridHeight + ofs - 1, (bit & 16) > 0); drawSquare(x * gridWidth + ofs - 1, y * gridHeight + ofs - 1, (bit & 32) > 0); drawSquare( x * gridWidth + ofs - 1, y * gridHeight + edge + ofs, (bit & 64) > 0); drawSquare( x * gridWidth + ofs - 1, y * gridHeight + edge * 2 + ofs + 1, (bit & 128) > 0); drawSquare( x * gridWidth + edge + ofs, y * gridHeight + edge + ofs, (bit & 256) > 0); bit++; } } } } else { automaton = new boolean[automatonWidth][automatonWidth]; automaton2 = new boolean[automatonWidth][automatonWidth]; if (parent.isInitPoint()) automaton[automatonWidth / 2][automatonWidth / 2] = true; } repaint(); } public Dimension getSize() { return new Dimension(automatonWidth, automatonWidth); } public Dimension getMinimumSize() { return getSize(); } public Dimension getPreferredSize() { return getSize(); } public Dimension getMaximumSize() { return getSize(); } /** * Overload base method to avoid background clear for flicker-free animation. */ public void update(Graphics g) { paint(g); } /** * Show picture. */ public void paint(Graphics g) { g.drawImage(buffer, 0, 0, this); } private void mouseCellAction(int x, int y, boolean pixel) { x /= 2; y /= 2; if (x >= 0 && x < automatonWidth && y >= 0 && y < automatonWidth) { automaton[x][y] = pixel; } if (parent.isPaused()) { copyBuffer(); repaint(); } } public boolean mouseDrag(Event evt, int x, int y) { if (parent.isRuleMode()) { } else { mouseCellAction(x, y, !evt.metaDown()); } return true; } public boolean mouseDown(Event evt, int x, int y) { if (parent.isRuleMode()) { x = (x - ofs) / gridWidth; y = (y - ofs) / gridHeight; if (cornerCount == 4) { if (x >= 0 && x < 8 && y >= 0 && y < 4) { int bit = x + y * 8; rule = rule.flipBit(bit); updateSquareResult(x, y, rule.testBit(bit)); parent.setRule(rule); repaint(); } } else if (cornerCount == 6) { if (x >= 0 && x < 16 && y >= 0 && y < 8) { int bit = x + y * 16; rule = rule.flipBit(bit); updateHexagonResult(x, y, rule.testBit(bit)); parent.setRule(rule); repaint(); } } else if (cornerCount == 8) { if (x >= 0 && x < 32 && y >= 0 && y < 16) { int bit = x + y * 32; rule = rule.flipBit(bit); updateSquareResult(x, y, rule.testBit(bit)); parent.setRule(rule); repaint(); } } } else { mouseCellAction(x, y, !evt.metaDown()); } return true; } private void copyBuffer() { int xp, yp; for (xp = 0; xp < automatonWidth; xp++) { for (yp = 0; yp < automatonWidth; yp++) { Color clr = null; if (automaton[xp][yp]) clr = black; else clr = white; gBuffer.setColor(clr); int x = xp + xp; int y = yp + yp; if ((yp % 2) == 1 && cornerCount == 6) x++; gBuffer.drawLine(x, y, x + 1, y); gBuffer.drawLine(x, y + 1, x + 1, y + 1); } } } /** * Calculate next generation. */ public void step() { int xp, yp; if (cornerCount == 4) { for (xp = 1; xp < automatonWidth - 1; xp++) { for (yp = 1; yp < automatonWidth - 1; yp++) { int rule = 0; if (automaton[xp][yp + 1]) rule |= 1; if (automaton[xp + 1][yp]) rule |= 2; if (automaton[xp][yp - 1]) rule |= 4; if (automaton[xp - 1][yp]) rule |= 8; if (automaton[xp][yp]) rule |= 16; automaton2[xp][yp] = rules[rule]; } } } else if (cornerCount == 6) { for (xp = 1; xp < automatonWidth - 1; xp++) { for (yp = 1; yp < automatonWidth - 1; yp++) { int rule = 0; if (automaton[xp - 1][yp]) rule |= 16; if (automaton[xp + 1][yp]) rule |= 2; if ((yp % 2) == 0) { if (automaton[xp - 1][yp - 1]) rule |= 8; if (automaton[xp - 1][yp + 1]) rule |= 32; if (automaton[xp][yp + 1]) rule |= 1; if (automaton[xp][yp - 1]) rule |= 4; } else { if (automaton[xp][yp - 1]) rule |= 8; if (automaton[xp][yp + 1]) rule |= 32; if (automaton[xp + 1][yp + 1]) rule |= 1; if (automaton[xp + 1][yp - 1]) rule |= 4; } if (automaton[xp][yp]) rule |= 64; automaton2[xp][yp] = rules[rule]; } } } else if (cornerCount == 8) { for (xp = 1; xp < automatonWidth - 1; xp++) { for (yp = 1; yp < automatonWidth - 1; yp++) { int rule = 0; if (automaton[xp][yp + 1]) rule |= 1; if (automaton[xp + 1][yp + 1]) rule |= 2; if (automaton[xp + 1][yp]) rule |= 4; if (automaton[xp + 1][yp - 1]) rule |= 8; if (automaton[xp][yp - 1]) rule |= 16; if (automaton[xp - 1][yp - 1]) rule |= 32; if (automaton[xp - 1][yp]) rule |= 64; if (automaton[xp - 1][yp + 1]) rule |= 128; if (automaton[xp][yp]) rule |= 256; automaton2[xp][yp] = rules[rule]; } } } boolean tmp[][] = automaton; automaton = automaton2; automaton2 = tmp; // copy to double buffer copyBuffer(); } }