// PA055 Vizualizace komplexnich dat (podzim 2013) // Martin Ukrop, 374297 // uloha 02 // screen dimensions int screenWidth = 800; int screenHeight = 600; // graph filename String filename = "graph.txt"; // colours color bgColor = color(0, 0, 0); color edgeColor = color(150, 150, 150); color clickedColor = color(255, 255, 255); // graph nodes and edges int numNodes = 40; static final float edgeProbability = 0.05; int edgeWeight = 3; float edgeMaxDistance = euclideanDistance(0, 0, screenWidth, screenHeight)/2; float nodeMovementSpeed = 0.1; Node[] nodes; boolean[][] edges; // global variables int draggingIndex = -1; void setup() { size(screenWidth, screenHeight); smooth(); if (!readGraph(filename)) { createRandomGraph(); } saveGraph(filename); } void draw() { background(bgColor); displayEdges(); for (int node = 0; node < numNodes; node++) { for (int node2 = 0; node2 < numNodes; node2++) { if (node != node2) adjustNode(node, node2); } nodes[node].display(); } } void mouseReleased() { draggingIndex = -1; } void mousePressed() { int closest = closestNode(mouseX, mouseY); nodes[closest].click(); draggingIndex = closest; } void mouseDragged() { nodes[draggingIndex].setPosition(mouseX, mouseY); } void createRandomGraph() { nodes = new Node[numNodes]; edges = new boolean[numNodes][numNodes]; for (int node = 0; node < numNodes; node++) { nodes[node] = new Node(); nodes[node].setLabel(node); edges[node][node] = false; for (int node2 = node + 1; node2 < numNodes; node2++) { edges[node][node2] = edges[node2][node] = random(0, 1) < edgeProbability ? true : false; } } } boolean readGraph(String file) { BufferedReader reader = createReader(file); int tempNumNodes; String line; try { line = reader.readLine(); tempNumNodes = int (line); nodes = new Node[tempNumNodes]; for (int node = 0; node < tempNumNodes; node++) { line = reader.readLine(); String[] data = split(line, TAB); nodes[node] = new Node(); nodes[node].setLabel(int(data[0])); nodes[node].setWeight(int(data[1])); nodes[node].setPosition(int(data[2]),int(data[3])); } edges = new boolean[tempNumNodes][tempNumNodes]; for (int node = 0; node < tempNumNodes; node++) { line = reader.readLine(); String[] data = split(line, TAB); for (int node2 = 0; node2 < tempNumNodes; node2++) { edges[node][node2] = boolean(data[node2]); } } numNodes = tempNumNodes; reader.close(); println("Graph successfully loaded from '"+file+"'."); return true; } catch (Exception e) { println("Cannot read graph from file '" + file + "'."); println("Cause: " + e); return false; } } boolean saveGraph(String file) { PrintWriter writer = createWriter(file); try { writer.println(numNodes); for (int node = 0; node < numNodes; node++) { writer.println(nodes[node].getLabel()+"\t"+nodes[node].getWeight()+"\t"+nodes[node].getX()+"\t"+nodes[node].getY()); } for (int node = 0; node < numNodes; node++) { for (int node2 = 0; node2 < numNodes; node2++) { writer.print(edges[node][node2]+"\t"); } writer.println(); } writer.close(); println("Graph successfully saved to '"+file+"'."); return true; } catch (Exception e) { println("Cannot write graph to file '" + file + "'."); println("Cause: " + e); return false; } } float euclideanDistance(float x1, float y1, float x2, float y2) { return sqrt(sq(x1 - x2) + sq(y1 - y2)); } int closestNode(float x, float y) { float minDistance = euclideanDistance(0, 0, screenWidth, screenHeight); int closestNode = -1; for (int node = 0; node < numNodes; node++) { float dist = nodes[node].distanceFromPoint(x, y); if (dist < minDistance) { minDistance = dist; closestNode = node; } } return closestNode; } void adjustNode(int node, int node2) { float distance = nodes[node].distanceFromNode(node2); float minDist = (nodes[node].getSize() + nodes[node2].getSize()); float maxDistance = edgeMaxDistance(node, node2); if (edges[node][node2]) { if (distance > maxDistance) { nodes[node].adjustPosition(node2, (-1)*nodeMovementSpeed*(1-maxDistance/distance)); } if (nodes[node].distanceFromNode(node2) < minDist) { nodes[node].adjustPosition(node2, 4*nodeMovementSpeed*(1-distance/minDist)); } } else { if (nodes[node].distanceFromNode(node2) < maxDistance) { nodes[node].adjustPosition(node2, nodeMovementSpeed*(1-distance/maxDistance)); } } } float edgeMaxDistance(int i, int j) { float coefficient = (nodes[i].getDistanceCoefficient() + nodes[j].getDistanceCoefficient()) / 2; return coefficient * edgeMaxDistance; } void displayEdges() { stroke(edgeColor); strokeWeight(edgeWeight); for (int node = 0; node < numNodes; node++) { for (int node2 = node + 1; node2 < numNodes; node2++) { if (edges[node][node2]) { line(nodes[node].getX(), nodes[node].getY(), nodes[node2].getX(), nodes[node2].getY()); } } } } private class Node { private final static int baseSize = 150; private final static int minSize = 10; private final static int maxWeight = 50; private final static float labelSizeConstant = 0.5; private float x; private float y; private int weight; private String label; private boolean clicked; Node() { clicked = false; weight = int(random(0, maxWeight)); setPosition(random(0, screenWidth), random(0, screenHeight)); } public int getLabel() { return int(label); } public void setLabel(int label) { this.label = Integer.toString(label); } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public void click() { clicked = !clicked; } public float distanceFromNode(int node) { return distanceFromPoint(nodes[node].getX(), nodes[node].getY()); } public float distanceFromPoint(float x, float y) { return euclideanDistance(x, y, this.x, this.y); } public void display() { fill(color(50+3*weight,100+2*weight,200)); if (clicked) { stroke(clickedColor); strokeWeight(2*edgeWeight); ellipse(x, y, getSize(), getSize()); fill(bgColor); int textSize = int(getSize() * labelSizeConstant); textSize(textSize); text(label, x-textWidth(label)/2, y+0.8*textAscent()/2); } else { noStroke(); ellipse(x, y, getSize(), getSize()); } } public void setPosition(float x, float y) { this.x = min(screenWidth-getSize()/2, max(getSize()/2, x)); this.y = min(screenHeight-getSize()/2, max(getSize()/2, y)); } public void adjustPosition(int neighbour, float factor) { setPosition( x + (x-nodes[neighbour].getX()) * factor, y + (y-nodes[neighbour].getY()) * factor); } public float getX() { return x; } public float getY() { return y; } public float getSize() { return getDistanceCoefficient() * baseSize + minSize; } public float getDistanceCoefficient() { float screenDiagonal = euclideanDistance(0, 0, screenWidth, screenHeight); return sq(1 - distanceFromPoint(screenWidth/2, screenHeight/2) * 2 / screenDiagonal); } }