|
import java.util.*; |
|
|
|
// Size of display |
|
int DISPLAY_WIDTH = 600; |
|
int DISPLAY_HEIGHT = 600; |
|
// Colours |
|
int BLACK = 0; |
|
int WHITE = 255; |
|
|
|
int CENTER_X = 0; |
|
int CENTER_Y = 0; |
|
|
|
SocialGraphCsv csv; |
|
|
|
void setup() { |
|
csv = new SocialGraphCsv("mutual_friends_2014_01_05.csv"); |
|
|
|
size(DISPLAY_WIDTH, DISPLAY_HEIGHT); |
|
background(WHITE); |
|
noLoop(); |
|
} |
|
|
|
void draw() { |
|
// Displace the drawn polygon by half width and half height of `display`, which means center. |
|
// With `translate`, we can calculate coodinate of each vertex as distance from the center of a |
|
// diagram, where it's coordinate is as (0, 0), and then, displace the polygon to actual coordinate |
|
// of `display`. |
|
translate(DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2); |
|
|
|
int radius = 200; |
|
SocialGraph socialGraph = new SocialGraph(csv, radius); |
|
socialGraph.drawNodes(); |
|
socialGraph.drawNodeNames(); |
|
socialGraph.drawFamiliarities(); |
|
|
|
saveFrame("social_graph.jpg"); |
|
} |
|
|
|
class SocialGraph { |
|
EquilateralPolygon polygon; |
|
Coordinate[] vertexes; |
|
ArrayList<ArrayList<String>> familiarities; |
|
SocialGraphCsv csv; |
|
|
|
SocialGraph(SocialGraphCsv csv, int radius) { |
|
this.csv = csv; |
|
this.familiarities = csv.familiarities; |
|
this.polygon = new EquilateralPolygon(this.familiarities.get(0).size(), radius); |
|
} |
|
|
|
public void drawNodes() { |
|
polygon.drawVertexes(); |
|
} |
|
|
|
public void drawNodeNames() { |
|
nodeNames names = new nodeNames(this.polygon, this.csv); |
|
names.draw(); |
|
} |
|
|
|
public void drawFamiliarities() { |
|
for(int i = 0; i < familiarities.size(); i++) { |
|
color colour = color(random(255), random(255), random(255), 128); |
|
|
|
for(int j = 0; j < familiarities.get(i).size(); j++) { |
|
FamiliarityAsLine line = new FamiliarityAsLine( |
|
familiarities.get(i).get(j), |
|
polygon.vertexes[i], |
|
polygon.vertexes[j] |
|
); |
|
|
|
line.drawLine(colour); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// This class saves coordinate |
|
class Coordinate { |
|
int x; |
|
int y; |
|
|
|
// The Constructor is defined with arguments. |
|
Coordinate(float _x, float _y) { |
|
x = int(_x); |
|
y = int(_y); |
|
} |
|
|
|
public String toString() { |
|
return x + ", " + y; |
|
} |
|
} |
|
|
|
// This class represents equilateral polygon, which draws circles on each vertex |
|
class EquilateralPolygon { |
|
int numVertexes; |
|
int radius; |
|
public float[] angles; |
|
Coordinate[] vertexes; |
|
|
|
EquilateralPolygon(int _numVertexes, int _radius) { |
|
this.numVertexes = _numVertexes; |
|
this.angles = new float[numVertexes]; |
|
this.radius = _radius; |
|
this.vertexes = vertexes(); |
|
} |
|
|
|
private float xVertex(int i) { |
|
return cos(angles[i]) * radius; |
|
} |
|
|
|
private float yVertex(int i) { |
|
return sin(angles[i]) * radius; |
|
} |
|
|
|
public Coordinate[] vertexes() { |
|
vertexes = new Coordinate[numVertexes]; |
|
float angleForSingleArc = 2 * PI / numVertexes; |
|
|
|
for(int i = 0; i < numVertexes; i++) { |
|
// Sum up angles |
|
angles[i] = HALF_PI - angleForSingleArc * i; |
|
|
|
float x = xVertex(i); |
|
float y = - yVertex(i); |
|
vertexes[i] = new Coordinate(x, y); |
|
} |
|
|
|
return vertexes; |
|
} |
|
|
|
// Put tiny circles onto each vertex |
|
public void drawVertexes() { |
|
smooth(); |
|
|
|
int r = 20; |
|
for (int i = 0; i < numVertexes; i++) { |
|
// Draw black circle for the first vertex and draw gray ones for the rest |
|
int colorValue = i == 0 ? 0 : 200; |
|
stroke(colorValue); |
|
strokeWeight(1); |
|
ellipse(vertexes[i].x, vertexes[i].y, r, r); |
|
} |
|
} |
|
} |
|
|
|
class nodeNames { |
|
EquilateralPolygon polygon; |
|
SocialGraphCsv csv; |
|
|
|
nodeNames(EquilateralPolygon polygon, SocialGraphCsv csv) { |
|
this.polygon = polygon; |
|
this.csv = csv; |
|
} |
|
|
|
public void draw() { |
|
fill(BLACK); |
|
textAlign(CENTER); |
|
|
|
for (int i = 0; i < polygon.angles.length; i++) { |
|
float edtendedRadius = polygon.radius + 30; |
|
float x = cos(polygon.angles[i]) * edtendedRadius; |
|
float y = - sin(polygon.angles[i]) * edtendedRadius; |
|
text(csv.nodeNames.get(i), x, y); |
|
} |
|
|
|
noFill(); |
|
} |
|
} |
|
|
|
class SocialGraphCsv { |
|
ArrayList<ArrayList<String>> stringList; |
|
ArrayList<String> nodeNames; |
|
ArrayList<ArrayList<String>> familiarities; |
|
|
|
SocialGraphCsv(String filePath){ |
|
String[] rows = loadStrings(filePath); |
|
this.stringList = convertToStrings(rows); |
|
this.nodeNames = nodeNames(this.stringList); |
|
this.familiarities = familiarities(this.stringList); |
|
} |
|
|
|
public ArrayList<String> nodeNames(ArrayList<ArrayList<String>> stringValues) { |
|
ArrayList<String> nameOfNodes = (ArrayList<String>) stringValues.get(0).clone(); |
|
nameOfNodes.remove(0); |
|
return nameOfNodes; |
|
} |
|
|
|
public ArrayList<ArrayList<String>> convertToStrings(String[] rows){ |
|
ArrayList<ArrayList<String>> csvRows = new ArrayList<ArrayList<String>>(rows.length -1); |
|
|
|
for(String row: rows) { |
|
String[] cols = cols(row); |
|
|
|
ArrayList<String> csvCols = new ArrayList<String>(); |
|
for(String col: cols) csvCols.add(col); |
|
csvRows.add(csvCols); |
|
} |
|
|
|
return csvRows; |
|
} |
|
|
|
private String[] cols(String row) { |
|
return split(row, ','); |
|
} |
|
|
|
public ArrayList<ArrayList<String>> familiarities(ArrayList<ArrayList<String>> stringValues) { |
|
ArrayList<ArrayList<String>> vals = (ArrayList<ArrayList<String>>) stringValues.clone(); |
|
|
|
for(ArrayList<String> row: vals) row.remove(0); |
|
vals.remove(0); |
|
return vals; |
|
} |
|
} |
|
|
|
class FamiliarityAsLine { |
|
Coordinate vertexMe; |
|
Coordinate vertexPeer; |
|
int weight = 0; |
|
|
|
FamiliarityAsLine(String familiarity, Coordinate vertexMe, Coordinate vertexPeer) { |
|
determineWeight(Integer.parseInt(familiarity)); |
|
|
|
this.vertexMe = vertexMe; |
|
this.vertexPeer = vertexPeer; |
|
} |
|
|
|
public void drawLine(color colour){ |
|
noFill(); |
|
println(vertexMe); |
|
println(vertexPeer); |
|
|
|
int x1 = this.vertexMe.x; |
|
int y1 = this.vertexMe.y; |
|
int x2 = this.vertexPeer.x; |
|
int y2 = this.vertexPeer.y; |
|
int ctlX1 = int(x1 - x1 * 0.3); |
|
int ctlY1 = int(y1 - y1 * 0.3); |
|
int ctlX2 = int(x2 - x2 * 0.4); |
|
int ctlY2 = int(y2 - y2 * 0.4); |
|
|
|
if(this.weight > 0) stroke(colour); |
|
bezier(x1, y1, ctlX1, ctlY1, ctlX2, ctlY2, x2, y2); |
|
} |
|
|
|
public void determineWeight(int familiarity) { |
|
if(familiarity == 0 || familiarity == 1) { |
|
noStroke(); |
|
this.weight = 0; |
|
} else { |
|
this.weight = familiarity; |
|
} |
|
strokeWeight(weight); |
|
} |
|
} |
https://www.diigo.com/item/p/qpeeeqszbsebdpdorzbcebboep