import gnu.io.CommPortIdentifier; import javax.swing.event.EventListenerList; import java.awt.Rectangle; import java.awt.geom.Area; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; // TODO: Loads of threads in this class, make sure everything is thread safe. public class PunchingBag implements Runnable { boolean ledsChanged = false; final public int ledHeight = 20; final public int ledWidth = 9; private byte[] rawLeds = new byte[6 * 8]; private Colour[][] leds = new Colour[ledWidth][ledHeight]; // NEVER ACCESS // DIRECTLY (Use // SetLedInternal) private byte[] ledGridIntensities = new byte[6]; final public int buttonHeight = 19; final public int buttonWidth = 8; private boolean[][] buttons = new boolean[buttonWidth][buttonHeight]; boolean buttonsChanged = false; int bottomAccelX = 0; int bottomAccelY = 0; int topAccelX = 0; int topAccelY = 0; boolean acccelChanged = false; private ArrayList runningEffects = new ArrayList(); Arduino buttonArduino = new Arduino(); Arduino ledArduino = new Arduino(); public enum Colour { None, Red, Yellow, Green }; public enum Direction { Right, Left, Up, Down }; // Is there a better class for this? One that it is not a swing class. private EventListenerList buttonListenerList = new EventListenerList(); private EventListenerList ledListenerList = new EventListenerList(); private EventListenerList accelListenerList = new EventListenerList(); public PunchingBag() { new Thread(this).start(); } /** * Adds an ActionListener to the button. * * @param l * the ActionListener to be added */ public void addButtonListener(ButtonListener l) { buttonListenerList.add(ButtonListener.class, l); } public void addLEDChangeListener(LEDListener l) { ledListenerList.add(LEDListener.class, l); } public void addAccelChangeListener(AccelListener l) { accelListenerList.add(AccelListener.class, l); } public Colour getLED(int x, int y) { return leds[x][y]; } private boolean setLEDInternal(int x, int y, Colour colour) { if (x >= 0 && x < ledWidth && y >= 0 && y < ledHeight) { if (leds[x][y] != colour) { leds[x][y] = colour; ledsChanged = true; } return true; } else { return false; } } public void setLED(int x, int y, Colour colour) { runningEffects.add(new Point(x, y, colour)); } void circleExpand(int x, int y, int intensity) { runningEffects.add(new CircleExpand(x, y, 100)); } void squareExpand(int x, int y, int intensity, Colour colour) { runningEffects.add(new SquareExpand(x, y, intensity, colour)); } void noise(Rectangle rect, long time) { runningEffects.add(new Noise(rect, System.currentTimeMillis() + time)); } abstract class Effect { long lastRefresh = 0; boolean stop = false; // int refreshRate = 60; // Times per second to refresh public void stop() { stop = true; } abstract public void draw(); } class Point extends Effect { final int x; final int y; final Colour colour; public Point(int x, int y, Colour colour) { this.x = x; this.y = y; this.colour = colour; } public void draw() { setLEDInternal(x, y, colour); } } class Text extends Effect { final String string; final Area area; final Direction direction; final int speed; public Text(String string, Area area, Direction direction, int speed) { this.string = string; this.area = area; this.direction = direction; this.speed = speed; } public void draw() { byte[] stringBytes = string.getBytes(); } } class CircleExpand extends Effect { final int x; final int y; int intensity; boolean drawnSomething = true; float currentRadius = 0; public CircleExpand(int x, int y, int intensity) { this.x = x; this.y = y; this.intensity = intensity; } public void draw() { Colour colour; if (intensity >= 10) { colour = Colour.Red; } else if (intensity >= 5) { colour = Colour.Yellow; } else { colour = Colour.Green; } intensity -= 5; currentRadius += 0.1 + 0.1 + (currentRadius / 20); // currentRadius += 0.01; // longhand: currentRadius = currentRadius + 0.1 + (currentRadius / // 10); if (!drawEllipse(x, y, (int) currentRadius, (int) currentRadius, colour)) stop = true; } } class SquareExpand extends Effect { final int x; final int y; final Colour colour; int intensity; boolean drawnSomething = true; int heightWidth = 2; public SquareExpand(int x, int y, int intensity, Colour colour) { this.x = x; this.y = y; this.intensity = intensity; this.colour = colour; } public void draw() { heightWidth += 0.1 + 0.1 + (heightWidth / 20); if (!drawRectCenter(x, y, heightWidth, heightWidth, colour)) stop = true; } } class Noise extends Effect { final Rectangle area; final long endTime; public Noise(Rectangle area, long endTime) { this.area = area; this.endTime = endTime; } @Override public void draw() { if (endTime >= System.currentTimeMillis()) { for (int y = area.y; y < (area.y + area.height); y++) { for (int x = area.x; x < (area.x + area.width); x++) { double random = Math.random(); if (random < 0.25) { setLEDInternal(x, y, Colour.Red); } else if (random < 0.5) { setLEDInternal(x, y, Colour.Yellow); } else if (random < 0.75) { setLEDInternal(x, y, Colour.Green); } else { setLEDInternal(x, y, Colour.None); } } } } else { stop(); } } } public boolean drawRectCenter(int x, int y, int height, int width, Colour colour) { if (height < 0) { height = 0; } if (width < 0) { width = 0; } if (height == 0 && width == 0) { return setLEDInternal(x, y, colour); } boolean doneSomething = false; int widthEx = 0; int heightEx = 0; do { if (setLEDInternal((x - (height / 2)) + widthEx, y - (height / 2), colour)) { doneSomething = true; } if (setLEDInternal(x - (height / 2), (y - (height / 2)) + heightEx, colour)) { doneSomething = true; } if (setLEDInternal((x - (height / 2)) + widthEx, y + (height / 2), colour)) { doneSomething = true; } if (setLEDInternal(x + (height / 2), (y - (height / 2)) + heightEx, colour)) { doneSomething = true; } if (heightEx < height) { heightEx++; } if (widthEx < width) { widthEx++; } } while (height >= 0 && width >= 0); return doneSomething; } public boolean drawRectCorner(int x, int y, int height, int width, Colour colour) { if (height < 0) { height = 0; } if (width < 0) { width = 0; } if (height == 0 && width == 0) { return setLEDInternal(x, y, colour); } boolean doneSomething = false; int heightEx = 0; int widthEx = 0; do { if (setLEDInternal(x + widthEx, y, colour)) { doneSomething = true; } if (setLEDInternal(x, y + heightEx, colour)) { doneSomething = true; } if (setLEDInternal(x + widthEx, y + height, colour)) { doneSomething = true; } if (setLEDInternal(x + width, y + heightEx, colour)) { doneSomething = true; } if (heightEx < height) { heightEx++; } if (widthEx < width) { heightEx++; } } while (height >= 0 && width >= 0); return doneSomething; } public boolean drawEllipse(int x, int y, int radx, int rady, Colour colour) { if (radx == 0 && rady == 0) { return setLEDInternal(x, y, colour); } boolean doneSomething = false; int dx = 0, dy = rady; /* first quadrant from top left to bottom right */ int a2 = radx * radx, b2 = rady * rady; int err = b2 - (2 * rady - 1) * a2, e2; /* error value in the first step */ do { if (setLEDInternal(x + dx, y + dy, colour)) {/* I. Quadrant */ doneSomething = true; } if (setLEDInternal(x - dx, y + dy, colour)) {/* II. Quadrant */ doneSomething = true; } if (setLEDInternal(x - dx, y - dy, colour)) {/* III. Quadrant */ doneSomething = true; } if (setLEDInternal(x + dx, y - dy, colour)) {/* IV. Quadrant */ doneSomething = true; } e2 = 2 * err; if (e2 < (2 * dx + 1) * b2) { dx++; err += (2 * dx + 1) * b2; } if (e2 > -(2 * dy - 1) * a2) { dy--; err -= (2 * dy - 1) * a2; } } while (dy >= 0); while (dx++ < radx) { /* correction for flat ellipses (b=1) */ if (setLEDInternal(x + dx, y, colour)) { doneSomething = true; } if (setLEDInternal(x - dx, y, colour)) { doneSomething = true; } } return doneSomething; } private void calculateRawLeds() { // First clear everything Arrays.fill(rawLeds, (byte) 0); // First loop through the 5 easy arrays for (int grid=0; grid<5; grid++) { for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { if ((y % 2) == 0) { if (leds[1 + x][(grid * 4) + (y/2)] == Colour.Green || leds[1 + x][(grid * 4) + (y/2)] == Colour.Yellow ) { //System.out.println("X: " + (x+1) + " Y: " + ((grid * 4) + (y/2)) + " : Green"); //printlnByte((byte) (1 << (8-y))); rawLeds[(grid*8) + x] = (byte) (rawLeds[(grid*8) + x] | (1 << (8-y))); } } else { if (leds[1 + x][(grid * 4) + (y/2)] == Colour.Red || leds[1 + x][(grid * 4) + (y/2)] == Colour.Yellow ) { rawLeds[(grid*8) + x] = (byte) (rawLeds[(grid*8) + x] | (1 << (8-y))); } } } } } /* * for (int y = 0; y <= 18; y++) { for (int x = 0; x <= 6; x++) { if ((y * % 2) == 0) { if (leds[x][y] == Colour.Green || leds[x][y] == * Colour.Yellow) { rawLeds[(int) Math.floor(y / 4)] = (byte) * (rawLeds[(int) Math .floor(y / 4)] | (1 << (7 - x))); } } else { if * (leds[x][y] == Colour.Red || leds[x][y] == Colour.Yellow) { * rawLeds[(int) Math.floor(y / 4)] = (byte) (rawLeds[(int) Math * .floor(y / 4)] | (1 << (7 - x))); } } } } */ /* * int x = 7; // TODO: Complete and test, or rethink the following? for * (int startY = 0; startY <= 4; startY++) { for (int y = 0; y <= 3; * y++) { if (leds[x][startY + (y * 4)] == Colour.Red || leds[x][startY * + (y * 4)] == Colour.Yellow) { rawLeds[(5 * 8) + startY] = (byte) * (rawLeds[(int) Math .floor(y / 4)] | (1 << (7 - x))); } if * (leds[x][y] == Colour.Green || leds[x][y] == Colour.Yellow) { * rawLeds[(int) Math.floor(y / 4)] = (byte) (rawLeds[(int) Math * .floor(y / 4)] | (1 << (7 - x))); } } } */ } /* Clears the led grid */ private void clearLEDGrid() { for (int x = 0; x < ledWidth; x++) { for (int y = 0; y < ledHeight; y++) { leds[x][y] = Colour.None; } } } private void clearButtonGrid() { for (int x = 0; x < buttonWidth; x++) { for (int y = 0; y < buttonHeight; y++) { buttons[x][y] = false; } } } private void clearEffects() { for (Iterator iter = runningEffects.iterator(); iter.hasNext();) { ((Effect) iter.next()).stop(); } } public void clearLEDs() { clearLEDGrid(); clearEffects(); } public void setLEDGridIntensity(byte grid, byte intensity) { ledGridIntensities[grid] = intensity; } public void setLEDIntensities(byte intensity) { Arrays.fill(ledGridIntensities, intensity); } public void buttonPressed(int x, int y) { buttons[x][y] = true; buttonsChanged = true; } public boolean connectToArduinos() { HashSet serialPorts = Arduino .getAvailableSerialPorts(); for (Iterator iter = serialPorts.iterator(); iter.hasNext();) { CommPortIdentifier comPort = (CommPortIdentifier) iter.next(); // HACK FOR WINDOWS TO IDENIFY PORTS LIKELY TO CONTAIN ARDUINO // (COM10 for instance). TODO: FIX if (comPort.getName().length() == 5) { System.out.println(comPort.getName()); } try { ledArduino.connect(comPort.getName()); // System.out.println("ID: " + buttonArduino.getID()); } catch (Exception e) { e.printStackTrace(); } } return true; } private void readAccelData(String data) { String[] nums = data.split(" "); for (int x = 1; x < 5; x++) { // Regex expression to strip newline at end (normally just 4th) int num = Integer.valueOf(nums[x].replaceAll("[^0-9]", "")); if (x == 1 && num != topAccelX) { topAccelX = num; acccelChanged = true; } else if (x == 2 && num != topAccelY) { topAccelY = num; acccelChanged = true; } else if (x == 3 && num != bottomAccelX) { bottomAccelX = num; acccelChanged = true; } else if (x == 4 && num != bottomAccelY) { bottomAccelY = num; acccelChanged = true; } } } private void readButtonData(String data) { if (data.replaceAll("[^0-9]", "").length() > 0) { // System.out.print(data + " length " + data.replaceAll("[^0-9]", // "").length()); int num = Integer.valueOf(data.replaceAll("[^0-9]", "")); int x = (num % 8) - 1; int y = num / 8; // System.out.println("X: " + x + " Y: " + y); buttons[x][y] = true; buttonsChanged = true; } } private void printByte(byte b) { String str; for (int j = 0; j < 8; j++) { if ((b & (1 << (8 - j))) > 0) { str = "1"; } else { str = "0"; } System.out.print(str); } } private void printlnByte(byte b) { printByte(b); System.out.println(""); } public void run() { while (true) { // System.out.println("R"); synchronized (leds) { clearLEDGrid(); // clearButtonGrid(); synchronized (runningEffects) { // Should prevent // ConcurrentModificationException's // (havent managed to produce one // though for (Iterator iter = runningEffects.iterator(); iter .hasNext();) { Effect ef = (Effect) iter.next(); if (ef.stop) { iter.remove(); } else {// if ((ef.lastRefresh + (1000 / // ef.refreshRate)) <= // Systems.currentTimeMillis()) { ef.draw(); ef.lastRefresh = System.currentTimeMillis(); } } } } if (buttonArduino.in != null) { try { int read; while ((read = buttonArduino.read()) != -1) { // System.out.print((char) read); if ((char) read == 'A') { String str = ""; // StringBuilder sb = new StringBuilder(20); while ((read = buttonArduino.read()) != '\n') { str = str + String.valueOf((char) read); // sb.append((char) read); } // System.out.println(""); readAccelData(str); // String[] nums = sbString.trim().split(" "); // System.out.println(nums); // for (int x=0; x<4; x++) { // System.out.println(nums[x]); // } } else if ((char) read == 'B') { String str = ""; // StringBuilder sb = new StringBuilder(20); while ((read = buttonArduino.read()) != '\n') { str = str + String.valueOf((char) read); // sb.append((char) read); } // System.out.println(""); readButtonData(str); } else { // System.out.println((char) read); } // System.out.print("|"); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } calculateRawLeds(); /*String str; for (int i = 0; i < (6 * 8); i++) { printByte(rawLeds[i]); } System.out.println("");*/ if (ledArduino.out != null) { //calculateRawLeds(); try { ledArduino.write(((byte) 108)); ledArduino.write(rawLeds); } catch (IOException e1) { e1.printStackTrace(); } } if (ledsChanged) { LEDListener[] LEDListeners = ledListenerList .getListeners(LEDListener.class); for (int i = 0; i < ledListenerList.getListenerCount(); i++) { LEDListeners[i].LEDchanged(); } } if (buttonsChanged) { ButtonListener[] buttonListeners = buttonListenerList .getListeners(ButtonListener.class); for (int i = 0; i < buttonListenerList.getListenerCount(); i++) { for (int x = 0; x < buttonWidth; x++) { for (int y = 0; y < buttonHeight; y++) { if (buttons[x][y]) buttonListeners[i].buttonPressed(x, y); } } } } clearButtonGrid(); try { Thread.sleep(1000 / 60); } catch (InterruptedException e) { e.printStackTrace(); } } } }