/* Copyright (C) 2002 J. M. Spivey */ import java.awt.*; // This was a TCL/Tk program once. /** GUI class for displaying a pentominoes board. * * Pentominoes are given to us as sets of squares (represented as an * array of Point objects), and we have the interesting task of * computing an outline for the shape that lies just inside its * boundary, so as to make a neat display with little gaps between the * pieces. The rest is straightforward. */ public class PentoBoard extends Canvas { private static final int unit = 40; // Unit square in pixels private static final int margin = 1; // Inset of piece from edge private static final int MAX = 30; // Max. no of pieces // Components of unit vectors in 4 directions private static final int dx[] = { 1, 0, -1, 0 }; private static final int dy[] = { 0, 1, 0, -1 }; private int ccw(int dir) { return (dir+1)%4; } private int cw(int dir) { return (dir+3)%4; } // Standing at (x,y) and looking in direction dir, the square to the left // of your path has coordinates (x+inx(dir), y+iny(dir)) for its SW corner. // Explanation: the far corner of that square is at (x,y) + dir + ccw(dir), // so the centre is at (x,y) + (dir + ccw(dir))/2, and the SW corner is at // (x,y) + (dir + ccw(dir))/2 - (1/2,1/2). private int inx(int dir) { return (dx[dir]+dx[ccw(dir)]-1)/2; } private int iny(int dir) { return (dy[dir]+dy[ccw(dir)]-1)/2; } /** memsquare -- test if a square belongs to a shape */ private boolean memsquare(int x, int y, Point z[], int n) { for (int i = 0; i < n; i++) if (x == z[i].x && y == z[i].y) return true; return false; } /** follow -- test if a given direction has a full square to the left */ private boolean follow(int x, int y, int dir, Point z[], int n) { int x1 = x+inx(dir), y1 = y+iny(dir); return memsquare(x1, y1, z, n); } /** outline -- compute the polygonal outline of a piece */ private Polygon outline(Point z[], int n) { // We assume there are no enclosed holes: true of pentominoes at least. // Find a horizontal segment of the boundary by moving South int x = z[0].x, y = z[0].y; while (memsquare(x, y-1, z, n)) y--; // Now stand with your left hand on the boundary and follow // it counter-clockwise Polygon vertices = new Polygon(); int x0 = x, y0 = y, dir = 0; for (;;) { // Take one pace forward x += dx[dir]; y += dy[dir]; // Examine the two squares in front if (follow(x, y, cw(dir), z, n)) { // Both are occupied: turn to the right vertices.addPoint(unit*x + margin*(dx[dir]+dx[ccw(dir)]), unit*y + margin*(dy[dir]+dy[ccw(dir)])); dir = cw(dir); } else if (! follow(x, y, dir, z, n)) { // Neither is occupied: turn to the left vertices.addPoint(unit*x + margin*(-dx[dir]+dx[ccw(dir)]), unit*y + margin*(-dy[dir]+dy[ccw(dir)])); dir = ccw(dir); } if (x == x0 && y == y0) break; } return vertices; } private Dimension size, wsize; private Polygon pieces[] = new Polygon[MAX]; private Color colors[] = new Color[MAX]; private int npieces = 0; private boolean dirty = true; private Image buffer = null; public synchronized void paint(Graphics g0) { if (buffer == null) buffer = createImage(unit*size.width+1, unit*size.height+1); if (dirty) { Graphics g = buffer.getGraphics(); // Clear to background g.setColor(getBackground()); g.fillRect(0, 0, buffer.getWidth(null), buffer.getHeight(null)); // Outline and fill each piece for (int i = 0; i < npieces; i++) { g.setColor(colors[i]); g.fillPolygon(pieces[i]); g.setColor(Color.black); g.drawPolygon(pieces[i]); } dirty = false; } g0.drawImage(buffer, unit/2, unit/2, null); } public void update(Graphics g) { paint(g); } // Exported methods public PentoBoard(Dimension size) { this.size = size; this.wsize = new Dimension(unit*(size.width+1), unit*(size.height+1)); } public synchronized void clear() { npieces = 0; } public synchronized void addPiece(Color color, Point z[], int n) { pieces[npieces] = outline(z, n); colors[npieces] = color; npieces++; } public synchronized void setDirty() { dirty = true; repaint(); } public Dimension getPreferredSize() { return wsize; } }