package ptft;
/*
* JCellularAutomataPaint.java
*
* Written by Will Braynen
* Group for Logic and Formal Semantics, SUNY Stony Brook (www.ptft.org)
*
* 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 should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Created on May 13, 2004, 9:01 PM
*/
import java.awt.*;
import java.awt.event.*;
import java.net.*;
/**
* Adds the functionality of a simple paint program so that the user can
* paint cells a different strategy or ethnicity color.
*
* @author Will Braynen
*/
public final class JPaintCellularAutomata extends JPtftCellularAutomata implements MouseListener, MouseMotionListener {
// fields
protected int m_activeStrategy; // to paint with
protected int m_activeEthnicity; // to paint with
protected JPtftCell m_lastDrawnCell;
protected boolean m_isPaintModeEnabled = true;
protected boolean m_isPencil = true;
protected boolean m_isMouseButtonPressed = false;
protected Cursor m_pencilCursor;
protected Cursor m_bucketCursor;
protected boolean[][] m_isFlooded;
private int m_n = 0;
/** Creates new form JPtftCellularAutomata */
public JPaintCellularAutomata() {
//Harry: this is the only place that the cell width needs to be set
//it will propogate from here to the rest of the classes
this(8);
//initComponents(); // DO NOT CALL THIS -- ALREADY CALLED IN PARENT CLASS
}
/** Creates new form JCellularAutomataPaint */
public JPaintCellularAutomata(int cellWidth) {
super(cellWidth);
//initComponents(); // DO NOT CALL THIS -- already called in parent constructor
// init fields
m_activeStrategy = 0;
m_activeEthnicity = 0;
m_isStrategyDisplayed = true;
// listen for mouse events
if (m_isPaintModeEnabled)
{
addMouseListener (this);
addMouseMotionListener (this);
}
m_pencilCursor = ImageToolkit.createCursor( this, "/ptft/images/pencil.gif", new Point(07,24), "pencil" );
m_bucketCursor = ImageToolkit.createCursor( this, "/ptft/images/bucket.gif", new Point(22,22), "bucket" );
m_isFlooded = new boolean[m_rows][m_columns];
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
private void initComponents() {//GEN-BEGIN:initComponents
setLayout(new java.awt.BorderLayout());
}//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
/** enables paint mode */
public void setPaintMode( boolean isEnabled, boolean isPencil ) {
// do nothing if this is the way things are already
if (m_isPaintModeEnabled == isEnabled && m_isPencil == isPencil) return;
m_isPaintModeEnabled = isEnabled;
m_isPencil = isPencil;
if (isEnabled)
{
// listen for mouse events
addMouseListener (this);
addMouseMotionListener (this);
// change the cursors
if (m_isPencil)
setCursor( m_pencilCursor );
else
setCursor( m_bucketCursor );
}
else // disable paint mode
{
// ignore mouse events
removeMouseListener (this);
removeMouseMotionListener (this);
// change the cursors
setCursor( new Cursor(Cursor.DEFAULT_CURSOR) );
}
}
public boolean isPaintModeEnabled() {
return m_isPaintModeEnabled;
}
public boolean isPencil() {
return m_isPencil;
}
/** User-painting will be done with this strategy color */
public void setActiveStrategy( int strategy ) {
m_activeStrategy = strategy;
}
/** User-painting will be done with this ethnicity color */
public void setActiveEthnicity( int ethnicity ) {
m_activeEthnicity = ethnicity;
}
/** Draws a line recursively connecting two cells
*
* @param c1 starting point of the line
* @param c2 ending point of the line
*/
protected void drawLine( JPtftCell c1, JPtftCell c2 ) {
if ((null == c1) || (null == c2)) return;
// If we've been dragging the mouse already, then fill in
// the cells between this one and the last one. (If the
// mouse movement was fast, we might've missed some cells)
// base case
if ((Math.abs (c1.m_row - c2.m_row) < 2) && (Math.abs (c1.m_column - c2.m_column) < 2))
{
// p1 and p2 are either the same or next to each other
drawCell( c1 );
drawCell( c2 );
return;
}
// recursive call: split the line in two
int midpointRow = (c1.m_row + c2.m_row) / 2;
int midpointColumn = (c1.m_column + c2.m_column) / 2;
JPtftCell midpointCell = (JPtftCell) m_grid[ midpointRow ][ midpointColumn ];
drawLine ( c1, midpointCell );
drawLine ( c2, midpointCell );
}
/**
* Depending on whether we're viewing strategies or ethnicities, changes
* the cell's strategy or ethnicity to m_activeStrategy
* or m_activeEthnicity.
*
* @see #setActiveStrategy
* @see #setActiveEthnicity
* @see #toggleViews
*/
protected void drawCell( JPtftCell cell ) {
if (null == cell) return;
if (m_isStrategyDisplayed)
{
cell.setStrategy (m_activeStrategy);
}
else // painting ethnicities
{
cell.setEthnicity (m_activeEthnicity);
}
m_lastDrawnCell = cell;
repaint();
}
/**
* Returns the cell located at the pixel coordinate p.
* This also helps us avoid missing mouse clicks that are in-between cells.
*/
protected JPtftCell getCell( Point p ) {
int row = (p.y - m_cellPadding) / (m_cellWidth + m_cellPadding);
int col = (p.x - m_cellPadding) / (m_cellWidth + m_cellPadding);
if ((row >= m_rows) || (col >= m_columns) || (row < 0) || (col < 0))
{
return null;
}
return (JPtftCell) m_grid[row][col];
}
public void fillWithBucket( JPtftCell cell, int strategy, int ethnicity ) {
//if (m_isFloodFilling) return;
//m_isFloodFilling = true;
for (int x = 0; x < m_columns; x++)
for (int y = 0; y < m_rows; y++)
m_isFlooded [x][y] = false;
int fillColor = m_isStrategyDisplayed ? strategy : ethnicity;
int oldColor = m_isStrategyDisplayed ? cell.getStrategy() : cell.getEthnicity();
m_n = 0;
floodFill( cell,
fillColor, oldColor, "original"
);
//m_isFloodFilling = false;
repaint();
} // end fillWithBucket
protected void setCell( JPtftCell cell, int color ) {
if (m_isStrategyDisplayed)
cell.setStrategy( color );
else
cell.setEthnicity( color );
} // end setCell
protected boolean sameColor( JPtftCell cell, int color ) {
return
( m_isStrategyDisplayed ?
cell.getStrategy() == color :
cell.getEthnicity() == color );
} // end sameColor
/**
* Fills regions of color "old" with the new color "fill" starting at
* the given coordinates
*
* @param cell starting cell for the flood fill
* @param oldColor color to paint with
*/
protected void floodFill( JPtftCell cell, int fillColor, int oldColor, String text )
{
int row = cell.getRow();
int column = cell.getColumn();
//if (m_isFlooded[row][column]) return;
if (row < 0 || row >= m_rows) return;
if (column < 0 || column >= m_columns) return;
System.out.println ("floodFill " + m_n++ + ", direction: " + text);
if ( sameColor( cell, oldColor ))
{
}
m_n--;
// int row = startCell.getRow();
// int column = startCell.getColumn();
// if (m_isFlooded[row][column]) return;
// if ( ! sameColor( startCell, oldColor )) return;
// Wraps around the grid while filling.
// (Otherwise, m_isFlooded array would not be necessary and
// we could instead just return when x or y fall lower than 0
// or exceed the width and height of the grid).
// int row = startCell.getRow();
// int column = startCell.getColumn();
// if (m_isFlooded[row][column]) return;
// if ( ! sameColor( startCell, oldColor )) return;
//
// // scan to the left, starting to the left of startCell
// JPtftCell cell = (JPtftCell)getNeighbor( startCell, Direction.W );
// row = cell.getRow();
// column = cell.getColumn();
// while ( ! m_isFlooded[row][column] && sameColor( cell, oldColor ))
// {
// setCell( cell, fillColor );
// m_isFlooded[row][column] = true;
//
// // next cell to the left
// cell = (JPtftCell)getNeighbor( cell, Direction.W );
// column = cell.getColumn();
//
// // fill children (neighbors above and below)
// floodFill( (JPtftCell)getNeighbor( cell, Direction.N ), fillColor, oldColor, "N" );
// floodFill( (JPtftCell)getNeighbor( cell, Direction.S ), fillColor, oldColor, "S" );
// }
//
// // scan to the right, starting with startCell
// cell = startCell;
// row = cell.getRow();
// column = cell.getColumn();
// while ( ! m_isFlooded[row][column] && sameColor( cell, oldColor ))
// {
// setCell( cell, fillColor );
// m_isFlooded[row][column] = true;
//
// // next cell to the right
// cell = (JPtftCell)getNeighbor( cell, Direction.E );
// column = cell.getColumn();
// }
} // end floodFill
public void mouseDragged(MouseEvent e) {
if (m_isPencil)
{
JPtftCell cell = getCell( e.getPoint() );
drawLine (cell, m_lastDrawnCell);
m_lastDrawnCell = cell;
}
}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
m_isMouseButtonPressed = true;
JPtftCell cell = getCell( e.getPoint() );
if (m_isPencil)
{
drawCell( cell );
}
else
{
fillWithBucket( cell, m_activeStrategy, m_activeEthnicity );
}
}
public void mouseReleased(MouseEvent e) {
// disable drawLine call in mouseDragged
m_lastDrawnCell = null;
m_isMouseButtonPressed = false;
}
}