// Squares // David A. Mellis // 12 October 2004 // // A program to arrange black squares on a white background. // Selected squares are drawn with a red border. // // Mouse: // Click the background to create a square (and select it). // Click a square to select it (and deselect the others). // Drag a square to move it. // // Keyboard: // a, A: select all squares // n, N: deselect all squares // UP, DOWN, LEFT, RIGHT: move selected squares // [, ]: rotate selected squares // <, >: shrink or expand selected squares // BACKSPACE: delete selected squares // g, G: toggle grid lines // s, S: save the layout of the squares // l, L: load the layout of the squares // p, P: save picture of squares // c, C: output Processing code to draw squares // add undo // add other shapes // add other colors // fix selection with the mouse int N = 0; Square[] squares = new Square[N]; boolean picture = false; boolean grid = false; void setup() { size(251, 251); newSquare(85, 85, 60, false); newSquare(165, 85, 60, false); newSquare(85, 165, 60, false); newSquare(165, 165, 60, false); } void loop() { background(255); rectMode(CORNER); if (grid) { noStroke(); for (int i = 0; i < 251; i += 5) { if (i % 50 == 0) fill(175); else if (i % 10 == 0) fill(200); else fill(225); // use rect instead of line here or the grid will be above the squares rect(0, i, 251, 1); rect(i, 0, 1, 251); } } stroke(0); noFill(); rect(0, 0, width - 1, height - 1); rectMode(CENTER_DIAMETER); for (int i = 0; i < N; i++) squares[i].draw(); if (picture == true) { saveFrame(); picture = false; } } void mousePressed() { boolean hit = false; //if (!keyPressed || key != SHIFT) selectSquares(false); for (int i = 0; i < N; i++) { if (within(squares[i].getX(), mouseX - squares[i].getSize() / 2, mouseX + squares[i].getSize() / 2) && within(squares[i].getY(), mouseY - squares[i].getSize() / 2, mouseY + squares[i].getSize() / 2)) { squares[i].setSelected(true); hit = true; } } if (!hit) newSquare(mouseX, mouseY); } void mouseDragged() { moveSquares(mouseX - pmouseX, mouseY - pmouseY); } boolean within(int x, int min, int max) { return x >= min && x <= max; } void keyPressed() { if (key == UP) moveSquares(0, -1); if (key == DOWN) moveSquares(0, 1); if (key == LEFT) moveSquares(-1, 0); if (key == RIGHT) moveSquares(1, 0); if (key == 'a' || key == 'A') selectSquares(true); if (key == '<' || key == ',') resizeSquares(-1); if (key == '>' || key == '.') resizeSquares(1); if (key == 'n' || key == 'N') selectSquares(false); if (key == '[' || key == '{') rotateSquares(-PI / 32); if (key == ']' || key == '}') rotateSquares(PI / 32); if (key == 8) deleteSquares(); if (key == 'g' || key == 'G') grid = !grid; if (key == 's' || key == 'S') saveSquares(); if (key == 'l' || key == 'L') loadSquares(); if (key == 'p' || key == 'P') picture = true; if (key == 'c' || key == 'C') codeSquares(); } void selectSquares(boolean selected) { for (int i = 0; i < N; i++) squares[i].setSelected(selected); } void moveSquares(int dx, int dy) { for (int i = 0; i < N; i++) if (squares[i].isSelected()) { squares[i].setX(squares[i].getX() + dx); squares[i].setY(squares[i].getY() + dy); } } void resizeSquares(int dsize) { for (int i = 0; i < N; i++) if (squares[i].isSelected()) squares[i].setSize(squares[i].getSize() + dsize); } void rotateSquares(float dangle) { for (int i = 0; i < N; i++) if (squares[i].isSelected()) squares[i].setAngle(squares[i].getAngle() + dangle); } void saveSquares() { String[] lines = new String[2 * N + 1]; lines[0] = "" + N; for (int i = 0; i < N; i++) { lines[2 * i + 1] = squares[i].getX() + " " + squares[i].getY() + " " + squares[i].getSize(); lines[2 * i + 2] = "" + squares[i].getAngle(); } saveStrings("squares.sav", lines); } void loadSquares() { String[] lines = loadStrings("squares.sav"); int n = splitInts(lines[0])[0]; Square[] newSquares = new Square[n]; for (int i = 0; i < n; i++) newSquares[i] = new Square(splitInts(lines[i * 2 + 1])[0], splitInts(lines[i * 2 + 1])[1], splitInts(lines[i * 2 + 1])[2], splitFloats(lines[i * 2 + 2])[0]); // these two lines are not thread safe (they need to happen atomically); is that a problem? squares = newSquares; N = n; } void codeSquares() { int lps = 6; String[] lines = new String[N * lps + 6]; lines[0] = "size(251, 251);"; lines[1] = "noStroke();"; lines[2] = "fill(0, 0, 0);"; lines[3] = "background(255);"; lines[4] = "rectMode(CENTER_DIAMETER);"; lines[5] = ""; for (int i = 0; i < N; i++) { lines[i * lps + 6] = "push();"; lines[i * lps + 7] = "translate(" + squares[i].getX() + ", " + squares[i].getY() + ");"; lines[i * lps + 8] = "rotate(" + squares[i].getAngle() + ");"; lines[i * lps + 9] = "rect(0, 0, " + squares[i].getSize() + ", " + squares[i].getSize() + ");"; lines[i * lps + 10] = "pop();"; lines[i * lps + 11] = ""; } saveStrings("squares.pde", lines); } void newSquare(int x, int y) { newSquare(x, y, 60, true); } void newSquare(int x, int y, int size, boolean selected) { Square[] newSquares = new Square[N + 1]; for (int i = 0; i < N; i++) newSquares[i] = squares[i]; newSquares[N] = new Square(x, y, size, 0.0); newSquares[N].setSelected(selected); squares = newSquares; N = N + 1; } void deleteSquares() { for (int i = N - 1; i >= 0; i--) { if (squares[i].isSelected()) { deleteSquare(i); } } } void deleteSquare(int n) { N = N - 1; for (int j = n; j < N; j++) squares[j] = squares[j + 1]; } class Square { int x, y; int size; float angle; boolean selected; public Square(int x, int y) { this(x, y, 20, 0.0); } public Square(int x, int y, int size, float angle) { this.x = x; this.y = y; this.size = size; this.angle = angle; this.selected = false; } public int getX() { return x; } public int getY() { return y; } public int getSize() { return size; } public float getAngle() { return angle; } public boolean isSelected() { return selected; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setSize(int size) { this.size = size; } public void setAngle(float angle) { this.angle = angle; } public void setSelected(boolean selected) { this.selected = selected; } public void draw() { if (selected) stroke(255, 0, 0); else noStroke(); fill(0, 0, 0); push(); translate(x, y); rotate(angle); rect(0, 0, size, size); pop(); } }