| C:\upload\java\Life\source\java\PaintBoardAndComputeNextGeneration.java |
/******************************************************************* This class takes care of the canvas on which the board lives. It also takes care of computing the next generation. Both of these: 1) Loop through entire board, and 2) Call the AliveInNext() routine, so we put them together. As a pr�cis, when we are done, we have done two things: 1) Computed what the board will look like in the next generation 2) Painted the current generation. ******************************************************************** */ import java.awt.*; final class PaintBoardAndComputeNextGeneration extends Canvas implements Constants, Strings { private Vars vars; private BoardUtilities boardUtils; // Uses these to determine who // will live & die in the // next generation. private ManageBoardCoordinates boardCoordinates;// This class manages // board coordinates. private PopupWindow error; // In case something goes wrong private int xVal, yVal; /* Sometime the caller only sets one cell, as only one cell of the board has changed. We do not waste CPU cycles repainting everyone. */ private boolean paintOneCell; // Do we color only one cell? /* Three colors: Things alive who will live at least one generation, things soon to die, and things soon to come to life. */ private Color aliveColor, soonToDieColor, soonToComeToLife, deadColor; private boolean trueSizeKnown; // Does this Canvas class know how big it is? private int [][] thisGeneration, nextGeneration; // Array which the board lives in. Local copy. private Dimension size; // How big are we? Lives in this parameter. private Point location; /******************************************************************* Constructor follows: ******************************************************************** */ PaintBoardAndComputeNextGeneration(Frame parentFrame, // Holds variables. Objects objects, ScreenLocations screenLocations, int w, int h) /* Initial guess at my size. Will change. */ { String errorString = ((parentFrame != null) && (objects != null) && (screenLocations != null)) ? NULL_STRING : "parentFrame = " + parentFrame + " objects = " + objects + " screenLocations = " + screenLocations; if (!errorString.equals(NULL_STRING)) { System.out.println("Error, null pointer, PaintBoardAndComputeNextGeneration " + errorString); return; } vars = null; // Can't do things without this parameter. error = new PopupWindow(parentFrame, screenLocations, ERROR_WINDOW_NAME);// In case of an error. // Set initial size. Fix later: size = new Dimension(w, h); location = new Point(0, 0); /* Next handles board coordinates. We tell it the number of rows & columns as soon as we know. */ boardCoordinates = objects.GetBoardCoordinates(); /* Next is general board utilities things like, "is this cell alive in the next generation?" */ boardUtils = objects.GetBoardUtilities(); // We set several class variables: aliveColor = COLOR_ALIVE; soonToDieColor = COLOR_SOON_TO_DIE; soonToComeToLife = COLOR_DEAD; deadColor = COLOR_DEAD; // We don't know how big we are yet: trueSizeKnown = false; paintOneCell = false; // Assume we paint the entire board. xVal = GARBAGE_VALUE; yVal = GARBAGE_VALUE; } /******************************************************************* Over-ride next for good measure. ******************************************************************** */ public void update(Graphics g) { super.paint(g); } /******************************************************************* Obvious by code: get local copy of *vars*. ******************************************************************** */ protected void SetVars(Vars vars) { this.vars = vars; } /******************************************************************* The < paint(Graphics g) > routine follows. NOTE: This routine does two things in one fell swoop: it paints the board, AND, computes the next generation. As both of these actions 1) Loop through entire board 2) Call the AliveInNext() routine, the writer decided to incorporate them for efficiency of code. ******************************************************************** */ public void paint(Graphics g) { /* Fudge explain: I couldn't get the blasted canvas to work just right, no matter what Java books I consulted. I resort to the next to force the canvas's position. */ setLocation(0, PIXELS_TO_SKIP_FROM_TOP_AND_BOTTOM); thisGeneration = vars.GetThisGeneration(); // Sometime between nextGeneration = vars.GetNextGeneration(); // games arrays change. /* We are very, very careful. Perhaps we have not been fed our true size, or a parameter is garbage. Best be safe. Do nothing if something isn't right. */ boolean showBoard = trueSizeKnown && (thisGeneration != null) && (nextGeneration != null) && (g != null) && (vars != null); if (showBoard) /* If we don't know our true size, or a parameter is garbage, do nothing. */ { int pixelsPerCell = vars.GetPixelsPerCell(); if (paintOneCell) // Sometimes we only paint one cell. Don't waste // CPU time painting the whole board if this is so. { handleOneCell(yVal, xVal, g, pixelsPerCell); /* Always set next, so the patron has to call < SetSingleCellToChange > whenever a single cell is changed. This way the code always knows when only a single cell is changed. */ paintOneCell = false; // These xVal = GARBAGE_VALUE; // must yVal = GARBAGE_VALUE; // be re-set! } else // We're painting the entire board. { int rows = vars.GetRows(); int columns = vars.GetColumns(); int i = 0; // Loop variables. int j = 0; // Set to zero or the try-catch // clause gets unhappy. // Clear things: g.clearRect(getLocation().x, getLocation().y, getSize().width, getSize().height); g.setColor(COLOR_BACKGROUND); g.fillRect(getLocation().x, getLocation().y, getSize().width, getSize().height); // Now paint the current board and compute the next generation. for (i = 1; i <= rows; i++) { for (j = 1; j <= columns; j++) { handleOneCell(i, j, g, pixelsPerCell); } // End j-loop } // End i-loop } } else // Parameter < showBoard > is false; something is { // not right, so ... ; // Do nothing. } } // End < paint() > routine. /******************************************************************* Next routine < handleOneCell > colors one cell of the board at indices (i, j). Note: some of this code is not as "graceful" as it could perhaps be. Because this gets called so much (rows * columns, each generation), I have axed "prettiness" for speed. ******************************************************************** */ private final void handleOneCell(int i, int j, Graphics g, int pixelsPerCell) { /* Painting one cell is dicey. As xVal and yVal are set to "garbage" values between calls (i.e, they must always be re-set), if we are painting a single cell, we are ultra-careful. */ /* if (paintOneCell && ((i == GARBAGE_VALUE) || (j == GARBAGE_VALUE))) { error.ShowMessage(GARBAGE_VALUES_OF_I_OR_J + " < handleOneCell PaintBoardAndComputeNextGeneration.", OK); // Button. } */ /* try {*/ if (boardUtils.AliveInNext(thisGeneration /* Next generation depends upon this one! */, i, j /* And what cell we are at. */)) { nextGeneration[i][j] = 1; // Cell is alive in the next generation. if (thisGeneration[i][j] == 1) g.setColor(aliveColor); else // Currently dead, about to. g.setColor(soonToComeToLife); // to life. } else { nextGeneration[i][j] = 0; // Cell is dead in the next generation. if (thisGeneration[i][j] == 1) g.setColor(soonToDieColor); else g.setColor(COLOR_DEAD); } // Now draw the cell: Point where = boardCoordinates.boardValuesToPixels(j, i, pixelsPerCell); int yValue = (int) where.getY(); int xValue = (int) where.getX(); g.fillRect(xValue, yValue, pixelsPerCell - 1, // Scale down a // little -- pixelsPerCell - 1); // makes things look // better /* } catch (ArrayIndexOutOfBoundsException e) // Array index out // of bounds!? { String errorStr = e.getMessage(); errorStr = errorStr + " Array bounds exception in " + " routine " + " computeOffScreenGraphicsForPrintingBoard: " + "rows = " + rows + ", columns = " + columns + ", i = " + i + ", and j = " + j; error.ShowMessage(errorStr, OK); }*/ } /******************************************************************* What if we color only one cell? Next takes care of setting the cell via globals < xVal > and < yVal >. ******************************************************************** */ public void SetSingleCellToChange(int xVal, int yVal) { if ((yVal < 1) || (yVal > vars.GetRows()) || // Input bad? We cannot (xVal < 1) || (xVal > vars.GetColumns())) // interpret this. { error.ShowMessage(UNRECOVERABLE_ERROR + " < SetSingleCellToChange : PaintBoardAndComputeNextGeneration >, " + "xVal = " + xVal + ", yVal = " + yVal + ", rows" + vars.GetRows() + ", " + AND + " columns = " + vars.GetColumns() + ". " + WE_CANNOT_INTERPRET_THIS + ".", OK); // Button. paintOneCell = false; // Set false for good measure. Nonsense! } else // Input good. { paintOneCell = true; /* This is called when we set one cell of the board. The < paint() > routine needs to know. This is set < false > again at the bottom of < paint() > -- this routine must be called by patron every time we only set one cell. */ this.xVal = xVal; // Set fed this.yVal = yVal; // parameters. } } /******************************************************************* Over-ridden routines that follow take care of dimension problems. Note how < setSize > sets a boolean telling the code, "I know how big I am." ******************************************************************** */ public Dimension getSize() // Tell caller our size. Over-ridden. { return getMinimumSize(); } public Dimension getMinimumSize() // Tell caller our min size. { // Over-ridden. return size; } public Dimension getPreferredSize() // Tell caller our max size. { // Over-ridden. return getMinimumSize(); } /******************************************************************* Next sets a boolean telling the code, "I know how big I am." ******************************************************************** */ public void setSize(int width, int height) /* Set our size. Over-ridden. */ { size = new Dimension(width, height); trueSizeKnown = true; // That boolean. The main code knows } // that our size has been set. /* ********************************************************************* The next three routines are short communication routines: calling program tells us colors of live & dead cells. ********************************************************************* */ protected final void setAliveColor(Color aliveColor) // What color is an { // "alive?" this.aliveColor = aliveColor; } protected final void setSoonDeadColor(Color soonToDieColor) // What color is { // "dead?" this.soonToDieColor = soonToDieColor; } // What color is a // "soon-to-be-alive?" protected final void setAboutToComeToLifeColor(Color soonToComeToLife) { this.soonToComeToLife = soonToComeToLife; } } // End < PaintBoardAndComputeNextGeneration > class.