int selected = -1; int n; Mass[] m; Spring[] s; Word[] w; Distance d; BFont font; void setup() { size(500, 400); ellipseMode(CENTER_DIAMETER); String[] Line = loadStrings("words.txt"); n = Line.length; d = new Distance(); m = new Mass[n]; for (int i = 0; i < n; i++) m[i] = new Mass(random(width), random(height), 100.0); w = new Word[n]; for (int i = 0; i < n; i++) w[i] = new Word(Line[i], m[i]); int wordi = 0; s = new Spring[n * n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { s[wordi] = new Spring(m[i], m[j], 0.001, 25.0 * d.LD(w[i].str, w[j].str)); wordi++; } } font = loadFont("Meta-Bold.vlw.gz"); } void loop() { background(255, 255, 255); for (int i = 0; i < n * n; i++) s[i].step(); for (int i = 0; i < n; i++) if (i != selected) m[i].step(); else { fill(0, 64, 128, 128); noStroke(); ellipse(m[i].px, m[i].py, 20, 20); } for (int i = 0; i < n; i++) { w[i].draw(); } } void mousePressed() { float mx = (float)max(min(mouseX, width - 1), 0); float my = (float)max(min(mouseY, height - 1), 0); for (int i = 0; i < n; i++) { float d = sqrt(sq(mx - m[i].px) + sq(my - m[i].py)); if (d < 10.0) { selected = i; return; } } } void mouseReleased() { selected = -1; } void mouseDragged() { if (selected != -1) { float mx = (float)max(min(mouseX, width - 40), 40); float my = (float)max(min(mouseY, height - 20), 20); m[selected].px = mx; m[selected].py = my; } } class Mass { float px, py, vx, vy, ax, ay, fx, fy, mass; Mass(float x, float y, float m) { px = x; py = y; vx = vy = ax = ay = fx = fy = 0; mass = m; } void addforce(float x, float y) { fx += x; fy += y; } void step() { ax = fx / mass; ay = fy / mass; vx += fx; vy += fy; px += vx; py += vy; fx = 0; fy = 0; vx *= 0.95; vy *= 0.95; px = max(min(px, width - 40), 40); py = max(min(py, height - 20), 20); } void draw() { noStroke(); fill(255, 0, 0, 128); ellipse(px, py, 7, 7); } } class Spring { float k, dist; Mass m1, m2; Spring(Mass a, Mass b, float springk, float d) { m1 = a; m2 = b; k = springk; dist = d; } void step() { float stretch = dist - sqrt(sq(m1.px - m2.px) + sq(m1.py - m2.py)); float direction = atan2(m1.px - m2.px, m1.py - m2.py); float force = k * stretch; m1.addforce(force * sin(direction), force * cos(direction)); m2.addforce(-force * sin(direction), -force * cos(direction)); } void draw() { stroke(0, 0, 0); line(m1.px, m1.py, m2.px, m2.py); } } class Word { String str; Mass m; Word(String s, Mass m1) { m = m1; str = s; } void draw() { fill(0, 0, 0); textFont(font, 15); textMode(ALIGN_CENTER); text(str, m.px, m.py - 4); m.draw(); } } /** Levenshtein Distance function ***********************/ /** code from http://www.merriampark.com/ld.htm *********/ public class Distance { //**************************** // Get minimum of three values //**************************** private int Minimum (int a, int b, int c) { int mi; mi = a; if (b < mi) { mi = b; } if (c < mi) { mi = c; } return mi; } //***************************** // Compute Levenshtein distance //***************************** public int LD (String s, String t) { int d[][]; // matrix int n; // length of s int m; // length of t int i; // iterates through s int j; // iterates through t char s_i; // ith character of s char t_j; // jth character of t int cost; // cost // Step 1 n = s.length (); m = t.length (); if (n == 0) { return m; } if (m == 0) { return n; } d = new int[n+1][m+1]; // Step 2 for (i = 0; i <= n; i++) { d[i][0] = i; } for (j = 0; j <= m; j++) { d[0][j] = j; } // Step 3 for (i = 1; i <= n; i++) { s_i = s.charAt (i - 1); // Step 4 for (j = 1; j <= m; j++) { t_j = t.charAt (j - 1); // Step 5 if (s_i == t_j) { cost = 0; } else { cost = 1; } // Step 6 d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost); } } // Step 7 return d[n][m]; } }