0
-# Graph library for Ruby-Processing
0
-# After Tom de Smedt's version for NodeBox.
0
-# WARNING: Not yet ready for any sort of use!
0
- LAYOUT_CIRCLE = :circle
0
- LAYOUT_SPRING = :spring
0
- def initialize(graph, opts={})
0
- @app = Processing::App.current
0
- defaults = {:graph => graph,
0
- :style => Style::DEFAULT,
0
- :vx => 0, :vy => 0 }.merge(opts)
0
- defaults.each_key {|k| instance_variable_set(k, defaults[k])}
0
- @force = layout.point(0, 0)
0
- @betweenness, @eigenvalue = nil, nil
0
- @betweenness || @graph.betweenness_centrality
0
- @eigenvalue || @graph.eigenvector_centrality
0
- # True if the point is in this node's absolute position
0
- return true if abs(@graph.x + x - point.x) < @radius * 2 && abs(@graph.y + y - point.y) < @radius * 2
0
- def append(node, edge = nil)
0
- @edges[node.id] = edge if edge
0
- @edges.delete(node.id) if @edges[node.id]
0
- id = id.id if id.is_a? Node
0
- attr_accessor :node1, :node2, :weight, :label
0
- def initialize(node1, node2, weight=0.0, label="")
0
- @node1, @node2, @weight, @label = node1, node2, weight, label
0
- def initialize(iterations=1000, distance=1.0, layout=LAYOUT_SPRING)
0
- @nodes, @edges = [], []
0
- @d = RAD * MULT * distance
0
- def copy( empty=false )
0
- g = Graph.new(@layout.n, @d, @layout.type)
0
- g.layout = @layout.copy(g)
0
- g.styles = @styles.copy(g)
0
- @nodes.each {|n| g.add_node(n.id, n.r, n.style, n.category, n.label, (n == @root))}
0
- @edges.each {|e| g.add_edge(e.node1.id, e.node2.id, e.weight, e.label)}
0
- @nodes, @edges = [], []
0
- def add_node(id, radius=8, style=Style::DEFAULT, category="", label=nil, root=false)
0
- return @graph[id] if @graph[id]
0
- style = style.name if style.is_a? Style
0
- n = Node.new(id, radius, style, category, label)
0
- nodes.each {|n| self.add_node(n) }
0
- def add_edge(id1, id2, weight=0.0, label="")
0
- # The weight of the edge represents importance, not cost. (0.0..1.0)
0
- return nil if id1 == id2
0
- self.add_node(id1) unless @graph.has_key?(id1)
0
- self.add_node(id2) unless @graph.has_key?(id2)
0
- n1, n2 = @graph[id1], @graph[id2]
0
- # Don't duplicate a -> b, but b -> a is ok.
0
- return self.edge(id1, id2) if n2.links.include?(n1) && n2.links.edge(n1).node1 == n1
0
- weight = [0.0, [weight, 1.0].min ].max
0
- e = Edge.new(n1, n2, weight, label)
0
- n1.links.append(n2, e)
0
- n2.links.append(n1, e)
0
- if @graph.has_key?(id)
0
- # Then remove all edges and links.
0
- nodes = [e.node1, e.node2]
0
- nodes.each {|other| other.links.remove(n) if other.links.include?(n) }
0
- def remove_edge(id1, id2)
0
- ids = [e.node1.id, e.node2.id]
0
- if ids.include?(id1) && ids.include?(id2)
0
- e.node1.links.remove(e.node2)
0
- e.node2.links.remove(e.node1)
0
- links = @graph[id1].links
0
- links.include?(id2) ? links.edge(id2) : nil
0
- def update(iterations=10)
0
- # Iterates the layout, updates node positions
0
- @alpha = 1.0 if @alpha > 1.0
0
- # Each step, the bounds are recalculated.
0
- n = [iterations, (i / 10 + 1)].min
0
- n.times { @layout.iterate }
0
- # Calculate the absolute center.
0
- min, max = @layout.bounds
0
- @x = (@app.width - max.x*@d - min.x*@d) / 2
0
- @y = (@app.height - max.y*@d - min.y*@d) / 2
0
- return !(@layout.done?)
0
- [@x + node.x - @app.width / 2, @y + node.y - @app.height / 2]
0
- # Layout the graph in increments.
0
- opts = {:dx => 0, :dy => 0,
0
- :traffic => nil }.merge(opts)
0
- # Center the graph on the page.
0
- @app.translate(@x + opts[:dx], @y + opts[:dy])
0
- # Indicate betweenness centrality ???
0
- traffic = opts[:traffic]
0
- traffic = 5 if traffic == true
0
- self.node_by_betweenness(traffic).each do |node|
0
- s = @styles[n.style] || @styles.default
0
- s.graph_traffic(s, n, @alpha) if s.graph_traffic
0
- # Draw the edges and edge labels.
0
- s.edges(s, @edges, @alpha, opts[:weighted], opts[:directed]) if s.edges
0
- # Draw each node, with style.
0
- s = @styles[n.style] || @styles.default
0
- s.node(s, n, @alpha) if s.node
0
- s.node_label(s, n, @alpha) if s.node_label
0
- # Highlight the shortest path.
0
- s = @styles.highlight || @styles.default
0
- s.path(s, self, highlight) if s.path
0
- @nodes.each {|n| self.remove_node(n.id) if n.links.size <= depth }
0
- alias_method :trim, :prune
0
- def shortest_path(id1, id2, heuristic=nil)
0
- Proximity.dijkstra_shortest_path(self, id1, id2, heuristic)
0
- def betweenness_centrality(normalized=true)
0
- # Calculates and returns node id => betweenness
0
- bc = Proximity.brandes_betweenness_centrality(self, normalized)
0
- bc.iter_items.each {|k, v| @graph[k].betweenness = v }
0
- def eigenvector_centrality( normalized=true, reversed=true, rating={},
0
- start=nil, iterations=100, tolerance=0.0001)
0
- ec = Proximity.eigenvector_centrality(self, normalized, reversed, rating, start, iterations, tolerance)
0
- ec.iter_items.each {|k, v| @graph[k].eigenvalue = v}
0
- def nodes_by_betweenness(threshold=0.0)
0
- # Heavily traffiked nodes come first.
0
- nodes = @nodes.select {|n| n.betweenness > threshold }
0
- nodes = nodes.sort {|a, b| b.betweenness <=> a.betweenness }
0
- alias_method :nodes_by_traffic, :nodes_by_betweenness
0
- def nodes_by_eigenvalue(threshold=0.0)
0
- # Heavily traffiked nodes (by eigenvalue) come first.
0
- nodes = @nodes.select{|n| n.eigenvalue > threshold}
0
- nodes = nodes.sort {|a, b| b.eigenvalue <=> a.eigenvalue }
0
- alias_method :nodes_by_weight, :nodes_by_eigenvalue
0
- def nodes_by_category(category)
0
- @nodes.select {|n| n.category == category}
0
- # The number of edges in relation to the total possible edges.
0
- 2.0 * @edges.size / (@nodes.size * (@nodes.size - 1))
0
- def is_complete; self.density == 1.0; end
0
- def is_dense; self.density > 0.65; end
0
- def is_sparse; self.density < 0.35; end
0
- def sub(id, distance=1)
0
- Cluster.subgraph(self, id, distance)
0
- alias_method :subgraph, :sub
0
- nodes = Cluster.intersection(Cluster.flatten(self), Cluster.flatten(graph))
0
- all = @graph | graph.graph
0
- return Cluster.subgraph(all, nodes, 0)
0
- alias_method :intersect, :and
0
- graph.nodes.each {|n| g.add_node(n.id, n.r, n.style, n.category, n.label, (g.root == nil && g.root == n))}
0
- graph.edges.each {|e| g.add_edge(e.node1.id, e.node2.id, e.weight, e.label) }
0
- alias_method :join, :or
0
- nodes = Cluster.difference(Cluster.flatten(self), Cluster.flatten(graph))
0
- all = @graph | graph.graph
0
- return Cluster.subgraph(all, nodes, 0)
0
- alias_method :subtract, :sub
0
- Cluster.is_clique(self)
0
- def clique(id, distance=0)
0
- Cluster.subgraph(self, Cluster.clique(self, id), distance)
0
- def cliques(threshold=3, distance=0)
0
- c = Cluster.cliques(self, threshold)
0
- c.each {|nodes| g << Cluster.subgraph(self, nodes, distance) }
0
- Cluster.partition(self)
0
- # And now for the class method to kick it all off. Kick it out!
0
- def self.create(iterations=1000, distance=1.0, layout=LAYOUT_SPRING, depth=true)
0
- g = Graph.new(iterations, distance, layout)
0
- g.styles.append(Style.new(Style.light, @app, app.color(0.0, 0.0, 0.0, 0.20)))
Comments
No one has commented yet.