Skip to content

Instantly share code, notes, and snippets.

@ptrv
Created November 17, 2012 01:35
Show Gist options
  • Save ptrv/4092517 to your computer and use it in GitHub Desktop.
Save ptrv/4092517 to your computer and use it in GitHub Desktop.
Simple force directed graph drawing algorithm
#!/usr/bin/env python
# Force-Directed Graph Drawing
import Tkinter
import random
import math
# d = [
# [.0, .3, .3, .0],
# [.3, .0, .3, .0],
# [.3, .3, .0, .3],
# [.0, .0, .3, .0]
# ]
d = []
nrows = 5
ncols = 5
for i in xrange(nrows * ncols):
ci = i % ncols
ri = i / ncols
dr = []
for j in xrange(nrows * ncols):
cj = j % ncols
rj = j / ncols
if (ci == cj) and (ri==rj-1 or ri==rj+1) or (ri==rj and (ci==cj-1 or ci==cj+1)):
dr.append(.1)
else:
dr.append(.0)
d.append(dr)
# mass
alpha = 1.0
beta = .0001
k = 1.0
#damping
eta = .99
delta_t = .01
m = len(d)
root = Tkinter.Tk()
canvas = Tkinter.Canvas(root, width=500, height=500, background="yellow")
canvas.pack()
x = []
v = []
ids = []
def move_oval(i):
newx = int(x[i][0] * 500)
newy = int(x[i][1] * 500)
canvas.coords(ids[i], newx - 5, newy - 5, newx + 5, newy + 5)
for i in xrange(m):
xi = [random.random(), random.random()]
x.append (xi)
v.append ([0.0, 0.0])
id = canvas.create_oval(245, 245, 255, 255, fill="red")
ids.append(id)
move_oval(i)
lids = []
def move_line(id, xi, xj):
canvas.coords(id, int(xi[0] * 500), int(xi[1] * 500), int(xj[0] * 500), int(xj[1] * 500))
for i in xrange(m):
for j in xrange(m):
if d[i][j] != 0:
id = canvas.create_line(0, 0, 0, 0)
lids.append(id)
move_line(id, x[i], x[j])
def Coulomb_force(xi, xj):
dx = xj[0] - xi[0]
dy = xj[1] - xi[1]
ds2 = dx*dx + dy*dy
ds = math.sqrt (ds2)
ds3 = ds2*ds
if ds3 == 0.0:
const = 0
else:
const = beta / (ds2*ds)
return [-const * dx, -const * dy]
def Hook_force(xi, xj, dij):
dx = xj[0] - xi[0]
dy = xj[1] - xi[1]
ds = math.sqrt (dx*dx + dy*dy)
dl = ds - dij
const = k * dl / ds
return [const * dx, const * dy]
def move ():
for i in xrange(m):
Fx = 0.0
Fy = 0.0
for j in xrange(m):
if j == 1:
continue
dij = d[i][j]
Fij = 0.0
if dij == 0.0:
Fij = Coulomb_force(x[i], x[j])
else:
Fij = Hook_force(x[i], x[j], dij)
Fx += Fij[0]
Fy += Fij[1]
v[i][0] = (v[i][0] + alpha * Fx * delta_t) * eta
v[i][1] = (v[i][1] + alpha * Fy * delta_t) * eta
for i in xrange(m):
x[i][0] += v[i][0] * delta_t
x[i][1] += v[i][1] * delta_t
move_oval(i)
li = 0
for i in xrange(m):
for j in xrange(m):
if d[i][j] != 0:
id = lids[li]
move_line(id, x[i], x[j])
li += 1
root.after(1, move)
root.after(1, move)
root.mainloop()
@magicmouse
Copy link

looks like there is a bug on line 111, shouldn't that be an i not a 1? i wish there were more comments about what the 'd' matrix does. It is a 5x5 matrix, with either 0 or 0.1 in it; some kind of convolution kernel?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment