// file FillDemo.java // Demonstrates slow and fast flood fills. // by Harriet Fell // fell@ccs.neu.edu // July 2004 import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; import java.awt.image.*; import java.util.Random; import java.lang.Math; public class FillDemo extends JComponent implements MouseMotionListener, MouseListener, ActionListener { //, Runnable { // The FillDemo menu static JMenuBar FillDemoMenuBar = new JMenuBar(); // Window width and height int wd, ht, size; // Mouse Points int mmX, mmY; // last mouse moved point coordinates int mdX, mdY; // last mouse dragged point coordinates int mcX, mcY; // last mouse clicked point coordinates // Seed for Fill is set boolean seedSet = false; // Shape to Fill int theShape = -1; // Shape to Fill int theFill = 1; // default is Recursive Fill // Array to store the dot information since reading a pixel is hard. static int [][] theGrid = new int[20][20]; // Array to store the stack order for the fast fill. static int [][] pushNum = new int[20][20]; public FillDemo(){ // Listen for mouse addMouseMotionListener(this); addMouseListener(this); // Add the menu bar setMenus(); // Initialize theGrid for (int i = 0; i < 20; i++) for (int j = 0; j < 20; j++){ theGrid[i][j] = 0; } // Initialize pushNum for (int i = 0; i < 20; i++) for (int j = 0; j < 20; j++){ pushNum[i][j] = 0; } // For timing // Thread t = new Thread(this); // t.start( ); } /* public void run( ) { try { while (true) { timeStep( ); repaint( ); Thread.sleep(1000 / 24); } } catch (InterruptedException ie) {} } private void timeStep( ) { } */ public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; // These are not currently used but might be useful. wd = getSize().width; ht = getSize().height; size = Math.min(wd/20, ht/20); // Draw grid Grid(g2, wd, ht); for (int i = 0; i < 20; i++) for (int j = 0; j < 20; j++) { Dot d = new Dot(g2, wd, ht, i, j, Color.white); if (theGrid[i][j] == 1) { // draw the boundary d.Set(i, j, Color.black); d.Fill(); } else if (theGrid[i][j] == 2) { // draw filled dots d.Set(i, j, Color.red); d.Fill(); } else if (theGrid[i][j] == 3) { // draw seed d.Set(i, j, Color.blue); d.Fill(); } else if (theGrid[i][j] == 4) { // for "flash" d.Set(i, j, Color.yellow); d.Fill(); } if (pushNum[i][j] != 0) { String s = "" + pushNum[i][j]; g2.setFont(new Font("Arial", Font.BOLD, size/2)); g2.setPaint(Color.black); g2.drawString(s, size*i + size/5, size*(j + 1) - size/4); } } //n++; System.out.println(n); } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseClicked(MouseEvent e) { mcX = e.getX(); mcY = e.getY(); seedSet = true; int h = mcX/size; int v = mcY/size; theGrid[h][v] = 3; // seed will be blue //System.out.println("Seed: " + h + " " + v); if (theFill == 1) BoundaryFill4(h, v); else BdyFill4(h, v); repaint(); } public void mouseDragged(MouseEvent e) { //System.out.println("dragged\n"); mdX = e.getX(); mdY = e.getY(); repaint(); } public void mouseMoved(MouseEvent e) { mmX = e.getX(); mmY = e.getY(); repaint(); } public void actionPerformed(ActionEvent e) { repaint(); } public void setMenus() { JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F); // Make "Quit" work properly JMenuItem quitItem = new JMenuItem("Quit"); quitItem.setMnemonic(KeyEvent.VK_Q); quitItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK)); quitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); fileMenu.add(quitItem); // Create Shape menu JMenu shapeMenu = new JMenu("Shape"); shapeMenu.setMnemonic(KeyEvent.VK_S); JMenuItem mItem = new JMenuItem("Rectangle"); shapeMenu.add(mItem); mItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { theShape = 1; System.out.println("Rectangle"); // Draw the rectngle FillDemoShapes fd = new FillDemoShapes(theShape, theGrid, pushNum); repaint(); } }); mItem = new JMenuItem("BigU"); shapeMenu.add(mItem); mItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { theShape = 2; System.out.println("BigU"); // Draw the BigU FillDemoShapes fd = new FillDemoShapes(theShape, theGrid, pushNum); repaint(); } }); mItem = new JMenuItem("Big Rectanglewith Bars"); shapeMenu.add(mItem); mItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { theShape = 1; System.out.println("Big Rectanglewith Bars"); theShape = 3; // Draw the Big Rectanglewith Bars FillDemoShapes fd = new FillDemoShapes(theShape, theGrid, pushNum); repaint(); } }); // Create Fill menu JMenu fillMenu = new JMenu("Fill"); shapeMenu.setMnemonic(KeyEvent.VK_S); mItem = new JMenuItem("Recursive Fill"); fillMenu.add(mItem); mItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { theFill= 1; System.out.println("Recursive Fill"); } }); mItem = new JMenuItem("Fast Fill"); fillMenu.add(mItem); mItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { theFill= 2; System.out.println("Fast Fill"); } }); // Put the menus and color button in the menu bar. FillDemoMenuBar.add(fileMenu); FillDemoMenuBar.add(shapeMenu); FillDemoMenuBar.add(fillMenu); } public void Grid(Graphics2D g2, int wd, int ht){ int size = Math.min(wd/20, ht/20); g2.setPaint(Color.green); g2.setStroke(new BasicStroke(2)); for (int x = 0; x <= wd; x += size){ g2.drawLine(x, 0, x, ht); } for (int y = 0; y <= ht; y += size){ g2.drawLine(0, y, wd, y); } //repaint(); g2.setPaint(Color.black); } // Fast Boundary Fill void BdyFill4(int h, int v){ Stack S = new Stack(); // stack of Point PointI P = new PointI(); PointI Q = new PointI(); boolean doneAbove = true; boolean doneBelow= true; int N = 1; //theGrid[h][v] = 0; // unfill seed to look for leftmost P.h = h; P.v = v; P = Leftmost(P.h, P.v); // leftmost on current scanline/region S.push(P); pushNum[P.h][P.v] = (N++); // The following forces the repaint. Graphics g = getGraphics(); update(g); //System.out.println("pushing " + P.h + " " + P.v); while (!S.IsEmpty()){ P = S.pop(); theGrid[P.h][P.v] = 3; // blue for pop color pushNum[h][v] = 0; // remove the number doneAbove=true; doneBelow=true; while (!Filled(P.h, P.v)){ // don't run out of window if (doneAbove){ if (!Filled(P.h, P.v-1)) { // unfilled above Q = Leftmost(P.h, P.v-1); // find Leftmost in row above S.push(Q); pushNum[Q.h][Q.v] = (N++); // push Leftmost for later work // The following forces the repaint. g = getGraphics(); update(g); // System.out.println("pushing " + Q.h + " " + Q.v); doneAbove = false; // remember unfilled above } } else if (Filled(P.h, P.v-1)) doneAbove = true;// remember filled above if (doneBelow){ if (!Filled(P.h, P.v+1)){ // unfilled below Q = Leftmost(P.h, P.v+1); S.push(Q); pushNum[Q.h][Q.v] = (N++); // The following forces the repaint. g = getGraphics(); update(g); // System.out.println("pushing " + Q.h + " " + Q.v); doneBelow = false; // remember unfilled below } } else if (Filled(P.h, P.v+1)) doneBelow = true; Flash(P.h, P.v); theGrid[P.h++][P.v] = 2; // fill color // The following forces the repaint. g = getGraphics(); update(g); } } } boolean Filled(int h, int v){ return (0 > h || h >= 20 || 0 > v || v >= 20 // (h, v) is not in theGrid || theGrid[h][v] == 1 || theGrid[h][v] == 2);// not filled } PointI Leftmost(int h, int v){ //assert(!Filled(h, v, fill, boundary)); int start = h; while (!Filled(start, v)) { start--; // System.out.println("Start " + start + " Color " + theGrid[h][v]); } PointI P = new PointI(); P.h = start+1; P.v = v; return P; } void WriteNum(int h, int v, int N){ pushNum[h][v] = N; } // recursive boundary fill boolean BoundaryFill4(int h, int v){ /*if (0 <= h && h < 20 && 0 <= v && v < 20 //(h,v) in figure && theGrid[h][v] != 1 // not boundary && theGrid[h][v] != 2) { // not fill color */ if (theGrid[h][v] != 3) { // not seed color theGrid[h][v] = 2; // fill (h, v) // System.out.println(h + " " + v); } //repaint(); // The following forces the repaint. //Graphics g = getGraphics(); //update(g); paintImmediately(0,0,1600, 1200); Flash(h, v); if (!Filled(h+1, v)) BoundaryFill4(h+1, v); if (!Filled(h-1, v)) BoundaryFill4(h-1, v); if (!Filled(h, v+1)) BoundaryFill4(h, v+1); if (!Filled(h, v-1)) BoundaryFill4(h, v-1); return true; //} //Flash(h, v); //return false; } void Flash(int h, int v){ // Make it blink. int c = theGrid[h][v]; theGrid[h][v] = 4; // yellow Graphics g = getGraphics(); update(g); theGrid[h][v] = c; g = getGraphics(); update(g); } public static void main(String[] args){ JFrame f = new JFrame("JFillDemo"); Container c = f.getContentPane(); c.setLayout(new BorderLayout()); c.add(new FillDemo(), BorderLayout.CENTER); f.setSize(600, 622); f.setJMenuBar(FillDemoMenuBar); f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e) { System.exit(0); } }); f.setVisible(true); FillDemoMenuBar.setVisible(true); } }