-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpagerank.coffee
303 lines (258 loc) · 7.94 KB
/
pagerank.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
---
---
window.onload = () ->
canvas = document.getElementById('myCanvas')
alert "Canvas not found" if !canvas
# we set the size of the canvas depending on the size of the screen
# 1) we get the screen size
img = document.getElementById('myCanvasDiv')
computed_style = getComputedStyle(img)
screen_width = parseInt(computed_style.getPropertyValue('width'), 10)
screen_height = parseInt(computed_style.getPropertyValue('height'), 10)
# 2) we set the canvas size
canvas.width = screen_width
canvas.height = screen_height
context = canvas.getContext('2d') # we need the context object to call drawing methods
alert "Context not found" if !context
# we create a node list and a link list
node_list = []
link_list = []
# we create a node class and a link class
class Node
radius: 10
class Link
constructor: (node_start, node_end) ->
@node_start = node_start
@node_end = node_end
matrix = []
userVector = []
linkingFlags = {linking: false}
mousePositionX = 0
mousePositionY = 0
# jQuery code to get mouse clicks, etc...
$('#myCanvas').mousedown((mouseEvent) ->
# we get the mouse position
$div = $(mouseEvent.target)
offset = $div.offset()
x = mouseEvent.clientX - offset.left
y = mouseEvent.clientY - offset.top
switch mouseEvent.which
when 1 # left mouse button
linking = false
# we check if there is already a node at that position
# if so, we prevent from creating a new one,
# we suppose the user is trying to link two nodes
for node in node_list
if window.distanceBetweenPoints(node.x, node.y, x, y) < 2*node.radius
linkingFlags = {linking: true, node_start: node}
linking = true
break
if linking is true # user is linking two nodes
break
else # user is creating a new node
node = new Node
node.x = x
node.y = y
node_list.push(node)
computePageRank() # we compute PageRank again
)
$('#myCanvas').mouseup((mouseEvent) ->
# we get the mouse position
$div = $(mouseEvent.target)
offset = $div.offset()
x = mouseEvent.clientX - offset.left
y = mouseEvent.clientY - offset.top
switch mouseEvent.which
when 1 # left mouse button
# if user was not trying to link nodes
# (user pressed button but mouse was not on a node)
if linkingFlags.linking is false
break
# we check if user release mouse button on a node
for node in node_list
if window.distanceBetweenPoints(node.x, node.y, x, y) < 2*node.radius
# we check that it's not the same node as the start node
if node == linkingFlags.node_start
break
# we check that the nodes weren't already linked
alreadyLinked = false
for link in link_list
if link.node_start == linkingFlags.node_start and link.node_end == node
alreadyLinked = true
break
if alreadyLinked is true
break
# we create a link between the two nodes
link = new Link(linkingFlags.node_start, node)
link_list.push(link)
computePageRank() # we compute PageRank again
break
linkingFlags.linking = false # reset flag
)
$('#clearButton').click(() ->
# we clear the display
node_list = []
link_list = []
)
$('#infoButton').click(() ->
# we display an info popup
str = 'Use the "clear" button to clear the display. It will remove all nodes and all links.\n\n'
str += 'ProTip: you can also use SUPPR key if you want to remove a single node.'
alert(str + '\n\n')
)
$('#matrixButton').click(() ->
# we display the stochastic matrix
updateMatrix() # we compute it again, in case nodes or links have been added
if matrix.length == 0
alert("The matrix is empty !\n")
return
# we round the coefficient for a better display
i = 0
while i < matrix.length
j = 0
while j < matrix[i].length
matrix[i][j] = matrix[i][j].toFixed(2)
j++
i++
# we convert the matrix into a string
i = 0
str = ""
while i < matrix.length
j = 0
while j < matrix[i].length
str += matrix[i][j].toString()
if j < matrix[i].length - 1
str += " "
else
str += "\n"
j++
i++
alert(str + '\n')
)
# we need to save mouse position
$(document).bind('mousemove',(mouseEvent) ->
$div = $(mouseEvent.target)
offset = $div.offset()
mousePositionX = mouseEvent.clientX - offset.left
mousePositionY = mouseEvent.clientY - offset.top
)
# to remove a node if we use SUPPR button over it
$('html').keyup((keyboardEvent) ->
if keyboardEvent.keyCode == 46
for node in node_list
if distanceBetweenPoints(node.x, node.y, mousePositionX, mousePositionY) < node.radius
# we remove all links that were starting or arriving to this node
i = 0
while i < link_list.length
if link_list[i].node_start is node or link_list[i].node_end is node
link_list.splice(i,1)
else
i++
# finally, we can remove the node
node_list.splice(node_list.indexOf(node),1)
computePageRank() # we update PageRank coefficients
break
)
# to save the canvas content in a PNG image
pictureCounter = 0
$('#pictureButton').click(() ->
pictureCounter++
this.href = canvas.toDataURL()
this.download = "PageRank" + pictureCounter + ".png"
)
# we compute the pagerank of each node
computePageRank = () ->
updateMatrix() # http://en.wikipedia.org/wiki/Stochastic_matrix
userVector = []
i = 0
while i < node_list.length
userVector[i] = 1 / node_list.length
i++
i = 0
while i < 11 # TODO: we should stop given a condition about if userVector converges or not
newClickOnLink()
i++
updateNodesSize()
printMatrix()
printUserVector()
# we update the stochastic matrix, in case user added pages (nodes) or links between pages
updateMatrix = () ->
# we create a new matrix
matrix = []
i = 0
while i < node_list.length
matrix[i] = []
i++
i = 0
while i < node_list.length
# we count the number of links to another page, from this page
n = 0
for link in link_list
if link.node_start is node_list[i]
n++
# we update coefficients for the current line in the matrix
j = 0
while j < node_list.length
# if n = 0 (no link to another the page), user should not be locked on the page,
# so user is redirected on an other page (all pages with same probability)
if n is 0
if node_list.length is 1
matrix[i][j] = 1
else # we only make links to another page, not to the same page
if j is i
matrix[i][j] = 0
else
matrix[i][j] = 1 / (node_list.length - 1)
else
# we search if the pages are linked
linked = false
for link in link_list
if link.node_start is node_list[i] and link.node_end is node_list[j]
matrix[i][j] = 1 / n
linked = true
break
if linked is false
matrix[i][j] = 0
j++
i++
newClickOnLink = () ->
i = 0
userVectorNew = []
while i < node_list.length
userVectorNew[i] = 0
j = 0
while j < node_list.length
userVectorNew[i] += userVector[j] * matrix[j][i]
j++
i++
userVector = userVectorNew
updateNodesSize = () ->
i = 0
while i < node_list.length
node_list[i].radius = 50 * userVector[i] + 10
i++
setInterval(`function() {window.animate(canvas, context, node_list, link_list, userVector)}`, 1000/10)
# the drawing function (in file display.coffee) will be called with a framerate of 10 FPS
printMatrix = () -> # debug
console.log("===== matrix ======")
# we round the coefficient for a better display
i = 0
while i < matrix.length
j = 0
while j < matrix[i].length
matrix[i][j] = matrix[i][j].toFixed(2)
j++
i++
# we display the matrix in a console (ctrl + shift + s -> display console in firefox)
i = 0
while i < matrix.length
console.log(matrix[i])
i++
printUserVector = () -> # debug
console.log("===== user vector =====")
# we round coefficients for a better display
i = 0
while i < userVector.length
userVector[i] = userVector[i].toFixed(2)
i++
console.log(userVector)