/** * BlackHole Source File * * GNU Copyright (C) 2008 Gaspar Sinai gaspar(at)adys.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * dated June 1991. See file COPYYING for details. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package sinai.gaspar.blackhole; import java.awt.Panel; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; /** * This is the panel where the animated Ulam Spiral is drawn. * This is used from BlackHole.java applet, but it could be * used stand-alone. * * @author Gaspar Sinai * @version 2008-04-30 */ public class BoardPanel extends Panel implements IOptions { private int cells = 1; private int origCells = 1; private int type = IOptions.TYPE_SPIRAL; private CoordConverter coordConverter = new CoordConverter (type, cells); private int cellSize = 4; private int origCellSize = 4; private boolean showCounter = true; private long delay = 100L; // ms private long origDelay = 100L; // ms private PrimeStack primeStack = new PrimeStack (cells); Color foreground = BoardPanel.parseColor ("000000"); Color background = BoardPanel.parseColor ("ffffff"); TimerThread timerThread = null; IChangeListener changeListener = null; /** * Create a default BoardPanel. */ public BoardPanel() { setBackground (background); setForeground (foreground); } public void setChangeListener (IChangeListener changeListener) { this.changeListener = changeListener; primeStack.setChangeListener (changeListener); } /** * Set the number of cells in a row/column. */ public void setCells (int cells) { this.cells = cells; this.origCells = cells; coordConverter = new CoordConverter (type, cells); long start = primeStack.getStart (); primeStack = new PrimeStack (cells); primeStack.setChangeListener (changeListener); if (start != primeStack.getStart()) { primeStack.setStart (start); } } public long getStart () { return primeStack.getStart (); } /** * Set the size of a cell. */ public void setCellSize (int cellSize) { this.cellSize = cellSize; this.origCellSize = cellSize; } /** * Zoom in and out. */ public int zoom (int value) { int newCellSize = origCellSize; int newCells = origCells; int size = origCellSize * origCells; // Zoom in int count = 0; if (value > 0) { while (count < value) { if (newCellSize == size) break; int test = newCellSize+1; while (test != size && size % test != 0) { test++; } newCells = size / test; newCellSize = test; count++; } } else if (value < 0) { // Zoom-out while (count > value) { if (newCellSize == 1) break; int test = newCellSize - 1; while (test > 0 && size % test != 0) { test--; } newCells = size / test; newCellSize = test; count--; } } int saveOrigCellSize = origCellSize; int saveOrigCells = origCells; synchronized (timerThread.lock) { setCells (newCells); setCellSize (newCellSize); timerThread.setPrimeStack (primeStack); } origCellSize = saveOrigCellSize; origCells = saveOrigCells; repaint(); return count; } // Double or half the speed. public int speed (int value) { long delaySaved = origDelay; long newDelay = origDelay; int count = 0; if (value > 0) { while (count < value && newDelay > 10) { newDelay = (count % 3 != 1) ? newDelay / 2 : newDelay * 10/ 25 ; count++; } if (newDelay < 10) newDelay = 10; } else if (value < 0) { while (count > value && newDelay <= 10000) { newDelay = (-count % 3 != 1) ? newDelay * 2 : newDelay * 25 / 10; count--; } if (newDelay > 10000) newDelay = Long.MAX_VALUE; } setDelay (newDelay); origDelay = delaySaved; return count; } public boolean isDelayLimited () { if (timerThread == null) return false; long ret = timerThread.getDelay (); return (ret <= 10L || ret == Long.MAX_VALUE); } public boolean isZoomLimited () { return (cells == 1 || cellSize == 1); } /** * Move back one frame. */ public void back () { primeStack.back(); repaint(); } /** * Move forward one frame. */ public void forward () { primeStack.forward(); repaint(); } /** * Set the size of a cell. */ public void setDelay (long delay) { this.delay = delay; this.origDelay = delay; if (timerThread != null) { timerThread.setDelay (delay); } } /** * Set the size of a cell. */ public void setStart (long start) { primeStack.setStart (start); repaint (); } /** * Set the background color */ public void setBackground (Color background) { this.background = background; super.setBackground (background); } /** * Set the foreground color */ public void setForeground (Color foreground) { this.foreground = foreground; super.setForeground (foreground); } /** * Toggle counter at the right-bottom edge of the screen. * @param showCounter is true if couter is shown. */ public void setShowCounter (boolean showCounter) { this.showCounter = showCounter; } /** * Set the type of the diagram. * @see IOptions#setType */ public void setType (int type) { coordConverter = new CoordConverter (type, cells); this.type = type; repaint(); } /** * Start animation. */ public void start () { // do a shift and repaint(); if (timerThread == null) { timerThread = new TimerThread (delay, primeStack, this); timerThread.th_start (); } else { timerThread.th_resume (); } } /** * Stop animation. */ public void stop () { // do a shift and repaint(); timerThread.th_stop (); } /** * Stop animation. */ public void destroy () { // do a shift and repaint(); timerThread.th_destroy (); } /** * In awt this is called first, and paint is called from here. * We implemented a double buffer here. */ public void update (Graphics graphics) { Image db = createImage (cells*cellSize, cells*cellSize); if (db == null) { return; } Graphics dbg = db.getGraphics(); paint (dbg); graphics.drawImage (db, 0, 0, this); } /** * Paint ourself. */ public void paint (Graphics graphics) { // Clear the area graphics.setColor (background); graphics.fillRect(0, 0, cells*cellSize, cells*cellSize); graphics.setColor (foreground); // The real center. long maxNum = cells * cells + primeStack.getStart(); int digits = countDigits (maxNum); boolean showNumber = showCounter && (digits * 8 + 8 < cellSize); for (int y=0; y= 10) printNum (graphics, x-8, y, num/10, fg); num = num%10; String f = FONT[(int)num]; if (fg == null) { graphics.setColor (background); graphics.fillRect (x, y, 8, 16); graphics.setColor (foreground); } else { graphics.setColor (fg); } for (int row=0; row<16; row++) { int b = Integer.parseInt (f.substring(row*2, row*2+2), 16); for (int column=0; column<8; column++) { if ((b & 0x80) == 0x80) { graphics.fillRect (x+column, y+row, 1, 1); } b = b << 1; } } } }