import java.applet.*; import java.awt.*; // Accompanying files: Screen.java, screenObject.java /****************************************************************************\ | Particle Board version 1.0 | | Programmed by Trevor Stone for CSCI 1300, Spring 1997, Karl Winklman prof. | | Programmed 100% in BBEdit Lite on a Macintosh, no program generated code! | | Compiled on Sun's Java Developer's Kit 1.0.2 Available on the web at | | http://robin.ml.org/~tstone/particle/ tstone@Colorado.EDU | \****************************************************************************/ /* Objective: Demonstrate the action of objects dropped down a peboard. For large numbers of objects it tends to end up in a normal distribution. Called Particle Board because the balls never come fully to rest, but move instead like particles. */ public class Board extends Applet { // quasi-enummeration public final static int LEFT = 0; public final static int UP = 1; public final static int RIGHT = 2; public final static int DOWN = 3; private int imageWidth, imageHeight; private Screen screen; // this handles things like placing objects private Image gridImage; // for graphics private Graphics gridG; // ditto private Dimension gridDimension; // more of the same private int displayRows, displayCols; private screenObject [] particles; // array of particles to move private static final int MAX_PARTICLES = 1000; private int numParticles = 0; // how many particles are there? private boolean running = false; // are the balls in progress? private boolean stopIt = false; // has the user asked to stop movement? private Move drop; // this is the object that controls the movement // menus, buttons, and related variables private Choice placeChoice; private String placeType = "Particle"; private int placeNum = 1; private Choice particlesToPlace; private Choice dirChoice; private int dropDirection = DOWN; private Button dropButton; private Button stopButton; private Button clearButton; private Button redrawButton; public Board() // constructor that doesn't do anything { } public void init() // called by applet { // set up display // make sure there's enough room imageWidth = (size().width >= 380) ? size().width : 380; imageHeight = (size().height >= 260) ? size().height : 260; screen = new Screen (imageWidth, imageHeight);//, displayCols, displayRows); displayCols = screen.displayCols(); // how wide is the grid? displayRows = screen.displayRows(); // how tall? resize (imageWidth, imageHeight); // stuff needed to go into graphics gridImage = createImage (imageWidth, imageHeight); gridG = gridImage.getGraphics(); gridDimension = new Dimension (imageWidth, imageHeight); initialize(); // set up board, buttons } // Java wants the following functions public void paint (Graphics g) { g.drawImage (gridImage, 0, 0, null); } public void update (Graphics g) { paint (g); } public void start() { setup_particles(); // makes 50 particles at the top of the screen repaint (); } public void initialize () // set up board { screen.initializeBoard (gridG, screen); control_strip(); // buttons, menus holding_pens(); // hold the particles at the bottom place_pins(); // stuff to be bounced off of } /********************* Particle/Object Functions ***********************/ public void putScreenObject (int row, int col, String obj) { // called from main program screen.putScreenObject (row, col, obj); // tell screen it's there screen.getScreenObject (row, col).draw (); // tell user it's there } public void placeObject (int row, int col) // user called function { // doesn't check if obstructed String type = placeType; if (!screen.onGrid (row, col)) // paranoia return; if (type == "Particle") // speial stuff for particles so they can { // be stacked on top of each other reindexParticles(); // make sure array is in order if (placeNum + numParticles > MAX_PARTICLES) // more particles placeNum = MAX_PARTICLES-numParticles; // than array slots? screen.getScreenObject (row, col).erase(); // get rid of prev obj putScreenObject (row, col, "Particle"); for (int i = 1; i <= placeNum; i++) // add to the array { particles [numParticles] = new screenObject (row, col, "Particle", screen, gridG); numParticles++; } } else // not a particle { screen.getScreenObject (row, col).erase(); // get rid of prev obj putScreenObject (row, col, type); remove_particles (row, col); // get rid of particles in this spot } repaint(); // show it } public void setup_particles () // 50 particles at top center { int center = (screen.obstructed(1,displayCols/2)) ? displayCols/2-1 : displayCols/2; int i; particles = new screenObject [MAX_PARTICLES]; for (i = 0; i < 50; i++) { particles[i] = new screenObject (1, center, "Particle", screen, gridG); numParticles++; } putScreenObject (1, center, "Particle"); } public void move_particles () { reindexParticles (); // make sure all are at front of array stopIt = false; // not yet asking it to stop moving // movement is a class so it can be threaded drop = new Move (particles, numParticles, dropDirection, this, running); drop.start(); // GO! } public void remove_particles (int row, int col) // get rid of particles { // in some spot for (int i = 0; i < MAX_PARTICLES; i++) if (particles[i] != null) // is there a particle here? if (particles[i].row() == row) if (particles[i].col() == col) { particles[i] = null; // get rid of it numParticles--; } reindexParticles(); // clear up array } public void reindexParticles () // clean up bad array entries { int i, j; for (i = 0; i < MAX_PARTICLES; i++) { if (particles[i] != null) // get rid of removed particles { int row = particles[i].row(); int col = particles[i].col(); // is it still a particle or has it been erased? if (!screen.getScreenObject(row, col).type().equals("Particle")) { particles[i] = null; numParticles--; } } if (particles[i] == null) // move nulls to the end of the array { for (j = i; j < MAX_PARTICLES; j++) { if (particles[j] != null) { particles[i] = particles[j]; particles[j] = null; if (j == MAX_PARTICLES) { numParticles = i; i = MAX_PARTICLES; } j = MAX_PARTICLES; // break out of inner for loop } } } // nice cascading closing braces, huh? } } public boolean stopIt () // has drop been asked to stop? { return stopIt; } /********************* Screen/Mouse Functions ***********************/ public void clear_screen () // lose everything! Entirely blank { int i, j; for (i = 1; i <= displayRows; i++) // get rid of all the objects for (j = 1; j <= displayCols; j++) screen.getScreenObject (i, j).erase(); for (i = 0; i < MAX_PARTICLES; i++) // and the particles particles[i] = null; numParticles = 0; refresh_screen(); // display the lack of objects } public void refresh_screen () { int i, j; gridG.setColor (Color.white); // background gridG.fillRect (screen.x(0)+1, screen.y(0)+1, screen.displayCols()*screen.squareSize()-3, screen.displayRows()*screen.squareSize()-2); for (i = 1; i <= displayRows; i++) // put 'em there for (j = 1; j <= displayCols; j++) screen.getScreenObject (i, j).draw(); gridG.setColor (Color.green); // box around screen gridG.drawRect (screen.x(0), screen.y(0), screen.displayCols()*screen.squareSize()-1, screen.displayRows()*screen.squareSize()); repaint(); // display it, baby } public void holding_pens () // set up holding pens at bottom { int i, j; for (j = 1; j <= displayCols; j+=2) for (i = displayRows; i>= displayRows -2*screen.holderLength()*4/5; i--) putScreenObject (i, j, "vLine"); } public void place_pins () // stuff to bounce off of { int i, j, n; //set up pins n = 1; for (i = 5; i < displayRows -2*screen.holderLength()*4/5-3; i+=2) { if (n%2 == 1) // mod so rows alternate (so it's not straight down) { for (j = 1/*screen.holderLength()+4*/; j <= displayCols /*-screen.holderLength()-4*/; j+=2) putScreenObject (i, j, "Pin"); n++; } else { for (j = 2/*screen.holderLength()+5*/; j <= displayCols /*-screen.holderLength()-4*/; j+=2) putScreenObject (i, j, "Pin"); n++; } } } public boolean mouseDown (Event evt, int x, int y) // mouse clicks { int row = screen.row (y); int col = screen.col (x); if (!screen.onGrid(row, col)) // don't handle stuff out of screen return true; placeObject (row, col); // different from putScreenObject return true; } public boolean mouseDrag (Event evt, int x, int y) // runs kinda slow { int row = screen.row(y); int col = screen.col(x); if (!screen.onGrid (row, col)) return true; placeObject (row, col); // basically multiple clicks return true; } /********************* Button and Menu Functions *********************/ private void control_strip () // buttons and menus { setLayout (null); // don't know what it does, but Karl had it particlesToPlace = new Choice(); // number of particles per click particlesToPlace.addItem ("1"); particlesToPlace.addItem ("5"); particlesToPlace.addItem ("10"); particlesToPlace.addItem ("50"); add (particlesToPlace); particlesToPlace.reshape (5, 5, 25, 20); placeChoice = new Choice (); // what to place placeChoice.addItem ("Particle"); placeChoice.addItem ("Blank"); placeChoice.addItem ("Pin"); placeChoice.addItem ("Vert. Line"); placeChoice.addItem ("Horiz. Line"); add (placeChoice); placeChoice.reshape (35, 5, 60, 20); dirChoice = new Choice (); // what direction to drop dirChoice.addItem ("Down"); dirChoice.addItem ("Up"); dirChoice.addItem ("Right"); dirChoice.addItem ("Left"); add (dirChoice); dirChoice.reshape (105, 5, 35, 20); dropButton = new Button("Drop Particles"); // GO! add (dropButton); dropButton.reshape (150, 5, 70, 20); stopButton = new Button("Stop Particles"); // Stop! add (stopButton); stopButton.reshape (225, 5, 70, 20); clearButton = new Button ("Clear"); // Clears screen totally add (clearButton); clearButton.reshape (305, 5, 30, 20); redrawButton = new Button ("Redraw"); // Redraw everything add (redrawButton); redrawButton.reshape (340, 5, 39, 20); repaint (); } public boolean action (Event e, Object o) // when button/menu used { if (e.target instanceof Button) { if (e.arg.equals ("Drop Particles")) { running = true; move_particles (); return true; } if (e.arg.equals ("Stop Particles")) { if (running == true) { running = false; // let functions know it's stopped stopIt = true; // let Move know to stop } return true; } if (e.arg.equals ("Clear")) { clear_screen(); return true; } if (e.arg.equals ("Redraw")) { refresh_screen(); return true; } } if (e.target instanceof Choice) { if (e.arg.equals ("Down")) { dropDirection = DOWN; return true; } if (e.arg.equals ("Up")) { dropDirection = UP; return true; } if (e.arg.equals ("Left")) { dropDirection = LEFT; return true; } if (e.arg.equals ("Right")) { dropDirection = RIGHT; return true; } // objects to place if (e.arg.equals ("Particle")) { placeType = "Particle"; return true; } if (e.arg.equals ("Blank")) { placeType = "Blank"; return true; } if (e.arg.equals ("Pin")) { placeType = "Pin"; return true; } if (e.arg.equals ("Vert. Line")) { placeType = "vLine"; return true; } if (e.arg.equals ("Horiz. Line")) { placeType = "hLine"; return true; } // how many particles per click? if (e.arg.equals ("1")) { placeNum = 1; return true; } if (e.arg.equals ("5")) { placeNum = 5; return true; } if (e.arg.equals ("10")) { placeNum = 10; return true; } if (e.arg.equals ("50")) { placeNum = 50; return true; } } return false; // action dosen't exist/unimplemented } } /****************** Move class: tells particles to drop ******************/ // this is a class instead of a function so that it can have other actions // happen while it is running (in other words, you can stop it) class Move extends Thread // in this file so only Board can access { screenObject [] particles; int numParticles; int dropDirection; Board board; boolean running; // constructor public Move (screenObject [] _particles, int _numParticles, int dir, Board b, boolean _running) { particles = _particles; numParticles = _numParticles; dropDirection = dir; board = b; running = _running; } public void run () // called with drop.start() { while (running) { for (int i = 0; i < numParticles; i++) { if (particles[i] != null) { particles[i].move (dropDirection); // in screenObjects.java board.repaint(); if (board.stopIt()) // have I been told to stop? { // move remaining objects so I don't freak the array board.putScreenObject(particles[i].row(), particles[i].col(), "Particle"); running = false; } } } try {Thread.sleep(5);} catch (Exception E) {} // less sleep and you can't see the balls } } }