From 25fa0bfee2798ab831c75ced310a05bc1b73b929 Mon Sep 17 00:00:00 2001 From: Pavel Govyadinov Date: Thu, 5 Dec 2019 14:53:20 -0600 Subject: [PATCH] Stable, pre-vispy update --- GraphCanvas.py | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------ GraphWidget.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- GuiVisPy_tube.py | 12 ++++++------ TubeCanvas.py | 10 +++++----- TubeWidget.py | 2 +- graph_shaders.py | 11 ++++------- network_dep.py | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- subgraph_shaders.py | 6 ++---- tube_shaders.py | 8 +++----- voronoi_test.py | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------- 10 files changed, 746 insertions(+), 205 deletions(-) diff --git a/GraphCanvas.py b/GraphCanvas.py index c1f96f6..38b4743 100644 --- a/GraphCanvas.py +++ b/GraphCanvas.py @@ -12,6 +12,7 @@ from vispy import gloo, scene from vispy.gloo import set_viewport, set_state, clear, set_blend_color, context from vispy.util.transforms import perspective, translate, rotate, scale import vispy.gloo.gl as glcore +from vispy import app import numpy as np import math @@ -94,7 +95,7 @@ class GraphCanvas(scene.SceneCanvas): n = 10 ne = 10 #Init dummy structures - self.uniforms = [('u_graph_size', np.float32, 3)] + #self.uniforms = [('u_graph_size', np.float32, 3)] self.data = np.zeros(n, dtype=[('a_position', np.float32, 3), ('a_fg_color', np.float32, 4), ('a_bg_color', np.float32, 4), @@ -102,13 +103,30 @@ class GraphCanvas(scene.SceneCanvas): ('a_linewidth', np.float32, 1), ('a_unique_id', np.float32, 4), ]) + + self.line_data = np.zeros(ne, dtype=[('a_position', np.float32, 3), + ('a_normal', np.float32, 2), + ('a_fg_color', np.float32, 4), + ('a_linewidth', np.float32, 1), + ]) + + + self.clusters = np.zeros(n*4, dtype=[('a_position', np.float32, 3), + ('a_value', np.float32, 2), + ('a_bg_color', np.float32, 4), + ('a_cluster_color', np.float32, 4), + ('a_arc_length', np.float32, 1), + ('a_outer_arc_length', np.float32, 4), + ('a_unique_id', np.float32, 4), + ]) - self.clusters = np.zeros(n, dtype=[('a_position', np.float32, 3), - ('a_bg_color', np.float32, 4), - ('a_value', np.float32, 2), - ('a_unique_id', np.float32, 4), - ]) + self.cluster_line_data = np.zeros(ne, dtype=[('a_position', np.float32, 3), + ('a_normal', np.float32, 2), + ('a_fg_color', np.float32, 4), + ('a_linewidth', np.float32, 1), + ]) + self.edges = np.random.randint(size=(ne, 2), low=0, high=n-1).astype(np.uint32) self.edges_s = np.random.randint(size=(ne, 4), low=0, @@ -134,7 +152,7 @@ class GraphCanvas(scene.SceneCanvas): self.vbo = gloo.VertexBuffer(self.data) self.vbo_s = gloo.VertexBuffer(self.clusters) - #Need to initialize thick lines. + #Need to initialize thic/k lines. self.index = gloo.IndexBuffer(self.edges) self.index_s = gloo.IndexBuffer(self.edges_s) @@ -151,35 +169,36 @@ class GraphCanvas(scene.SceneCanvas): self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_projection'] = self.projection - self.program['u_graph_size'] = [1.0, 1.0, 1.0] + #self.program['u_graph_size'] = [1.0, 1.0, 1.0] self.program['u_picking'] = False + self.vbo_line = gloo.VertexBuffer(self.line_data) #init shades used for the edges in the graph self.program_e = gloo.Program(vs, fs) - self.program_e['u_size'] = 1 +# self.program_e['u_size'] = 1 self.program_e['u_model'] = self.model self.program_e['u_view'] = self.view self.program_e['u_projection'] = self.projection #self.program_e['l_color'] = self.linecolor.astype(np.float32) - self.program_e.bind(self.vbo) - + self.program_e.bind(self.vbo_line) + #init shaders used to the vertices in the subgraph graph. self.program_s = gloo.Program(vert_s, frag_s) self.program_s.bind(self.vbo_s) self.program_s['u_model'] = self.model self.program_s['u_view'] = self.view self.program_s['u_projection'] = self.projection - self.program_s['u_graph_size'] = [1.0, 1.0, 1.0] + #self.program_s['u_graph_size'] = [1.0, 1.0, 1.0] self.program_s['u_picking'] = False #init shaders used for the subgraph-edges self.program_e_s = gloo.Program(vs_s, fs_s) - self.program_e_s['u_size'] = 1 self.program_e_s['u_model'] = self.model self.program_e_s['u_view'] = self.view self.program_e_s['u_projection'] = self.projection #self.program_e['l_color'] = self.linecolor.astype(np.float32) - self.program_e_s.bind(self.vbo_s) + self.vbo_cluster_lines = gloo.VertexBuffer(self.cluster_line_data) + self.program_e_s.bind(self.vbo_cluster_lines) #set up the viewport and the gl state. @@ -187,6 +206,86 @@ class GraphCanvas(scene.SceneCanvas): set_state(clear_color='white', depth_test=True, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha'), depth_func = ('lequal')) + + + self.timer = app.Timer('auto', connect=self.on_timer, start=False) + + def on_timer(self, event): + #get the temporary positions of the vertices (and edges) + self.old_pos = np.add(self.old_pos, self.slopes) + #during each iteration set the new positions in the GPU + self.data['a_position'] = self.old_pos + #Adjust the edges + edges = self.G.get_edges() + for e in range(edges.shape[0]): + idx = int(4*edges[e][2]) + p0 = self.old_pos[edges[e][0], :] + p1 = self.old_pos[edges[e][1], :] + d = np.subtract(p1, p0) + #d_norm = np.multiply(d, 1/np.sqrt(np.power(d[0],2) + np.power(d[1],2))) + d_norm = d[0:2] + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2)) + norm = np.zeros((2,), dtype=np.float32) + norm[0] = d_norm[1] + norm[1] = d_norm[0]*-1 + + self.edge_dict[int(edges[e][0]), int(edges[e][1])] = int(edges[e][2]) + self.line_data['a_position'][idx] = p0 + self.line_data['a_normal'][idx] = norm + + self.line_data['a_position'][idx+1] = p1 + self.line_data['a_normal'][idx+1] = norm + + self.line_data['a_position'][idx+2] = p0 + self.line_data['a_normal'][idx+2] = -norm + + self.line_data['a_position'][idx+3] = p1 + self.line_data['a_normal'][idx+3] = -norm + + #send data to GPU renderer + self.vbo.set_data(self.data) + self.program.bind(self.vbo) + self.vbo_line = gloo.VertexBuffer(self.line_data) + self.program_e.bind(self.vbo_line) + + + self.update_clusters(self.old_pos) + edges = self.G_cluster.get_edges() +# #generate the vertex buffer and the connections buffer. + for e in range(edges.shape[0]): + idx = int(4*edges[e][2]) + p0 = self.cluster_pos[int(edges[e][0])] + p1 = self.cluster_pos[int(edges[e][1])] + #p0 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][0])] + #p1 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][1])] + d = np.subtract(p1, p0) + #d_norm = np.multiply(d, 1/np.sqrt(np.power(d[0],2) + np.power(d[1],2))) + d_norm = d[0:2] + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2)) + norm = np.zeros((2,), dtype=np.float32) + norm[0] = d_norm[1] + norm[1] = d_norm[0]*-1 + #print(np.sqrt(norm[0]*norm[0] + norm[1]*norm[1])) + #thickness = G.edge_properties["thickness"][e] + #self.cluster_dict[int(edges[e][0]), int(edges[e][1])] = int(edges[e][2]) + self.cluster_line_data['a_position'][idx] = p0 + self.cluster_line_data['a_normal'][idx] = norm + + self.cluster_line_data['a_position'][idx+1] = p1 + self.cluster_line_data['a_normal'][idx+1] = norm + + self.cluster_line_data['a_position'][idx+2] = p0 + self.cluster_line_data['a_normal'][idx+2] = -norm + + self.cluster_line_data['a_position'][idx+3] = p1 + self.cluster_line_data['a_normal'][idx+3] = -norm + + self.vbo_cluster_lines.set_data(self.cluster_line_data) + self.vbo_s.set_data(self.clusters) + self.program_s.bind(self.vbo_s) + self.program_e_s.bind(self.vbo_cluster_lines) + + self.update() """ Function that recolors vertices based on the selected statistic @@ -377,6 +476,14 @@ class GraphCanvas(scene.SceneCanvas): else: G.edge_properties["RGBA"][e] = [0.0, 0.0, 0.0, 0.8] + """ + Test function that only binds the buffer + """ + def gen_vertex_vbo_minimalist(self): + self.update() + self.vbo.set_data(self.data) + self.program.bind(self.vbo) + self.update() """ Helper function that generates the framebuffer object that stores the vertices @@ -418,7 +525,7 @@ class GraphCanvas(scene.SceneCanvas): self.data['a_selection'] = G.vertex_properties["selection"].get_array() #self.data['a_graph_size'] = [bbu-bbl] - self.program['u_graph_size'] = [self.bbu-self.bbl] + #self.program['u_graph_size'] = [self.bbu-self.bbl] self.vbo = gloo.VertexBuffer(self.data) self.gen_line_vbo(G) @@ -499,7 +606,7 @@ class GraphCanvas(scene.SceneCanvas): #self.program_e['l_color'] = self.linecolor.astype(np.float32) self.vbo_line = gloo.VertexBuffer(self.line_data) self.index = gloo.IndexBuffer(self.edges) - self.program_e['u_size'] = 1 +# self.program_e['u_size'] = 1 self.program_e['u_model'] = self.model self.program_e['u_view'] = self.view self.program_e['u_projection'] = self.projection @@ -591,7 +698,6 @@ class GraphCanvas(scene.SceneCanvas): self.index_clusters_s = gloo.IndexBuffer(self.cluster_edges) self.vbo_cluster_lines = gloo.VertexBuffer(self.cluster_line_data) - self.program_e_s['u_size'] = 1 self.program_e_s['u_model'] = self.model self.program_e_s['u_view'] = self.view self.program_e_s['u_projection'] = self.projection @@ -648,8 +754,7 @@ class GraphCanvas(scene.SceneCanvas): self.program_e_s = gloo.Program(vs_s, fs_s) self.index_clusters_s = gloo.IndexBuffer(self.cluster_edges) self.vbo_cluster_lines = gloo.VertexBuffer(self.cluster_line_data) - - self.program_e_s['u_size'] = 1 + self.program_e_s['u_model'] = self.model self.program_e_s['u_view'] = self.view self.program_e_s['u_projection'] = self.projection @@ -721,78 +826,99 @@ class GraphCanvas(scene.SceneCanvas): def expand_based_on_clusters(self, G, n): pos = G.vertex_properties["pos"].get_2d_array(range(3)).T for p in range(pos.shape[0]): - pos[p][0] = pos[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0] - pos[p][1] = pos[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1] - pos[p][2] = pos[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2] + pos[p][0] = pos[p][0] - self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0] + pos[p][1] = pos[p][1] - self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1] + pos[p][2] = pos[p][2] - self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2] G.vertex_properties["pos"] = G.new_vertex_property("vector", vals = pos) - for i in range(n): - index = 4*i - #generate the vertex filter for this cluster - num_v_in_cluster = len(np.argwhere(self.labels == i)) - vfilt = np.zeros([G.num_vertices(), 1], dtype="bool") - vfilt[np.argwhere(self.labels == i)] = 1 - vfilt_prop = G.new_vertex_property("bool", vals = vfilt) - G.set_vertex_filter(vfilt_prop) +# for i in range(n): +# index = 4*i +# #generate the vertex filter for this cluster +# num_v_in_cluster = len(np.argwhere(self.labels == i)) +# vfilt = np.zeros([G.num_vertices(), 1], dtype="bool") +# vfilt[np.argwhere(self.labels == i)] = 1 +# vfilt_prop = G.new_vertex_property("bool", vals = vfilt) +# G.set_vertex_filter(vfilt_prop) +# +# #get the filtered properties +# g = nwt.gt.Graph(G, prune=True, directed=False) +# positions = g.vertex_properties["pos"].get_2d_array(range(3)).T +# position = np.sum(positions, 0)/num_v_in_cluster +# p, v = self.gen_cluster_coords(position, np.sum(g.vertex_properties['degree'].get_array())) +# self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32) +# self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32) +# G.clear_filters() +# self.cluster_pos[i] = position +# color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T +# size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], 'degree').get_array() +# +# position = G.vertex_properties["pos"].get_2d_array(range(3)).T +# #for p in range(position.shape[0]): +# # position[p][0] = position[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0] +# # position[p][1] = position[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1] +# # position[p][2] = position[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2] +# #G.vertex_properties["pos"] = G.new_vertex_property("vector", vals = position) +# +# +# edges = G.get_edges(); +# edges = edges[:, 0:2] +# #width = nwt.Network.map_edges_to_range(G, [1*self.pixel_scale, 5*self.pixel_scale], 'volume').get_array() +# #ecolor = G.edge_properties["RGBA"].get_2d_array(range(4)).T +# +# self.data = np.zeros(G.num_vertices(), dtype=[('a_position', np.float32, 3), +# ('a_fg_color', np.float32, 4), +# ('a_bg_color', np.float32, 4), +# ('a_size', np.float32, 1), +# ('a_linewidth', np.float32, 1), +# ('a_unique_id', np.float32, 4), +# ('a_selection', np.float32, 1), +# ]) +# +# #self.edges = edges.astype(np.uint32) +# self.data['a_position'] = position +# #fg color is the color of the ring +# self.data['a_fg_color'] = 0, 0, 0, 1 +# self.data['a_bg_color'] = color +# self.data['a_size'] = size +# self.data['a_linewidth'] = 4.*self.pixel_scale +# self.data['a_unique_id'] = self.gen_vertex_id(G) +# self.data['a_selection'] = G.vertex_properties["selection"].get_array() +# #self.data['a_graph_size'] = [bbu-bbl] +# +# self.program['u_graph_size'] = [self.bbu-self.bbl] +# self.vbo = gloo.VertexBuffer(self.data) +# self.gen_line_vbo(G) +# if(self.subgraphs): +# self.vbo_s = gloo.VertexBuffer(self.clusters) +# self.index_s = gloo.IndexBuffer(self.edges_s) +# #self.index = gloo.IndexBuffer(self.edges) +# self.program_e.bind(self.vbo_line) +# self.program.bind(self.vbo) +# if(self.subgraphs): +# #self.program_e_s.bind(self.vbo_s) +# self.program_s.bind(self.vbo_s) +# if DEBUG: +# print(self.view) +# self.update() - #get the filtered properties - g = nwt.gt.Graph(G, prune=True, directed=False) - positions = g.vertex_properties["pos"].get_2d_array(range(3)).T - position = np.sum(positions, 0)/num_v_in_cluster - p, v = self.gen_cluster_coords(position, np.sum(g.vertex_properties['degree'].get_array())) + + """ + Function that updates the cluster positions based on new vertex positions + in the graph. Primarity used for animation. + """ + def update_clusters(self, new_pos): + clusters = self.G.vertex_properties["clusters"].get_array().T + for i in range(self.n_c): + idx = np.argwhere(clusters == i) + pos = np.sum(new_pos[idx], 0)/len(idx) + self.cluster_pos[i] = pos.reshape(3) + index = i*4 + p, v = self.gen_cluster_coords(self.cluster_pos[i], self.cluster_size[i]) self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32) self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32) - G.clear_filters() - self.cluster_pos[i] = position - color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T - size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], 'degree').get_array() - - position = G.vertex_properties["pos"].get_2d_array(range(3)).T - #for p in range(position.shape[0]): - # position[p][0] = position[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0] - # position[p][1] = position[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1] - # position[p][2] = position[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2] - #G.vertex_properties["pos"] = G.new_vertex_property("vector", vals = position) - edges = G.get_edges(); - edges = edges[:, 0:2] - #width = nwt.Network.map_edges_to_range(G, [1*self.pixel_scale, 5*self.pixel_scale], 'volume').get_array() - #ecolor = G.edge_properties["RGBA"].get_2d_array(range(4)).T - - self.data = np.zeros(G.num_vertices(), dtype=[('a_position', np.float32, 3), - ('a_fg_color', np.float32, 4), - ('a_bg_color', np.float32, 4), - ('a_size', np.float32, 1), - ('a_linewidth', np.float32, 1), - ('a_unique_id', np.float32, 4), - ('a_selection', np.float32, 1), - ]) - - #self.edges = edges.astype(np.uint32) - self.data['a_position'] = position - #fg color is the color of the ring - self.data['a_fg_color'] = 0, 0, 0, 1 - self.data['a_bg_color'] = color - self.data['a_size'] = size - self.data['a_linewidth'] = 4.*self.pixel_scale - self.data['a_unique_id'] = self.gen_vertex_id(G) - self.data['a_selection'] = G.vertex_properties["selection"].get_array() - #self.data['a_graph_size'] = [bbu-bbl] - - self.program['u_graph_size'] = [bbu-bbl] - self.vbo = gloo.VertexBuffer(self.data) - self.gen_line_vbo(G) - if(self.subgraphs): - self.vbo_s = gloo.VertexBuffer(self.clusters) - self.index_s = gloo.IndexBuffer(self.edges_s) - #self.index = gloo.IndexBuffer(self.edges) - self.program_e.bind(self.vbo_line) - self.program.bind(self.vbo) - if(self.subgraphs): - #self.program_e_s.bind(self.vbo_s) - self.program_s.bind(self.vbo_s) - if DEBUG: - print(self.view) - self.update() - + self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(i)] = self.cluster_pos[i] + + + """ Function that generates the clusters for an unclustered graph @@ -827,7 +953,7 @@ class GraphCanvas(scene.SceneCanvas): #self.clusters['a_fg_color'] = 1., 1., 1., 0.0 #self.clusters['a_linewidth'] = 4.*self.pixel_scale - G.vertex_properties["pos"] = nwt.gt.sfdp_layout(G, groups = G.vertex_properties["clusters"], pos = G.vertex_properties["pos"]) + G.vertex_properties["pos"] = nwt.gt.sfdp_layout(G, groups = G.vertex_properties["clusters"], pos = G.vertex_properties["pos"], C = 1.0, K = 10) temp = [] temp_pos = [] #Find the global total of the metric. @@ -897,14 +1023,11 @@ class GraphCanvas(scene.SceneCanvas): print(self.clusters['a_outer_arc_length']) maximum = max(temp) minimum = min(temp) - if len(temp) > 1: - temp = ((temp-minimum)/(maximum-minimum)*(60*self.pixel_scale)+20*self.pixel_scale) - else: - temp = [60*self.pixel_scale] + temp = ((temp-minimum)/(maximum-minimum)*(60*self.pixel_scale)+20*self.pixel_scale)*2.0 for i in range(num_clusters): index = i*4 index_t = i*2 - p, v = self.gen_cluster_coords(temp_pos[i], temp[i]*2.0) + p, v = self.gen_cluster_coords(temp_pos[i], temp[i]) self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32) self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32) @@ -913,6 +1036,7 @@ class GraphCanvas(scene.SceneCanvas): #self.edges_s[i][0:4] = np.asarray(range(index, index+4), dtype=np.uint32) #self.edges_s[i] self.cluster_pos = temp_pos + self.cluster_size = temp #self.expand_based_on_clusters(G, self.n_c) G.clear_filters() # self.edges_s[1][0:4] = np.asarray(range(0, 0+4), dtype=np.uint32) @@ -924,7 +1048,7 @@ class GraphCanvas(scene.SceneCanvas): # self.edges_s[0][5] = index+2 #self.clusters['a_size'] = temp self.gen_cluster_line_vbo(G) - self.program_s['u_graph_size'] = [bbu-bbl] + #self.program_s['u_graph_size'] = [bbu-bbl] #if len(temp_e) > 0: # self.edges_s = np.unique(np.asarray(temp_e, np.uint32), axis=0) #else: @@ -1003,7 +1127,7 @@ class GraphCanvas(scene.SceneCanvas): self.data['a_selection'] = G.vertex_properties["selection"].get_array() #self.data['a_graph_size'] = [bbu-bbl] - self.program['u_graph_size'] = [bbu-bbl] + #self.program['u_graph_size'] = [bbu-bbl] self.vbo = gloo.VertexBuffer(self.data) self.gen_line_vbo(G) @@ -1033,6 +1157,7 @@ class GraphCanvas(scene.SceneCanvas): Overloaded function that is called during every self.update() call """ def on_draw(self, event): + gloo.clear() clear(color='white', depth=True) self.program_e.draw('triangles', indices=self.index) self.program.draw('points') @@ -1041,6 +1166,49 @@ class GraphCanvas(scene.SceneCanvas): self.program_e_s.draw('triangles', indices=self.index_clusters_s) self.program_s.draw('triangles', indices=self.index_s) + +# """ +# A function to animate from one layout to another layout given a new G. +# """ + def animate(self, old_pos, new_pos): + +# old_pos = self.G.vertex_properties["pos"].get_2d_array(range(3)).T +# new_pos = new_G.vertex_properties["pos"].get_2d_array(range(3)).T + #we want to animate the move over 2 seconds. At 60 frames per second + #we'd get 120 different positions along the transitional distance. + self.new_pos = new_pos + self.old_pos = old_pos + self.slopes = np.zeros(old_pos.shape) + for i in range(old_pos.shape[0]): + self.slopes[i, 0] = (new_pos[i, 0] - old_pos[i, 0])/120.0 + self.slopes[i, 1] = (new_pos[i, 1] - old_pos[i, 1])/120.0 + + #self.timer.start() + #for i in range(120): + #self.old_pos = np.add(old_pos, self.slopes) + self.timer.start(iterations=120) + #self.data['a_position'] = old_pos + #self.gen_vertex_vbo_minimalist() + #self.update() +## self.gen_line_vbo(self.G) +## self.program_e.bind(self.vbo_line) +## self.program.bind(self.vbo) +# self.update() +# #self.program_e.draw('lines') +# print(i) + #self.timer.stop() + + #self.G = new_G + #self.gen_vertex_vbo(self.G) + #self.update() + +# def on_timer(self, event): +# self.vbo = gloo.VertexBuffer(self.data) +# #self.gen_line_vbo(self.G) +# #self.program_e.bind(self.vbo_line) +# self.program.bind(self.vbo) +# self.update() +# gloo.wrappers.flush() """ Function performed during a mouse click (either right or left) gets the unique id of the object drawn underneath the cursor @@ -1356,7 +1524,7 @@ class GraphCanvas(scene.SceneCanvas): #get the filtered properties g = nwt.gt.Graph(G, prune=True, directed=False) - p, v = self.gen_cluster_coords(pos, np.sum(g.vertex_properties['degree'].get_array())) + p, v = self.gen_cluster_coords(pos, self.cluster_size[c_id]) self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32) self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32) G.clear_filters() diff --git a/GraphWidget.py b/GraphWidget.py index c9dacdb..6ce1b88 100644 --- a/GraphWidget.py +++ b/GraphWidget.py @@ -10,6 +10,8 @@ from GraphCanvas import GraphCanvas from pyqtgraph.Qt import QtCore, QtGui, QtWidgets import network_dep as nwt +import numpy as np + DEBUG = False """ @@ -69,6 +71,9 @@ class GraphWidget(QtGui.QWidget): #EXP = menu.addAction('Export VTK') #EXP_adv = menu.addAction('Export VTK Advanced') NL = menu.addAction('New Layout') + NSL = menu.addAction('New Spring-Onion Layout (sum)') + NSLMI = menu.addAction('New Spring-Onion Layout(max)') + EXPAND = menu.addAction('Expand') action = menu.exec_(event.native.globalPos()) if DEBUG: print(action) @@ -108,11 +113,43 @@ class GraphWidget(QtGui.QWidget): #self.cb.currentIndexChanged.connect(self.selection_change) #self.cb.show() if action == NL: - self.canvas.G = nwt.Network.gen_new_fd_layout(self.canvas.G) - self.canvas.gen_vertex_vbo(self.canvas.G) + old_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T + nG = nwt.Network.gen_new_fd_layout(self.canvas.G) + new_pos = nG.vertex_properties["pos"].get_2d_array(range(3)).T + self.canvas.animate(old_pos, new_pos) + + #self.canvas.gen_vertex_vbo(self.canvas.G) #self.canvas.set_data(self.canvas.G, self.canvas.bbl, self.canvas.bbu) + #self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c) + if action == NSL: + old_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T + nG = nwt.Network.gen_spring_onion_layout(self.canvas.G, 'degree', 1.0, 1000, 0.01, 1.0) + new_pos = nG.vertex_properties["pos"].get_2d_array(range(3)).T + + self.canvas.animate(old_pos, new_pos) + #self.canvas.set_data(nG, self.canvas.bbl, self.canvas.bbu) + + #self.canvas.gen_vertex_vbo(self.canvas.G) + #self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c) + if action == NSLMI: + old_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T + nG = nwt.Network.gen_spring_onion_layout(self.canvas.G, 'degree', 1.0, 1000, 0.01, 1.0, False) + new_pos = nG.vertex_properties["pos"].get_2d_array(range(3)).T + + self.canvas.animate(old_pos, new_pos) + #self.canvas.set_data(nG, self.canvas.bbl, self.canvas.bbu) + #self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c) + + #self.canvas.gen_vertex_vbo(self.canvas.G) + + if action == EXPAND: + #self.canvas.gen_vertex_vbo(self.canvas.G) + old_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c) - + new_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T + #self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c) + + self.canvas.animate(old_pos, new_pos) #self.canvas.size_vertices(self.canvas.G, 'degree_volume') else: if self.canvas.view[0][0] >= 0.0010: @@ -144,6 +181,12 @@ class GraphWidget(QtGui.QWidget): def on_mouse_double_click(self, event): self.canvas.update_path(event) self.select.emit(self.canvas.get_path()) + old_pos = self.canvas.G.vertex_properties["pos"].get_2d_array(range(3)).T + nG = nwt.Network.gen_new_fd_layout(self.canvas.G) + # def animate(self, new_G): +# + new_pos = nG.vertex_properties["pos"].get_2d_array(range(3)).T + self.canvas.animate(old_pos, new_pos) """ Handles the mouse release event. diff --git a/GuiVisPy_tube.py b/GuiVisPy_tube.py index 48ca01b..86cdf9c 100644 --- a/GuiVisPy_tube.py +++ b/GuiVisPy_tube.py @@ -39,7 +39,6 @@ top = QtGui.QWidget() top.resize(900, 900) -hist = pg.PlotWidget() #fibers = FiberView() fibers = TubeWidget() fibers.canvas.create_native() @@ -66,15 +65,16 @@ top.setLayout(layout) top.show() -def draw_histogram(G, value): - vals = G.edge_properties[value].get_array() - y, x = np.histogram(vals,40) - hist.plot(x,y, stepMode=True, fillLevel=0, brush=(0,0,255,150)) +#def draw_histogram(G, value): +# vals = G.edge_properties[value].get_array() +# y, x = np.histogram(vals,40) +# hist.plot(x,y, stepMode=True, fillLevel=0, brush=(0,0,255,150)) def load_nwt(filepath): net = nwt.Network(filepath) G = net.createFullGraph_gt() G = net.filterDisconnected(G) + #G = net.gen_spring_onion_layout(G, 'degree', 1.0, 1000, 0.01, 1.0) #G = net.filterFullGraph_gt(G, erode=True) #G = net.filterFullGraph_gt(G, erode=True) #G = net.gen_new_fd_layout(G) @@ -104,7 +104,7 @@ center = (bbu-bbl)/2.0 #fibers.opts['distance'] = 5 # item = NodeItem(G) # graph.addItem(item) -draw_histogram(G, "length") +#draw_histogram(G, "length") graph.canvas.set_data(G, bbl, bbu) fibers.canvas.set_data(G, bbl, bbu, 16) #fibers.draw_all(G, center, fibers, graph, node_tex) diff --git a/TubeCanvas.py b/TubeCanvas.py index b430c1b..4ecd8da 100644 --- a/TubeCanvas.py +++ b/TubeCanvas.py @@ -68,7 +68,7 @@ class TubeDraw(scene.SceneCanvas): blend_func=('src_alpha', 'one_minus_src_alpha'), depth_func = ('lequal')) #set_blend_color(color='black') #set_state('translucent') - self.program['u_LightPos'] = [0., 0., -1000.] + #self.program['u_LightPos'] = [0., 0., -1000.] #self.camera = self.central_widget.add_view() #self.camera.camera = 'turntable' self.down = False @@ -107,7 +107,7 @@ class TubeDraw(scene.SceneCanvas): self.projection = perspective(90.0, self.physical_size[0]/self.physical_size[1], 1.0, 1000.0) #self.projection = perspective(90.0, 1.0, -1.0, 1.0) self.program['u_model'] = self.model - self.program['u_LightPos'] = [0., 0., 1000.] + #self.program['u_LightPos'] = [0., 0., 1000.] #self.program['u_view'] = self.view self.program['u_projection'] = self.projection self.program.bind(self.vbo) @@ -481,8 +481,8 @@ class TubeDraw(scene.SceneCanvas): #coord[1] = 0 #coord2[1] = 0 - theta = (coord[0]-coord2[0])*360.0/2.0/np.pi - phi = (coord[1]-coord2[1])*360.0/2.0/np.pi + phi = (coord[0]-coord2[0])*360.0/2.0/np.pi + theta = (coord[1]-coord2[1])*360.0/2.0/np.pi if DEBUG: print(theta*360.0/2.0/np.pi, -phi*360.0/2.0/np.pi) self.camera = self.camera - self.translate @@ -533,7 +533,7 @@ class TubeDraw(scene.SceneCanvas): print("current position ", self.camera, " and up vector ", self.up) self.program['u_eye'] = self.camera self.program['u_up'] = self.up - self.program['u_LightPos'] = [self.camera[0], self.camera[1], self.camera[2]] + #self.program['u_LightPos'] = [self.camera[0], self.camera[1], self.camera[2]] self.update() #reverts the mouse state during release. diff --git a/TubeWidget.py b/TubeWidget.py index b451563..8a88e2c 100644 --- a/TubeWidget.py +++ b/TubeWidget.py @@ -14,7 +14,7 @@ Created on Mon Aug 5 15:53:16 2019 from pyqtgraph.Qt import QtCore, QtGui, QtWidgets from TubeCanvas import TubeDraw import numpy as np -import trimesh as tm +#import trimesh as tm DEBUG = False diff --git a/graph_shaders.py b/graph_shaders.py index d2b6a5f..8d2e0c4 100644 --- a/graph_shaders.py +++ b/graph_shaders.py @@ -14,14 +14,14 @@ vert = """ #version 120 // Uniforms // ------------------------------------ +uniform float u_size; +uniform float u_antialias; uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; -uniform float u_antialias; -uniform float u_size; +//uniform vec3 u_graph_size; uniform bool u_picking; -uniform vec3 u_graph_size; // Attributes // ------------------------------------ attribute vec3 a_position; @@ -65,7 +65,6 @@ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_antialias; -uniform float u_size; uniform bool u_picking; // Varyings @@ -192,8 +191,6 @@ attribute vec3 a_position; attribute vec2 a_normal; attribute vec4 a_fg_color; attribute float a_linewidth; -//attribute vec4 a_unique_id; -//attribute vec4 l_color; // Varyings // ------------------------------------ @@ -253,4 +250,4 @@ float new_alpha(float zoom_level) return val; } -""" \ No newline at end of file +""" diff --git a/network_dep.py b/network_dep.py index b6d22e3..cbf965f 100755 --- a/network_dep.py +++ b/network_dep.py @@ -1115,9 +1115,163 @@ class Network: G_0.clear_filters() return graph + + ''' + Generates a layout based a 3 neighbors spring system. + Vertices within 1 edge, 2 edges and 3 edges act upon one another based on + Hooke's law. 1e springs are the stronges and strength decreases as edges get further away + + ''' + def gen_spring_onion_layout(G, v_property, k, n_steps, time_step, min_distance, Sum=True): + start = time.time() + + one_neighbors = G.new_vertex_property("vector") + o1_n = [] + two_neighbors = G.new_vertex_property("vector") + o2_n = [] + three_neighbors = G.new_vertex_property("vector") + o3_n = [] + #generate the map of 1, 2 and 3 neighborhood vertices. + for v in G.vertices(): + one_n = [] + two_n = [] + three_n = [] + #find all 1 neighbors + for i in v.all_neighbors(): + one_n.append(int(i)) + if(len(one_n) == 0): + print("WTF") + #find all 2 neighbors + for i in one_n: + for j in G.vertex(i).all_neighbors(): + if(int(j) not in one_n): + two_n.append(int(j)) + + #find all 3 neighbors + for i in two_n: + for j in G.vertex(i).all_neighbors(): + if(int(j) not in one_n and int(j) not in two_n): + three_n.append(int(j)) + #we do not keep ones that have already counted, i.e. a vertex + #appears in all 3 arrays only once. + #it's ok to add self to any array because the distance -k(x=0) = 0 + one_neighbors[v] = np.asarray(one_n) + o1_n.append(np.asarray(one_n)) + two_neighbors[v] = np.asarray(two_n) + o2_n.append(np.asarray(two_n)) + three_neighbors[v] = np.asarray(three_n) + o3_n.append(np.asarray(three_n)) - + print("generating neighborhoods: ", time.time() - start) + start = time.time() + G.vertex_properties["1_neighbor"] = one_neighbors + G.vertex_properties["2_neighbor"] = two_neighbors + G.vertex_properties["3_neighbor"] = three_neighbors + + cooling = 1.0 + cooling_step = cooling/n_steps + + +# #get points of the centers of every cluster +# #generate voronoi region and plot it. +# fig, ax = plt.subplots(2, 1, sharex='col', sharey='row') +# fig.tight_layout() +# grid = plt.GridSpec(2,1) +# grid.update(wspace=0.025, hspace=0.2) +# ax[0].axis('off') +# ax[1].axis('off') +# +# +# before = fig.add_subplot(grid[0]) +# after = fig.add_subplot(grid[1]) +# pts = G.vertex_properties["pos"].get_2d_array(range(2)).T +# before.scatter(pts[:,0], pts[:, 1], marker="*") +# +# #plot the connections of the top level graph +# for e in G.edges(): +# coord = G.vertex_properties["pos"][e.source()] +# coord2 = G.vertex_properties["pos"][e.target()] +# x = [coord[0], coord2[0]] +# y = [coord[1], coord2[1]] +# before.plot(x, y, 'go--', linewidth=1, markersize=1) +# +# #plot the connections of the top level graph +# for e in G.edges(): +# coord = G.vertex_properties["pos"][e.source()] +# coord2 = G.vertex_properties["pos"][e.target()] +# x = [coord[0], coord2[0]] +# y = [coord[1], coord2[1]] +# before.plot(x, y, 'go--', linewidth=1, markersize=1) + + velocities = np.zeros([G.num_vertices(), 3]) + print("setting neighborhoods: ", time.time() - start) + start = time.time() + + pos = G.vertex_properties["pos"].get_2d_array(range(3)).T + weight = G.vertex_properties[v_property].get_array().T + + for n in range(n_steps+1): + forces = np.zeros([G.num_vertices(), 3]) + #pos = G.vertex_properties["pos"].get_2d_array(range(2)).T + if Sum: + for v in G.vertices(): + for i in o1_n[int(v)]: + x = pos[int(i), :] - pos[int(v), :] + forces[int(v), 0] += -k*(x[0]) + forces[int(v), 1] += -k*(x[1]) + for i in o2_n[int(v)]: + x = pos[int(i)] - pos[int(v)] + forces[int(v), 0] += -k*(x[0])/2.0 + forces[int(v), 1] += -k*(x[1])/2.0 + for i in o3_n[int(v)]: + x = pos[int(i)] - pos[int(v)] + forces[int(v), 0] += -k*(x[0])/4.0 + forces[int(v), 1] += -k*(x[1])/4.0 + else: + for v in G.vertices(): + for i in o3_n[int(v)]: + x = pos[int(i)] - pos[int(v)] + forces[int(v), 0] = -k*(x[0])/4.0 + forces[int(v), 1] = -k*(x[1])/4.0 + + forces[:, 0] = forces[:, 0]/weight[:] + forces[:, 1] = forces[:, 1]/weight[:] +# if n < 1: +# velocities = forces * time_step*G.num_vertices() +# else: +# pos += velocities * time_step +# velocities = forces * time_step * cooling +# cooling -= cooling_step +# + + for v in G.vertices(): + if n < 1: + velocities = forces * time_step + #G.vertex_properties["pos"][v] = pos[int(v), :] + forces[int(v), :] * time_step + else: + pos[int(v), :] = pos[int(v), :] + velocities[int(v), :] * time_step + velocities = forces * time_step * cooling + cooling -= cooling_step + + G.vertex_properties["pos"] = G.new_vertex_property("vector", vals = pos) + print("calculating new positions: ", time.time() - start) +# pts = G.vertex_properties["pos"].get_2d_array(range(2)).T +# after.scatter(pts[:,0], pts[:, 1], marker="*") +# +# #plot the connections of the top level graph +# for e in G.edges(): +# coord = G.vertex_properties["pos"][e.source()] +# coord2 = G.vertex_properties["pos"][e.target()] +# x = [coord[0], coord2[0]] +# y = [coord[1], coord2[1]] +# after.plot(x, y, 'go--', linewidth=1, markersize=1) +# +# plt.show() + return G + + + ''' Creates a graph from a list of nodes and a list of edges @@ -1537,8 +1691,9 @@ class Network: def gen_new_fd_layout(G): - pos = gt.sfdp_layout(G, C = 1.0, K = 10) - G.vertex_properties["pos"] = pos + G.vertex_properties["pos"] = gt.sfdp_layout(G, groups = G.vertex_properties["clusters"], pos = G.vertex_properties["pos"], C = 1.0, K = 10) + #pos = gt.sfdp_layout(G, C = 1.0, K = 10) + #G.vertex_properties["pos"] = pos return G def map_edges_to_range(G, rng, propertymap): @@ -1668,7 +1823,7 @@ class Network: G.vertex_properties["partition"] = G.new_vertex_property("vector", array_cluster_bel) G.edge_properties["partition"] = G.new_edge_property("vector", array_ecluster_bel) j = 0 - gt.graph_draw(G, pos = gt.sfdp_layout(G), output="Graph_full.pdf") + #gt.graph_draw(G, pos = gt.sfdp_layout(G), output="Graph_full.pdf") for i in range(c): TFv = G.new_vertex_property("bool", vals=array_cluster_bel[i,:]) TFe = G.new_edge_property("bool", vals=array_ecluster_bel[i,:]) diff --git a/subgraph_shaders.py b/subgraph_shaders.py index aac8838..18062a5 100644 --- a/subgraph_shaders.py +++ b/subgraph_shaders.py @@ -14,7 +14,7 @@ vert_s = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; -uniform vec3 u_graph_size; +//uniform vec3 u_graph_size; uniform bool u_picking; // Attributes // ------------------------------------ @@ -214,8 +214,6 @@ attribute vec3 a_position; attribute vec2 a_normal; attribute vec4 a_fg_color; attribute float a_linewidth; -//attribute vec4 a_unique_id; -//attribute vec4 l_color; // Varyings // ------------------------------------ @@ -273,4 +271,4 @@ float new_alpha(float zoom_level) } return val; } -""" \ No newline at end of file +""" diff --git a/tube_shaders.py b/tube_shaders.py index 85bf562..aca4b04 100644 --- a/tube_shaders.py +++ b/tube_shaders.py @@ -11,7 +11,6 @@ VERT_SHADER = """ // Uniforms // ------------------------------------ uniform vec4 u_bb[26]; -uniform vec3 u_LightPost; uniform mat4 u_model; //uniform mat4 u_view; uniform mat4 u_projection; @@ -53,8 +52,8 @@ void main (void) { v_position = vec3(MV*vec4(a_position, 1.0)); vt_position = vec3(a_position); v_fg_color = a_fg_color; - v_selection = a_selection; gl_Position = MVP * vec4(a_position, 1.0); + v_selection = a_selection; } mat4 make_view(vec3 eye, vec3 target, vec3 up) @@ -87,7 +86,6 @@ FRAG_SHADER = """ // Uniforms // ------------------------------------ uniform vec3 u_bb[26]; -uniform vec3 u_LightPos; uniform mat4 u_model; //uniform mat4 u_view; uniform mat4 u_projection; @@ -198,7 +196,7 @@ float new_alpha() float bin_alpha(float alpha) { - + if(alpha > 0.8) return 0.0; else if(alpha > 0.6) @@ -211,7 +209,7 @@ float bin_alpha(float alpha) return 1.0; else return 1.0; - + // if(alpha > 0.9) // return 0.0; // else if(alpha > 0.8) diff --git a/voronoi_test.py b/voronoi_test.py index 34f5f65..ca51823 100644 --- a/voronoi_test.py +++ b/voronoi_test.py @@ -31,32 +31,50 @@ import network_dep as nwt class Polygon_mass: def __init__(self, G): self.G = G + print(nwt.gt.graph_tool.topology.is_planar(G)) self.get_aabb() self.gen_polygon() self.torque = [] + self.forces_r = np.zeros(2) + self.forces_a = np.zeros(2) self.vel = 0.0 self.aa = 0.0 + self.degree = 0 def clear_torques(self): self.torque = [] + def clear_forces(self): + self.forces_r = np.zeros(2) + self.forces_a = np.zeros(2) + + def set_degree(self, degree): + self.degree = degree + def add_torque(self, p, f): #direction of the torque = cross of (r, f) #magnitude = ||r||*||f||*sin(theta) #r = level arm vector d = self.CoM - p - r = np.linalg.norm(self.CoM - p) - theta = math.acos(np.dot(d, f)/np.dot(d, d)/np.dot(f, f)) - torque = math.sin(theta) * np.sqrt(f[0]*f[0]+f[1]*f[1]) * np.sqrt(r[0]*r[0]+r[1]*r[1]) + #r = np.linalg.norm(self.CoM - p) + value = np.dot(d, f)/np.dot(d, d)/np.dot(f, f) + if value < 1.0 and value > -1.0: + theta = math.acos(value) + torque = math.sin(theta) * np.sqrt(f[0]*f[0]+f[1]*f[1]) * np.sqrt(d[0]*d[0]+d[1]*d[1]) + else: + #print("value = ", value) + torque = 0.0 + #if < 0 then clockwise, else counter - direction = np.cross(r, f) + direction = np.cross(d, f) if direction < 0: - self.torque.append([torque, "clockwise"]) + self.torque.append([torque, f, p, "counterclock"]) else: - self.torque.append([torque, "counterclock"]) + self.torque.append([torque, f, p, "clockwise"]) + - def calculate_moment(self): + def calculate_moment(self, use_graph=False): #returns the area of a triangle defined by two points def area(t): @@ -65,6 +83,7 @@ class Polygon_mass: def center(t): output = np.asarray([(t[0,0]+t[1,0]+t[2,0])/3.0, (t[0,1]+t[1,1]+t[2,1])/3.0]) + return output segs = [] pts = np.asarray(self.polygon.exterior.xy).T @@ -72,9 +91,19 @@ class Polygon_mass: for k in range(pts.shape[0]-1): segs.append([k, k+1]) segs.append([pts.shape[0]-1, 0]) + if use_graph: + points = self.G.vertex_properties["pos"].get_2d_array(range(2)).T + segs2 = [] + n_pts = pts.shape[0] + for e in self.G.edges(): + segs2.append([int(e.source())+n_pts, int(e.target())+n_pts]) + pts = np.concatenate((pts, points)) + segs = segs + segs2 mesh = dict(vertices=pts, segments=segs) #print(self.polygon.area()) - tri = triangulate(mesh, 'pq20D') + tri = triangulate(mesh, 'pq20Ds') + self.mesh = mesh + self.tri = tri moment = 0.0 #NEED TO ADD MASS maybe? for i in range(tri['triangles'].shape[0]): @@ -84,59 +113,75 @@ class Polygon_mass: Moi = A+A*np.linalg.norm(C-self.CoM)**2 moment += Moi - self.MoI = moment + self.MoI = abs(math.log(moment)) + #self.MoI = 10 + print(self.MoI) # triangle_plot(plt.gca(), **tri) # plt.gca().set_title(str(self.polygon.area)) - + + def translate(self, step): + d = self.forces_a + self.forces_r + #print(self.forces_a, self.forces_r) + d0 = step*d + self.CoM = self.CoM + d0 + pos = self.G.vertex_properties["pos"].get_2d_array(range(2)).T + pos = pos + d0 + self.G.vertex_properties["pos"] = self.G.new_vertex_property("vector", vals = pos) + def rotate(self, phi, direction = "counterclock"): if("counterclock"): - for v in self.G.vertices: - p = self.G.vertex_properties["pos"][v] - p[0] = self.CoM[0] + math.cos(phi) * (self.CoM[0] - p[0]) - math.sin(phi) * (p[1] - self.CoM[1]) - p[1] = self.CoM[1] + math.sin(phi) * (self.CoM[0] - p[0]) + math.cos(phi) * (p[1] - self.CoM[1]) - self.G.vertex_properties["pos"][v] = p + for v in self.G.vertices(): + p_prime = copy.deepcopy(self.G.vertex_properties["pos"][v]) + p = copy.deepcopy(self.G.vertex_properties["pos"][v]) + p_prime[0] = self.CoM[0] + math.cos(phi) * (p[0] - self.CoM[0]) - math.sin(phi) * (p[1] - self.CoM[1]) + p_prime[1] = self.CoM[1] + math.sin(phi) * (p[0] - self.CoM[0]) + math.cos(phi) * (p[1] - self.CoM[1]) + self.G.vertex_properties["pos"][v] = p_prime + #rotate points in mesh + points = copy.deepcopy(self.mesh['vertices']) + for v in range(points.shape[0]): + points[v][0] = self.CoM[0] + math.cos(phi) * (self.mesh['vertices'][v][0] - self.CoM[0]) - math.sin(phi) * (self.mesh['vertices'][v][1] - self.CoM[1]) + points[v][1] = self.CoM[1] + math.sin(phi) * (self.mesh['vertices'][v][0] - self.CoM[0]) + math.cos(phi) * (self.mesh['vertices'][v][1] - self.CoM[1]) + self.mesh['vertices'] = points else: - for v in self.G.vertices: - p = self.G.vertex_properties["pos"][v] - p[0] = self.CoM[0] + math.cos(phi) * (self.CoM[0] + p[0]) + math.sin(phi) * (p[1] - self.CoM[1]) - p[1] = self.CoM[1] + math.sin(phi) * (self.CoM[0] + p[0]) - math.cos(phi) * (p[1] - self.CoM[1]) - self.G.vertex_properties["pos"][v] = p + for v in self.G.vertices(): + p_prime = copy.deepcopy(self.G.vertex_properties["pos"][v]) + p = copy.deepcopy(self.G.vertex_properties["pos"][v]) + p_prime[0] = self.CoM[0] + math.cos(phi) * (p[0] - self.CoM[0]) + math.sin(phi) * (p[1] - self.CoM[1]) + p_prime[1] = self.CoM[1] - math.sin(phi) * (p[0] - self.CoM[0]) + math.cos(phi) * (p[1] - self.CoM[1]) + self.G.vertex_properties["pos"][v] = p_prime + #rotate points in mesh + points = copy.deepcopy(self.mesh['vertices']) + for v in range(points.shape[0]): + points[v][0] = self.CoM[0] + math.cos(phi) * (self.mesh['vertices'][v][0] - self.CoM[0]) + math.sin(phi) * (self.mesh['vertices'][v][1] - self.CoM[1]) + points[v][1] = self.CoM[1] - math.sin(phi) * (self.mesh['vertices'][v][0] - self.CoM[0]) + math.cos(phi) * (self.mesh['vertices'][v][1] - self.CoM[1]) + self.mesh['vertices'] = points + def plot_graph(self, D, x, y): plt.figure() ext = [self.a[0], self.b[0], self.a[1], self.b[1]] - plt.imshow(D, origin = 'lower', extent=ext) + #plt.imshow(D, origin = 'lower', extent=ext) p = self.G.vertex_properties["pos"].get_2d_array(range(2)).T plt.scatter(p[:,0], p[:,1], color='r') plt.scatter(self.CoM[0], self.CoM[1], marker='*') + #mesh = dict(vertices=pts, segments=segs) + #print(self.polygon.area()) + #tri = triangulate(mesh, 'pq20Ds') + triangle_plot(plt.gca(), **self.mesh) - segs = [] - pts = np.asarray(self.polygon.exterior.xy).T - pts = pts[:-1, :] - for k in range(pts.shape[0]-1): - segs.append([k, k+1]) - segs.append([pts.shape[0]-1, 0]) - points = self.G.vertex_properties["pos"].get_2d_array(range(2)).T - segs2 = [] - n_pts = pts.shape[0] - for e in self.G.edges(): - segs2.append([int(e.source())+n_pts, int(e.target())+n_pts]) - + #plot polygon + #plt.plot(*self.polygon.exterior.xy, color = 'r') - pts = np.concatenate((pts, points)) - segs = segs + segs2 - mesh = dict(vertices=pts, segments=segs) - #print(self.polygon.area()) - # tri = triangulate(mesh, 'pq20D') - triangle_plot(plt.gca(), **mesh) - for i in range(len(segs)): - plt.plot((pts[segs[i][0]][0], pts[segs[i][0]][1]), (pts[segs[i][1]][0], pts[segs[i][1]][1]), color='b') +# for i in range(len(segs)): +# plt.plot((pts[segs[i][0]][0], pts[segs[i][1]][0]), (pts[segs[i][0]][1], pts[segs[i][1]][1]), color='b') plt.gca().set_title(str(self.polygon.area)) + for e in self.torque: + plt.quiver(e[2][0], e[2][1], e[1][0], e[1][1], color='r') #tri = Delaunay(np.asarray(self.polygon.exterior.coords.xy).T) #tri = triangulate(mesh, 'pq20a' + str(self.polygon.area/100.0)+'D') #delaunay_plot_2d(tri) @@ -159,8 +204,7 @@ class Polygon_mass: # Y = [coord[1], coord2[1]] # #all_plots.plot(x, y, 'go--', linewidth=1, markersize=1) # plt.plot(X, Y, 'go--', linewidth=1, markersize=1) -# - plt.plot(*self.polygon.exterior.xy, color = 'r') +# plt.show() def get_aabb(self): @@ -200,7 +244,7 @@ class Polygon_mass: contour[: ,1] = Y(cn[0][:, 0]) self.polygon = Polygon(contour) self.CoM = self.centroid_com(contour) - + self.calculate_moment(True) #cn = plt.contour(x, y, D, levels = [level]) #cn = plt.contour(x, y, D, levels = [level]) # plt.close() @@ -215,12 +259,11 @@ class Polygon_mass: # #nlist = c.trace(level, level, 0) # #segs = nlist[:len(nlist)//2] #self.polygon = Polygon(pts) - self.plot_graph(D, x, y) + #self.plot_graph(D, x, y) def distancefield(self): - #generate a meshgrid of the appropriate size and resolution to surround the network #get the space occupied by the network lower = self.a @@ -271,6 +314,9 @@ class Polygon_mass: # R = (200, 200, 200) D, I = tree.query(Q) + self.D = D + self.x = x + self.y = y return D, x, y @@ -301,23 +347,66 @@ def get_torques(G, masses): #if the source and target cluster is not equal to each other #add an inter subgraph edge. if(G.vertex_properties["clusters"][e.source()] != G.vertex_properties["clusters"][e.target()]): - pf0 = G.vertex_properties["pos"][e.target()].get_2D_array(range(2)) - pf1 = G.vertex_properties["pos"][e.source()].get_2D_array(range(2)) + #index of the cluster + t0 = G.vertex_properties["clusters"][e.target()] + t1 = G.vertex_properties["clusters"][e.source()] + #index of the vertex outside of the subgraph + v0_index = G.vertex_properties["idx"][e.target()] + v1_index = G.vertex_properties["idx"][e.source()] + #location of torque arm in the subgraph + p0 = masses[t0].G.vertex_properties["pos"][np.argwhere(masses[t0].G.vertex_properties["idx"].get_array() == v0_index)] + p1 = masses[t1].G.vertex_properties["pos"][np.argwhere(masses[t1].G.vertex_properties["idx"].get_array() == v1_index)] - t0 = G.vertex_properties["clusters"][e.source()] - t1 = G.vertex_properties["clusters"][e.target()] + f0 = np.subtract(p0, p1) + f1 = np.subtract(p1, p0) + masses[t0].add_torque(p0, f1) + masses[t1].add_torque(p1, f0) +''' + c1 scales the attractive force before log. + c2 scales the attractive force inside log. + c3 scales the repulsive force. +''' +def get_forces(G, masses, c1 = 50.0, c2 = 1.0, c3 = 1.0): + for i in range(len(masses)): + masses[i].clear_forces() + f_total = np.zeros(2) + for j in range(len(masses)): + if i != j: + f0 = np.subtract(masses[i].CoM, masses[j].CoM) + f1 = np.power(f0, 3.0) + f1[0] = c3/f1[0]*np.sign(f0[0]) + f1[1] = c3/f1[1]*np.sign(f0[1]) + f_total = np.add(f_total, f1) + masses[i].forces_r = f_total - v0_index = G.vertex_properties["idx"][e.source()] - v1_index = G.vertex_properties["idx"][e.target()] + for e in G.edges(): + #if the source and target cluster is not equal to each other + #add an inter subgraph edge. + if(G.vertex_properties["clusters"][e.source()] != G.vertex_properties["clusters"][e.target()]): + #index of the cluster + t0 = G.vertex_properties["clusters"][e.target()] + t1 = G.vertex_properties["clusters"][e.source()] + #index of the vertex outside of the subgraph + v0_index = G.vertex_properties["idx"][e.target()] + v1_index = G.vertex_properties["idx"][e.source()] + #location of torque arm in the subgraph + p0 = masses[t0].G.vertex_properties["pos"][np.argwhere(masses[t0].G.vertex_properties["idx"].get_array() == v0_index)] + p1 = masses[t1].G.vertex_properties["pos"][np.argwhere(masses[t1].G.vertex_properties["idx"].get_array() == v1_index)] - p0 = masses[t0].G.vertex_properties["pos"][masses[t0].G.vertex(v0_index)].get_2D_array(range(2)) - p1 = masses[t1].G.vertex_properties["pos"][masses[t1].G.vertex(v1_index)].get_2D_array(range(2)) + f0 = np.subtract(p1, p0) + f0_1 = abs(f0) + f0_1 = c1*np.log(f0_1/c2)/masses[t0].degree + f0_1 = f0_1*np.sign(f0) + f1 = np.subtract(p0, p1) + f1_1 = abs(f1) + f1_1 = c1*np.log(f1_1/c2)/masses[t1].degree + f1_1 = f1_1*np.sign(f1) + masses[t0].forces_a = np.add(masses[t0].forces_a, f1_1) + masses[t1].forces_a = np.add(masses[t1].forces_a, f0_1) - f0 = pf0-p1 - f1 = pf1-p0 - masses[t0].add_torque(p0, f0) - masses[t1].add_torque(p1, f1) - + + + def voronoi_polygons(voronoi, diameter): """Generate shapely.geometry.Polygon objects corresponding to the @@ -446,7 +535,7 @@ def gen_cluster_graph(G, num_clusters, cluster_pos): G_cluster.vertex_properties["bc"] = vbetweeness_centrality G_cluster.edge_properties["bc"] = ebetweeness_centrality G_cluster.edge_properties["bc_scaled"] = G_cluster.new_edge_property("double", vals=ebc) - + G_cluster.edge_properties["log"] = G_cluster.new_edge_property("double", vals=abs(np.log(G_cluster.edge_properties["volume"].get_array()))) dg = G_cluster.vertex_properties["degree"].get_array() dg = 2*max(dg) - dg d = G_cluster.new_vertex_property("int", vals=dg) @@ -570,7 +659,35 @@ def centroid_region(vertices): C_y = (1.0 / (6.0 * A)) * C_y return np.array([C_x, C_y]) - + +def find_equlibrium(masses, t = 0.01): + for m in masses: + sum_torque = 0 + for torque in m.torque: + if torque[3] == "clockwise": + sum_torque -= torque[0] + else: + sum_torque += torque[0] + m.vel = m.aa * t + m.vel + m.aa = sum_torque/m.MoI + #print(m.G.vertex_properties["clusters"][0], m.vel) + if m.vel != 0.0: + if m.vel < 0.0: + m.rotate(abs(m.vel * t),"counterclock") + else: + m.rotate(abs(m.vel * t), "clockwise") + + +def gen_Eades(G, masses, M = 10): + for i in range(M): + get_forces(G, masses) + for j in masses: + j.translate(0.001) + +def onion_springs(G, masses, min_length): + for v in G.vertices(): + + def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): #def gen_image(G, G_c, vor, vor_filtered): @@ -580,18 +697,24 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): #get points of the centers of every cluster #generate voronoi region and plot it. - fig, ax = plt.subplots(3, 1, sharex='col', sharey='row') + fig, ax = plt.subplots(4, 1, sharex='col', sharey='row') fig.tight_layout() - grid = plt.GridSpec(3,1) + grid = plt.GridSpec(4,1) grid.update(wspace=0.025, hspace=0.2) ax[0].axis('off') ax[1].axis('off') ax[2].axis('off') + ax[3].axis('off') + + #Add plots to the axes and get their handles all_plots = fig.add_subplot(grid[0]) ax[0].set_title(itr) no_links = fig.add_subplot(grid[1], sharey=all_plots, sharex=all_plots) voronoi = fig.add_subplot(grid[2], sharey=all_plots, sharex=all_plots) + rotated = fig.add_subplot(grid[3], sharey=all_plots, sharex=all_plots) + + #Get the points and generate the voronoi region pts = G_c.vertex_properties["pos"].get_2d_array(range(2)).T if bb_flag == False: vor = Voronoi(pts) @@ -601,16 +724,20 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): a = voronoi.get_ylim() b = voronoi.get_xlim() bb = np.array([b[0], b[1], a[0], a[1]]) + + #generate the polygons based on the voronoi regions G_c, regions, vor = gen_polygons(G_c, bb) if bb_flag == True: voronoi_plot_2d(vor, all_plots) voronoi_plot_2d(vor, no_links) voronoi_plot_2d(vor, voronoi) + #plot the top-level graph pts = G_c.vertex_properties["pos"].get_2d_array(range(2)).T all_plots.scatter(pts[:,0], pts[:, 1], s=20*G_c.vertex_properties["degree"].get_array(), marker="*") no_links.scatter(pts[:,0], pts[:, 1], s=20*G_c.vertex_properties["degree"].get_array(), marker="*") voronoi.scatter(pts[:,0], pts[:, 1], s=20*G_c.vertex_properties["degree"].get_array(), marker="*") + #plot the connections of the top level graph for e in G_c.edges(): coord = G_c.vertex_properties["pos"][e.source()] @@ -621,18 +748,25 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): no_links.plot(x, y, 'go--', linewidth=1, markersize=1) voronoi.plot(x, y, 'go--', linewidth=1, markersize=1) - #for every subgraph generate a layout and plot the resulting Polygon_mass + #For every subgraph generate a layout and plot the resulting Polygon_mass + #These polygons are not the same as the voronoi polygons and instead surround + #graph. masses = [] for i in range(num_clusters): g, center = gen_subclusters(G, G_c, i, reposition) d = G_c.vertex_properties["pos"][i] - center - t = Polygon_mass(g) - masses.append(g) - #t.distancefield() for v in g.vertices(): G.vertex_properties["pos"][g.vertex_properties["idx"][v]] = g.vertex_properties["pos"][v] + d + g.vertex_properties["pos"][v] = g.vertex_properties["pos"][v] + d + t = Polygon_mass(g) + t.set_degree(G_c.vertex_properties["degree"][i]) + masses.append(t) + #t.distancefield() - + #get the torques generated by the positioning of the graphs. + get_torques(G, masses) +# for i in masses: +# i.plot_graph(i.D, i.x, i.y) #g.vertex_properties["pos"][g.vertex_properties["idx"][v]] = g.vertex_properties["pos"][v] + d #sub_pts = g.vertex_properties["pos"].get_2d_array(range(2)).T #all_plots.scatter(pts[:,0], pts[:, 1], marker="*") @@ -642,7 +776,9 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): # x = [coord[0], coord2[0]] # y = [coord[1], coord2[1]] # plt.plot(x, y, 'ro--', linewidth=1, markersize=1) - + + + #Plot the cluster level connections and the vertex level connections. for e in G.edges(): coord = G.vertex_properties["pos"][e.source()] coord2 = G.vertex_properties["pos"][e.target()] @@ -653,7 +789,8 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): no_links.plot(x, y, 'ro--', linewidth=1, markersize=1) else: all_plots.plot(x, y, 'bo--', linewidth=1, markersize=1) - + + #Update the centroids based on the voronoi polygons no_links.xaxis.set_visible(False) all_plots.xaxis.set_visible(False) for v in G_c.vertices(): @@ -661,11 +798,16 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): centroid = centroid_region(region[0]) G_c.vertex_properties["pos"][v] = centroid + + print(G_c.num_vertices(), G_c.num_edges()) + + #Plots the vertices of the subgraphs pts_temp = G_c.vertex_properties["pos"].get_2d_array(range(2)).T all_plots.scatter(pts_temp[:,0], pts_temp[:, 1], marker='.', color='r') no_links.scatter(pts_temp[:,0], pts_temp[:, 1], marker='.', color='r') voronoi.scatter(pts_temp[:,0], pts_temp[:, 1], marker='.', color='r') + #set the limits of the plots. all_plots.set_xlim([bb[0], bb[1]]) all_plots.set_ylim([bb[2], bb[3]]) @@ -675,10 +817,44 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): voronoi.set_xlim([bb[0], bb[1]]) voronoi.set_ylim([bb[2], bb[3]]) + #show the plots. plt.show() +# for j in [7, 10, 15]: +# masses[j].plot_graph(masses[j].D, masses[j].x, masses[j].y) + + for j in range(10): + for i in range(100): + get_torques(G, masses) + find_equlibrium(masses) + gen_Eades(G, masses) +# for j in [7, 10]: +# masses[j].plot_graph(masses[j].D, masses[j].x, masses[j].y) +# for j in [7, 10, 15]: +# masses[j].plot_graph(masses[j].D, masses[j].x, masses[j].y) + + for i in masses: + for v in i.G.vertices(): + G.vertex_properties["pos"][i.G.vertex_properties["idx"][v]] = i.G.vertex_properties["pos"][v] + + + #Plot the cluster level connections and the vertex level connections. + for e in G.edges(): + coord = G.vertex_properties["pos"][e.source()] + coord2 = G.vertex_properties["pos"][e.target()] + x = [coord[0], coord2[0]] + y = [coord[1], coord2[1]] + if (G.vertex_properties["clusters"][e.source()] == G.vertex_properties["clusters"][e.target()]): + rotated.plot(x, y, 'ro--', linewidth=1, markersize=1) + else: + rotated.plot(x, y, 'bo--', linewidth=1, markersize=1) + + pts_temp = G.vertex_properties["pos"].get_2d_array(range(2)).T + rotated.scatter(pts_temp[:,0], pts_temp[:, 1], marker='.', color='r') + + - return G, G_c, bb + return G, G_c, bb, masses @@ -691,19 +867,25 @@ def gen_image(G, G_c, itr, bb_flag = False, bb = None, reposition = False): G, bbl, bbu = load_nwt("/home/pavel/Documents/Python/GraphGuiQt/network_4.nwt") G_c, G = gen_clusters(G, bbl, bbu) + num_clusters = 20 #G_c.vertex_properties["pos"] = nwt.gt.radial_tree_layout(G_c, root=np.argwhere(G_c.vertex_properties["degree"].get_array() == max(G_c.vertex_properties["degree"].get_array())), node_weight = G_c.vertex_properties["10-degree"], r= 2.0) G_c.vertex_properties["pos"] = nwt.gt.sfdp_layout(G_c, eweight=G_c.edge_properties["volume"], vweight=G_c.vertex_properties["degree"], C = 1.0, K = 10) -G, G_c, bb = gen_image(G, G_c, "base", reposition = True) + + +G, G_c, bb, masses = gen_image(G, G_c, "base", reposition = True) + +print("Planarity test: G_c, G = " , nwt.gt.graph_tool.topology.is_planar(G_c), nwt.gt.graph_tool.topology.is_planar(G)) + itr = 0 #for itr in range(5): -# G, G_c, bb = gen_image(G, G_c, itr, True, bb) +# G, G_c, bb, masses = gen_image(G, G_c, itr, True, bb) # itr+=1 -g, center = gen_subclusters(G, G_c) +#g, center = gen_subclusters(G, G_c) #d = G_c.vertex_properties["pos"][0] - center #for v in g.vertices(): # g.vertex_properties["pos"][v] = g.vertex_properties["pos"][v] + d -- libgit2 0.21.4