Commit 9f9f17883ce16db5a1cfa46f0dfa21853c0f4f60

Authored by Pavel Govyadinov
0 parents

clead up version of the GUI submitted to vis 2019

GraphCanvas.py 0 → 100644
  1 +++ a/GraphCanvas.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:43:59 2019
  5 +
  6 +@author: pavel
  7 + The GraphCanvas class that extends the scene class in vispy in order to draw
  8 + the graph object. This class is wrapped in a QTcanvas.
  9 +"""
  10 +
  11 +from vispy import gloo, scene
  12 +from vispy.gloo import set_viewport, set_state, clear, set_blend_color
  13 +from vispy.util.transforms import perspective, translate, rotate, scale
  14 +import vispy.gloo.gl as glcore
  15 +
  16 +import numpy as np
  17 +import math
  18 +import network_dep as nwt
  19 +
  20 +#import the graph shaders.
  21 +from graph_shaders import vert, frag, vs, fs
  22 +from subgraph_shaders import vert_s, frag_s, vs_s, fs_s
  23 +
  24 +#The graph canvas class that
  25 +class GraphCanvas(scene.SceneCanvas):
  26 +
  27 + """
  28 + Initialization method.
  29 + Generates the 512x512 canvas, makes it available for drawing the fills all
  30 + the GLSL shaders with dummy data.
  31 + """
  32 + def __init__(self, **kwargs):
  33 + # Initialize the canvas for real
  34 + scene.SceneCanvas.__init__(self, size=(512, 512), **kwargs)
  35 + #Unfreeze the canvas to make dynamic interaction possible
  36 + self.unfreeze()
  37 +
  38 + #initialize all the boolean and dictionary variables.
  39 + ps = self.pixel_scale
  40 + self.subgraphs = False
  41 + self.position = 50, 50
  42 + self.down=False;
  43 +
  44 + #Dictionaries to store the unique color ID, cluster ID, and edge-to-ID
  45 + #dictionaries.
  46 + self.color_dict = {}
  47 + self.cluster_dict = {}
  48 + self.edge_dict = {}
  49 +
  50 + #Booleans the storage for the current "Path", i.e. edges the user selected.
  51 + self.pathing = False
  52 + self.path = []
  53 + self.full_path = []
  54 +
  55 + #utility variables used for storing the cluster being moved and all the
  56 + #nodes and edges that belong to that cluster and move along with it.
  57 + self.moving = False
  58 + self.moving_cluster = False
  59 + self.selection = False
  60 +
  61 + n = 10
  62 + ne = 10
  63 + #Init dummy structures
  64 + self.uniforms = [('u_graph_size', np.float32, 3)]
  65 + self.data = np.zeros(n, dtype=[('a_position', np.float32, 3),
  66 + ('a_fg_color', np.float32, 4),
  67 + ('a_bg_color', np.float32, 4),
  68 + ('a_size', np.float32, 1),
  69 + ('a_linewidth', np.float32, 1),
  70 + ('a_unique_id', np.float32, 4),
  71 + ])
  72 +
  73 + self.clusters = np.zeros(n, dtype=[('a_position', np.float32, 3),
  74 + ('a_bg_color', np.float32, 4),
  75 + ('a_value', np.float32, 2),
  76 + ('a_unique_id', np.float32, 4),
  77 + ])
  78 +
  79 + self.edges = np.random.randint(size=(ne, 2), low=0,
  80 + high=n-1).astype(np.uint32)
  81 + self.edges_s = np.random.randint(size=(ne, 4), low=0,
  82 + high=n-1).astype(np.uint32)
  83 + self.data['a_position'] = np.hstack((0.25 * np.random.randn(n, 2),
  84 + np.zeros((n, 1))))
  85 + self.data['a_fg_color'] = 0, 0, 0, 1.0
  86 + color = np.random.uniform(0.5, 1., (n, 3))
  87 + self.data['a_bg_color'] = np.hstack((color, np.zeros((n, 1))))
  88 + self.data['a_size'] = np.random.randint(size=n, low=8*ps, high=20*ps)
  89 + self.data['a_linewidth'] = 8.*ps
  90 + self.data['a_unique_id'] = np.hstack((color, np.ones((n, 1))))
  91 + #self.uniforms['u_graph_size'] = [1.0, 1.0, 1.0]
  92 + self.translate = [0,0,0]
  93 + self.scale = [1,1,1]
  94 + #color = np.random.uniform(0.5, 1., (ne, 3))
  95 + #self.linecolor = np.hstack((color, np.ones((ne, 1))))
  96 + #color = np.random.uniform(0.5, 1., (ne, 3))
  97 + #self.linecolor = np.hstack((color, np.ones((ne, 1))))
  98 + self.u_antialias = 1
  99 +
  100 + #init dummy vertex and index buffers.
  101 + self.vbo = gloo.VertexBuffer(self.data)
  102 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  103 +
  104 + #Need to initialize thick lines.
  105 + self.index = gloo.IndexBuffer(self.edges)
  106 + self.index_s = gloo.IndexBuffer(self.edges_s)
  107 +
  108 + #Set the view matrices.
  109 + self.view = np.eye(4, dtype=np.float32)
  110 + self.model = np.eye(4, dtype=np.float32)
  111 + self.projection = np.eye(4, dtype=np.float32)
  112 +
  113 + #init shaders used for vertices of the full graph.
  114 + self.program = gloo.Program(vert, frag)
  115 + self.program.bind(self.vbo)
  116 + self.program['u_size'] = 1
  117 + self.program['u_antialias'] = self.u_antialias
  118 + self.program['u_model'] = self.model
  119 + self.program['u_view'] = self.view
  120 + self.program['u_projection'] = self.projection
  121 + self.program['u_graph_size'] = [1.0, 1.0, 1.0]
  122 + self.program['u_picking'] = False
  123 +
  124 + #init shades used for the edges in the graph
  125 + self.program_e = gloo.Program(vs, fs)
  126 + self.program_e['u_size'] = 1
  127 + self.program_e['u_model'] = self.model
  128 + self.program_e['u_view'] = self.view
  129 + self.program_e['u_projection'] = self.projection
  130 + #self.program_e['l_color'] = self.linecolor.astype(np.float32)
  131 + self.program_e.bind(self.vbo)
  132 +
  133 + #init shaders used to the vertices in the subgraph graph.
  134 + self.program_s = gloo.Program(vert_s, frag_s)
  135 + self.program_s.bind(self.vbo_s)
  136 + self.program_s['u_model'] = self.model
  137 + self.program_s['u_view'] = self.view
  138 + self.program_s['u_projection'] = self.projection
  139 + self.program_s['u_graph_size'] = [1.0, 1.0, 1.0]
  140 + self.program_s['u_picking'] = False
  141 +
  142 + #init shaders used for the subgraph-edges
  143 + self.program_e_s = gloo.Program(vs_s, fs_s)
  144 + self.program_e_s['u_size'] = 1
  145 + self.program_e_s['u_model'] = self.model
  146 + self.program_e_s['u_view'] = self.view
  147 + self.program_e_s['u_projection'] = self.projection
  148 + #self.program_e['l_color'] = self.linecolor.astype(np.float32)
  149 + self.program_e_s.bind(self.vbo_s)
  150 +
  151 +
  152 + #set up the viewport and the gl state.
  153 + set_viewport(0, 0, *self.physical_size)
  154 +
  155 + set_state(clear_color='white', depth_test=True, blend=True,
  156 + blend_func=('src_alpha', 'one_minus_src_alpha'), depth_func = ('lequal'))
  157 +
  158 + """
  159 + Function that recolors vertices based on the selected statistic
  160 + Maps the statisic stored in G to a colormap passed to the function
  161 + Then updates the necessary color array.
  162 + """
  163 + def color_vertices(self, G, vertex_property, dtype = False, cm = 'plasma'):
  164 + #if we are visualing the clusters we should use a discrete colormap
  165 + #otherwise use the passed colormap
  166 + if dtype == True:
  167 + G.vertex_properties["RGBA"] = nwt.Network.map_property_to_color(G, G.vertex_properties["clusters"])
  168 + else:
  169 + G.vertex_properties["RGBA"] = nwt.Network.map_property_to_color(G, G.vertex_properties[vertex_property], colormap=cm)
  170 + #set the color and update the Vertices.
  171 + self.current_color = vertex_property
  172 + color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T
  173 + self.data['a_bg_color'] = color
  174 + self.vbo = gloo.VertexBuffer(self.data)
  175 + self.program.bind(self.vbo)
  176 + #self.program_e.bind(self.vbo)
  177 + self.update()
  178 +
  179 + """
  180 + Maps a statistic of the vertices based on the size of the canvas to size of
  181 + the drawn object.
  182 + """
  183 + def size_vertices(self, G, propertymap):
  184 + size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], propertymap).get_array()
  185 + self.data['a_size'] = size
  186 + self.vbo = gloo.VertexBuffer(self.data)
  187 + self.program.bind(self.vbo)
  188 + #self.program_e.bind(self.vbo)
  189 + self.update()
  190 +
  191 +
  192 + """
  193 + Function to dim all nodes and edges that do not belong to a cluster chosen
  194 + in the graph view. Returns a copy of the graph with the alpha channel saved.
  195 + OPTMIZE HERE: could just return an alpha array to reduce memory usage.
  196 + """
  197 + def focus_on_cluster(self, G, c_id):
  198 + G_copy = nwt.gt.Graph(G, directed=False)
  199 + e_color = G_copy.edge_properties["RGBA"].get_2d_array(range(4)).T
  200 + vertices = np.argwhere(self.labels != c_id)
  201 + for v in range(vertices.shape[0]):
  202 + idx = vertices[v][0]
  203 + vtx = G_copy.vertex(idx)
  204 + for e in vtx.all_edges():
  205 + if (int(e.source()), int(e.target())) in self.edge_dict.keys():
  206 + index = int(self.edge_dict[int(e.source()), int(e.target())])
  207 + if vtx == int(e.source()):
  208 + e_color[index][3] = 0.05
  209 + elif vtx == int(e.target()):
  210 + e_color[index][3] = 0.05
  211 + else:
  212 + index = int(self.edge_dict[int(e.target()), int(e.source())])
  213 + if vtx == int(e.target()):
  214 + e_color[index][3] = 0.05
  215 + elif vtx == int(e.source()):
  216 + e_color[index][3] = 0.05
  217 +
  218 + G_copy.edge_properties["RGBA"] = G_copy.new_edge_property("vector<double>", vals = e_color)
  219 +
  220 + return G_copy
  221 +
  222 + """
  223 + Function that sets the size of the vertices based on the distance from the
  224 + camera.
  225 + """
  226 + def vertexSizeFromDistance(self, G, camera_pos):
  227 + location = G.vertex_properties["p"].get_2d_array(range(3)).T
  228 + cam_array = np.zeros(location.shape, dtype=np.float32)
  229 + len_array = np.zeros(location.shape[0])
  230 + offset_array = np.zeros(location.shape, dtype=np.float32)
  231 + cam_array[:][0:3] = camera_pos
  232 + offset = [(bbu[0]-bbl[0])/2, (bbu[1]-bbl[1])/2, (bbu[2]-bbl[2])/2]
  233 + location = location - offset
  234 + location = location - camera_pos
  235 + for i in range(location.shape[0]):
  236 + len_array[i] = np.sqrt(np.power(location[i][0],2) + np.power(location[i][1],2) + np.power(location[i][2],2))
  237 +
  238 + G.vertex_properties['dist_from_camera'] = G.new_vertex_property('float', vals=len_array)
  239 + self.data['a_size'] = nwt.Network.map_vertices_to_range(G, [1*self.pixel_scale, 60*self.pixel_scale], 'dist_from_camera').get_array()
  240 +
  241 +
  242 + size = nwt.Network.map_vertices_to_range(G, [1.0, 0.5], 'dist_from_camera').get_array()
  243 + edges = G.get_edges()
  244 + for e in range(edges.shape[0]):
  245 + idx = int(4*edges[e][2])
  246 + self.line_data['a_linewidth'][idx] = size[edges[e][0]]
  247 + self.line_data['a_linewidth'][idx+1] = size[edges[e][1]]
  248 + self.line_data['a_linewidth'][idx+2] = size[edges[e][0]]
  249 + self.line_data['a_linewidth'][idx+3] = size[edges[e][1]]
  250 +
  251 +
  252 + #self.vbo = gloo.VertexBuffer(self.data)
  253 + #self.vbo_line = gloo.VertexBuffer(self.line_data)
  254 + #self.program.bind(self.vbo)
  255 + #self.program_e.bind(self.vbo_line)
  256 + #self.update()
  257 +
  258 + """
  259 + Function that scales the alpha channel of each vertex in the graph based on
  260 + The distance from the camera.
  261 + Sometimes needs to be done separetly.
  262 + """
  263 + def vertexAlphaFromDistance(self, G, camera_pos):
  264 + location = G.vertex_properties["p"].get_2d_array(range(3)).T
  265 + cam_array = np.zeros(location.shape, dtype=np.float32)
  266 + len_array = np.zeros(location.shape[0])
  267 + #offset_array = np.zeros(location.shape, dtype=np.float32)
  268 + cam_array[:][0:3] = camera_pos
  269 + offset = [(bbu[0]-bbl[0])/2, (bbu[1]-bbl[1])/2, (bbu[2]-bbl[2])/2]
  270 + location = location - offset
  271 + location = location - camera_pos
  272 + for i in range(location.shape[0]):
  273 + len_array[i] = np.sqrt(np.power(location[i][0],2) + np.power(location[i][1],2) + np.power(location[i][2],2))
  274 +
  275 +
  276 + test = nwt.Network.map_vertices_to_range(G, [0.0, 1.0], 'dist_from_camera').get_array()
  277 + color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T
  278 + for i in range(location.shape[0]):
  279 + color[i][3] = test[i]
  280 + G.vertex_properties["RGBA"] = G.new_vertex_property("vector<double>", vals = color)
  281 + self.data['a_bg_color'] = color
  282 +
  283 + edges = G.get_edges()
  284 + for e in range(edges.shape[0]):
  285 + idx = int(4*edges[e][2])
  286 + self.line_data['a_fg_color'][idx] = color[edges[e][0]]
  287 + self.line_data['a_fg_color'][idx+1] = color[edges[e][1]]
  288 + self.line_data['a_fg_color'][idx+2] = color[edges[e][0]]
  289 + self.line_data['a_fg_color'][idx+3] = color[edges[e][1]]
  290 +
  291 +
  292 +
  293 + self.vbo = gloo.VertexBuffer(self.data)
  294 + self.vbo_line = gloo.VertexBuffer(self.line_data)
  295 + self.program.bind(self.vbo)
  296 + self.program_e.bind(self.vbo_line)
  297 + self.update()
  298 +
  299 + """
  300 + Sets the edge color based on the the cluster the vertices belongs to
  301 + Propertymap is a VERTEXPROPERTYMAP since the color of the edges is based
  302 + on the clusters the edges belong to.
  303 + """
  304 + def color_edges(self, G, propertymap="clusters"):
  305 + if propertymap == "clusters":
  306 + for e in G.edges():
  307 + if G.vertex_properties[propertymap][e.source()] == G.vertex_properties[propertymap][e.target()]:
  308 + G.edge_properties["RGBA"][e] = G.vertex_properties["RGBA"][e.source()]
  309 + else:
  310 + G.edge_properties["RGBA"][e] = [0.0, 0.0, 0.0, 0.8]
  311 +
  312 +
  313 + """
  314 + Helper function that generates the framebuffer object that stores the vertices
  315 + Generates the vertex buffer based on the graph G that is passed to the function
  316 + Sets the color, generates the graph and subgraph color if necessary.
  317 + """
  318 + def gen_vertex_vbo(self, G):
  319 + color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T
  320 + size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], 'degree').get_array()
  321 +
  322 + position = G.vertex_properties["pos"].get_2d_array(range(3)).T
  323 + #for p in range(position.shape[0]):
  324 + # position[p][0] = position[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0]
  325 + # position[p][1] = position[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1]
  326 + # position[p][2] = position[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2]
  327 + #G.vertex_properties["pos"] = G.new_vertex_property("vector<double>", vals = position)
  328 + edges = G.get_edges();
  329 + edges = edges[:, 0:2]
  330 + #width = nwt.Network.map_edges_to_range(G, [1*self.pixel_scale, 5*self.pixel_scale], 'volume').get_array()
  331 + #ecolor = G.edge_properties["RGBA"].get_2d_array(range(4)).T
  332 +
  333 + self.data = np.zeros(G.num_vertices(), dtype=[('a_position', np.float32, 3),
  334 + ('a_fg_color', np.float32, 4),
  335 + ('a_bg_color', np.float32, 4),
  336 + ('a_size', np.float32, 1),
  337 + ('a_linewidth', np.float32, 1),
  338 + ('a_unique_id', np.float32, 4),
  339 + ('a_selection', np.float32, 1),
  340 + ])
  341 +
  342 + #self.edges = edges.astype(np.uint32)
  343 + self.data['a_position'] = position
  344 + #fg color is the color of the ring
  345 + self.data['a_fg_color'] = 0, 0, 0, 1
  346 + self.data['a_bg_color'] = color
  347 + self.data['a_size'] = size
  348 + self.data['a_linewidth'] = 4.*self.pixel_scale
  349 + self.data['a_unique_id'] = self.gen_vertex_id(G)
  350 + self.data['a_selection'] = G.vertex_properties["selection"].get_array()
  351 + #self.data['a_graph_size'] = [bbu-bbl]
  352 +
  353 + self.program['u_graph_size'] = [bbu-bbl]
  354 +
  355 + self.vbo = gloo.VertexBuffer(self.data)
  356 + self.gen_line_vbo(G)
  357 + if(self.subgraphs):
  358 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  359 + self.index_s = gloo.IndexBuffer(self.edges_s)
  360 + #self.index = gloo.IndexBuffer(self.edges)
  361 + self.program_e.bind(self.vbo_line)
  362 + self.program.bind(self.vbo)
  363 + if(self.subgraphs):
  364 + #self.program_e_s.bind(self.vbo_s)
  365 + self.program_s.bind(self.vbo_s)
  366 + print(self.view)
  367 + self.update()
  368 +
  369 + """
  370 + Helper function that creates colored "block" lines based on the edges
  371 + in the graph. Generates the framebuffer object and fills it with the relavant data.
  372 + Note that each line segment is saved as a two triangles that share the same
  373 + two points on the centerline, but are offset according to the normal of the
  374 + line segmente to control thickness dynamically.
  375 + """
  376 + def gen_line_vbo(self, G):
  377 + #Set the data.
  378 + self.line_data = np.zeros(G.num_edges()*4, dtype=[('a_position', np.float32, 3),
  379 + ('a_normal', np.float32, 2),
  380 + ('a_fg_color', np.float32, 4),
  381 + ('a_linewidth', np.float32, 1),
  382 + ])
  383 + self.edges = np.random.randint(size=(G.num_edges()*2, 3), low=0,
  384 + high=(G.num_edges()-1)).astype(np.uint32)
  385 + color = G.edge_properties["RGBA"].get_2d_array(range(4)).T
  386 + edges = G.get_edges()
  387 + #size need to be changed to the size based on the current property map
  388 + size = nwt.Network.map_vertices_to_range(G, [1.0, 0.5], 'degree').get_array()
  389 + for e in range(edges.shape[0]):
  390 + idx = int(4*edges[e][2])
  391 + p0 = G.vertex_properties["pos"][G.vertex(edges[e][0])]
  392 + p1 = G.vertex_properties["pos"][G.vertex(edges[e][1])]
  393 + d = np.subtract(p1, p0)
  394 + #d_norm = np.multiply(d, 1/np.sqrt(np.power(d[0],2) + np.power(d[1],2)))
  395 + d_norm = d[0:2]
  396 + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2))
  397 + norm = np.zeros((2,), dtype=np.float32)
  398 + norm[0] = d_norm[1]
  399 + norm[1] = d_norm[0]*-1
  400 + #print(np.sqrt(norm[0]*norm[0] + norm[1]*norm[1]))
  401 + #thickness = G.edge_properties["thickness"][e]
  402 + thickness = 1.0
  403 + self.edge_dict[int(edges[e][0]), int(edges[e][1])] = int(edges[e][2])
  404 + self.line_data['a_position'][idx] = p0
  405 + self.line_data['a_normal'][idx] = norm
  406 + self.line_data['a_fg_color'][idx] = color[edges[e][2]]
  407 + #a_linewidth is a vector.
  408 + self.line_data['a_linewidth'][idx] = size[edges[e][0]]
  409 +
  410 + self.line_data['a_position'][idx+1] = p1
  411 + self.line_data['a_normal'][idx+1] = norm
  412 + self.line_data['a_fg_color'][idx+1] = color[edges[e][2]]
  413 + self.line_data['a_linewidth'][idx+1] = size[edges[e][1]]
  414 +
  415 + self.line_data['a_position'][idx+2] = p0
  416 + self.line_data['a_normal'][idx+2] = -norm
  417 + self.line_data['a_fg_color'][idx+2] = color[edges[e][2]]
  418 + self.line_data['a_linewidth'][idx+2] = size[edges[e][0]]
  419 +
  420 + self.line_data['a_position'][idx+3] = p1
  421 + self.line_data['a_normal'][idx+3] = -norm
  422 + self.line_data['a_fg_color'][idx+3] = color[edges[e][2]]
  423 + self.line_data['a_linewidth'][idx+3] = size[edges[e][1]]
  424 +
  425 + self.edges[e*2] = [idx, idx+1, idx+3]
  426 + self.edges[e*2+1] = [idx, idx+2, idx+3]
  427 +
  428 + #Set the buffer object and update the shader programs.
  429 + self.program_e = gloo.Program(vs, fs)
  430 + #self.program_e['l_color'] = self.linecolor.astype(np.float32)
  431 + self.vbo_line = gloo.VertexBuffer(self.line_data)
  432 + self.index = gloo.IndexBuffer(self.edges)
  433 + self.program_e['u_size'] = 1
  434 + self.program_e['u_model'] = self.model
  435 + self.program_e['u_view'] = self.view
  436 + self.program_e['u_projection'] = self.projection
  437 + self.program_e.bind(self.vbo_line)
  438 +
  439 +
  440 + """
  441 + Helper function that generates the edges between the cluster in the layout.
  442 + Color is based on the cluster source/target color and transitions between the
  443 + two.
  444 + """
  445 + def gen_cluster_line_vbo(self, G):
  446 + #create a graph that stores the edges of between the clusters
  447 + self.G_cluster = nwt.gt.Graph(directed=False)
  448 + self.G_cluster.vertex_properties["pos"] = self.G_cluster.new_vertex_property("vector<double>", val=np.zeros((3,1), dtype=np.float32))
  449 + self.G_cluster.vertex_properties["RGBA"] = self.G_cluster.new_vertex_property("vector<double>", val=np.zeros((4,1), dtype=np.float32))
  450 + for v in range(len(self.cluster_pos)):
  451 + self.G_cluster.add_vertex()
  452 + self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(v)] = np.asarray(self.cluster_pos[v], dtype=np.float32)
  453 + self.G_cluster.edge_properties["weight"] = self.G_cluster.new_edge_property("int", val = 0)
  454 + #for each edge in the original graph, generate appropriate subgraph edges without repretiions
  455 + #i.e. controls the thichness of the edges in the subgraph view.
  456 + for e in G.edges():
  457 + #if the source and target cluster is not equal to each other
  458 + #add an inter subgraph edge.
  459 + if(G.vertex_properties["clusters"][e.source()] != G.vertex_properties["clusters"][e.target()]):
  460 + #temp_e.append([G.vertex_properties["clusters"][e.source()], G.vertex_properties["clusters"][e.target()]])
  461 + self.G_cluster.add_edge(self.G_cluster.vertex(G.vertex_properties["clusters"][e.source()]), \
  462 + self.G_cluster.vertex(G.vertex_properties["clusters"][e.target()]))
  463 + self.G_cluster.edge_properties["weight"][self.G_cluster.edge(self.G_cluster.vertex(G.vertex_properties["clusters"][e.source()]), \
  464 + self.G_cluster.vertex(G.vertex_properties["clusters"][e.target()]))] += 1
  465 + self.G_cluster.vertex_properties["RGBA"][self.G_cluster.vertex(G.vertex_properties["clusters"][e.source()])] \
  466 + = G.vertex_properties["RGBA"][e.source()]
  467 + self.G_cluster.vertex_properties["RGBA"][self.G_cluster.vertex(G.vertex_properties["clusters"][e.target()])] \
  468 + = G.vertex_properties["RGBA"][e.target()]
  469 +
  470 + self.cluster_line_data = np.zeros(self.G_cluster.num_edges()*4, dtype=[('a_position', np.float32, 3),
  471 + ('a_normal', np.float32, 2),
  472 + ('a_fg_color', np.float32, 4),
  473 + ('a_linewidth', np.float32, 1),
  474 + ])
  475 + self.cluster_edges = np.random.randint(size=(self.G_cluster.num_edges()*2, 3), low=0,
  476 + high=(G.num_edges()-1)).astype(np.uint32)
  477 +
  478 + edges = self.G_cluster.get_edges()
  479 + #size need to be changed to the size based on the current property map
  480 + size = nwt.Network.map_edges_to_range(self.G_cluster, [1.0, 0.5], 'weight').get_array()
  481 + color = self.G_cluster.vertex_properties["RGBA"].get_2d_array(range(4)).T
  482 + #generate the vertex buffer and the connections buffer.
  483 + for e in range(edges.shape[0]):
  484 + idx = int(4*edges[e][2])
  485 + p0 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][0])]
  486 + p1 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][1])]
  487 + d = np.subtract(p1, p0)
  488 + #d_norm = np.multiply(d, 1/np.sqrt(np.power(d[0],2) + np.power(d[1],2)))
  489 + d_norm = d[0:2]
  490 + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2))
  491 + norm = np.zeros((2,), dtype=np.float32)
  492 + norm[0] = d_norm[1]
  493 + norm[1] = d_norm[0]*-1
  494 + #print(np.sqrt(norm[0]*norm[0] + norm[1]*norm[1]))
  495 + #thickness = G.edge_properties["thickness"][e]
  496 + self.cluster_dict[int(edges[e][0]), int(edges[e][1])] = int(edges[e][2])
  497 + self.cluster_line_data['a_position'][idx] = p0
  498 + self.cluster_line_data['a_normal'][idx] = norm
  499 + self.cluster_line_data['a_fg_color'][idx] = color[edges[e][0]]
  500 + self.cluster_line_data['a_linewidth'][idx] = size[e]
  501 +
  502 + self.cluster_line_data['a_position'][idx+1] = p1
  503 + self.cluster_line_data['a_normal'][idx+1] = norm
  504 + self.cluster_line_data['a_fg_color'][idx+1] = color[edges[e][1]]
  505 + self.cluster_line_data['a_linewidth'][idx+1] = size[e]
  506 +
  507 + self.cluster_line_data['a_position'][idx+2] = p0
  508 + self.cluster_line_data['a_normal'][idx+2] = -norm
  509 + self.cluster_line_data['a_fg_color'][idx+2] = color[edges[e][0]]
  510 + self.cluster_line_data['a_linewidth'][idx+2] = size[e]
  511 +
  512 + self.cluster_line_data['a_position'][idx+3] = p1
  513 + self.cluster_line_data['a_normal'][idx+3] = -norm
  514 + self.cluster_line_data['a_fg_color'][idx+3] = color[edges[e][1]]
  515 + self.cluster_line_data['a_linewidth'][idx+3] = size[e]
  516 +
  517 + self.cluster_edges[e*2] = [idx, idx+1, idx+3]
  518 + self.cluster_edges[e*2+1] = [idx, idx+2, idx+3]
  519 +
  520 +
  521 + self.program_e_s = gloo.Program(vs_s, fs_s)
  522 + self.index_clusters_s = gloo.IndexBuffer(self.cluster_edges)
  523 + self.vbo_cluster_lines = gloo.VertexBuffer(self.cluster_line_data)
  524 +
  525 + self.program_e_s['u_size'] = 1
  526 + self.program_e_s['u_model'] = self.model
  527 + self.program_e_s['u_view'] = self.view
  528 + self.program_e_s['u_projection'] = self.projection
  529 + self.program_e_s.bind(self.vbo_cluster_lines)
  530 +
  531 +
  532 + """
  533 + Updates the vertex buffers based on the current position of the cluster.
  534 + Updates it's position.
  535 + """
  536 + def update_cluster_line_vbo(self):
  537 +
  538 + for v in range(len(self.cluster_pos)):
  539 + self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(v)] = np.asarray(self.cluster_pos[v], dtype=np.float32)
  540 + #OPTIMIZE HERE to update only one cluster at a time.
  541 + edges = self.G_cluster.get_edges()
  542 + #size need to be changed to the size based on the current property map
  543 + size = nwt.Network.map_edges_to_range(self.G_cluster, [1.0, 0.5], 'weight').get_array()
  544 + color = self.G_cluster.vertex_properties["RGBA"].get_2d_array(range(4)).T
  545 + for e in range(edges.shape[0]):
  546 + idx = int(4*edges[e][2])
  547 + p0 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][0])]
  548 + p1 = self.G_cluster.vertex_properties["pos"][self.G_cluster.vertex(edges[e][1])]
  549 + d = np.subtract(p1, p0)
  550 + #d_norm = np.multiply(d, 1/np.sqrt(np.power(d[0],2) + np.power(d[1],2)))
  551 + d_norm = d[0:2]
  552 + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2))
  553 + norm = np.zeros((2,), dtype=np.float32)
  554 + norm[0] = d_norm[1]
  555 + norm[1] = d_norm[0]*-1
  556 + #print(np.sqrt(norm[0]*norm[0] + norm[1]*norm[1]))
  557 + #thickness = G.edge_properties["thickness"][e]
  558 + self.cluster_dict[int(edges[e][0]), int(edges[e][1])] = int(edges[e][2])
  559 + self.cluster_line_data['a_position'][idx] = p0
  560 + self.cluster_line_data['a_normal'][idx] = norm
  561 + self.cluster_line_data['a_fg_color'][idx] = color[edges[e][0]]
  562 + self.cluster_line_data['a_linewidth'][idx] = size[e]
  563 +
  564 + self.cluster_line_data['a_position'][idx+1] = p1
  565 + self.cluster_line_data['a_normal'][idx+1] = norm
  566 + self.cluster_line_data['a_fg_color'][idx+1] = color[edges[e][1]]
  567 + self.cluster_line_data['a_linewidth'][idx+1] = size[e]
  568 +
  569 + self.cluster_line_data['a_position'][idx+2] = p0
  570 + self.cluster_line_data['a_normal'][idx+2] = -norm
  571 + self.cluster_line_data['a_fg_color'][idx+2] = color[edges[e][0]]
  572 + self.cluster_line_data['a_linewidth'][idx+2] = size[e]
  573 +
  574 + self.cluster_line_data['a_position'][idx+3] = p1
  575 + self.cluster_line_data['a_normal'][idx+3] = -norm
  576 + self.cluster_line_data['a_fg_color'][idx+3] = color[edges[e][1]]
  577 + self.cluster_line_data['a_linewidth'][idx+3] = size[e]
  578 +
  579 + self.program_e_s = gloo.Program(vs_s, fs_s)
  580 + self.index_clusters_s = gloo.IndexBuffer(self.cluster_edges)
  581 + self.vbo_cluster_lines = gloo.VertexBuffer(self.cluster_line_data)
  582 +
  583 + self.program_e_s['u_size'] = 1
  584 + self.program_e_s['u_model'] = self.model
  585 + self.program_e_s['u_view'] = self.view
  586 + self.program_e_s['u_projection'] = self.projection
  587 + self.program_e_s.bind(self.vbo_cluster_lines)
  588 +
  589 + """
  590 + Genererates a unique index for every vertex.
  591 + """
  592 + def gen_vertex_id(self, G):
  593 + self.color_dict = {}
  594 + base = [0, 0, 0, 255]
  595 + idx = 0
  596 + #colors = cm.get_cmap('Wistia', G.num_vertices()*2)
  597 + v_id = np.zeros((G.num_vertices(), 4), dtype=np.float32)
  598 + for v in G.vertices():
  599 + color = np.multiply(base, 1/255.0)
  600 + v_id[int(v)] = color
  601 + self.color_dict[tuple(color)] = int(v)
  602 + idx += 1
  603 + base = [int(idx/(255*255)), int((idx/255)%255), int(idx%255), 255]
  604 +
  605 + return(v_id)
  606 +
  607 + """
  608 + Generates a unique index for every cluster.
  609 + """
  610 + def gen_cluster_id(self, G):
  611 + self.cluster_dict = {}
  612 + base = [0, 0, 0, 255]
  613 + idx = 0
  614 + #colors = cm.get_cmap('Wistia', G.num_vertices()*2)
  615 + v_id = np.zeros((self.n_c, 4), dtype=np.float32)
  616 + for v in range(self.n_c):
  617 + color = np.multiply(base, 1/255.0)
  618 + v_id[int(v)] = color
  619 + self.cluster_dict[tuple(color)] = int(v)
  620 + idx += 1
  621 + base = [int(idx/(255*255)), int((idx/255)%255), int(idx%255), 255]
  622 +
  623 + return(v_id)
  624 +
  625 +
  626 + """
  627 + Generates the bounding box of the radial glyph.
  628 + """
  629 + def gen_cluster_coords(self, center, diameter):
  630 + radius = diameter/2.0
  631 + top = center[1]+radius
  632 + bottom = center[1]-radius
  633 + left = center[0]-radius
  634 + right = center[0]+radius
  635 +
  636 + positions = [[right, bottom, center[2]],
  637 + [right, top, center[2]],
  638 + [left, top, center[2]],
  639 + [left, bottom, center[2]]]
  640 +
  641 +
  642 + values = [[1.0, -1.0],
  643 + [1.0, 1.0,],
  644 + [-1.0, 1.0],
  645 + [-1.0, -1.0]]
  646 + return positions, values
  647 +
  648 +
  649 + """
  650 + Layout algorithm that expands the cluster based on the location of the of the clusters
  651 + """
  652 + def expand_based_on_clusters(self, G, n):
  653 + pos = G.vertex_properties["pos"].get_2d_array(range(3)).T
  654 + for p in range(pos.shape[0]):
  655 + pos[p][0] = pos[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0]
  656 + pos[p][1] = pos[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1]
  657 + pos[p][2] = pos[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2]
  658 + G.vertex_properties["pos"] = G.new_vertex_property("vector<double>", vals = pos)
  659 + for i in range(n):
  660 + index = 4*i
  661 + #generate the vertex filter for this cluster
  662 + num_v_in_cluster = len(np.argwhere(self.labels == i))
  663 + vfilt = np.zeros([G.num_vertices(), 1], dtype="bool")
  664 + vfilt[np.argwhere(self.labels == i)] = 1
  665 + vfilt_prop = G.new_vertex_property("bool", vals = vfilt)
  666 + G.set_vertex_filter(vfilt_prop)
  667 +
  668 + #get the filtered properties
  669 + g = nwt.gt.Graph(G, prune=True, directed=False)
  670 + positions = g.vertex_properties["pos"].get_2d_array(range(3)).T
  671 + position = np.sum(positions, 0)/num_v_in_cluster
  672 + p, v = self.gen_cluster_coords(position, np.sum(g.vertex_properties['degree'].get_array()))
  673 + self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32)
  674 + self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32)
  675 + G.clear_filters()
  676 + self.cluster_pos[i] = position
  677 + color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T
  678 + size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], 'degree').get_array()
  679 +
  680 + position = G.vertex_properties["pos"].get_2d_array(range(3)).T
  681 + #for p in range(position.shape[0]):
  682 + # position[p][0] = position[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0]
  683 + # position[p][1] = position[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1]
  684 + # position[p][2] = position[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2]
  685 + #G.vertex_properties["pos"] = G.new_vertex_property("vector<double>", vals = position)
  686 + edges = G.get_edges();
  687 + edges = edges[:, 0:2]
  688 + #width = nwt.Network.map_edges_to_range(G, [1*self.pixel_scale, 5*self.pixel_scale], 'volume').get_array()
  689 + #ecolor = G.edge_properties["RGBA"].get_2d_array(range(4)).T
  690 +
  691 + self.data = np.zeros(G.num_vertices(), dtype=[('a_position', np.float32, 3),
  692 + ('a_fg_color', np.float32, 4),
  693 + ('a_bg_color', np.float32, 4),
  694 + ('a_size', np.float32, 1),
  695 + ('a_linewidth', np.float32, 1),
  696 + ('a_unique_id', np.float32, 4),
  697 + ('a_selection', np.float32, 1),
  698 + ])
  699 +
  700 + #self.edges = edges.astype(np.uint32)
  701 + self.data['a_position'] = position
  702 + #fg color is the color of the ring
  703 + self.data['a_fg_color'] = 0, 0, 0, 1
  704 + self.data['a_bg_color'] = color
  705 + self.data['a_size'] = size
  706 + self.data['a_linewidth'] = 4.*self.pixel_scale
  707 + self.data['a_unique_id'] = self.gen_vertex_id(G)
  708 + self.data['a_selection'] = G.vertex_properties["selection"].get_array()
  709 + #self.data['a_graph_size'] = [bbu-bbl]
  710 +
  711 + self.program['u_graph_size'] = [bbu-bbl]
  712 + self.vbo = gloo.VertexBuffer(self.data)
  713 + self.gen_line_vbo(G)
  714 + if(self.subgraphs):
  715 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  716 + self.index_s = gloo.IndexBuffer(self.edges_s)
  717 + #self.index = gloo.IndexBuffer(self.edges)
  718 + self.program_e.bind(self.vbo_line)
  719 + self.program.bind(self.vbo)
  720 + if(self.subgraphs):
  721 + #self.program_e_s.bind(self.vbo_s)
  722 + self.program_s.bind(self.vbo_s)
  723 + print(self.view)
  724 + self.update()
  725 +
  726 +
  727 + """
  728 + Function that generates the clusters for an unclustered graph
  729 + These are to be represented by the arcs
  730 + """
  731 + def gen_clusters(self, G, bbl, bbu, n_c = None, edge_metric = 'volume', vertex_metric = 'degree'):
  732 +
  733 + #Generate the clusters
  734 + self.labels = nwt.Network.spectral_clustering(G,'length', n_clusters = n_c)
  735 + #self.labels = nwt.Network.spectral_clustering(G,'length')
  736 +
  737 + #Add clusters as a vertex property
  738 + G.vertex_properties["clusters"] = G.new_vertex_property("int", vals=self.labels)
  739 + num_clusters = len(np.unique(self.labels))
  740 + self.n_c = n_c
  741 +
  742 + #add colormap
  743 + G.vertex_properties["RGBA"] = nwt.Network.map_property_to_color(G, G.vertex_properties["clusters"])
  744 +
  745 + #generate an empty property set for the clusters.
  746 + self.clusters = np.zeros(num_clusters*4, dtype=[('a_position', np.float32, 3),
  747 + ('a_value', np.float32, 2),
  748 + ('a_bg_color', np.float32, 4),
  749 + ('a_cluster_color', np.float32, 4),
  750 + ('a_arc_length', np.float32, 1),
  751 + ('a_outer_arc_length', np.float32, 4),
  752 + ('a_unique_id', np.float32, 4),
  753 + ])
  754 + self.edges_s = np.random.randint(size=(num_clusters*2, 3), low=0,
  755 + high=4).astype(np.uint32)
  756 + #fill the foreground color as halo
  757 + #self.clusters['a_fg_color'] = 1., 1., 1., 0.0
  758 + #self.clusters['a_linewidth'] = 4.*self.pixel_scale
  759 +
  760 + G.vertex_properties["pos"] = nwt.gt.sfdp_layout(G, groups = G.vertex_properties["clusters"], pos = G.vertex_properties["pos"])
  761 + temp = [];
  762 + temp_pos = [];
  763 + #Find the global total of the metric.
  764 + global_metric = np.sum(G.edge_properties[edge_metric].get_array(), 0)
  765 + unique_color = self.gen_cluster_id(G)
  766 +
  767 + #generate the property values for every cluster
  768 + for i in range(num_clusters):
  769 + idx = 4*i
  770 + #generate the vertex filter for this cluster
  771 + num_v_in_cluster = len(np.argwhere(self.labels == i))
  772 + vfilt = np.zeros([G.num_vertices(), 1], dtype="bool")
  773 + vfilt[np.argwhere(self.labels == i)] = 1
  774 + vfilt_prop = G.new_vertex_property("bool", vals = vfilt)
  775 + G.set_vertex_filter(vfilt_prop)
  776 +
  777 + #get the filtered properties
  778 + g = nwt.gt.Graph(G, prune=True, directed=False)
  779 + positions = g.vertex_properties["pos"].get_2d_array(range(3)).T
  780 + position = np.sum(positions, 0)/num_v_in_cluster
  781 +
  782 + #calculate the arclength for the global statistic
  783 + arc_length = np.sum(g.edge_properties[edge_metric].get_array(), 0)/global_metric*np.pi*2
  784 + arc_length_vertex = np.ones((4,1), dtype = np.float32)
  785 + array = g.vertex_properties[vertex_metric].get_array()
  786 +
  787 + #calculate metric distribution and turn it into arc_lengths
  788 + t_vertex_metric = np.sum(array)
  789 + arc_length_vertex[0] = np.sum(array < 2)/t_vertex_metric
  790 + arc_length_vertex[1] = np.sum(array == 2)/t_vertex_metric
  791 + arc_length_vertex[2] = np.sum(array == 3)/t_vertex_metric
  792 + arc_length_vertex[3] = np.sum(array > 3)/t_vertex_metric
  793 +
  794 + #arc_length_vertex = np.asarray(arc_length_vertex, dtype = np.float32)
  795 + #arc_length_vertex = (max(arc_length_vertex) - min(arc_length_vertex)) \
  796 + #* (arc_length_vertex- min(arc_length_vertex))
  797 + for j in range(len(arc_length_vertex)):
  798 + if j != 0:
  799 + arc_length_vertex[j] += arc_length_vertex[j-1]
  800 + print("arc_length before ", arc_length_vertex, " and sum to ", sum(arc_length_vertex))
  801 + arc_length_vertex = np.asarray(arc_length_vertex, dtype = np.float32)
  802 + arc_length_vertex = (np.pi - -np.pi)/(max(arc_length_vertex) - min(arc_length_vertex)) \
  803 + * (arc_length_vertex- min(arc_length_vertex)) + (-np.pi)
  804 + print(arc_length_vertex)
  805 + #print(arc_length)
  806 +
  807 +
  808 + temp_pos.append(position)
  809 +
  810 + #generate the color for every vertex,
  811 + #since all vertices belong to the same cluster we can check only
  812 + #one vertex for the cluster color.
  813 +
  814 + self.clusters['a_cluster_color'][idx:idx+4] = g.vertex_properties["RGBA"][g.vertex(0)]
  815 + self.clusters['a_bg_color'][idx:idx+4] = [0.1, 0.1, 0.1, 1.0]
  816 + self.clusters['a_unique_id'][idx:idx+4] = unique_color[i]
  817 +
  818 + #The arc-length representing one global metric.
  819 + self.clusters['a_arc_length'][idx:idx+4] = arc_length
  820 + self.clusters['a_outer_arc_length'][idx:idx+4] = arc_length_vertex[:].T
  821 +
  822 + temp.append(np.sum(g.vertex_properties['degree'].get_array()))
  823 + G.clear_filters()
  824 + print(self.clusters['a_outer_arc_length'])
  825 + maximum = max(temp)
  826 + minimum = min(temp)
  827 + if len(temp) > 1:
  828 + temp = ((temp-minimum)/(maximum-minimum)*(60*self.pixel_scale)+20*self.pixel_scale)
  829 + else:
  830 + temp = [60*self.pixel_scale]
  831 + for i in range(num_clusters):
  832 + index = i*4
  833 + index_t = i*2
  834 + p, v = self.gen_cluster_coords(temp_pos[i], temp[i]*2.0)
  835 + self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32)
  836 + self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32)
  837 +
  838 + self.edges_s[index_t] = [index, index+1, index+2]
  839 + self.edges_s[index_t+1] = [index, index+2, index+3]
  840 + #self.edges_s[i][0:4] = np.asarray(range(index, index+4), dtype=np.uint32)
  841 + #self.edges_s[i]
  842 + self.cluster_pos = temp_pos
  843 + #self.expand_based_on_clusters(G, self.n_c)
  844 + G.clear_filters()
  845 +# self.edges_s[1][0:4] = np.asarray(range(0, 0+4), dtype=np.uint32)
  846 +# self.edges_s[1][4] = 0
  847 +# self.edges_s[1][5] = 0+2
  848 +#
  849 +# self.edges_s[0][0:4] = np.asarray(range(index, index+4), dtype=np.uint32)
  850 +# self.edges_s[0][4] = index
  851 +# self.edges_s[0][5] = index+2
  852 + #self.clusters['a_size'] = temp
  853 + self.gen_cluster_line_vbo(G)
  854 + self.program_s['u_graph_size'] = [bbu-bbl]
  855 + #if len(temp_e) > 0:
  856 + # self.edges_s = np.unique(np.asarray(temp_e, np.uint32), axis=0)
  857 + #else:
  858 + # self.edges_s = []
  859 + #print(self.edges_s)
  860 +
  861 +
  862 + """
  863 + Function that expands that generates the layout and updates the buffer
  864 +
  865 + """
  866 + def expand_clusters(self, G, n_c):
  867 + self.expand_based_on_clusters(G, n_c)
  868 + self.gen_cluster_line_vbo(G)
  869 + if(self.subgraphs):
  870 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  871 + self.index_s = gloo.IndexBuffer(self.edges_s)
  872 + self.program_e.bind(self.vbo_line)
  873 + self.program.bind(self.vbo)
  874 + if(self.subgraphs):
  875 + self.program_s.bind(self.vbo_s)
  876 + print(self.view)
  877 + self.update()
  878 +
  879 + """
  880 + Loads the data G and generates all the buffers necessary as well as performs
  881 + spectral clustering on the graph passed if the subgraph is set to true.
  882 + """
  883 + def set_data(self, G, bbl, bbu, subgraph=True):
  884 + print("Setting data")
  885 + self.G = G
  886 + self.bbl = bbl
  887 + self.bbu = bbu
  888 + clear(color=True, depth=True)
  889 + self.subgraphs = True
  890 + self.current_color = "clusters"
  891 + if(subgraph==True):
  892 + self.gen_clusters(G, bbl, bbu, n_c=19)
  893 +
  894 + #color based on clusters
  895 + self.color_edges(G)
  896 +
  897 + color = G.vertex_properties["RGBA"].get_2d_array(range(4)).T
  898 + size = nwt.Network.map_vertices_to_range(G, [30*self.pixel_scale, 8*self.pixel_scale], 'degree').get_array()
  899 +
  900 + position = G.vertex_properties["pos"].get_2d_array(range(3)).T
  901 + #for p in range(position.shape[0]):
  902 + # position[p][0] = position[p][0] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][0]
  903 + # position[p][1] = position[p][1] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][1]
  904 + # position[p][2] = position[p][2] + self.clusters["a_position"][G.vertex_properties["clusters"][G.vertex(p)]][2]
  905 + #G.vertex_properties["pos"] = G.new_vertex_property("vector<double>", vals = position)
  906 + edges = G.get_edges();
  907 + edges = edges[:, 0:2]
  908 + #width = nwt.Network.map_edges_to_range(G, [1*self.pixel_scale, 5*self.pixel_scale], 'volume').get_array()
  909 + #ecolor = G.edge_properties["RGBA"].get_2d_array(range(4)).T
  910 +
  911 + self.data = np.zeros(G.num_vertices(), dtype=[('a_position', np.float32, 3),
  912 + ('a_fg_color', np.float32, 4),
  913 + ('a_bg_color', np.float32, 4),
  914 + ('a_size', np.float32, 1),
  915 + ('a_linewidth', np.float32, 1),
  916 + ('a_unique_id', np.float32, 4),
  917 + ('a_selection', np.float32, 1),
  918 + ])
  919 +
  920 + #self.edges = edges.astype(np.uint32)
  921 + self.data['a_position'] = position
  922 + #fg color is the color of the ring
  923 + self.data['a_fg_color'] = 0, 0, 0, 1
  924 + self.data['a_bg_color'] = color
  925 + self.data['a_size'] = size
  926 + self.data['a_linewidth'] = 4.*self.pixel_scale
  927 + self.data['a_unique_id'] = self.gen_vertex_id(G)
  928 + self.data['a_selection'] = G.vertex_properties["selection"].get_array()
  929 + #self.data['a_graph_size'] = [bbu-bbl]
  930 +
  931 + self.program['u_graph_size'] = [bbu-bbl]
  932 +
  933 + self.vbo = gloo.VertexBuffer(self.data)
  934 + self.gen_line_vbo(G)
  935 + #self.gen_cylinder_vbo(G)
  936 + if(self.subgraphs):
  937 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  938 + self.index_s = gloo.IndexBuffer(self.edges_s)
  939 + #self.index = gloo.IndexBuffer(self.edges)
  940 + self.program_e.bind(self.vbo_line)
  941 + self.program.bind(self.vbo)
  942 + if(self.subgraphs):
  943 + #self.program_e_s.bind(self.vbo_s)
  944 + self.program_s.bind(self.vbo_s)
  945 + print(self.view)
  946 + self.update()
  947 +
  948 + """
  949 + Function that changes and redraws the buffer during a resize event.
  950 + """
  951 + def on_resize(self, event):
  952 + set_viewport(0, 0, *event.physical_size)
  953 + self.fbo = gloo.FrameBuffer(color=gloo.RenderBuffer(self.size[::-1]), depth=gloo.RenderBuffer(self.size[::-1]))
  954 +
  955 +
  956 + """
  957 + Overloaded function that is called during every self.update() call
  958 + """
  959 + def on_draw(self, event):
  960 + clear(color='white', depth=True)
  961 + self.program_e.draw('triangles', indices=self.index)
  962 + self.program.draw('points')
  963 + #self.program_e.draw('lines')
  964 + if(self.subgraphs):
  965 + self.program_e_s.draw('triangles', indices=self.index_clusters_s)
  966 + self.program_s.draw('triangles', indices=self.index_s)
  967 +
  968 + """
  969 + Function performed during a mouse click (either right or left)
  970 + gets the unique id of the object drawn underneath the cursor
  971 + handles the cases depending on whether the click happened to a cluster
  972 + or a vertex. Edges are not interactable (yet)
  973 + """
  974 + def get_clicked_id(self, event, clusters = False):
  975 + #Get the framebuffer coordinates of the click
  976 + coord = self.transforms.get_transform('canvas', 'framebuffer').map(event.pos)
  977 +
  978 + #get the framebuffer where each element is rendered as a unique color
  979 + size = self.size;
  980 + self.fbo = gloo.FrameBuffer(color=gloo.RenderBuffer(size[::-1]), depth=gloo.RenderBuffer(size[::-1]))
  981 + buff = gloo.read_pixels((0,0,self.physical_size[0], self.physical_size[1]))
  982 + #imsave("test_ori.png", buff)
  983 + self.fbo.activate()
  984 + if clusters == False:
  985 + self.program['u_picking'] = True
  986 + clear(color='white', depth=True)
  987 + self.program.draw('points')
  988 + buff = gloo.read_pixels((0,0,self.physical_size[0], self.physical_size[1]))
  989 + #imsave("test.png", buff)
  990 +
  991 + #return to the original state
  992 + self.fbo.deactivate()
  993 + self.program['u_picking'] = False
  994 + else:
  995 + self.program_s['u_picking'] = True
  996 + clear(color='white', depth=True)
  997 + self.program_s.draw('triangles', indices=self.index_s)
  998 + buff = gloo.read_pixels((0,0,self.physical_size[0], self.physical_size[1]))
  999 + #imsave("test.png", buff)
  1000 +
  1001 + #return to the original state
  1002 + self.fbo.deactivate()
  1003 + self.program_s['u_picking'] = False
  1004 +
  1005 + #print(buff[self.physical_size[1]-int(coord[1]), int(coord[0])])
  1006 +
  1007 + #Get the color under the click.
  1008 + #Keep in mind that the buff is y, x
  1009 + #And 0,0 is in the top RIGHT corner.
  1010 + #IGNORE THE DOCUMENTATION
  1011 + color = np.multiply(buff[self.physical_size[1]-int(coord[1]), int(coord[0])], 1/255.0)
  1012 + #if (tuple(color) not in self.color_dict):
  1013 + # print("clicked on nothing")
  1014 + #else:
  1015 + # print(self.color_dict[tuple(color)])
  1016 +
  1017 + #reset the original buffer
  1018 + self.update()
  1019 +
  1020 + #Return the element under the click.
  1021 + if clusters == False:
  1022 + if(tuple(color) not in self.color_dict):
  1023 + return None
  1024 + else:
  1025 + return self.color_dict[tuple(color)]
  1026 + else:
  1027 + if(tuple(color) not in self.cluster_dict):
  1028 + return None
  1029 + else:
  1030 + return self.cluster_dict[tuple(color)]
  1031 +
  1032 +
  1033 + """
  1034 + Top level handle-mouse presee event for either left or right click
  1035 + """
  1036 + def on_mouse_press(self, event):
  1037 +
  1038 + def update_view():
  1039 + self.location = event.pos
  1040 + self.program['u_view'] = self.view
  1041 + self.program_e['u_view'] = self.view
  1042 + self.program_s['u_view'] = self.view
  1043 + self.program_e_s['u_view'] = self.view
  1044 + self.down = True
  1045 +
  1046 +
  1047 +# if(event.button == 2):
  1048 +## menu = QtWidgets.QMenu(self.parent)
  1049 +## NS = menu.addAction('Node Size')
  1050 +## NC = menu.addAction('Node Color')
  1051 +## action = menu.exec_(self.parent.globalPos())
  1052 +## if action == NS:
  1053 +# print("right_click")
  1054 +# #if menu.exec_(event.globalPos()):
  1055 +# # print(item.text())
  1056 + if(event.button == 1):
  1057 + if(self.view[0][0] > 0.0010):
  1058 + c_id = self.get_clicked_id(event)
  1059 + if(c_id != None):
  1060 + self.original_point = G.vertex_properties["pos"][G.vertex(c_id)]
  1061 + self.location = event.pos
  1062 + self.moving = True
  1063 + self.down = True
  1064 + self.c_id = [c_id]
  1065 + else:
  1066 + update_view()
  1067 + #print("Clicked on:", event.pos)
  1068 + else:
  1069 + c_id = self.get_clicked_id(event, True)
  1070 + print(c_id)
  1071 + if(c_id != None):
  1072 + self.original_point = self.cluster_pos[c_id]
  1073 + self.location = event.pos
  1074 + self.moving = True
  1075 + self.down = True
  1076 + self.c_id = [c_id]
  1077 + self.moving_cluster = True
  1078 + else:
  1079 + update_view()
  1080 +
  1081 +
  1082 + """
  1083 + Handles the double click event that it responsible for path selection.
  1084 + Generates paths our of consecutive paths out of the selected vertices.
  1085 + """
  1086 + def on_mouse_double_click(self, event):
  1087 + def update_vbo(self):
  1088 + self.vbo = gloo.VertexBuffer(self.data)
  1089 + self.program.bind(self.vbo)
  1090 + self.update()
  1091 +
  1092 + def add_to_path(self, source, target):
  1093 + vl, el = nwt.gt.graph_tool.topology.shortest_path(G, G.vertex(source), G.vertex(target), weights=G.edge_properties["av_radius"])
  1094 + for v in vl:
  1095 + if(int(v) not in self.path):
  1096 + G.vertex_properties["selection"][v] = 2.0
  1097 + self.data['a_selection'][int(v)] = 2.0
  1098 + if(int(v) not in self.full_path):
  1099 + self.full_path.append(int(v))
  1100 +
  1101 + if (event.button == 1):
  1102 + if(self.view[0][0] > 0.0010):
  1103 + c_id = self.get_clicked_id(event)
  1104 + if(c_id != None):
  1105 + #check whether this is the first node to be selected
  1106 + if(self.pathing == False):
  1107 + #if it is, select that node and turn the pathing variable on.
  1108 + G.vertex_properties["selection"][G.vertex(c_id)] = 1.0
  1109 + self.pathing = True
  1110 + if(c_id not in self.path):
  1111 + self.path.append(c_id)
  1112 + self.data['a_selection'][c_id] = 1.0
  1113 + update_vbo(self)
  1114 + print("I turned on the first node")
  1115 + else:
  1116 + if(G.vertex_properties["selection"][G.vertex(c_id)] == 1.0):
  1117 + G.vertex_properties["selection"][G.vertex(c_id)] = 0.0
  1118 + self.path.remove(c_id)
  1119 + self.data['a_selection'][c_id] = 0.0
  1120 + update_vbo(self)
  1121 + print("I turned off a node")
  1122 + elif(G.vertex_properties["selection"][G.vertex(c_id)] == 0.0):
  1123 + G.vertex_properties["selection"][G.vertex(c_id)] = 1.0
  1124 + if(c_id not in self.path):
  1125 + self.path.append(c_id)
  1126 + self.data['a_selection'][c_id] = 1.0
  1127 + update_vbo(self)
  1128 + print("I turned on a node")
  1129 + if(len(self.path) >= 2):
  1130 + for i in range(len(self.path)-1):
  1131 + add_to_path(self, self.path[i], self.path[i+1])
  1132 + update_vbo(self)
  1133 + #THIS IS WHERE I LEFT IT OFF.
  1134 + if(np.sum(G.vertex_properties["selection"].get_array()) == 0):
  1135 + self.pathing = False
  1136 +
  1137 +
  1138 +
  1139 +# elif(np.sum(self.G.vertex_properties["selection"].get_array()) :
  1140 +# self.G.vertex_properties["selection"][self.G.vertex(c_id)] == False
  1141 + print("clicked on: ", c_id, " ", self.path)
  1142 +
  1143 + """
  1144 + Resets the variables that are used during the pressdown and move events
  1145 + """
  1146 + def on_mouse_release(self, event):
  1147 + self.down = False
  1148 + self.moving = False
  1149 + self.moving_cluster = False
  1150 + self.c_id = []
  1151 + #self.location = event.pos
  1152 + #print("Clicked off:", event.pos)
  1153 +
  1154 + """
  1155 + used during the drag evern to update the position of the clusters
  1156 + """
  1157 + def update_cluster_position(self, G, pos, offset, c_id):
  1158 + v_pos = G.vertex_properties["pos"].get_2d_array(range(3)).T
  1159 + vertices = np.argwhere(self.labels == c_id)
  1160 + for v in range(vertices.shape[0]):
  1161 + idx = vertices[v][0]
  1162 + v_pos[idx][0] = v_pos[idx][0] + offset[0]
  1163 + v_pos[idx][1] = v_pos[idx][1] + offset[1]
  1164 + v_pos[idx][2] = v_pos[idx][2] + offset[2]
  1165 + self.data['a_position'][idx] = np.asarray([v_pos[idx][0], v_pos[idx][1], v_pos[idx][2]], dtype = np.float32)
  1166 + #update the edge data by finding all edges connected to the vertex
  1167 + vtx = self.G.vertex(idx)
  1168 + for e in vtx.all_edges():
  1169 + d = np.subtract(G.vertex_properties["pos"][e.source()], G.vertex_properties["pos"][e.target()])
  1170 + d_norm = d[0:2]
  1171 + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2))
  1172 + norm = np.zeros((2,), dtype=np.float32)
  1173 + norm[0] = d_norm[1]
  1174 + norm[1] = d_norm[0]*-1
  1175 + if (int(e.source()), int(e.target())) in self.edge_dict.keys():
  1176 + index = int(self.edge_dict[int(e.source()), int(e.target())])
  1177 + if vtx == int(e.source()):
  1178 + self.line_data['a_position'][index*4] = v_pos[idx]
  1179 + self.line_data['a_position'][index*4+2] = v_pos[idx]
  1180 + self.line_data['a_normal'][index*4] = norm
  1181 + self.line_data['a_normal'][index*4+2] = -norm
  1182 + self.line_data['a_normal'][index*4+1] = norm
  1183 + self.line_data['a_normal'][index*4+3] = -norm
  1184 + elif vtx == int(e.target()):
  1185 + self.line_data['a_position'][index*4+1] = v_pos[idx]
  1186 + self.line_data['a_position'][index*4+3] = v_pos[idx]
  1187 + self.line_data['a_normal'][index*4] = norm
  1188 + self.line_data['a_normal'][index*4+2] = -norm
  1189 + self.line_data['a_normal'][index*4+1] = norm
  1190 + self.line_data['a_normal'][index*4+3] = -norm
  1191 + else:
  1192 + index = int(self.edge_dict[int(e.target()), int(e.source())])
  1193 + if vtx == int(e.target()):
  1194 + self.line_data['a_position'][index*4] = v_pos[idx]
  1195 + self.line_data['a_position'][index*4+2] = v_pos[idx]
  1196 + self.line_data['a_normal'][index*4] = norm
  1197 + self.line_data['a_normal'][index*4+2] = -norm
  1198 + self.line_data['a_normal'][index*4+1] = norm
  1199 + self.line_data['a_normal'][index*4+3] = -norm
  1200 + elif vtx == int(e.source()):
  1201 + self.line_data['a_position'][index*4+1] = v_pos[idx]
  1202 + self.line_data['a_position'][index*4+3] = v_pos[idx]
  1203 + self.line_data['a_normal'][index*4] = norm
  1204 + self.line_data['a_normal'][index*4+2] = -norm
  1205 + self.line_data['a_normal'][index*4+1] = norm
  1206 + self.line_data['a_normal'][index*4+3] = -norm
  1207 +
  1208 +
  1209 + G.vertex_properties["pos"] = G.new_vertex_property("vector<double>", vals = v_pos)
  1210 + index = 4*c_id
  1211 + #generate the vertex filter for this cluster
  1212 + vfilt = np.zeros([G.num_vertices(), 1], dtype="bool")
  1213 + vfilt[np.argwhere(self.labels == c_id)] = 1
  1214 + vfilt_prop = G.new_vertex_property("bool", vals = vfilt)
  1215 + G.set_vertex_filter(vfilt_prop)
  1216 +
  1217 + #get the filtered properties
  1218 + g = nwt.gt.Graph(G, prune=True, directed=False)
  1219 + p, v = self.gen_cluster_coords(pos, np.sum(g.vertex_properties['degree'].get_array()))
  1220 + self.clusters['a_position'][index:index+4] = np.asarray(p, dtype=np.float32)
  1221 + self.clusters['a_value'][index:index+4] = np.asarray(v, dtype=np.float32)
  1222 + G.clear_filters()
  1223 + self.cluster_pos[c_id] = pos
  1224 + self.original_point = pos
  1225 +
  1226 +
  1227 + """
  1228 + function that handles the mouse move event in a way that depends on a set
  1229 + of variables: state of the mouse button, the type of object selected and
  1230 + the number of objects.
  1231 + """
  1232 + def on_mouse_move(self, event):
  1233 + if(self.down == True):
  1234 + if(self.moving == True and self.moving_cluster == False):
  1235 + if(len(self.c_id) < 2):
  1236 + #Project into GLSpace and get before and after move coordinates
  1237 + coord = self.transforms.get_transform('canvas', 'render').map(self.location)[:2]
  1238 + coord2 = self.transforms.get_transform('canvas', 'render').map(event.pos)[:2]
  1239 + cur_pos = G.vertex_properties["pos"][G.vertex(self.c_id[0])]
  1240 + #print(cur_pos, " Before")
  1241 +
  1242 + #Adjust the position of the node based on the current view matrix.
  1243 + cur_pos[0] = cur_pos[0] - (coord[0]-coord2[0])/self.view[0][0]
  1244 + cur_pos[1] = cur_pos[1] - (coord[1]-coord2[1])/self.view[0][0]
  1245 +
  1246 + #print(cur_pos, " After")
  1247 + #Upload the changed data.
  1248 + G.vertex_properties["pos"][G.vertex(self.c_id[0])] = cur_pos
  1249 + self.data['a_position'][self.c_id[0]] = cur_pos
  1250 +
  1251 + #update the edge data by finding all edges connected to the vertex
  1252 + v = self.G.vertex(self.c_id[0])
  1253 + for e in v.all_edges():
  1254 + d = np.subtract(G.vertex_properties["pos"][e.source()], G.vertex_properties["pos"][e.target()])
  1255 + d_norm = d[0:2]
  1256 + d_norm = d_norm / np.sqrt(np.power(d_norm[0],2) + np.power(d_norm[1],2))
  1257 + norm = np.zeros((2,), dtype=np.float32)
  1258 + norm[0] = d_norm[1]
  1259 + norm[1] = d_norm[0]*-1
  1260 + if (int(e.source()), int(e.target())) in self.edge_dict.keys():
  1261 + idx = int(self.edge_dict[int(e.source()), int(e.target())])
  1262 + if self.c_id[0] == int(e.source()):
  1263 + self.line_data['a_position'][idx*4] = cur_pos
  1264 + self.line_data['a_position'][idx*4+2] = cur_pos
  1265 + self.line_data['a_normal'][idx*4] = norm
  1266 + self.line_data['a_normal'][idx*4+2] = -norm
  1267 + self.line_data['a_normal'][idx*4+1] = norm
  1268 + self.line_data['a_normal'][idx*4+3] = -norm
  1269 + elif self.c_id[0] == int(e.target()):
  1270 + self.line_data['a_position'][idx*4+1] = cur_pos
  1271 + self.line_data['a_position'][idx*4+3] = cur_pos
  1272 + self.line_data['a_normal'][idx*4] = norm
  1273 + self.line_data['a_normal'][idx*4+2] = -norm
  1274 + self.line_data['a_normal'][idx*4+1] = norm
  1275 + self.line_data['a_normal'][idx*4+3] = -norm
  1276 + else:
  1277 + idx = int(self.edge_dict[int(e.target()), int(e.source())])
  1278 + if self.c_id[0] == int(e.target()):
  1279 + self.line_data['a_position'][idx*4] = cur_pos
  1280 + self.line_data['a_position'][idx*4+2] = cur_pos
  1281 + self.line_data['a_normal'][idx*4] = norm
  1282 + self.line_data['a_normal'][idx*4+2] = -norm
  1283 + self.line_data['a_normal'][idx*4+1] = norm
  1284 + self.line_data['a_normal'][idx*4+3] = -norm
  1285 + elif self.c_id[0] == int(e.source()):
  1286 + self.line_data['a_position'][idx*4+1] = cur_pos
  1287 + self.line_data['a_position'][idx*4+3] = cur_pos
  1288 + self.line_data['a_normal'][idx*4] = norm
  1289 + self.line_data['a_normal'][idx*4+2] = -norm
  1290 + self.line_data['a_normal'][idx*4+1] = norm
  1291 + self.line_data['a_normal'][idx*4+3] = -norm
  1292 + #self.line_data['a_position'][self.c_id[0]] =
  1293 + self.vbo = gloo.VertexBuffer(self.data)
  1294 + self.vbo_line = gloo.VertexBuffer(self.line_data)
  1295 + #Bind the buffer and redraw.
  1296 + self.program.bind(self.vbo)
  1297 + self.program_e.bind(self.vbo_line)
  1298 + #self.program.draw('points')
  1299 + self.location = event.pos
  1300 + self.update()
  1301 + elif(self.moving == True and self.moving_cluster == True):
  1302 + if(len(self.c_id) < 2):
  1303 + #Project into GLSpace and get before and after move coordinates
  1304 + coord = self.transforms.get_transform('canvas', 'render').map(self.location)[:2]
  1305 + coord2 = self.transforms.get_transform('canvas', 'render').map(event.pos)[:2]
  1306 + cur_pos = np.zeros(self.cluster_pos[self.c_id[0]].shape, dtype = np.float32)
  1307 + offset = np.zeros(self.cluster_pos[self.c_id[0]].shape, dtype = np.float32)
  1308 + cur_pos[0] = self.cluster_pos[self.c_id[0]][0]
  1309 + cur_pos[1] = self.cluster_pos[self.c_id[0]][1]
  1310 + cur_pos[2] = self.cluster_pos[self.c_id[0]][2]
  1311 + offset[0] = self.cluster_pos[self.c_id[0]][0]
  1312 + offset[1] = self.cluster_pos[self.c_id[0]][1]
  1313 + offset[2] = self.cluster_pos[self.c_id[0]][2]
  1314 +# ofset = self.cluster_pos[self.c_id[0]]
  1315 +
  1316 + #Adjust the position of the node based on the current view matrix.
  1317 + offset[0] = self.original_point[0] - cur_pos[0] - (coord[0]-coord2[0])/self.view[0][0]
  1318 + offset[1] = self.original_point[1] - cur_pos[1] - (coord[1]-coord2[1])/self.view[0][0]
  1319 + cur_pos[0] = cur_pos[0] - (coord[0]-coord2[0])/self.view[0][0]
  1320 + cur_pos[1] = cur_pos[1] - (coord[1]-coord2[1])/self.view[0][0]
  1321 +
  1322 + self.update_cluster_position(G, cur_pos, offset, self.c_id[0])
  1323 + #self.original_point = cur_pos
  1324 + self.vbo = gloo.VertexBuffer(self.data)
  1325 + self.vbo_line = gloo.VertexBuffer(self.line_data)
  1326 + #Bind the buffer and redraw.
  1327 + self.program.bind(self.vbo)
  1328 + self.program_e.bind(self.vbo_line)
  1329 + #self.program.draw('points')
  1330 + self.location = event.pos
  1331 + if(self.subgraphs):
  1332 + self.vbo_s = gloo.VertexBuffer(self.clusters)
  1333 + self.program_s.bind(self.vbo_s)
  1334 + self.update_cluster_line_vbo()
  1335 + self.update()
  1336 +
  1337 +
  1338 + else:
  1339 + #print("Mouse at:", event.pos)
  1340 + #new_model = np.eye(4, dtype=np.float32)
  1341 + coord = self.transforms.get_transform('canvas', 'render').map(self.location)[:2]
  1342 + coord2 = self.transforms.get_transform('canvas', 'render').map(event.pos)[:2]
  1343 + self.translate[0] += (coord[0]-coord2[0])/self.view[0][0]
  1344 + self.translate[1] += (coord[1]-coord2[1])/self.view[1][1]
  1345 + #self.view[3][0] = self.view[3][0]-(self.location[0]-event.pos[0])/10000.0
  1346 + #self.view[3][1] = self.view[3][1]+(self.location[1]-event.pos[1])/10000.0
  1347 +
  1348 + self.view = np.matmul(translate((self.translate[0], self.translate[1], 0)), scale((self.scale[0], self.scale[1], 0)))
  1349 +
  1350 + self.program['u_view'] = self.view
  1351 + self.program_e['u_view'] = self.view
  1352 + self.program_s['u_view'] = self.view
  1353 + self.program_e_s['u_view'] = self.view
  1354 + self.location = event.pos
  1355 + self.update()
  1356 +
  1357 + """
  1358 + Handles the mouse wheel zoom event.
  1359 + """
  1360 + def on_mouse_wheel(self, event):
  1361 +
  1362 + #print(self.view)
  1363 + #TO_DO IMPLEMENT ZOOM TO CURSOR
  1364 + #self.view[3][0] = self.view[3][0]-event.pos[0]/10000.0
  1365 + #self.view[3][1] = self.view[3][1]-event.pos[1]/10000.0
  1366 + #print(self.scale[0] , self.scale[0]*event.delta[1]*0.05)
  1367 + self.scale[0] = self.scale[0] + self.scale[0]*event.delta[1]*0.05
  1368 + self.scale[1] = self.scale[1] + self.scale[1]*event.delta[1]*0.05
  1369 +
  1370 + self.view = np.matmul(translate((self.translate[0], self.translate[1], 0)),
  1371 + scale((self.scale[0], self.scale[1], 0)))
  1372 +
  1373 + #self.view[0][0] = self.view[0][0]+self.view[0][0]*event.delta[1]*0.05
  1374 + #self.view[1][1] = self.view[1][1]+self.view[1][1]*event.delta[1]*0.05
  1375 + #print(self.view[0][0], " ",self.view[1][1])
  1376 + #print(self.view)
  1377 + self.program['u_view'] = self.view
  1378 + self.program_e['u_view'] = self.view
  1379 + self.program_s['u_view'] = self.view
  1380 + self.program_e_s['u_view'] = self.view
  1381 + #print(event.delta[1])
  1382 + self.update()
... ...
GraphWidget.py 0 → 100644
  1 +++ a/GraphWidget.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:42:19 2019
  5 +
  6 +@author: pavel
  7 +"""
  8 +
  9 +from GraphCanvas import GraphCanvas
  10 +from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
  11 +import network_dep as nwt
  12 +
  13 +
  14 +"""
  15 +Initializes the entire QTlayout and sets the mouse press events.
  16 +These are connected to the slots such that each is processes by this class
  17 +and the class it wraps.
  18 +"""
  19 +class GraphWidget(QtGui.QWidget):
  20 + def __init__(self):
  21 + super(GraphWidget, self).__init__()
  22 + box = QtGui.QVBoxLayout(self)
  23 + self.resize(500,500)
  24 + self.setLayout(box)
  25 + self.canvas = GraphCanvas()
  26 + #self.canvas.create_native()
  27 + box.addWidget(self.canvas.native)
  28 + self.color = True
  29 + self.use_3D = False
  30 + self.camera = [0,0,0]
  31 +
  32 + self.canvas.events.mouse_press.connect(self.on_mouse_press)
  33 + self.canvas.events.mouse_release.connect(self.on_mouse_release)
  34 + self.canvas.events.mouse_move.connect(self.on_mouse_move)
  35 + self.canvas.events.mouse_double_click.connect(self.on_mouse_double_click)
  36 +
  37 + #self.show()
  38 +
  39 + """
  40 + Handles the right click mouse event at the QWidget level.
  41 + Depending on where the mouse button in clicked and the current zoom level,
  42 + the function gets the unique id of the node, cluster or background under the
  43 + cursor and opens a context menu based on the ID.
  44 +
  45 + The context menu level actions are also coded in this section of the code.
  46 + """
  47 + def on_mouse_press(self, event):
  48 + if event.button == 2:
  49 + if self.canvas.view[0][0] >= 0.0010:
  50 + c_id = self.canvas.get_clicked_id(event)
  51 + else:
  52 + c_id = self.canvas.get_clicked_id(event, clusters=True)
  53 + #right click
  54 + if(c_id == None):
  55 + menu = QtWidgets.QMenu(self)
  56 + NS = menu.addAction('Node Size')
  57 +
  58 + #tmp = menu.addAction('temp')
  59 + tmp = menu.addMenu('Node Color')
  60 + vertex_props = self.canvas.G.vertex_properties.keys()
  61 + vertex_actions = []
  62 + for i in range(len(vertex_props)):
  63 + if(vertex_props[i] != "map" or vertex_props[i] != "RGBA"):
  64 + vertex_actions.append(tmp.addAction(vertex_props[i]))
  65 + #tmp.addAction("Cluster")
  66 + #NC = menu.addAction('Node Color')
  67 + #EXP = menu.addAction('Export VTK')
  68 + #EXP_adv = menu.addAction('Export VTK Advanced')
  69 + NL = menu.addAction('New Layout')
  70 + action = menu.exec_(event.native.globalPos())
  71 + print(action)
  72 + for i in range(len(vertex_actions)):
  73 + if action == vertex_actions[i]:
  74 + if vertex_props[i] == "clusters":
  75 + self.canvas.color_vertices(self.canvas.G, vertex_props[i], dtype=True)
  76 + else:
  77 + self.canvas.color_vertices(self.canvas.G, vertex_props[i])
  78 + print(vertex_props[i])
  79 + if action == NS:
  80 + if self.use_3D == False:
  81 + self.use_3D = True
  82 + else:
  83 + self.use_3D = False
  84 + self.canvas.size_vertices(self.canvas.G, 'degree' )
  85 + self.canvas.color_vertices(self.canvas.G)
  86 + #if action == NC:
  87 + # self.canvas.color_vertices(self.canvas.G, not self.color)
  88 + # self.color = not self.color
  89 +# if action == EXP:
  90 +# nwt.Network.write_vtk(self.canvas.G, "./nwt.vtk")
  91 +# if action == EXP_adv:
  92 +# nwt.Network.write_vtk(self.canvas.G, "./nwt_median_binned.vtk")
  93 +# nwt.Network.write_vtk(self.canvas.G, "./nwt_median_non_binned.vtk", binning=False)
  94 +# nwt.Network.write_vtk(self.canvas.G, "./nwt_points_wise_binned.vtk", camera=self.camera, binning = True)
  95 +# nwt.Network.write_vtk(self.canvas.G, "./nwt_points_wise_non_binned.vtk", camera=self.camera, binning = False)
  96 + if action == tmp:
  97 + print("Stuff")
  98 + #self.cb = QtWidgets.QComboBox()
  99 + #vertex_props = self.canvas.G.vertex_properties.keys()
  100 + #for i in range(len(vertex_props)):
  101 + # self.cb.addItem(vertex_props[i])
  102 + #self.cb.currentIndexChanged.connect(self.selection_change)
  103 + #self.cb.show()
  104 + if action == NL:
  105 + self.canvas.G = nwt.Network.gen_new_fd_layout(self.canvas.G)
  106 + self.canvas.gen_vertex_vbo(self.canvas.G)
  107 + #self.canvas.set_data(self.canvas.G, self.canvas.bbl, self.canvas.bbu)
  108 + self.canvas.expand_clusters(self.canvas.G, self.canvas.n_c)
  109 +
  110 + #self.canvas.size_vertices(self.canvas.G, 'degree_volume')
  111 + else:
  112 + if self.canvas.view[0][0] >= 0.0010:
  113 + menu = QtWidgets.QMenu(self)
  114 + NS = menu.addAction('Display Node Info')
  115 + action = menu.exec_(event.native.globalPos())
  116 + if action == NS:
  117 + print("Display Node Info")
  118 + else:
  119 + menu = QtWidgets.QMenu(self)
  120 + MnOC = menu.addAction('Minimize Other Clusters')
  121 + RA = menu.addAction('Reset Alpha')
  122 + action = menu.exec_(event.native.globalPos())
  123 + if action == MnOC:
  124 + new_Graph = self.canvas.focus_on_cluster(self.canvas.G, c_id)
  125 + self.test.draw_new(new_Graph)
  126 + if action == RA:
  127 + self.canvas.reset_alpha(c_id[0])
  128 +
  129 +
  130 +# def selection_change(self, i):
  131 +# self.canvas.size_vertices(self.canvas.G, self.cb.currentText())
  132 +
  133 +
  134 + """
  135 + Handles the mouse double click event.
  136 + Pass-through function.
  137 + """
  138 + def on_mouse_double_click(self, event):
  139 + #print("in applet")
  140 + n = 0
  141 +
  142 + """
  143 + Handles the mouse release event.
  144 + Pass-through function.
  145 + """
  146 + def on_mouse_release(self, event):
  147 + n = 1
  148 +
  149 + """
  150 + Handles the mouse move event.
  151 + Pass-through function.
  152 + """
  153 + def on_mouse_move(self, event):
  154 + n = 2
  155 +
  156 + """
  157 + Internal function that interacts with the tube visualization.
  158 + """
  159 + def set_g_view(self, test):
  160 + self.test = test
  161 +
  162 + """
  163 + Function to handle the pyqt Slot that receives the interacted-with signal
  164 + from the Tube visualization. The signal sends the camera position in 3D coordinates
  165 + every time the camera is moved.
  166 + """
  167 + @QtCore.pyqtSlot(float, float, float)
  168 + def sigUpdate(self, x, y, z):
  169 + self.camera = [x,y,z]
  170 + if(self.use_3D == True):
  171 + self.camera = [x,y,z]
  172 + self.canvas.vertexSizeFromDistance(self.canvas.G, [x,y,z])
  173 + self.canvas.vertexAlphaFromDistance(self.canvas.G, [x,y,z])
  174 + #print("got signal", x, y, z)
  175 +
  176 + """
  177 + Initialization method that connects the slot to the function that handles
  178 + the information being sent.
  179 + """
  180 + def connect(self, signal_object):
  181 + signal_object.sigUpdate.connect(self.sigUpdate)
  182 +
  183 +
  184 +
... ...
GuiVisPy_tube.py 0 → 100644
  1 +++ a/GuiVisPy_tube.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Thu Jan 31 15:29:40 2019
  5 +
  6 +@author: pavel
  7 +`"""
  8 +
  9 +from vispy import app
  10 +
  11 +
  12 +import network_dep as nwt
  13 +
  14 +from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
  15 +import pyqtgraph as pg
  16 +import numpy as np
  17 +import math
  18 +
  19 +from mpl_toolkits.mplot3d import Axes3D
  20 +import matplotlib
  21 +import matplotlib.pyplot as plt
  22 +matplotlib.use('Qt5Agg')
  23 +
  24 +from GraphWidget import GraphWidget
  25 +from TubeWidget import TubeWidget
  26 +
  27 +
  28 +#set the backend. for different versions of PyQt
  29 +app.use_app(backend_name='PyQt5')
  30 +
  31 +#Define a top level application
  32 +appMain = QtGui.QApplication([])
  33 +
  34 +##Define a toplevel Widget
  35 +top = QtGui.QWidget()
  36 +top.resize(900, 900)
  37 +
  38 +
  39 +hist = pg.PlotWidget()
  40 +#fibers = FiberView()
  41 +fibers = TubeWidget()
  42 +fibers.canvas.create_native()
  43 +#fibers = gl.GLViewWidget()
  44 +
  45 +#plt = hist.addPlot()
  46 +
  47 +layout = QtGui.QGridLayout()
  48 +graph = GraphWidget()
  49 +graph.canvas.create_native()
  50 +
  51 +graph.connect(fibers)
  52 +
  53 +#initialize the layout
  54 +layout.addWidget(graph, 0, 0, 2, 3)
  55 +layout.addWidget(fibers, 0, 2, 2, 3)
  56 +layout.setColumnStretch(0, 2)
  57 +layout.setRowStretch(0, 2)
  58 +layout.setColumnStretch(2, 1)
  59 +layout.setRowStretch(0, 1)
  60 +
  61 +top.setLayout(layout)
  62 +top.show()
  63 +
  64 +
  65 +def draw_histogram(G, value):
  66 + vals = G.edge_properties[value].get_array()
  67 + y, x = np.histogram(vals,40)
  68 + hist.plot(x,y, stepMode=True, fillLevel=0, brush=(0,0,255,150))
  69 +
  70 +def load_nwt(filepath):
  71 + net = nwt.Network(filepath)
  72 + G = net.createFullGraph_gt()
  73 + G = net.filterDisconnected(G)
  74 + #G = net.filterFullGraph_gt(G, erode=True)
  75 + #G = net.filterFullGraph_gt(G, erode=True)
  76 + #G = net.gen_new_fd_layout(G)
  77 + #G = net.filterBorder(G)
  78 + #nwt.Network.saveGraph_gt(nwt, G, "FilteredNetwork_JACK_3.nwt")
  79 + color = np.zeros(4, dtype = np.double)
  80 + color = [0.0, 1.0, 0.0, 1.0]
  81 + G.edge_properties["RGBA"] = G.new_edge_property("vector<double>", val=color)
  82 + color = [1.0, 0.0, 0.0, 0.9]
  83 + G.vertex_properties["RGBA"] = G.new_vertex_property("vector<double>", val=color)
  84 + bbl, bbu = net.aabb()
  85 +
  86 +
  87 + return G, bbl, bbu
  88 +
  89 +G, bbl, bbu = load_nwt("/home/pavel/Documents/Python/GraphGuiQt/network_4.nwt")
  90 +#nwt.Network.write_vtk(G)
  91 +
  92 +#G, bbl, bbu = load_nwt("/home/pavel/Documents/Python/GraphGuiQt/network_test_251_1kx3_short_NEW.nwt")
  93 +#ret = nwt.Network.get_affinity_matrix(G, "length")
  94 +node_image = QtGui.QImage("/home/pavel/Documents/Python/GraphGuiQt/node_tex.jpg")
  95 +node_tex = QtGui.QBitmap.fromImage(node_image)
  96 +print(node_tex.depth())
  97 +center = (bbu-bbl)/2.0
  98 +#fibers.opts['distance'] = 5
  99 +# item = NodeItem(G)
  100 +# graph.addItem(item)
  101 +draw_histogram(G, "length")
  102 +graph.canvas.set_data(G, bbl, bbu)
  103 +fibers.canvas.set_data(G, bbl, bbu, 16)
  104 +#fibers.draw_all(G, center, fibers, graph, node_tex)
  105 +graph.set_g_view(fibers)
  106 +## Start Qt event loop unless running in interactive mode.
  107 +if __name__ == '__main__':
  108 + import sys
  109 + if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
  110 + QtGui.QApplication.instance().exec_()
  111 +
  112 +
... ...
TubeCanvas.py 0 → 100644
  1 +++ a/TubeCanvas.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:56:47 2019
  5 +
  6 +@author: pavel
  7 +"""
  8 +
  9 +"""
  10 + Class that extends the vispy SceneCanvas to draw 3D tubes
  11 +"""
  12 +
  13 +from vispy import gloo, scene
  14 +from vispy.gloo import set_viewport, set_state, clear, set_blend_color
  15 +from vispy.util.transforms import perspective, translate, rotate, scale
  16 +import vispy.gloo.gl as glcore
  17 +from vispy.util.quaternion import Quaternion
  18 +
  19 +import numpy as np
  20 +import math
  21 +import network_dep as nwt
  22 +
  23 +
  24 +from tube_shaders import FRAG_SHADER, VERT_SHADER
  25 +
  26 +class TubeDraw(scene.SceneCanvas):
  27 + #sigUpdate = QtCore.pyqtSignal(float, float, float)
  28 +
  29 + #Initiates the canvas.
  30 + def __init__(self, **kwargs):
  31 + #Initialte the class by calling the superclass
  32 + scene.SceneCanvas.__init__(self, size=(512,512), keys='interactive', **kwargs)
  33 + #unfreeze the drawing area to allow for dynamic drawing and interaction
  34 + self.unfreeze()
  35 +
  36 + #generate dummy buffers for the meshes
  37 + self.program = gloo.Program(VERT_SHADER, FRAG_SHADER)
  38 + self.cylinder_data = np.zeros(5*5, dtype=[('a_position', np.float32, 3),
  39 + ('a_normal', np.float32, 3),
  40 + ('a_fg_color', np.float32, 4),
  41 + #('a_linewidth', np.float32, 1),
  42 + ])
  43 + self.triangle_data = np.random.randint(size=(5, 3), low=0,
  44 + high=(4-1)).astype(np.uint32)
  45 + self.vbo = gloo.VertexBuffer(self.cylinder_data)
  46 + self.triangles = gloo.IndexBuffer(self.triangle_data)
  47 + self.program.bind(self.vbo)
  48 + self.scale = [1,1,1]
  49 + self.r1 = np.eye(4, dtype=np.float32)
  50 + self.r2 = np.eye(4, dtype=np.float32)
  51 + set_viewport(0,0,*self.physical_size)
  52 + #set_state(clear_color='white', depth_test=True, blend=True,
  53 + # blend_func=('src_alpha', 'one_minus_src_alpha'), depth_func = ('less'), cull_face='back')
  54 + set_state(clear_color='white', depth_test=True, blend=True,
  55 + blend_func=('src_alpha', 'one_minus_src_alpha'), depth_func = ('lequal'))
  56 + #set_blend_color(color='black')
  57 + #set_state('translucent')
  58 + self.program['u_LightPos'] = [0., 0., -1000.]
  59 + #self.camera = self.central_widget.add_view()
  60 + #self.camera.camera = 'turntable'
  61 + self.down = False
  62 + self.camera = np.asarray([0.0, 0.0, 200.0], dtype=np.float32)
  63 + self.up = np.asarray([0., 1., 0.], dtype=np.float32)
  64 + #self.init_camera = [0.,0.,1000.]
  65 +
  66 + ##### prototype #####
  67 + #Set the visualization matrices
  68 + self.program['u_eye'] = self.camera
  69 + self.program['u_up'] = self.up
  70 + self.program['u_target'] = np.asarray([0., 0., 0.], dtype=np.float32)
  71 +
  72 +
  73 +
  74 + #Load the data necessary to draw all of the microvessels
  75 + def set_data(self, G, bbu, bbl, num_sides):
  76 + self.G = G
  77 + self.num_sides = num_sides
  78 + self.bbu = bbu
  79 + self.bbl = bbl
  80 + bb = nwt.AABB(G).resample_sides(3)
  81 +
  82 +
  83 + #create program
  84 + self.gen_cylinder_vbo(self.G, self.num_sides)
  85 + self.vbo = gloo.VertexBuffer(self.cylinder_data)
  86 + self.triangles = gloo.IndexBuffer(self.triangle_data)
  87 +
  88 + #self.view = np.eye(4, dtype=np.float32)
  89 + self.model = np.eye(4, dtype=np.float32)
  90 + self.projection = np.eye(4, dtype=np.float32)
  91 + self.projection = perspective(90.0, self.physical_size[0]/self.physical_size[1], 1.0, 1000.0)
  92 + #self.projection = perspective(90.0, 1.0, -1.0, 1.0)
  93 + self.program['u_model'] = self.model
  94 + self.program['u_LightPos'] = [0., 0., 1000.]
  95 + #self.program['u_view'] = self.view
  96 + self.program['u_projection'] = self.projection
  97 + self.program.bind(self.vbo)
  98 +
  99 + gloo.set_clear_color('white')
  100 + self.center = (bbu-bbl)/2.0
  101 + self.translate = [-self.center[0], -self.center[1], -self.center[2]]
  102 +
  103 + self.bb = np.ones((26, 3), dtype=np.float32)
  104 + for i in range(26):
  105 + for j in range(3):
  106 + self.bb[i,j] = bb[i][j]
  107 + self.program['u_bb'] = self.bb
  108 + print('bb is ', self.bb)
  109 +# for i in range(len(self.translate)):
  110 +# self.camera[i] += self.translate[i]
  111 +
  112 +
  113 + ##### prototype #####
  114 + self.camera = self.camera - self.translate
  115 + self.program['u_eye'] = self.camera
  116 + self.up = np.cross((np.asarray(self.center, dtype=np.float32)-np.asarray(self.camera, dtype=np.float32)), np.asarray(self.up))
  117 + self.program['u_up'] = self.up
  118 + self.program['u_target'] = self.translate
  119 +
  120 +
  121 +
  122 +
  123 + #self.show()
  124 +
  125 + #Called during resize of the window in order to redraw the same image in the
  126 + #larger/smaller area.
  127 + def on_resize(self, event):
  128 + width, height = event.physical_size
  129 + gloo.set_viewport(0, 0, width, height)
  130 + print(self.physical_size)
  131 +
  132 + #overloaded function called during the self.update() call to update the current
  133 + #frame using the GLSL frag/vert shaders
  134 + def on_draw(self, event):
  135 + clear(color='white', depth=True)
  136 + gloo.set_clear_color('white')
  137 + self.program.draw('triangles', self.triangles)
  138 + self.projection = perspective(90.0, self.physical_size[0]/self.physical_size[1], 1.0, 1000.0)
  139 + self.program['u_projection'] = self.projection
  140 +
  141 + #Creates a cylinder around ever segment in the microvascular network.
  142 + def gen_cylinder_vbo(self, G, num_sides = 32):
  143 + i = 0
  144 + num_pts = 0
  145 + num_tri = 0
  146 + for e in G.edges():
  147 + num_pts += len(self.G.edge_properties["x"][e])
  148 + num_tri += (len(self.G.edge_properties["x"][e])-1)*num_sides*2
  149 + self.cylinder_data = np.zeros(num_pts*num_sides, dtype=[('a_position', np.float32, 3),
  150 + ('a_normal', np.float32, 3),
  151 + ('a_fg_color', np.float32, 4),
  152 + #('a_linewidth', np.float32, 1),
  153 + ])
  154 + self.triangle_data = np.random.randint(size=(num_tri, 3), low=0,
  155 + high=(G.num_edges()-1)).astype(np.uint32)
  156 + index = 0
  157 + t_index = 0
  158 + #for each edge generate a cylinder.
  159 + for e in G.edges():
  160 + #print("done")
  161 + #for each fiber get all the points and the radii
  162 + X = self.G.edge_properties["x"][e]
  163 + Y = self.G.edge_properties["y"][e]
  164 + Z = self.G.edge_properties["z"][e]
  165 + R = self.G.edge_properties["r"][e]
  166 + color = G.edge_properties["RGBA"][e]
  167 + pts = np.array([X,Y,Z]).T
  168 + circle_pts = np.zeros((pts.shape[0], num_sides, 3), dtype = np.float32)
  169 + step = 2*np.pi/num_sides
  170 +# U = np.zeros(pts.shape, dtype=np.float32)
  171 +# V = np.zeros(pts.shape, dtype=np.float32)
  172 +# direction = np.zeros(pts.shape, dtype=np.float32)
  173 +
  174 + #for every point in the edge
  175 + for p in range(pts.shape[0]):
  176 + #if first point, generate the circles.
  177 + #In this case we want to generate a cap if we see the first circle or the last.
  178 + if(p == 0):
  179 + #get the direction
  180 + direction = (pts[p+1] - pts[p])
  181 + #normalize direction
  182 + direction = direction/np.sqrt(direction[0]*direction[0] + direction[1]*direction[1] + direction[2]*direction[2])
  183 + #generate a vector to use as an element of the cross product
  184 + Y = np.zeros((3,), dtype = np.float32)
  185 + Y[0] = 1.
  186 + #if direction and Y are parallel, change Y
  187 + if(np.cos(np.dot(Y, direction)) < 0.087):
  188 + Y[0] = 0.0
  189 + Y[2] = 1.0
  190 + #generate first plane vector
  191 + U = np.cross(direction, Y)
  192 + U = U/np.sqrt(U[0]*U[0] + U[1]*U[1] + U[2]*U[2])
  193 + #generate second plane vector
  194 + V = np.cross(direction, U)
  195 + V = V/np.sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2])
  196 + #print(R[p],pts[p])
  197 + #generate circle.
  198 + for s in range(num_sides):
  199 + circle_pts[p][s][0] = R[p]*np.cos(s*step)*V[0]*0.5 + R[p]*np.sin(s*step)*U[0]*0.5
  200 + circle_pts[p][s][1] = R[p]*np.cos(s*step)*V[1]*0.5 + R[p]*np.sin(s*step)*U[1]*0.5
  201 + circle_pts[p][s][2] = R[p]*np.cos(s*step)*V[2]*0.5 + R[p]*np.sin(s*step)*U[2]*0.5
  202 + #if last point, copy the previous circle.
  203 + elif(p == pts.shape[0]-1):
  204 + for s in range(num_sides):
  205 + circle_pts[p][s] = circle_pts[p-1][s]
  206 + for v in range(pts.shape[0]):
  207 + circle_pts[v,:,0] += pts[v][0]
  208 + circle_pts[v,:,1] += pts[v][1]
  209 + circle_pts[v,:,2] += pts[v][2]
  210 + #otherwise, rotate the circle
  211 + else:
  212 + #print(R[p], pts[p])
  213 + #generate a new direction vector.
  214 + dir_new = (pts[p+1]-pts[p])
  215 + dir_new = dir_new/np.sqrt(dir_new[0]*dir_new[0] + dir_new[1]*dir_new[1] + dir_new[2]*dir_new[2])
  216 + dir_new2 = (pts[p]-pts[p-1])
  217 + dir_new2 = dir_new2/np.sqrt(dir_new2[0]*dir_new2[0] + dir_new2[1]*dir_new2[1] + dir_new2[2]*dir_new2[2])
  218 + dir_new = dir_new+dir_new2
  219 + dir_new = dir_new/np.sqrt(dir_new[0]*dir_new[0] + dir_new[1]*dir_new[1] + dir_new[2]*dir_new[2])
  220 + #print(dir_new, direction)
  221 + #generate the quaternion rotation vector for the shortest arc
  222 + k = 1.0 + np.dot(direction, dir_new)
  223 + s = 1/np.sqrt(k+k)
  224 + r = s*np.cross(direction, dir_new)
  225 + theta = k*s
  226 + #r = np.cross(direction, dir_new)
  227 + #r = r/np.sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2])
  228 + #calculate the degree of quaternion rotation.
  229 + #cos_theta = np.sqrt(np.sqrt(np.dot(dir_new, dir_new)) * np.sqrt(np.dot(direction, direction))) + np.dot(dir_new, direction)
  230 + #cos_theta = np.dot(direction, dir_new)
  231 + #theta = np.arccos(cos_theta)/2.0
  232 + #print(r, cos_theta, theta)
  233 + #quat = np.append(theta, r)
  234 + q = Quaternion(w=theta, x = r[0], y = r[1], z = r[2]).normalize()
  235 +
  236 + #print(quat)
  237 + #q = np.quaternion(quat[0], quat[1], quat[2], quat[3])
  238 + #rot = Rotation.from_quat(quat, normalized=False)
  239 + #rot.as_quat()
  240 + for s in range(num_sides):
  241 + circle_pts[p][s] = q.rotate_point(circle_pts[p-1][s])
  242 + #circle_pts[p][s] = rot.apply(circle_pts[p-1][s])
  243 + #circle_pts[p][s] = q.rotate(circle_pts[p-1][s])
  244 + #circle_pts[p][s] = np.quaternion.rotate_vectors(q, circle_pts[p][s])
  245 + #circle_pts[p][s] = q.rotate_vectors(q, circle_pts[p][s])
  246 + #circle_pts[p][s] = circle_pts[p][s]
  247 + direction = dir_new
  248 + #generate the triangles
  249 + triangles = np.random.randint(size=((pts.shape[0]-1)*2*(num_sides), 3), low=0,
  250 + high=(G.num_edges()-1)).astype(np.uint32)
  251 +
  252 + t_index_temp = 0
  253 + for ring in range(pts.shape[0]-1):
  254 + for side in range(num_sides):
  255 + if(side < num_sides-1):
  256 + idx_current_point = index+ring*num_sides + side
  257 + idx_next_ring = index + (ring+1) * num_sides + side
  258 + triangle1 = [idx_current_point, idx_next_ring, idx_next_ring+1]
  259 + triangle2 = [idx_next_ring+1, idx_current_point+1, idx_current_point]
  260 + triangles[t_index_temp] = [idx_current_point, idx_next_ring, idx_next_ring+1]
  261 + triangles[t_index_temp+1] = [idx_next_ring+1, idx_current_point+1, idx_current_point]
  262 + self.triangle_data[t_index] = triangle1
  263 + self.triangle_data[t_index+1] = triangle2
  264 + t_index += 2
  265 + t_index_temp += 2
  266 + else:
  267 + idx_current_point = index + ring*num_sides + side
  268 + idx_next_ring = index + (ring+1)*num_sides + side
  269 + triangle1 = [idx_current_point, idx_next_ring, idx_next_ring-num_sides+1]
  270 + triangle2 = [idx_next_ring-num_sides+1, idx_current_point-num_sides+1, idx_current_point]
  271 + triangles[t_index_temp] = [idx_current_point, idx_next_ring-num_sides, idx_next_ring-num_sides+1]
  272 + triangles[t_index_temp+1] = [idx_next_ring-num_sides+1, idx_current_point-num_sides+1, idx_current_point]
  273 + self.triangle_data[t_index] = triangle1
  274 + self.triangle_data[t_index+1] = triangle2
  275 + t_index += 2
  276 + t_index_temp += 2
  277 +
  278 + #generate the points data structure
  279 + circle_pts_data = circle_pts.reshape((pts.shape[0]*num_sides, 3))
  280 + self.cylinder_data['a_position'][index:(pts.shape[0]*num_sides+index)] = circle_pts_data
  281 + self.cylinder_data['a_fg_color'][index:(pts.shape[0]*num_sides+index)] = color
  282 + #generate the normals data structure
  283 + pts_normals = circle_pts.copy()
  284 + for p in range(pts.shape[0]):
  285 + pts_normals[p][:] = pts_normals[p][:] - pts[p]
  286 + for s in range(num_sides):
  287 + pts_normals[p][s] = pts_normals[p][s]/np.sqrt(pts_normals[p][s][0]*pts_normals[p][s][0] \
  288 + + pts_normals[p][s][1]*pts_normals[p][s][1] + pts_normals[p][s][2]*pts_normals[p][s][2])
  289 + self.cylinder_data['a_normal'][index:(pts.shape[0]*num_sides+index)] = \
  290 + pts_normals.reshape((pts.shape[0]*num_sides, 3))
  291 +
  292 + index += pts.shape[0]*num_sides
  293 +
  294 + #Add the caps for each of the endpoints.
  295 +
  296 +
  297 +
  298 + if(i == 2):
  299 + fig = plt.figure()
  300 + ax = fig.add_subplot(111, projection='3d')
  301 + #ax.scatter(circle_pts[:,:,0], circle_pts[:,:,1], circle_pts[:,:,2])
  302 + ax.plot(pts[:,0], pts[:,1], pts[:,2])
  303 + for j in range(pts.shape[0]):
  304 + ax.plot(circle_pts[j,:,0], circle_pts[j,:,1], circle_pts[j,:,2])
  305 + for j in range(triangles.shape[0]):
  306 + tri = np.zeros((3,4))
  307 + tri[:,0] = self.cylinder_data['a_position'][triangles[j][0]]
  308 + tri[:,1] = self.cylinder_data['a_position'][triangles[j][1]]
  309 + tri[:,2] = self.cylinder_data['a_position'][triangles[j][2]]
  310 + tri[:,3] = self.cylinder_data['a_position'][triangles[j][0]]
  311 + ax.plot(tri[0,:], tri[1,:], tri[2,:], c='b')
  312 + for j in range(triangles.shape[0]):
  313 + tri = np.zeros((3,3))
  314 + tri[:,0] = self.cylinder_data['a_position'][triangles[j][0]]
  315 + tri[:,1] = self.cylinder_data['a_position'][triangles[j][1]]
  316 + tri[:,2] = self.cylinder_data['a_position'][triangles[j][2]]
  317 + norm = np.zeros((3,3))
  318 + norm[:,0] = self.cylinder_data['a_normal'][triangles[j][0]]
  319 + norm[:,1] = self.cylinder_data['a_normal'][triangles[j][1]]
  320 + norm[:,2] = self.cylinder_data['a_normal'][triangles[j][2]]
  321 + ax.quiver(tri[0,:], tri[1,:], tri[2,:], norm[0,:], norm[1,:], norm[2,:], colors = 'r')
  322 + plt.show()
  323 + i+=1
  324 + #create the data.
  325 +
  326 + #Handles the mouse wheel event, i.e., zoom
  327 + def on_mouse_wheel(self, event):
  328 +# self.scale[0] = self.scale[0] + self.scale[0]*event.delta[1]*0.05
  329 +# self.scale[1] = self.scale[1] + self.scale[1]*event.delta[1]*0.05
  330 +# self.scale[2] = self.scale[2] + self.scale[2]*event.delta[1]*0.05
  331 +## self.view[0][0] = self.scale[0]
  332 +## self.view[1][1] = self.scale[1]
  333 +## self.view[2][2] = self.scale[2]
  334 +# print("in mouse wheel ", self.r1, self.r2)
  335 +# self.view = np.matmul(translate((self.translate[0], self.translate[1], self.translate[2])), self.r1)
  336 +# self.view = np.matmul(self.view, self.r2)
  337 +# self.view = np.matmul(self.view, scale((self.scale[0], self.scale[1], self.scale[2])))
  338 +# #self.view = np.matmul(self.view, self.r1)
  339 +# #self.view = np.matmul(self.view, self.r2)
  340 +# #self.view = np.matmul(translate((self.translate[0], self.translate[1], self.translate[2])), scale((self.scale[0], self.scale[1], self.scale[2])))
  341 +# #self.rotate = [0., 0.]
  342 +# #self.camera =
  343 +#
  344 +# #self.view[1][1] = self.view[1][1]+self.view[1][1]*event.delta[1]*0.05
  345 +# #print(self.view[0][0], " ",self.view[1][1])
  346 +# #print(self.view)
  347 +# self.camera = [0.0, 0.0, -100.0, 1.0]
  348 +## for i in range(len(self.translate)):
  349 +## self.camera[i] += self.translate[i]
  350 +# self.program['u_view'] = self.view
  351 +
  352 +# if(np.asarray(self.camera).all() == np.asarray([0., 0., 0.]).all()):
  353 +# self.camera = np.asarray([0., 0., 0.])
  354 +# else:
  355 + direction = np.asarray(self.translate) - np.asarray(self.camera)
  356 + direction = direction/np.sqrt(np.dot(direction, direction))
  357 + for i in range(3):
  358 + self.camera[i] = self.camera[i] + direction[i]*event.delta[1]*2.0
  359 +
  360 + self.program['u_eye'] = self.camera
  361 + #print(self.view)
  362 + #print(event.delta[1])
  363 + self.update()
  364 +
  365 +
  366 +
  367 + #Handles the mouse press event to rotate the camera if the left mouse button
  368 + #if clicked down.
  369 + def on_mouse_press(self, event):
  370 + def update_view():
  371 + self.location = event.pos
  372 + #self.program['u_view'] = self.view
  373 + self.down = True
  374 +
  375 + if(event.button == 1):
  376 + update_view()
  377 +
  378 +
  379 + #Handles the rotation of the camera using a quaternion centered around the
  380 + #focus point.
  381 + def on_mouse_move(self, event):
  382 + if(self.down == True):
  383 + coord = self.transforms.get_transform('canvas', 'render').map(self.location)[:2]
  384 + coord2 = self.transforms.get_transform('canvas', 'render').map(event.pos)[:2]
  385 + #coord[1] = 0
  386 + #coord2[1] = 0
  387 +
  388 + theta = (coord[0]-coord2[0])*360.0/2.0/np.pi
  389 + phi = (coord[1]-coord2[1])*360.0/2.0/np.pi
  390 + print(theta*360.0/2.0/np.pi, -phi*360.0/2.0/np.pi)
  391 + self.camera = self.camera - self.translate
  392 + q1 = Quaternion.create_from_axis_angle(angle=phi, ax=0.0, ay=1.0, az=0.0, degrees=True)
  393 + q2 = Quaternion.create_from_axis_angle(angle=theta, ax=1.0, ay=0.0, az=0.0, degrees=True)
  394 + #q1 = Quaternion(w=theta, x = 0, y = 1, z = 0).inverse().normalize()
  395 + #q2 = Quaternion(w=-phi, x = 1, y = 0, z = 0).inverse().normalize()
  396 +
  397 + q = q1*q2
  398 +
  399 +# #print("Angle in Degrees is ", theta, " ", phi, coord[0] - coord2[0])
  400 +# self.r1 = rotate(theta, (0, 1, 0))
  401 +# self.r2 = rotate(-phi, (1, 0, 0))
  402 +#
  403 +# print("in on mouse move ", self.r1, self.r2)
  404 +#
  405 +# self.view = np.matmul(self.view, self.r1)
  406 +# #print("r1", self.view)
  407 +# self.view = np.matmul(self.view, self.r2)
  408 +# #print("r2", self.view)
  409 +#
  410 +## self.view = np.matmul(self.view, q1.get_matrix().T)
  411 +## self.view = np.matmul(self.view, q2.get_matrix().T)
  412 +#
  413 +# self.program['u_view'] = self.view
  414 +#
  415 + self.location = event.pos
  416 +# #print("Angle in Degrees is ", theta, " ", phi)
  417 +# #print(self.camera)
  418 + self.camera = np.asarray(q.rotate_point(self.camera), dtype=np.float)
  419 + self.camera = self.camera + self.translate
  420 + self.up = np.asarray(q.rotate_point(self.up), dtype=np.float)
  421 + self.up = self.up/np.sqrt(np.dot(self.up, self.up))
  422 + #self.rotate[0] = self.rotate[0] + theta
  423 + #self.rotate[1] = self.rotate[1] + phi
  424 + #print(self.rotate)f
  425 + #radius = np.sqrt(np.dot(self.center, self.center))*2
  426 + #test = np.linalg.inv(self.view).T
  427 + #print(test)
  428 +
  429 +
  430 +
  431 + #self.camera = sph2cart(self.rotate[0]/360.0*2.0*np.pi+np.pi, self.rotate[1]/360.0*2.0*np.pi+np.pi, 1000.0)
  432 + #self.camera[0] = self.camera[0] + self.center[0]
  433 + #self.camera[1] = self.camera[1] + self.center[1]
  434 + #self.camera[2] = self.camera[2] - self.center[2]
  435 + print("current position ", self.camera, " and up vector ", self.up)
  436 + self.program['u_eye'] = self.camera
  437 + self.program['u_up'] = self.up
  438 + self.program['u_LightPos'] = [self.camera[0], self.camera[1], self.camera[2]]
  439 + self.update()
  440 +
  441 + #reverts the mouse state during release.
  442 + def on_mouse_release(self, event):
  443 + self.down = False
... ...
TubeWidget.py 0 → 100644
  1 +++ a/TubeWidget.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:53:16 2019
  5 +
  6 +@author: pavel
  7 +"""
  8 +
  9 +"""
  10 + Qt wrapper/container class that handles all the top level events and maintains the
  11 + methods necessary to use the QT signals and slots API.
  12 +"""
  13 +
  14 +from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
  15 +from TubeCanvas import TubeDraw
  16 +
  17 +class TubeWidget(QtGui.QWidget):
  18 + sigUpdate = QtCore.pyqtSignal(float, float, float)
  19 + #Initializes the QT wrapper class.
  20 + def __init__(self):
  21 + super(TubeWidget, self).__init__()
  22 + box = QtGui.QVBoxLayout(self)
  23 + self.resize(500,500)
  24 + self.setLayout(box)
  25 + self.canvas = TubeDraw()
  26 + #self.canvas.create_native()
  27 + box.addWidget(self.canvas.native)
  28 + self.camera = [0,0,0]
  29 + self.down = False
  30 +
  31 + self.canvas.events.mouse_press.connect(self.on_mouse_press)
  32 + self.canvas.events.mouse_release.connect(self.on_mouse_release)
  33 + self.canvas.events.mouse_move.connect(self.on_mouse_move)
  34 + self.canvas.events.mouse_wheel.connect(self.on_mouse_wheel)
  35 +
  36 + #self.show()
  37 +
  38 + #Handles the mouse release event
  39 + def on_mouse_release(self, event):
  40 + self.down=False
  41 + self.sendCameraInfo()
  42 +
  43 + #Handles the mouse move event
  44 + def on_mouse_move(self, event):
  45 + if self.down:
  46 + self.sendCameraInfo()
  47 +
  48 + #Handles the mouse press event
  49 + def on_mouse_press(self, event):
  50 + self.down = True
  51 + n = 3
  52 +
  53 + #Handles the mouse wheel event
  54 + def on_mouse_wheel(self, event):
  55 + self.sendCameraInfo()
  56 +
  57 + #controls the emit function of the QT class to send a signal to the slot
  58 + #located in the other window.
  59 + def sendCameraInfo(self):
  60 + #self.view = self.canvas.view
  61 + self.camera = self.canvas.camera
  62 + #print("stuff", self.view[3, 0], self.view[3, 1], self.view[3, 2])
  63 + print("stuff", self.camera[0], self.camera[1], self.camera[2])
  64 + self.sigUpdate.emit(self.camera[0], self.camera[1], self.camera[2])
  65 +
... ...
graph_shaders.py 0 → 100644
  1 +++ a/graph_shaders.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 14:48:00 2019
  5 +Collection of shaders necessary to draw all elements of the graph.
  6 +@author: pavel
  7 +"""
  8 +
  9 +from vispy import gloo, app, scene
  10 +
  11 +#shaders for drawing nodes
  12 +
  13 +vert = """
  14 +#version 120
  15 +// Uniforms
  16 +// ------------------------------------
  17 +uniform mat4 u_model;
  18 +uniform mat4 u_view;
  19 +uniform mat4 u_projection;
  20 +uniform float u_antialias;
  21 +uniform float u_size;
  22 +uniform bool u_picking;
  23 +
  24 +uniform vec3 u_graph_size;
  25 +// Attributes
  26 +// ------------------------------------
  27 +attribute vec3 a_position;
  28 +attribute vec4 a_fg_color;
  29 +attribute vec4 a_bg_color;
  30 +attribute float a_linewidth;
  31 +attribute float a_size;
  32 +attribute vec4 a_unique_id;
  33 +attribute float a_selection;
  34 +// Varyings
  35 +// ------------------------------------
  36 +varying vec4 v_fg_color;
  37 +varying vec4 v_bg_color;
  38 +varying float v_size;
  39 +varying float v_linewidth;
  40 +varying float v_antialias;
  41 +varying vec4 v_unique_id;
  42 +varying float v_selection;
  43 +
  44 +varying float v_zoom_level;
  45 +void main (void) {
  46 + v_size = a_size * u_size;
  47 + v_linewidth = a_linewidth;
  48 + v_antialias = u_antialias;
  49 + v_fg_color = a_fg_color;
  50 + v_bg_color = a_bg_color;
  51 + gl_Position = u_projection * u_view * u_model *
  52 + vec4(a_position*u_size,1.0);
  53 + gl_PointSize = v_size + 2*(v_linewidth + 1.5*v_antialias);
  54 + v_zoom_level = u_view[0][0];
  55 + v_unique_id = a_unique_id;
  56 + v_selection = a_selection;
  57 +}
  58 +"""
  59 +
  60 +frag = """
  61 +#version 120
  62 +// Constants
  63 +// ------------------------------------
  64 +uniform mat4 u_model;
  65 +uniform mat4 u_view;
  66 +uniform mat4 u_projection;
  67 +uniform float u_antialias;
  68 +uniform float u_size;
  69 +uniform bool u_picking;
  70 +
  71 +// Varyings
  72 +// ------------------------------------
  73 +varying vec4 v_fg_color;
  74 +varying vec4 v_bg_color;
  75 +varying float v_size;
  76 +varying float v_linewidth;
  77 +varying float v_antialias;
  78 +varying vec4 v_unique_id;
  79 +
  80 +varying float v_zoom_level;
  81 +varying float v_selection;
  82 +// Functions
  83 +// ------------------------------------
  84 +float marker(vec2 P, float size);
  85 +float new_alpha(float zoom_level);
  86 +// Main
  87 +// ------------------------------------
  88 +void main()
  89 +{
  90 + if(!u_picking)
  91 + {
  92 + float size = v_size + 2*(v_linewidth + 1.5*v_antialias);
  93 + float t = v_linewidth/2.0-v_antialias;
  94 + // The marker function needs to be linked with this shader
  95 + float r = marker(gl_PointCoord, size);
  96 + float d = abs(r) - t;
  97 + if( r > (v_linewidth/2.0+v_antialias))
  98 + {
  99 + discard;
  100 + }
  101 + else if( d < 0.0 )
  102 + {
  103 + if(v_zoom_level < 0.0010)
  104 + {
  105 + float alpha = d/v_antialias;
  106 + alpha = new_alpha(v_zoom_level);
  107 + gl_FragColor = mix(vec4(1,1,1,0.9), v_bg_color, max(1.0-alpha, 0.3));
  108 + }
  109 + else
  110 + {
  111 + if(v_selection == 1.0)
  112 + gl_FragColor = vec4(1.0, 0.0, 0.0, v_fg_color.a);
  113 + else if(v_selection == 2.0)
  114 + gl_FragColor = vec4(0.0, 1.0, 0.0, v_fg_color.a);
  115 + else
  116 + gl_FragColor = v_fg_color;
  117 + }
  118 + }
  119 + else
  120 + {
  121 + float alpha = d/v_antialias;
  122 + if(v_zoom_level < 0.0010)
  123 + alpha = new_alpha(v_zoom_level);
  124 + else
  125 + alpha = exp(-alpha*alpha);
  126 +
  127 + if (r > 0 && v_zoom_level >= 0.0010)
  128 + gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a);
  129 + else
  130 + if(v_zoom_level < 0.0010)
  131 + if(vec3(gl_Color.rgb) != vec3(v_bg_color.rgb))
  132 + gl_FragColor = mix(vec4(1,1,1,0.9), v_bg_color, max(1.0-alpha, 0.3));
  133 + else
  134 + gl_FragColor = vec4(gl_Color.rgb, max(1.0-alpha, 0.1));
  135 + else
  136 + gl_FragColor = mix(v_bg_color, v_fg_color, alpha);
  137 + }
  138 +
  139 +
  140 + } else {
  141 + float size = v_size +2*(v_linewidth + 1.5*v_antialias);
  142 + float t = v_linewidth/2.0-v_antialias;
  143 + // The marker function needs to be linked with this shader
  144 + float r = marker(gl_PointCoord, size);
  145 + float d = abs(r) - t;
  146 + float alpha = d/v_antialias;
  147 + if(v_zoom_level < 0.0010)
  148 + alpha = new_alpha(v_zoom_level);
  149 + else
  150 + alpha = exp(-alpha*alpha);
  151 + if(r > 0)
  152 + gl_FragColor = v_unique_id;
  153 + else
  154 + gl_FragColor = v_unique_id;
  155 +
  156 + }
  157 +}
  158 +
  159 +float marker(vec2 P, float size)
  160 +{
  161 + float r = length((P.xy - vec2(0.5,0.5))*size);
  162 + r -= v_size/2;
  163 + return r;
  164 +}
  165 +
  166 +float new_alpha(float zoom_level)
  167 +{
  168 + float val = (zoom_level - 0.0010)/(0.00075-0.0010);
  169 + if(val < 0)
  170 + {
  171 + val = 0;
  172 + }
  173 + return val;
  174 +}
  175 +"""
  176 +
  177 +#Shaders for the lines between the nodes
  178 +
  179 +
  180 +vs = """
  181 +#version 120
  182 +
  183 +// Uniforms
  184 +// ------------------------------------
  185 +uniform mat4 u_model;
  186 +uniform mat4 u_view;
  187 +uniform mat4 u_projection;
  188 +
  189 +// Attributes
  190 +// ------------------------------------
  191 +attribute vec3 a_position;
  192 +attribute vec2 a_normal;
  193 +attribute vec4 a_fg_color;
  194 +attribute float a_linewidth;
  195 +//attribute vec4 a_unique_id;
  196 +//attribute vec4 l_color;
  197 +
  198 +// Varyings
  199 +// ------------------------------------
  200 +varying vec4 v_fg_color;
  201 +varying float v_zoom_level;
  202 +varying vec2 v_normal;
  203 +varying float v_linewidth;
  204 +
  205 +void main() {
  206 + vec3 delta = vec3(a_normal*a_linewidth/(1-u_view[0][0]), 0);
  207 + //vec4 pos = u_model * u_view * vec4(a_position, 1.0);
  208 + //gl_Position = u_projection * (pos + vec4(delta, 1.0));
  209 + gl_Position = u_model * u_view * u_projection * vec4(a_position+delta, 1.0);
  210 + //gl_Position = u_projection * u_view * u_model *
  211 + // vec4(a_position, 1.0);
  212 + v_zoom_level = u_view[0][0];
  213 + v_fg_color = a_fg_color;
  214 + v_normal = a_normal;
  215 + v_linewidth = a_linewidth;
  216 +
  217 +}
  218 +"""
  219 +
  220 +fs = """
  221 +#version 120
  222 +// Varying
  223 +// ------------------------------------
  224 +varying vec4 v_fg_color;
  225 +varying float v_zoom_level;
  226 +varying vec2 v_normal;
  227 +varying float v_linewidth;
  228 +
  229 +float new_alpha(float zoom_level);
  230 +
  231 +void main()
  232 +{
  233 + float l = length(v_normal);
  234 + float feather = 0.5;
  235 + float alpha = 1.0;
  236 + if(l > v_linewidth/2.0+feather)
  237 + discard;
  238 + else
  239 + alpha = 0.5;
  240 +
  241 + if(v_zoom_level < 0.0010)
  242 + alpha = new_alpha(v_zoom_level);
  243 + gl_FragColor = vec4(v_fg_color.rgb, alpha);
  244 +}
  245 +
  246 +float new_alpha(float zoom_level)
  247 +{
  248 + float val = (zoom_level-0.00075)/(0.0010-0.00075);
  249 + if(val < 0.)
  250 + {
  251 + val = 0.;
  252 + }
  253 + return val;
  254 +
  255 +}
  256 +"""
0 257 \ No newline at end of file
... ...
network_dep.py 0 → 100755
  1 +++ a/network_dep.py
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sat Sep 16 16:34:49 2017
  4 +
  5 +@author: pavel
  6 +"""
  7 +
  8 +import struct
  9 +import numpy as np
  10 +from scipy.sparse.linalg import eigsh
  11 +import scipy as sp
  12 +from sklearn.cluster import SpectralClustering
  13 +
  14 +
  15 +#import networkx as nx
  16 +import matplotlib.pyplot as plt
  17 +from matplotlib import cm
  18 +import math
  19 +import time
  20 +#import spharmonics
  21 +import graph_tool.all as gt
  22 +import copy
  23 +#import matplotlib
  24 +
  25 +#for testing
  26 +#import matplotlib.mlab as mlab
  27 +#import matplotlib.pyplot as plt
  28 +#from matplotlib import cm
  29 +
  30 +'''
  31 + Definition of the Node class
  32 + Duplicate of the node class in network
  33 + Stores the physical position, outgoing edges list and incoming edges list.
  34 +'''
  35 +class Node:
  36 + def __init__(self, point, outgoing, incoming):
  37 + self.p = point
  38 + self.o = outgoing
  39 + self.i = incoming
  40 +
  41 +# def p():
  42 +# return self.p
  43 +
  44 +'''
  45 + Definition of the Fiber class.
  46 + Duplicate of the Node class in network
  47 + Stores the starting vertex, the ending vertex, the points array and the radius array
  48 +'''
  49 +class Fiber:
  50 +
  51 +
  52 + def __init__ (self, p1, p2, pois, rads):
  53 + self.v0 = p1
  54 + self.v1 = p2
  55 + self.points = pois
  56 + self.radii = rads
  57 + '''
  58 + return the array-likes of the x,y,z,r coordinates of the fiber for the
  59 + gt representation
  60 + '''
  61 +
  62 + def getcoords(self):
  63 + x = np.zeros(len(self.points), dtype=np.double)
  64 + y = np.zeros(len(self.points), dtype=np.double)
  65 + z = np.zeros(len(self.points), dtype=np.double)
  66 + r = np.zeros(len(self.points), dtype=np.double)
  67 + for i in range(len(self.points)):
  68 + x[i] = self.points[i][0]
  69 + y[i] = self.points[i][1]
  70 + z[i] = self.points[i][2]
  71 + r[i] = self.radii[i]
  72 +
  73 + return x,y,z,r
  74 +
  75 +
  76 + '''
  77 + return the length of the fiber.
  78 + '''
  79 + def length(self):
  80 + length = 0
  81 + for i in range(len(self.points)-1):
  82 + length = length + math.sqrt(pow(self.points[i][0]- self.points[i+1][0],2) + pow(self.points[i][1]- self.points[i+1][1],2) + pow(self.points[i][2]- self.points[i+1][2],2))
  83 + if(length == 0):
  84 + print(self.points)
  85 + print(len(self.points))
  86 + print(self.v0, " ", self.v1)
  87 + return length
  88 +
  89 + '''
  90 + returns the turtuosity of the fiber.
  91 + '''
  92 + def turtuosity(self):
  93 + turtuosity = 1.0
  94 + distance = math.sqrt(math.pow(self.points[0][0]- self.points[len(self.points)-1][0],2) + math.pow(self.points[0][1]- self.points[len(self.points)-1][1],2) + math.pow(self.points[0][2]- self.points[len(self.points)-1][2],2))
  95 + if(distance > 0):
  96 + turtuosity = self.length()/distance
  97 + #print(turtuosity)
  98 +
  99 + return turtuosity
  100 +
  101 + '''
  102 + returns the volume of the fiber.
  103 + '''
  104 + def volume(self):
  105 + volume = 0
  106 + for i in range(len(self.points)-1):
  107 + volume = volume + 1.0/3.0 * math.pi * (math.pow(self.radii[i]*10e-6,2) + math.pow(self.radii[i+1]*10e-6,2) + self.radii[i]*10e-6*self.radii[i+1]*10e-6) * math.sqrt(math.pow(self.points[i][0]- self.points[i+1][0],2) + math.pow(self.points[i][1]- self.points[i+1][1],2) + math.pow(self.points[i][2]- self.points[i+1][2],2))
  108 +
  109 + #print(volume)
  110 + return volume
  111 +
  112 + def av_radius(self):
  113 + radius = 0.0
  114 + for i in range(len(self.radii)):
  115 + radius = radius + self.radii[i]
  116 +
  117 + return radius/len(self.radii)
  118 +
  119 +class NWT:
  120 +
  121 + '''
  122 + Writes the header given and open file descripion, number of verticies and number of edges.
  123 + '''
  124 + def writeHeader(open_file, numVerts, numEdges):
  125 + txt = "nwtFileFormat fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata"
  126 + b = bytearray()
  127 + b.extend(txt.encode())
  128 + open_file.write(b)
  129 + open_file.write(struct.pack('i', numVerts))
  130 + open_file.write(struct.pack('i', numEdges))
  131 +
  132 +
  133 + '''
  134 + Writes a single vertex to a file.
  135 + '''
  136 + def writeVertex(open_file, vertex):
  137 + open_file.write(struct.pack('<f',vertex.p[0]))
  138 + open_file.write(struct.pack('<f',vertex.p[1]))
  139 + open_file.write(struct.pack('<f',vertex.p[2]))
  140 + open_file.write(struct.pack('i', len(vertex.o)))
  141 + open_file.write(struct.pack('i', len(vertex.i)))
  142 + for j in range(len(vertex.o)):
  143 + open_file.write(struct.pack('i',vertex.o[j]))
  144 +
  145 + for j in range(len(vertex.i)):
  146 + open_file.write(struct.pack('i', vertex.i[j]))
  147 +
  148 + return
  149 +
  150 + '''
  151 + Writes a single fiber to a file.
  152 + '''
  153 + def writeFiber(open_file, edge):
  154 + open_file.write(struct.pack('i',edge.v0))
  155 + open_file.write(struct.pack('i',edge.v1))
  156 + open_file.write(struct.pack('i',len(edge.points)))
  157 + for j in range(len(edge.points)):
  158 + open_file.write(struct.pack('<f', edge.points[j][0]))
  159 + open_file.write(struct.pack('<f', edge.points[j][1]))
  160 + open_file.write(struct.pack('<f', edge.points[j][2]))
  161 + open_file.write(struct.pack('<f', edge.radii[j]))
  162 +
  163 + return
  164 +
  165 + '''
  166 + Writes the entire network to a file in str given the vertices array and the edges array.
  167 + '''
  168 + def exportNWT(str, vertices, edges):
  169 + with open(str, "wb") as file:
  170 + NWT.writeHeader(file, len(vertices), len(edges))
  171 + for i in range(len(vertices)):
  172 + NWT.writeVertex(file, vertices[i])
  173 +
  174 + for i in range(len(edges)):
  175 + NWT.writeFiber(file, edges[i])
  176 +
  177 + return
  178 +
  179 +
  180 + '''
  181 + Reads a single vertex from an open file and returns a node Object.
  182 + '''
  183 + def readVertex(open_file):
  184 + points = np.tile(0., 3)
  185 + bytes = open_file.read(4)
  186 + points[0] = struct.unpack('f', bytes)[0]
  187 + bytes = open_file.read(4)
  188 + points[1] = struct.unpack('f', bytes)[0]
  189 + bytes = open_file.read(4)
  190 + points[2] = struct.unpack('f', bytes)[0]
  191 + bytes = open_file.read(4)
  192 +
  193 + numO = int.from_bytes(bytes, byteorder='little')
  194 + outgoing = np.tile(0, numO)
  195 + bts = open_file.read(4)
  196 + numI = int.from_bytes(bts, byteorder='little')
  197 + incoming = np.tile(0, numI)
  198 + for j in range(numO):
  199 + bytes = open_file.read(4)
  200 + outgoing[j] = int.from_bytes(bytes, byteorder='little')
  201 +
  202 + for j in range(numI):
  203 + bytes = open_file.read(4)
  204 + incoming[j] = int.from_bytes(bytes, byteorder='little')
  205 +
  206 + node = Node(points, outgoing, incoming)
  207 + return node
  208 +
  209 +
  210 + '''
  211 + Reads a single fiber from an open file and returns a Fiber object .
  212 + '''
  213 + def readFiber(open_file):
  214 + bytes = open_file.read(4)
  215 + vtx0 = int.from_bytes(bytes, byteorder = 'little')
  216 + bytes = open_file.read(4)
  217 + vtx1 = int.from_bytes(bytes, byteorder = 'little')
  218 + bytes = open_file.read(4)
  219 + numVerts = int.from_bytes(bytes, byteorder = 'little')
  220 + pts = []
  221 + rads = []
  222 +
  223 + for j in range(numVerts):
  224 + point = np.tile(0., 3)
  225 + bytes = open_file.read(4)
  226 + point[0] = struct.unpack('f', bytes)[0]
  227 + bytes = open_file.read(4)
  228 + point[1] = struct.unpack('f', bytes)[0]
  229 + bytes = open_file.read(4)
  230 + point[2] = struct.unpack('f', bytes)[0]
  231 + bytes = open_file.read(4)
  232 + radius = struct.unpack('f', bytes)[0]
  233 + pts.append(point)
  234 + rads.append(radius)
  235 +
  236 + F = Fiber(vtx0, vtx1, pts, rads)
  237 +
  238 + return F
  239 +
  240 + '''
  241 + Imports a NWT file at location str.
  242 + Returns a list of Nodes objects and a list of Fiber objects.
  243 + '''
  244 +
  245 +'''
  246 +Class defining an aabb around the graph
  247 +'''
  248 +
  249 +class AABB():
  250 + def __init__(self, G, is_dual=False):
  251 + #designate whether we will be generating a bounding box for a dual graph
  252 + # or a normal graph.
  253 + self.is_dual = is_dual
  254 + #minimum vertex
  255 + self.A = np.full((3,1), 1000000.0, dtype=float)
  256 + #maximum vertex
  257 + self.B = np.full((3,1), -1000000.0, dtype=float)
  258 + if(is_dual == False):
  259 + #find the minumum and the maximum of the graph.
  260 + for v in G.vertices():
  261 + if G.vertex_properties["p"][v][0] < self.A[0]:
  262 + self.A[0] = G.vertex_properties["p"][v][0]
  263 + if G.vertex_properties["p"][v][0] > self.B[0]:
  264 + self.B[0] = G.vertex_properties["p"][v][0]
  265 + if G.vertex_properties["p"][v][1] < self.A[1]:
  266 + self.A[1] = G.vertex_properties["p"][v][1]
  267 + if G.vertex_properties["p"][v][1] > self.B[1]:
  268 + self.B[1] = G.vertex_properties["p"][v][1]
  269 + if G.vertex_properties["p"][v][2] < self.A[2]:
  270 + self.A[2] = G.vertex_properties["p"][v][2]
  271 + if G.vertex_properties["p"][v][2] > self.B[2]:
  272 + self.B[2] = G.vertex_properties["p"][v][2]
  273 + #In case of a dual graph we have to scane the first and last points
  274 + # of every fiber to find the true bounding box
  275 + else:
  276 + for v in G.vertices():
  277 + l = len(G.vertex_properties["x"][v])
  278 + if G.vertex_properties["x"][v][0] < self.A[0]:
  279 + self.A[0] = G.vertex_properties["x"][v][0]
  280 + if G.vertex_properties["x"][v][l-1] < self.A[0]:
  281 + self.A[0] = G.vertex_properties["x"][v][l-1]
  282 + if G.vertex_properties["y"][v][0] < self.A[1]:
  283 + self.A[1] = G.vertex_properties["y"][v][0]
  284 + if G.vertex_properties["y"][v][l-1] < self.A[1]:
  285 + self.A[1] = G.vertex_properties["y"][v][l-1]
  286 + if G.vertex_properties["z"][v][0] < self.A[2]:
  287 + self.A[2] = G.vertex_properties["z"][v][0]
  288 + if G.vertex_properties["z"][v][l-1] < self.A[2]:
  289 + self.A[2] = G.vertex_properties["z"][v][l-1]
  290 +
  291 + if G.vertex_properties["x"][v][0] > self.B[0]:
  292 + self.B[0] = G.vertex_properties["x"][v][0]
  293 + if G.vertex_properties["x"][v][l-1] > self.B[0]:
  294 + self.B[0] = G.vertex_properties["x"][v][l-1]
  295 + if G.vertex_properties["y"][v][0] > self.B[1]:
  296 + self.B[1] = G.vertex_properties["y"][v][0]
  297 + if G.vertex_properties["y"][v][l-1] > self.B[1]:
  298 + self.B[1] = G.vertex_properties["y"][v][l-1]
  299 + if G.vertex_properties["z"][v][0] > self.B[2]:
  300 + self.B[2] = G.vertex_properties["z"][v][0]
  301 + if G.vertex_properties["z"][v][l-1] > self.B[2]:
  302 + self.B[2] = G.vertex_properties["z"][v][l-1]
  303 + #print(self.A, self.B)
  304 + self.O = (self.A+self.B)*0.5
  305 + self.planes = []
  306 + #append x planes described by a point and a vector
  307 + self.planes.append((np.array([self.A[0], 0.0, 0.0]), np.array([1.0, 0.0, 0.0])))
  308 + self.planes.append((np.array([self.B[0], 0.0, 0.0]), np.array([-1.0, 0.0, 0.0])))
  309 + #append y planes described by a point and a vector
  310 + self.planes.append((np.array([0.0, self.A[1], 0.0]), np.array([0.0, 1.0, 0.0])))
  311 + self.planes.append((np.array([0.0, self.B[1], 0.0]), np.array([0.0, -1.0, 0.0])))
  312 + #append z planes described by a point and a vector
  313 + self.planes.append((np.array([0.0, 0.0, self.A[2]]), np.array([0.0, 0.0, 1.0])))
  314 + self.planes.append((np.array([0.0, 0.0, self.B[2]]), np.array([0.0, 0.0, -1.0])))
  315 +
  316 +
  317 + def distance(self, pt):
  318 + dist = 10000000
  319 + for i in self.planes:
  320 + V = i[0]-pt
  321 + if np.dot(V, i[1]) < dist:
  322 + dist = np.dot(V, i[1])
  323 +
  324 + return dist
  325 +
  326 + def project_grid(self, n):
  327 + #r = abs(self.A - self.B)
  328 + x = np.linspace(self.A[0], self.B[0], (n+2))
  329 + x = x[1:-1]
  330 + y = np.linspace(self.A[1], self.B[1], (n+2))
  331 + y = y[1:-1]
  332 + z = np.linspace(self.A[2], self.B[2], (n+2))
  333 + z = z[1:-1]
  334 +
  335 + return x,y,z
  336 +
  337 + #returns a resampled bounding both with nxn points on each side.
  338 + def resample_sides(self, n):
  339 + #get the size of the cube in every cardinal direction
  340 + size = abs(self.A - self.B)
  341 + #set the stepsize for each subdivided point
  342 + dist_x = size[0]/(n-1)
  343 + dist_y = size[1]/(n-1)
  344 + dist_z = size[2]/(n-1)
  345 + #generate the original 8 corners
  346 + vertices = []
  347 + #generate the points for the yz planes.
  348 + for i in range(n):
  349 + for j in range(n):
  350 + #generate the temporary vectors for both the planes
  351 + temp_p1 = copy.deepcopy(self.A)
  352 + temp_p2 = copy.deepcopy(self.A)
  353 + temp_p2[0] = self.B[0]
  354 +
  355 + temp_p1[1] = temp_p1[1] + dist_y*i
  356 + temp_p1[2] = temp_p1[2] + dist_z*j
  357 +
  358 + temp_p2[1] = temp_p2[1] + dist_y*i
  359 + temp_p2[2] = temp_p2[2] + dist_z*j
  360 +
  361 + vertices.append(temp_p1)
  362 + vertices.append(temp_p2)
  363 + #generate the points for the xz planes.
  364 + for i in range(n):
  365 + for j in range(n):
  366 + #generate the temporary vectors for both the planes
  367 + temp_p1 = copy.deepcopy(self.A)
  368 + temp_p2 = copy.deepcopy(self.A)
  369 + temp_p2[1] = self.B[1]
  370 +
  371 + temp_p1[0] = temp_p1[0] + dist_x*i
  372 + temp_p1[2] = temp_p1[2] + dist_z*j
  373 +
  374 + temp_p2[0] = temp_p2[0] + dist_x*i
  375 + temp_p2[2] = temp_p2[2] + dist_z*j
  376 +
  377 + vertices.append(temp_p1)
  378 + vertices.append(temp_p2)
  379 +
  380 + #generate the points for the xy planes.
  381 + for i in range(n):
  382 + for j in range(n):
  383 + #generate the temporary vectors for both the planes
  384 + temp_p1 = copy.deepcopy(self.A)
  385 + temp_p2 = copy.deepcopy(self.A)
  386 + temp_p2[2] = self.B[2]
  387 +
  388 + temp_p1[0] = temp_p1[0] + dist_x*i
  389 + temp_p1[1] = temp_p1[1] + dist_y*j
  390 +
  391 + temp_p2[0] = temp_p2[0] + dist_x*i
  392 + temp_p2[1] = temp_p2[1] + dist_y*j
  393 +
  394 + vertices.append(temp_p1)
  395 + vertices.append(temp_p2)
  396 +
  397 +
  398 + vertices = list(np.unique(np.array(vertices), axis=0))
  399 + import matplotlib.pyplot as plt
  400 + fig = plt.figure()
  401 + ax = plt.axes(projection='3d')
  402 + ax.scatter3D(np.array(vertices)[:, 0], np.array(vertices)[:, 1], np.array(vertices)[:, 2])
  403 +
  404 + print("THERE ARE THIS MANY ", len(vertices))
  405 + return vertices
  406 +
  407 + def getVolume(self):
  408 + size = abs(self.A - self.B)
  409 + return size[0]*size[1]*size[2]
  410 +
  411 + def vertices(self):
  412 + size = abs(self.A - self.B)
  413 + points = []
  414 + points.append(self.A)
  415 +
  416 +
  417 + temp = copy.deepcopy(self.A)
  418 + temp[0] = temp[0] + size[0]
  419 + points.append(temp)
  420 + print('1', temp)
  421 +
  422 + temp = copy.deepcopy(self.A)
  423 + temp[1] = temp[1] + size[1]
  424 + points.append(temp)
  425 + print('1', temp)
  426 +
  427 + temp = copy.deepcopy(self.A)
  428 + temp[2] = temp[2] + size[2]
  429 + points.append(temp)
  430 + print('1', temp)
  431 +
  432 + temp = copy.deepcopy(self.A)
  433 + temp[1] = temp[1] + size[1]
  434 + temp[0] = temp[0] + size[0]
  435 + points.append(temp)
  436 + print('1', temp)
  437 +
  438 + temp = copy.deepcopy(self.A)
  439 + temp[2] = temp[2] + size[2]
  440 + temp[0] = temp[0] + size[0]
  441 + points.append(temp)
  442 + print('1', temp)
  443 +
  444 + temp = copy.deepcopy(self.A)
  445 + temp[1] = temp[1] + size[1]
  446 + temp[2] = temp[2] + size[2]
  447 + points.append(temp)
  448 + print('1', temp)
  449 +
  450 + temp = copy.deepcopy(self.A)
  451 + temp[0] = temp[0] + size[0]
  452 + temp[1] = temp[1] + size[1]
  453 + temp[2] = temp[2] + size[2]
  454 + points.append(temp)
  455 + print('1', temp)
  456 +
  457 + return points
  458 +
  459 +
  460 +'''
  461 +Class defining the BFS traversal of all the vertices in the graph and labeling
  462 +all interconnected vertices.
  463 +'''
  464 +class VisitorClassDisconnected(gt.BFSVisitor):
  465 + def __init__(self, ecluster, cluster, c):
  466 + self.ecluster = ecluster
  467 + self.cluster = cluster
  468 + self.c = c
  469 +
  470 + def examine_vertex(self, u):
  471 + if(self.cluster[u] == -1):
  472 + self.cluster[u] = self.c
  473 +
  474 + def examine_edge(self, e):
  475 + if(self.ecluster[e] == -1):
  476 + self.ecluster[e] = self.c
  477 +
  478 +
  479 +'''
  480 +Class defining the BFS traversal of all the vertices in the graph and labeling
  481 +all interconnected vertices.
  482 +'''
  483 +class VisitorClassPartition(gt.BFSVisitor):
  484 + def __init__(self, cluster, dist, c, iterations):
  485 + self.cluster = cluster
  486 + self.dist = dist
  487 + self.c = c
  488 + self.total_iter = iterations
  489 +
  490 + def examine_vertex(self, u):
  491 + if(self.cluster[u] == -1):
  492 + self.cluster[u] = self.c
  493 +
  494 + def tree_edge(self, e):
  495 + d = self.dist[e.source()]
  496 + #print(d, self.total_iter)
  497 + if d <= self.total_iter:
  498 + self.dist[e.target()] = self.dist[e.source()] + 1
  499 +
  500 +class Network:
  501 + def __init__(self, filename, clock=False):
  502 + if clock:
  503 + start_time = time.time()
  504 +
  505 + with open(filename, "rb") as file:
  506 + header = file.read(72)
  507 + bytes = file.read(4)
  508 + numVertex = int.from_bytes(bytes, byteorder='little')
  509 + bytes = file.read(4)
  510 + numEdges = int.from_bytes(bytes, byteorder='little')
  511 +
  512 + self.N = []
  513 + self.F = []
  514 + for i in range(numVertex):
  515 + node = NWT.readVertex(file)
  516 + self.N.append(node)
  517 +
  518 + for i in range(numEdges):
  519 + edge = NWT.readFiber(file)
  520 + self.F.append(edge)
  521 + if clock:
  522 + print("Network initialization: " + str(time.time() - start_time) + "s")
  523 + '''
  524 + Takes in a graph object (networkx Graph) and saves it as a network file
  525 + Resulting nwt, does not contain incomind or outgoing fibers
  526 + '''
  527 + def saveGraph(self, G, path):
  528 + G_nodes = list(G.nodes())
  529 + G_edges = list(G.edges())
  530 + Nodes = []
  531 + Edges = []
  532 + points = list(nx.get_node_attributes(G, 'p').values())
  533 + for i in range(len(G_nodes)):
  534 + node = Node(points[i], [], [])
  535 + Nodes.append(node)
  536 + for e in G_edges:
  537 + edge = Fiber(e[0], e[1], G[e[0]][e[1]]['pts'], G[e[0]][e[1]]['rads'])
  538 + Edges.append(edge)
  539 +
  540 + NWT.exportNWT(path, Nodes, Edges)
  541 +
  542 + '''
  543 + Saves the graph-tool graph as a nwt file with all the variables.
  544 + '''
  545 +
  546 + def saveGraph_gt(self, G, path):
  547 + points = G.vertex_properties["p"]
  548 + Nodes = []
  549 + Edges = []
  550 + for i in range(G.num_vertices()):
  551 + #array = G.get_out_edges(i)
  552 + outgoing = []
  553 + for edge in G.get_out_edges(i):
  554 + outgoing.append(edge[2])
  555 +
  556 + node = Node(points[i], outgoing, outgoing)
  557 + Nodes.append(node)
  558 +
  559 + for e in G.edges():
  560 + x = G.edge_properties["x"][e].get_array()
  561 + y = G.edge_properties["y"][e].get_array()
  562 + z = G.edge_properties["z"][e].get_array()
  563 + r = G.edge_properties["r"][e].get_array()
  564 + pts = []
  565 + for i in range(len(x)):
  566 + pt = []
  567 + pt.append(x[i])
  568 + pt.append(y[i])
  569 + pt.append(z[i])
  570 + pts.append(pt)
  571 + #pts = np.ndarray.tolist(np.dstack([x,y,z]))
  572 + #print(pts)
  573 + edge = Fiber(int(e.source()), int(e.target()), pts, np.ndarray.tolist(r))
  574 + if(edge.length() > 0.0):
  575 + Edges.append(edge)
  576 +
  577 + NWT.exportNWT(path, Nodes, Edges)
  578 +
  579 +
  580 +
  581 + def saveGraphStatistics_gt(self, G, path, prefix, label = "none", norm=False):
  582 + location = path + "/" + prefix + "_edges.txt"
  583 + f = open(location, "a+")
  584 + f.write("inverted\t")
  585 + f.write("length\t")
  586 + f.write("tortuosity\t")
  587 + f.write("volume\t")
  588 + f.write("inverted_volume\t")
  589 + f.write("gaussian\t")
  590 + f.write("bc\t")
  591 + f.write("bc*length\t")
  592 + f.write("mst\t\n")
  593 + for e in G.edges():
  594 + f.write("%.15f\t" % G.edge_properties["inverted"][e])
  595 + f.write("%.15f\t" % G.edge_properties["length"][e])
  596 + f.write("%.15f\t" % G.edge_properties["tortuosity"][e])
  597 + f.write("%.15f\t" % G.edge_properties["volume"][e])
  598 + f.write("%.15f\t" % G.edge_properties["inverted_volume"][e])
  599 + f.write("%.15f\t" % G.edge_properties["gaussian"][e])
  600 + f.write("%.15f\t" % G.edge_properties["bc"][e])
  601 + f.write("%.15f\t" % G.edge_properties["bc*length"][e])
  602 + f.write("%.15f\t" % G.edge_properties["mst"][e])
  603 + f.write("%s\n" % label)
  604 +
  605 + f.close()
  606 +
  607 + location = path+"/" + prefix + "_vertices.txt"
  608 + f = open(location, "a+")
  609 + f.write("bc\t")
  610 + f.write("degree\t")
  611 + f.write("degree_volume\t")
  612 + f.write("degree_tortuosity\t\n")
  613 + for v in G.vertices():
  614 + f.write("%.15f\t" % G.vertex_properties["bc"][v])
  615 + f.write("%.15f\t" % G.vertex_properties["degree"][v])
  616 + f.write("%.15f\t" % G.vertex_properties["degree_volume"][v])
  617 + f.write("%.15f\t" % G.vertex_properties["degree_tortuosity"][v])
  618 + f.write("%s\n" % label)
  619 +
  620 + '''
  621 + Saves the graph as a sample. Additionally saves the key as a list of string if writeKey is True
  622 + saves the file at path with prefix_s.txt
  623 + '''
  624 + def saveSample_gt(self, G, path, prefix, is_dual, label = "none", norm=False, writeKey=False):
  625 + location = path + "/" + prefix + "s.txt"
  626 + f = open(location, "a+")
  627 +
  628 +
  629 + aabb = AABB(G, is_dual)
  630 + array = G.vertex_properties["bc"].get_array()
  631 + av_array = np.sum(array)/G.num_vertices()
  632 + G.graph_properties["global_vertex_bc"] = G.new_graph_property("double", val = av_array)
  633 + f.write("%.15f\t" % av_array)
  634 +
  635 + array = G.vertex_properties["bc"].get_array()
  636 + av_array = np.sum(array)/aabb.getVolume()
  637 + G.graph_properties["global_vascular_vertex_bc"] = G.new_graph_property("double", val = av_array)
  638 + f.write("%.15f\t" % av_array)
  639 +
  640 + array = G.edge_properties["bc"].get_array()
  641 + av_array = np.sum(array)/aabb.getVolume()
  642 + G.graph_properties["global_vascular_edge_bc"] = G.new_graph_property("double", val = av_array)
  643 + f.write("%.15f\t" % av_array)
  644 +
  645 + #G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G)
  646 + G.graph_properties["global_mst_ratio"] = G.new_graph_property("double", val = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges()))
  647 + f.write("%.15f\t" % G.graph_properties["global_mst_ratio"])
  648 +
  649 + #G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G, weights=G.edge_properties["volume"])
  650 + G.graph_properties["global_mst_ratio_vascular_volume"] = G.new_graph_property("double", val = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges())/aabb.getVolume())
  651 + f.write("%.15f\t" % G.graph_properties["global_mst_ratio"])
  652 +
  653 + ########################Local Statistics###############################
  654 + self.recalculate_metrics(G, is_dual, norm)
  655 + ########################Biological Statistics##########################
  656 + # Some biological statistics are reported as a ratio to physical volume of vessels
  657 + array = G.edge_properties["length"].get_array()
  658 + av_array = np.sum(array)/G.num_edges()
  659 + G.graph_properties["average_length"] = G.new_graph_property("double", val= av_array)
  660 + f.write("%.15f\t" % av_array)
  661 +
  662 + array = G.edge_properties["volume"].get_array()
  663 + av_array = np.sum(array)/G.num_edges()
  664 + G.graph_properties["average_volume"] = G.new_graph_property("double", val= av_array)
  665 + f.write("%.15f\t" % av_array)
  666 +
  667 + array = G.edge_properties["tortuosity"].get_array()
  668 + av_array = np.sum(array)/G.num_edges()
  669 + G.graph_properties["average_tortuosity"] = G.new_graph_property("double", val= av_array)
  670 + f.write("%.15f\t" % av_array)
  671 +
  672 + array = G.edge_properties["length"].get_array()
  673 + av_array = np.sum(array)/aabb.getVolume()
  674 + G.graph_properties["vascular length"] = G.new_graph_property("double", val= av_array)
  675 + f.write("%.15f\t" % av_array)
  676 +
  677 + array = G.edge_properties["volume"].get_array()
  678 + av_array = np.sum(array)/aabb.getVolume()
  679 + G.graph_properties["vascular_volume"] = G.new_graph_property("double", val= av_array)
  680 + f.write("%.15f\t" % av_array)
  681 +
  682 + array = G.edge_properties["tortuosity"].get_array()
  683 + av_array = np.sum(array)/aabb.getVolume()
  684 + G.graph_properties["vascular_tortuosity"] = G.new_graph_property("double", val= av_array)
  685 + f.write("%.15f\t" % av_array)
  686 + ############################Graph Statistics###########################
  687 +
  688 + # Some Graph Statistics are reported as values representing the graph
  689 + # Some statistics are reported as a function of volume.
  690 + array = G.vertex_properties["degree"].get_array()
  691 + av_array = np.sum(array)/G.num_vertices()
  692 + G.graph_properties["local_average_degree"] = G.new_graph_property("double", val = av_array)
  693 + f.write("%.15f\t" % av_array)
  694 +
  695 + array = G.vertex_properties["degree_volume"].get_array()
  696 + av_array = np.sum(array)/G.num_vertices()
  697 + G.graph_properties["local_average_degree_volume"] = G.new_graph_property("double", val = av_array)
  698 + f.write("%.15f\t" % av_array)
  699 +
  700 + array = G.vertex_properties["degree_tortuosity"].get_array()
  701 + av_array = np.sum(array)/G.num_vertices()
  702 + G.graph_properties["local_average_degree_tortuosity"] = G.new_graph_property("double", val = av_array)
  703 + f.write("%.15f\t" % av_array)
  704 +
  705 + array = G.vertex_properties["bc"].get_array()
  706 + av_array = np.sum(array)/G.num_vertices()
  707 + G.graph_properties["local_vertex_bc"] = G.new_graph_property("double", val = av_array)
  708 + f.write("%.15f\t" % av_array)
  709 +
  710 + array = G.vertex_properties["degree"].get_array()
  711 + av_array = np.sum(array)/aabb.getVolume()
  712 + G.graph_properties["local_vascular_degree"] = G.new_graph_property("double", val = av_array)
  713 + f.write("%.15f\t" % av_array)
  714 +
  715 + array = G.vertex_properties["degree_volume"].get_array()
  716 + av_array = np.sum(array)/aabb.getVolume()
  717 + G.graph_properties["local_vascular_degree_volume"] = G.new_graph_property("double", val = av_array)
  718 + f.write("%.15f\t" % av_array)
  719 +
  720 + array = G.vertex_properties["degree_tortuosity"].get_array()
  721 + av_array = np.sum(array)/aabb.getVolume()
  722 + G.graph_properties["local_vascular_degree_tortuosity"] = G.new_graph_property("double", val = av_array)
  723 + f.write("%.15f\t" % av_array)
  724 +
  725 + array = G.vertex_properties["bc"].get_array()
  726 + av_array = np.sum(array)/aabb.getVolume()
  727 + G.graph_properties["local_vascular_vertex_bc"] = G.new_graph_property("double", val = av_array)
  728 + f.write("%.15f\t" % av_array)
  729 +
  730 + array = G.edge_properties["bc"].get_array()
  731 + av_array = np.sum(array)/aabb.getVolume()
  732 + G.graph_properties["local_vascular_edge_bc"] = G.new_graph_property("double", val = av_array)
  733 + f.write("%.15f\t" % av_array)
  734 +
  735 + G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G)
  736 + G.graph_properties["local_mst_ratio"] = G.new_graph_property("double", val = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges()))
  737 + f.write("%.15f\t" % G.graph_properties["local_mst_ratio"])
  738 +
  739 + G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G, weights=G.edge_properties["volume"])
  740 + G.graph_properties["local_mst_ratio_vascular_volume"] = G.new_graph_property("double", val = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges())/aabb.getVolume())
  741 + f.write("%.15f\t" % G.graph_properties["local_mst_ratio_vascular_volume"])
  742 +
  743 + [lE, lV] = gt.graph_tool.centrality.eigenvector(G)
  744 + G.graph_properties["Largest Eigenvector"] = G.new_graph_property("double", val = lE)
  745 + f.write("%.15f\t" % lE)
  746 + ############################# hybrid metrics ##########################
  747 + self.add_rst_metric(G, is_dual=is_dual)
  748 + f.write("%.15f\t" % G.graph_properties["sensitivity"])
  749 + f.write("%s" % label)
  750 + f.write("\n")
  751 +
  752 + f.close()
  753 + if(writeKey):
  754 + location = path + "/key.txt"
  755 + f = open(location, "w+")
  756 + f.write("%s\t" % "global_vertex_bc")
  757 + f.write("%s\t" % "global_vascular_vertex_bc")
  758 + f.write("%s\t" % "global_vascular_edge_bc")
  759 + f.write("%s\t" % "global_mst_ratio")
  760 + f.write("%s\t" % "global_mst_ratio_vascular_volume")
  761 + f.write("%s\t" % "average_length")
  762 + f.write("%s\t" % "average_volume")
  763 + f.write("%s\t" % "average_tortuosity")
  764 + f.write("%s\t" % "vascular_length")
  765 + f.write("%s\t" % "vascular_volume")
  766 + f.write("%s\t" % "vascular_tortuosity")
  767 + ############################Graph Statistics###########################
  768 + f.write("%s\t" % "local_average_degree")
  769 + f.write("%s\t" % "local_average_degree_volume")
  770 + f.write("%s\t" % "local_average_degree_tortuosity")
  771 + f.write("%s\t" % "local_vertex_bc")
  772 + f.write("%s\t" % "local_vascular_degree")
  773 + f.write("%s\t" % "local_vascular_degree_volume")
  774 + f.write("%s\t" % "local_vascular_degree_tortuosity")
  775 + f.write("%s\t" % "local_vascular_vertex_bc")
  776 + f.write("%s\t" % "local_vascular_edge_bc")
  777 + f.write("%s\t" % "local_mst_ratio")
  778 + f.write("%s\t" % "local_mst_ratio_vascular_volume")
  779 + f.write("%s\t" % "Largest Eigenvector")
  780 + f.write("%s\t" % "label")
  781 + f.write("%s\t" % "sensitivity")
  782 + f.close()
  783 +
  784 +
  785 + '''
  786 + Creates a graph from a list of nodes and a list of edges.
  787 + Uses edge length as weight.
  788 + Returns a NetworkX Object.
  789 + '''
  790 + def createLengthGraph(self):
  791 + G = nx.Graph()
  792 + for i in range(len(self.N)):
  793 + G.add_node(i, p=self.N[i].p)
  794 + for i in range(len(self.F)):
  795 + G.add_edge(self.F[i].v0, self.F[i].v1, weight = self.F[i].length())
  796 + G[self.F[i].v0][self.F[i].v1]['pts'] = self.F[i].points
  797 + G[self.F[i].v0][self.F[i].v1]['rads'] = self.F[i].radii
  798 +
  799 + return G
  800 +
  801 + '''
  802 + filters all the vertices that are close to the border with deg=1
  803 + As well as affiliated edges. It is recommended thatthe graph have all the
  804 + graph metrics refreshed after filtering.
  805 + '''
  806 +
  807 + def filterBorder(self, G, is_dual=False):
  808 + bb = AABB(G, is_dual)
  809 + TFv = G.new_vertex_property("bool", vals = np.full((G.num_vertices(),1), True, dtype=bool))
  810 + TFe = G.new_edge_property("bool", vals = np.full((G.num_edges(),1), True, dtype=bool))
  811 + if(is_dual==False):
  812 + for v in G.vertices():
  813 + pt = np.array([G.vertex_properties["p"][v][0], G.vertex_properties["p"][v][1], G.vertex_properties["p"][v][2]])
  814 + if G.vertex_properties["degree"][v] == 1 and bb.distance(pt) < 5.0:
  815 + TFv[v] = False
  816 + for e in v.all_edges():
  817 + TFe[G.edge(e.source(), e.target())] = False
  818 + else:
  819 + #print("I have entered this method")
  820 + for v in G.vertices():
  821 + l = len(G.vertex_properties["x"][v])
  822 + pt_0 = np.array([G.vertex_properties["x"][v][0], G.vertex_properties["y"][v][0], G.vertex_properties["z"][v][0]])
  823 + pt_1 = np.array([G.vertex_properties["x"][v][l-1], G.vertex_properties["y"][v][l-1], G.vertex_properties["z"][v][l-1]])
  824 + if (G.vertex_properties["degree"][v] == 2):
  825 + if ((bb.distance(pt_0) < 5.0) or (bb.distance(pt_1) < 5.0)):
  826 + #print("length", l, ": ", G.vertex_properties["x"][v])
  827 + #print("degree", G.vertex_properties["degree"][v])
  828 + TFv[v] = False
  829 + for e in v.all_edges():
  830 + TFe[G.edge(e.source(), e.target())] = False
  831 +
  832 + G.set_filters(TFe, TFv)
  833 + G1 = gt.Graph(G, directed=False, prune=True)
  834 + G.clear_filters()
  835 + G1.clear_filters()
  836 +
  837 + return G1
  838 +
  839 +
  840 + '''
  841 + Returns a Graph object that retains all the properties of the original
  842 + if return_largest is set as false, then all graphs with more than 20 nodes
  843 + are returned in a list. Metrics might have to be recalculated after
  844 + It is recommended that the graph have all the metrics necessary prior to filtering
  845 + In order to avoid internal boost index errors.
  846 + '''
  847 + def filterDisconnected(self, G, return_largest=True):
  848 + #create masks
  849 + clusters = G.new_vertex_property("int", vals=np.full((G.num_vertices(), 1), -1, dtype=int))
  850 + eclusters = G.new_edge_property("int", vals=np.full((G.num_edges(), 1), -1, dtype=int))
  851 + c = 0
  852 +
  853 + #run a bfs that sets visited nodes on each vertex
  854 + for i in G.vertices():
  855 + if clusters[i] == -1:
  856 + gt.bfs_search(G, i, VisitorClassDisconnected(eclusters, clusters, c))
  857 + c = c + 1
  858 +
  859 + #find the number of clusters
  860 + unique, counts = np.unique(clusters.get_array(), return_counts=True)
  861 + eunique, ecounts = np.unique(clusters.get_array(), return_counts=True)
  862 +
  863 + if(return_largest == True):
  864 + #find the argument of the largest
  865 + a = counts.argmax()
  866 + b = ecounts.argmax()
  867 +
  868 + #turn the largest cluster into a TF graph mask for the vertices and the edges
  869 + TFv = G.new_vertex_property("bool", vals = np.full((G.num_vertices(), 1), False, dtype=bool))
  870 + TFe = G.new_edge_property("bool", vals = np.full((G.num_edges(),1), False, dtype=bool))
  871 + for i in G.vertices():
  872 + if clusters[i] == unique[a]:
  873 + TFv[i] = True
  874 +
  875 + e = G.get_edges();
  876 + for i in range(len(e)):
  877 + if eclusters[G.edge(G.vertex(e[i][0]), G.vertex(e[i][1]))] == eunique[b]:
  878 + TFe[G.edge(G.vertex(e[i][0]), G.vertex(e[i][1]))] = True
  879 + #set the filters and return the vertices
  880 +
  881 + G.set_filters(TFe, TFv)
  882 + G1=gt.Graph(G, directed=False, prune=True)
  883 + G.clear_filters()
  884 + G1.clear_filters()
  885 + return G1
  886 + else:
  887 + Graphs = []
  888 + #repeat the process above for each unique disconncted component.
  889 + for j in range(len(unique)):
  890 + if(counts[j] > 20):
  891 + TFv = G.new_vertex_property("bool", vals = np.full((G.num_vertices(),1), False, dtype=bool))
  892 + TFe = G.new_edge_property("bool", vals = np.full((G.num_edges(),1), False, dtype=bool))
  893 + for i in range(G.num_vertices()):
  894 + if clusters[G.vertex(i)] == unique[j]:
  895 + TFv[G.vertex(i)] = True
  896 +
  897 + e = G.get_edges();
  898 + for i in range(len(e)):
  899 + if eclusters[G.edge(G.vertex(e[i][0]), G.vertex(e[i][1]))] == eunique[j]:
  900 + TFe[G.edge(G.vertex(e[i][0]), G.vertex(e[i][1]))] = True
  901 + G.set_filters(TFe, TFv)
  902 + G1=gt.Graph(G, directed=False, prune=True)
  903 + G.clear_filters()
  904 + G1.clear_filters()
  905 + Graphs.append(G1)
  906 + return Graphs
  907 +
  908 +
  909 + def simulate_fractures(self, G, remove=10):
  910 + num_removed = int(np.floor(G.num_edges()*remove/100))
  911 + print("num of edges to begin with = ", G.num_edges())
  912 + indices = np.random.randint(0, int(G.num_edges()), size = [num_removed,1])
  913 + #aabb = AABB(G, is_dual)
  914 + tf = np.full((G.num_edges(), 1), True, dtype=bool)
  915 + for j in range(indices.shape[0]):
  916 + tf[indices[j]] = False
  917 + TFe = G.new_edge_property("bool", vals = tf)
  918 + G.set_edge_filter(TFe)
  919 + G1 = gt.Graph(G, directed=False, prune=True)
  920 + G.clear_filters()
  921 + G1.clear_filters()
  922 + G1 = self.filterDisconnected(G1)
  923 + G1.vertex_properties["degree"] = G1.degree_property_map("total")
  924 + print("num of edges left = ", G1.num_edges())
  925 + print("I should have removed, ", num_removed)
  926 + print("Instead I removed, ", G.num_edges() - G1.num_edges())
  927 +
  928 + return G1
  929 +
  930 + '''
  931 + Adds the mst based- sensitivity metrics. N is the number of random removals
  932 + remove is the percentage*100 of the vessels removed in each iteration.
  933 + The number of vessels removed will be floor(num_edges*remove/100)
  934 + '''
  935 + def add_rst_metric(self, G, N=100, remove=10, is_dual=False):
  936 + #rst_ratio = G.new_graph_property("double")
  937 + rst_r = 0
  938 + #G.nu
  939 + num_removed = int(np.floor(G.num_edges()*remove/100))
  940 + indices = np.random.randint(0, int(G.num_edges()), size = [N, num_removed])
  941 + aabb = AABB(G, is_dual)
  942 +
  943 + for i in range(N):
  944 + tf = np.full((G.num_edges(),1), True, dtype=bool)
  945 + for j in range(num_removed):
  946 + tf[indices[i, j]] = False
  947 +# rst = gt.graph_tool.topology.min_spanning_tree(G, weights = G.)
  948 +# ratio = np.double(np.sum(rst.get_array()))/np.double(G.num_edges())
  949 +# rst_dist.append(ratio)
  950 +# rst_r = rst_r + ratio
  951 + TFe = G.new_edge_property("bool", vals = tf)
  952 + G.set_edge_filter(TFe)
  953 + G1 = gt.Graph(G, directed=False, prune=True)
  954 + G.clear_filters()
  955 + G1.clear_filters()
  956 + while(np.any(G1.degree_property_map("total").get_array() == True)):
  957 + #print(np.any(G1.degree_property_map("total").get_array()), " ", i)
  958 + G1 = self.filterDisconnected(G1)
  959 + G1 = self.filterBorder(G1, is_dual)
  960 + G1 = self.recalculate_metrics(G1, is_dual)
  961 +
  962 + if(not is_dual and G1.num_edges() > 2):
  963 + G1.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G1, weights=G1.edge_properties["volume"])
  964 + tvalues = G1.edge_properties["mst"].get_array()
  965 + #print(tvalues)
  966 + values = G1.edge_properties["inverted_volume"].get_array()
  967 + #print(values)
  968 + for k in range(G1.num_edges()):
  969 + if(tvalues[k] == True):
  970 + rst_r = rst_r + values[k]
  971 + elif(is_dual and G1.num_edges() > 2):
  972 + #This is non-sensical for now.
  973 + G1.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G1)
  974 + tvalues = G1.vertex_properties["mst"].get_array()
  975 + values = G1.edge_properties["inverted_volume"].get_array()
  976 + for k in range(G1.num_edges()):
  977 + if(tvalues[k] == True):
  978 + rst_r = rst_r + values[k]
  979 +
  980 + G.graph_properties["sensitivity"] = G.new_graph_property("double", rst_r/N)
  981 +
  982 + return G
  983 +
  984 + '''
  985 + Re-calculates connectivity based metrics and returns the resulting graph
  986 + '''
  987 + def recalculate_metrics(self, G, is_dual=False, normalize=False):
  988 + gt.graph_tool.centrality.betweenness(G, vprop=G.vertex_properties["bc"], eprop=G.edge_properties["bc"], norm=normalize)
  989 + if(is_dual == False):
  990 + #gt.graph_tool.centrality.betweenness(G, G.vertex_properties["bc"], G.edge_properties["bc"], norm=True, is_dual=False)
  991 + #generate minimum spanning tree
  992 +
  993 + G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G, weights=G.edge_properties["length"])
  994 + G.graph_properties["mst_ratio"] = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges())
  995 + print(np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges()))
  996 + G.vertex_properties["degree"] = G.degree_property_map("total")
  997 + G.vertex_properties["degree_volume"] = G.degree_property_map("total", weight=G.edge_properties["volume"])
  998 + G.vertex_properties["degree_tortuosity"] = G.degree_property_map("total", G.edge_properties["tortuosity"])
  999 +
  1000 + else:
  1001 + #gt.graph_tool.centrality.betweenness(G, G.vertex_properties["bc"], G.edge_properties["bc"], norm=True, is_dual=False)
  1002 + G.edge_properties["mst"] = gt.graph_tool.topology.min_spanning_tree(G, weights=G.edge_properties["bc"]) #Boolean value deligating belongance to the minimum spanning tree.
  1003 + G.graph_properties["mst_ratio"] = np.double(np.sum(G.edge_properties["mst"].get_array()))/np.double(G.num_edges())
  1004 + G.vertex_properties["degree"] = G.degree_property_map("total")
  1005 + G.vertex_properties["degree_centrality"] = G.degree_property_map("total", weight=G.edge_properties["bc"])
  1006 +
  1007 + return G
  1008 +
  1009 + '''
  1010 + Filter the graph to remove the disconnected components of the graph if
  1011 + disconnected is set to true. if borders are set to True, the algorithm
  1012 + cleans up the nodes with degree one that are close to the edge of the volume.
  1013 + Returns a Graph object with updated internal property maps.
  1014 +
  1015 + if return_largerst == False, will return an array of graphs.
  1016 + disconnected == apply the disconnected componenet filter
  1017 + return_largest == if false returns the largest set of graphs (n > 10)
  1018 + borders == True, if true applies the border (deg == 1 near border) filter
  1019 + add_rst == True, if true adds the rst_metric
  1020 + '''
  1021 + def filterFullGraph_gt(self, G, disconnected=True, return_largest=True, borders=True, erode = False, add_rst=False, dual=False):
  1022 + if(disconnected==True):
  1023 + if(return_largest==True and borders==True):
  1024 + G1 = self.filterDisconnected(G, return_largest)
  1025 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1026 + G1 = self.filterBorder(G1, is_dual=dual)
  1027 + if(erode == True):
  1028 + while(np.any(G1.degree_property_map("total").get_array() == True)):
  1029 + G1 = self.filterBorder(G1, is_dual=dual)
  1030 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1031 + else:
  1032 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1033 + if(add_rst == True):
  1034 + G1 = self.add_rst_metric(G1)
  1035 + elif(return_largest==True and borders==False):
  1036 + G1 = self.filterDisconnected(G, return_largest)
  1037 + if(erode == True):
  1038 + while(np.any(G1.degree_property_map("total").get_array() == True)):
  1039 + G1 = self.filterBorder(G1, is_dual=dual)
  1040 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1041 + else:
  1042 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1043 + if(add_rst == True):
  1044 + G1 = self.add_rst_metric(G1)
  1045 + elif(return_largest==False and borders == True):
  1046 + G1 = self.filterDisconnected(G, return_largest)
  1047 + for graph in G1:
  1048 + graph = self.recalculate_metrics(graph, is_dual=dual)
  1049 + graph = self.filterBorder(graph, is_dual=dual)
  1050 + if(erode == True):
  1051 + while(np.any(graph.degree_property_map("total").get_array() == True)):
  1052 + graph = self.filterBorder(graph, is_dual=dual)
  1053 + graph = self.recalculate_metrics(graph, is_dual=dual)
  1054 + else:
  1055 + graph = self.recalculate_metrics(graph, is_dual=dual)
  1056 + if(add_rst == True):
  1057 + graph = self.add_rst_metric(graph)
  1058 + else:
  1059 + G1 = self.filterDisconnected(G, return_largest)
  1060 + for graph in G1:
  1061 + graph = self.recalculate_metrics(graph, is_dual=dual)
  1062 + if(add_rst == True):
  1063 + graph = self.add_rst_metric(graph)
  1064 + else:
  1065 + G1 = self.filterBorder(G, is_dual=dual);
  1066 + if(erode == True):
  1067 + while(np.any(G1.degree_property_map("total").get_array() == True)):
  1068 + print(G1.num_vertices())
  1069 + G1 = self.filterBorder(G1, is_dual=dual)
  1070 + print(G1.num_vertices())
  1071 + G1 = self.recalculate_metrics(G1, is_dual=dual, )
  1072 + else:
  1073 + G1 = self.recalculate_metrics(G1, is_dual=dual)
  1074 + if(add_rst==True):
  1075 + G1 = self.add_rst_metric(G1)
  1076 +
  1077 + return G1
  1078 +
  1079 + '''
  1080 + Accretes the graph in G using the nodes and edges in G_0
  1081 + Assumes that G_0 has all the same properties as G, including "idx"
  1082 + idx is an immutable set of indices that do not change when the graph is
  1083 + modified
  1084 + '''
  1085 + def accrete(self, G, G_0):
  1086 +
  1087 + v_filters = G_0.new_vertex_property("bool", vals = np.full((G_0.num_vertices(),1), False, dtype=bool))
  1088 + e_filters = G_0.new_edge_property("bool", vals = np.full((G_0.num_edges(),1), False, dtype=bool))
  1089 + for v in G.vertices():
  1090 + #print(v)
  1091 + G_0_vertex = G_0.vertex(G.vertex_properties["idx"][v])
  1092 + v_filters[G_0_vertex] = True
  1093 + for e in G_0_vertex.all_edges():
  1094 + e_filters[e] = True
  1095 + for v_0 in G_0_vertex.all_neighbors():
  1096 + v_filters[v_0] = True
  1097 +
  1098 + G_0.set_filters(e_filters, v_filters)
  1099 +
  1100 + graph = gt.Graph(G_0, directed=False, prune=True)
  1101 + G_0.clear_filters()
  1102 +
  1103 + return graph
  1104 +
  1105 +
  1106 +
  1107 +
  1108 + '''
  1109 + Creates a graph from a list of nodes and a list of edges
  1110 + The graph is the "inverse" of the original graph,
  1111 + Vertices are edges and edges are
  1112 +
  1113 + '''
  1114 + def createDualGraph_gt(self):
  1115 + #Generate the undirected graph
  1116 + G = gt.Graph()
  1117 + G.set_directed(False)
  1118 +
  1119 + #add all the required vertex properties
  1120 + vpos = G.new_vertex_property("vector<double>") #original position of the edge (placed as midpoin of edge)
  1121 + pos = G.new_vertex_property("vector<double>") #positions according to the fdl
  1122 + rpos = G.new_vertex_property("vector<double>") #positions according to the radial layout
  1123 + x_edge = G.new_vertex_property("vector<double>") #x-coordinated of the edge
  1124 + y_edge = G.new_vertex_property("vector<double>") #y-coordinates of the edge
  1125 + z_edge = G.new_vertex_property("vector<double>") #z-coordinates of the edge
  1126 + r_edge = G.new_vertex_property("vector<double>") #r-values at each x,y,z
  1127 + l_edge = G.new_vertex_property("double") #length of the edge
  1128 + i_edge = G.new_vertex_property("double") #1/length
  1129 + iv_edge = G.new_vertex_property("double") #1/volume
  1130 + t_edge = G.new_vertex_property("double") #tortuosity of the edge
  1131 + v_edge = G.new_vertex_property("double") #volume of the edge
  1132 + vbetweeness_centrality = G.new_vertex_property("double") #betweeneness centrality of the vertex
  1133 + gaussian = G.new_vertex_property("double") #empty property map for gaussian values downstream
  1134 + degree = G.new_vertex_property("int")
  1135 + degree_centrality = G.new_vertex_property("int")
  1136 +
  1137 + #add all the required edge properties
  1138 + ebetweeness_centrality = G.new_edge_property("double") #betweeness centrality of the edge
  1139 + mst = G.new_edge_property("bool") #Boolean value deligating belongance to the minimum spanning tree.
  1140 + l_edge = G.new_edge_property("double") #length of the edge
  1141 + gaussian = G.new_edge_property("double")
  1142 +
  1143 +
  1144 + #add all graph properties
  1145 + mst_ratio = G.new_graph_property("double")
  1146 +
  1147 + #add all the edges as vertices
  1148 + for i in range(len(self.F)):
  1149 + if(self.F[i].length() > 0):
  1150 + G.add_vertex()
  1151 + x,y,z,r = self.F[i].getcoords()
  1152 + x_edge[G.vertex(i)] = x
  1153 + y_edge[G.vertex(i)] = y
  1154 + z_edge[G.vertex(i)] = z
  1155 + r_edge[G.vertex(i)] = r
  1156 + l_edge[G.vertex(i)] = self.F[i].length()
  1157 + gaussian[G.vertex(i)] = np.exp(-np.power(l_edge[G.vertex(i)], 2.) / (2 * np.power(60.0, 2.)))
  1158 + i_edge[G.vertex(i)] = 1/l_edge[G.vertex(i)]
  1159 + t_edge[G.vertex(i)] = self.F[i].turtuosity()
  1160 + v_edge[G.vertex(i)] = self.F[i].volume()
  1161 + iv_edge[G.vertex(i)] = 1/v_edge[G.vertex(i)]
  1162 + vpos[G.vertex(i)] = np.array([x[int(len(x)/2)], y[int(len(x)/2)], z[int(len(x)/2)]], dtype=float)
  1163 +
  1164 + #based on the neighbors add edges. If two edges share a vertex, there is a vertex between them.
  1165 + for i in range(len(self.N)):
  1166 + #in case there are 3 or more edges attached to a vertex
  1167 + if(len(self.N[i].o) > 2):
  1168 + for j in range(len(self.N[i].o)-1):
  1169 + G.add_edge(G.vertex(self.N[i].o[j]), G.vertex(self.N[i].o[j+1]))
  1170 + l_edge[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = 1
  1171 + gaussian[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = \
  1172 + np.exp(-np.power(l_edge[G.edge(G.vertex(self.N[i].o[j]),G.vertex(self.N[i].o[j+1]))], 2.) / (2 * np.power(60.0, 2.)))
  1173 + #add the final edge.
  1174 + G.add_edge(G.vertex(self.N[i].o[0]), G.vertex(self.N[i].o[len(self.N[i].o)-1]))
  1175 + l_edge[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = 1
  1176 + gaussian[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = \
  1177 + np.exp(-np.power(l_edge[G.edge(G.vertex(self.N[i].o[j]),G.vertex(self.N[i].o[j+1]))], 2.) / (2 * np.power(60.0, 2.)))
  1178 + #in case there are 2 edges attached to a vertex
  1179 + elif(len(self.N[i].o) == 2):
  1180 + G.add_edge(G.vertex(self.N[i].o[0]), G.vertex(self.N[i].o[1]-1))
  1181 + l_edge[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = 1
  1182 + gaussian[G.edge(G.vertex(self.N[i].o[j])), G.vertex(self.N[i].o[j+1])] = \
  1183 + np.exp(-np.power(l_edge[G.edge(G.vertex(self.N[i].o[j]),G.vertex(self.N[i].o[j+1]))], 2.) / (2 * np.power(60.0, 2.)))
  1184 + #in case there is only a single edge coming into the vertex we do
  1185 + # not add anything
  1186 +
  1187 + #generate centrality map
  1188 + gt.graph_tool.centrality.betweenness(G, vbetweeness_centrality, ebetweeness_centrality, norm=True)
  1189 + #generate minimum spanning tree
  1190 + mst = gt.graph_tool.topology.min_spanning_tree(G, weights=ebetweeness_centrality)
  1191 + mst_ratio[G] = np.double(np.sum(mst.get_array()))/np.double(len(self.N))
  1192 + degree = G.degree_property_map("total")
  1193 + degree_centrality = G.degree_property_map("total", weight=ebetweeness_centrality)
  1194 + #degree_tortuosity = G.degree_property_map("total", weight=t_edge)
  1195 +
  1196 + pos = gt.sfdp_layout(G)
  1197 +
  1198 + rpos = gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array())))
  1199 +
  1200 + #set property maps for the vertices
  1201 + G.vertex_properties["p"] = vpos
  1202 + G.vertex_properties["pos"] = pos
  1203 + G.vertex_properties["rpos"] = rpos
  1204 + G.vertex_properties["bc"] = vbetweeness_centrality
  1205 + G.vertex_properties["degree_centrality"] = degree_centrality
  1206 + G.vertex_properties["degree"] = degree
  1207 + G.vertex_properties["x"] = x_edge
  1208 + G.vertex_properties["y"] = y_edge
  1209 + G.vertex_properties["z"] = z_edge
  1210 + G.vertex_properties["r"] = r_edge
  1211 + G.vertex_properties["inverted"] = i_edge
  1212 + G.vertex_properties["inverted_volume"] = iv_edge
  1213 + G.vertex_properties["length"] = l_edge
  1214 + G.vertex_properties["tortuosity"] = t_edge
  1215 + G.vertex_properties["volume"] = v_edge
  1216 + G.vertex_properties["bc"] = vbetweeness_centrality
  1217 +
  1218 + #G.vertex_properties["pos"] = gt.fruchterman_reingold_layout(G, weight=G.edge_properties["length"], circular = True, n_iter= 10000, pos=G.vertex_properties["pos"], r = 2.0)
  1219 + G.vertex_properties["pos"] = gt.sfdp_layout(G, C = 0.5, p = 3.0)
  1220 + #set property maps for the edges
  1221 + G.edge_properties["bc"] = ebetweeness_centrality
  1222 + G.edge_properties["mst"] = mst
  1223 +
  1224 + #set graph properies
  1225 + G.graph_properties["mst_ratio"] = mst_ratio
  1226 +
  1227 +
  1228 +
  1229 +# title = "raw_dual_cortical_7_6_before_delete.pdf"
  1230 +# title2 = "raw_dual_radial_cordical_7_6_before_delete.pdf"
  1231 +#
  1232 +# gt.graph_draw(G, pos=gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array()))), edge_pen_width = 2.0, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1233 +# gt.graph_draw(G, pos=pos, edge_pen_width = 4.0, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1234 +#
  1235 + title = "./Original_2D_Graph.png"
  1236 +# title2 = "raw_dual_radial_cordical_7_6_after_delete.pdf"
  1237 +#
  1238 + G1 = self.filterFullGraph_gt(G, borders=False, dual=True)
  1239 +
  1240 +# #print(clusters.get_array()[:], clusters.get_array()[:])
  1241 +# #pos = gt.sfdp_layout(G)
  1242 +#
  1243 +# #gt.graph_draw(G, pos=,edge_color=mst, vertex_fill_color=vbetweeness_centrality, edge_pen_width=ebetweeness_centrality, output="raw.pdf")
  1244 +#
  1245 +# gt.graph_draw(G1, pos=gt.radial_tree_layout(G1, root=G1.vertex(np.argmax(G1.vertex_properties["bc"].get_array()))), edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1246 +# gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1247 + gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1248 + title = "./Original_2D_Layout.png"
  1249 + gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000), vertex_font_size = 6)
  1250 +#gt.graph_draw(G1, pos=G1.vertex_properties["pos"], edge_pen_width = 4.0, vertex_size=degree, edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title)
  1251 +#
  1252 +#
  1253 +# G2 = self.filterFullGraph_gt(G1, add_rst=False, dual=True)
  1254 +# title = "raw_dual_cortical_7_6_after_borders.pdf"
  1255 +# title2 = "raw_dual_radial_cortical_7_6_after_borders.pdf"
  1256 +# gt.graph_draw(G2, pos=G2.vertex_properties["pos"], edge_pen_width = 4.0, vertex_size=G2.vertex_properties["degree"], edge_color=G2.edge_properties["mst"], vertex_fill_color=G2.vertex_properties["bc"], output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G2.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1257 +# gt.graph_draw(G2, pos=gt.radial_tree_layout(G2, root=G2.vertex(np.argmax(G2.vertex_properties["bc"].get_array()))), edge_pen_width = 4.0, vertex_size=G2.vertex_properties["degree"], edge_color=G2.edge_properties["mst"], vertex_fill_color=G2.vertex_properties["bc"], output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G2.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1258 + return G
  1259 +
  1260 +
  1261 + '''
  1262 + Create a graph from a list of nodes and a list of edges.
  1263 + Populates the Graph with a bunch of property maps in respect to edges and nodes
  1264 + each vertex is assigned 3 positional coordiantes:
  1265 + The original x,y,z location of the graph.
  1266 + x,y locations according to force directed layout,
  1267 + and x,y positions according to a radial layout
  1268 + Returns a graph_tool object
  1269 + '''
  1270 + def createFullGraph_gt(self):
  1271 + #Generate the undirected graph
  1272 + G = gt.Graph()
  1273 + G.set_directed(False)
  1274 +
  1275 + #add all the required vertex properties
  1276 + vpos = G.new_vertex_property("vector<double>") #original vertex positions
  1277 + pos = G.new_vertex_property("vector<double>") #positions according to the fdl
  1278 + rpos = G.new_vertex_property("vector<double>") #positions according to the radial layout
  1279 + vbetweeness_centrality = G.new_vertex_property("double") #betweeneness centrality of the vertex
  1280 + degree = G.new_vertex_property("int") #degree
  1281 + degree_volume = G.new_vertex_property("double") #degree scaled by the volume of all in fibers
  1282 + degree_tortuosity = G.new_vertex_property("double") #degree scaled by the tortuosity of all in fibers.
  1283 +
  1284 +
  1285 + #add all the required edge properties
  1286 + #G.properties[("x,y,z,r")] = G.new_edge_property("vector<double")
  1287 + x_edge = G.new_edge_property("vector<double>") #x-coordinated of the edge
  1288 + y_edge = G.new_edge_property("vector<double>") #y-coordinates of the edge
  1289 + z_edge = G.new_edge_property("vector<double>") #z-coordinates of the edge
  1290 + r_edge = G.new_edge_property("vector<double>") #r-values at each x,y,z
  1291 + av_edge = G.new_edge_property("double") #average edge radius
  1292 + l_edge = G.new_edge_property("double") #length of the edge
  1293 + i_edge = G.new_edge_property("double") #1/length
  1294 + iv_edge = G.new_edge_property("double") #1/volume
  1295 + t_edge = G.new_edge_property("double") #tortuosity of the edge
  1296 + v_edge = G.new_edge_property("double") #volume of the edge
  1297 + ebetweeness_centrality = G.new_edge_property("double") #betweeness centrality of the edge
  1298 + ebc_length = G.new_edge_property("double") #betweeneness centrality scaled by the length
  1299 + mst = G.new_edge_property("bool") #Boolean value deligating belongance to the minimum spanning tree.
  1300 +
  1301 +
  1302 + #This map gets updated.
  1303 + gaussian = G.new_edge_property("double") #empty property map for gaussian values downstream
  1304 +
  1305 +
  1306 + #Graph properties (once per region)
  1307 + mst_ratio = G.new_graph_property("double") #Graph based metric of mst edges/total edges.
  1308 +
  1309 +
  1310 + #add verticies and set their properties
  1311 + for i in range(len(self.N)):
  1312 + G.add_vertex()
  1313 + vpos[G.vertex(i)] = self.N[i].p
  1314 +
  1315 + #add edges and set their properties.
  1316 + for i in range(len(self.F)):
  1317 + v0 = self.F[i].v0
  1318 + v1 = self.F[i].v1
  1319 + if self.F[i].length() > 0.0:
  1320 + x,y,z,r = self.F[i].getcoords()
  1321 + G.add_edge(G.vertex(v0), G.vertex(v1))
  1322 + l_edge[G.edge(v0,v1)] = self.F[i].length()
  1323 + gaussian[G.edge(v0,v1)] = np.exp(-np.power(l_edge[G.edge(v0,v1)], 2.) / (2 * np.power(60.0, 2.)))
  1324 + i_edge[G.edge(v0,v1)] = 1/l_edge[G.edge(v0,v1)]
  1325 + t_edge[G.edge(v0,v1)] = self.F[i].turtuosity()
  1326 + v_edge[G.edge(v0,v1)] = self.F[i].volume()
  1327 + iv_edge[G.edge(v0,v1)] = 1/v_edge[G.edge(v0,v1)]
  1328 + x_edge[G.edge(v0,v1)] = x
  1329 + y_edge[G.edge(v0,v1)] = y
  1330 + z_edge[G.edge(v0,v1)] = z
  1331 + r_edge[G.edge(v0,v1)] = r
  1332 + av_edge[G.edge(v0,v1)] = self.F[i].av_radius()
  1333 + else:
  1334 + print("WTF")
  1335 +
  1336 +
  1337 + #generate centrality map
  1338 + gt.graph_tool.centrality.betweenness(G, vprop=vbetweeness_centrality, eprop=ebetweeness_centrality, norm=True)
  1339 + #generate minimum spanning tree
  1340 + mst = gt.graph_tool.topology.min_spanning_tree(G, weights=l_edge)
  1341 + mst_ratio[G] = np.double(np.sum(mst.get_array()))/np.double(len(self.N))
  1342 + degree = G.degree_property_map("total")
  1343 + degree_volume = G.degree_property_map("total", weight=v_edge)
  1344 +
  1345 + dg = degree_volume.get_array()
  1346 + dg = np.exp(-np.power(dg, 2.) / (2 * np.power(0.000005, 2.)))
  1347 + degree_volume = G.new_vertex_property("double", vals=dg)
  1348 + degree_tortuosity = G.degree_property_map("total", weight=t_edge)
  1349 +
  1350 +
  1351 + #print(clusters.get_array()[:], clusters.get_array()[:])
  1352 + pos = gt.sfdp_layout(G, C = 1.0, K = 10)
  1353 + rpos = gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array())))
  1354 +
  1355 +
  1356 +
  1357 + for e in G.edges():
  1358 + ebc_length[G.edge(e.source(), e.target())] = ebetweeness_centrality[G.edge(e.source(), e.target())]*l_edge[G.edge(e.source(), e.target())]
  1359 +
  1360 + #set property maps for the vertices
  1361 + G.vertex_properties["p"] = vpos
  1362 + G.vertex_properties["pos"] = pos
  1363 + G.vertex_properties["rpos"] = rpos
  1364 + G.vertex_properties["bc"] = vbetweeness_centrality
  1365 + G.vertex_properties["degree"] = degree
  1366 + G.vertex_properties["degree_volume"] = degree_volume
  1367 + G.vertex_properties["degree_tortuosity"] = degree_tortuosity
  1368 + G.vertex_properties["selection"] = G.new_vertex_property("bool", val=False)
  1369 +
  1370 + #set property maps for the edges
  1371 + G.edge_properties["x"] = x_edge
  1372 + G.edge_properties["y"] = y_edge
  1373 + G.edge_properties["z"] = z_edge
  1374 + G.edge_properties["r"] = r_edge
  1375 + G.edge_properties["inverted"] = i_edge
  1376 + G.edge_properties["length"] = l_edge
  1377 + G.edge_properties["tortuosity"] = t_edge
  1378 + G.edge_properties["av_radius"] = av_edge
  1379 + G.edge_properties["volume"] = v_edge
  1380 + G.edge_properties["bc"] = ebetweeness_centrality
  1381 + G.edge_properties["bc*length"] = ebc_length
  1382 + G.edge_properties["mst"] = mst
  1383 + G.edge_properties["inverted_volume"] = iv_edge
  1384 + G.edge_properties["gaussian"] = gaussian
  1385 + G.edge_properties["selection"] = G.new_edge_property("double", val=0.0)
  1386 +
  1387 + #set graph properies
  1388 + G.graph_properties["mst_ratio"] = mst_ratio
  1389 +
  1390 + N = 0
  1391 + for e in G.edges():
  1392 + N += len(G.edge_properties["x"][e])
  1393 + point_cloud_x = np.zeros((N, 1), dtype=np.float64)
  1394 + point_cloud_y = np.zeros((N, 1), dtype=np.float64)
  1395 + point_cloud_z = np.zeros((N, 1), dtype=np.float64)
  1396 +
  1397 + n = 0
  1398 + for e in G.edges():
  1399 + for p in range(len(G.edge_properties["x"][e])):
  1400 + point_cloud_x[n][0] = G.edge_properties["x"][e][p]
  1401 + point_cloud_y[n][0] = G.edge_properties["y"][e][p]
  1402 + point_cloud_z[n][0] = G.edge_properties["z"][e][p]
  1403 + n += 1
  1404 +
  1405 + G.graph_properties["point_cloud_x"] = G.new_graph_property("vector<double>")
  1406 + G.graph_properties["point_cloud_y"] = G.new_graph_property("vector<double>")
  1407 + G.graph_properties["point_cloud_z"] = G.new_graph_property("vector<double>")
  1408 + G.graph_properties["point_cloud_x"] = point_cloud_x
  1409 + G.graph_properties["point_cloud_y"] = point_cloud_y
  1410 + G.graph_properties["point_cloud_z"] = point_cloud_z
  1411 + #save the original graph indices for the edges and the vertices.
  1412 + G.vertex_properties["idx"] = G.vertex_index
  1413 + G.edge_properties["idx"] = G.edge_index
  1414 +
  1415 + title = "~/Pictures/Original_2D_Graph_2.png"
  1416 +# title2 = "raw_dual_radial_cordical_7_6_after_delete.pdf"
  1417 +#
  1418 +# G1 = self.filterFullGraph_gt(G, borders=False, dual=False, add_rst = False)
  1419 +##
  1420 +## #print(clusters.get_array()[:], clusters.get_array()[:])
  1421 +## #pos = gt.sfdp_layout(G)
  1422 +##
  1423 +## #gt.graph_draw(G, pos=,edge_color=mst, vertex_fill_color=vbetweeness_centrality, edge_pen_width=ebetweeness_centrality, output="raw.pdf")
  1424 +##
  1425 +## gt.graph_draw(G1, pos=gt.radial_tree_layout(G1, root=G1.vertex(np.argmax(G1.vertex_properties["bc"].get_array()))), edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1426 +## gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1427 +# gt.graphviz_draw(G1, pos = G1.vertex_properties["p"], ratio="fill", size = (100,100), layout = None, pin = True, overlap=True, vsize=(G1.vertex_properties["degree_tortuosity"], 2.), vprops={"index":G1.vertex_index}, penwidth= 8.0, vcolor = G1.vertex_properties["bc"], vcmap = cm.get_cmap("inferno"), output=title)
  1428 +# #gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 8.0, output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_size=60, vertex_fill_color=G1.vertex_properties["bc"], vertex_text=G1.vertex_index, output_size=(3200,3200),vertex_font_size = 32)
  1429 +# title = "~/Pictures/Original_2D_Layout.png"
  1430 +# gt.graph_draw(G1, pos=G1.vertex_properties["pos"], edge_pen_width = 8.0, edge_color=G1.edge_properties["mst"], vertex_size=40, vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G1.vertex_index, output_size=(3200,3200), vertex_font_size = 32)
  1431 +#
  1432 +#
  1433 +# G1 = self.filterFullGraph_gt(G, borders=True, dual=False, erode=True, add_rst = False)
  1434 +# title = "~/Pictures/Original_2D_Graph_Eroded.png"
  1435 +# gt.graph_draw(G1, pos=G1.vertex_properties["p"], edge_pen_width = 8.0, output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_size=60, vertex_fill_color=G1.vertex_properties["bc"], vertex_text=G1.vertex_index, output_size=(3200,3200),vertex_font_size = 32)
  1436 +# title = "~/Pictures/Original_2D_Layout_Eroded.png"
  1437 +# gt.graph_draw(G1, pos=G1.vertex_properties["pos"], edge_pen_width = 8.0, edge_color=G1.edge_properties["mst"], vertex_size=40, vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G1.vertex_index, output_size=(3200,3200), vertex_font_size = 32)
  1438 +
  1439 +# title = "raw_full_cortical_7_6_before_delete.pdf"
  1440 +# title2 = "raw_full_radial_cordical_7_6_before_delete.pdf"
  1441 +# #G.graph_properties["rst_ratio"] = rst_ratio
  1442 +# #print("")
  1443 +# #print(G.graph_properties["rst_ratio"])
  1444 +# #print(G.edge_properties["bc"].get_array())
  1445 +# #print()
  1446 +#
  1447 +# #G.vertex_properties[("p","betweeness_centrality")] = [vpos, vbetweeness_centrality]
  1448 +# #G.edge_properties[("x", "y", "z", "r", "length", "tortuosity", "volume", "betweeness_centrality")] = []
  1449 +# #G.set_edge_filter(mst)
  1450 +# #G.set_edge_filter(mst)
  1451 +#
  1452 +# #Experimental code for drawing graphs.
  1453 +# #gt.graph_draw(G,pos=vpos,edge_color=mst, vertex_fill_color=vbetweeness_centrality, edge_pen_width=ebetweeness_centrality, output="raw.pdf")
  1454 +# #gt.graph_draw(G,pos=pos, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title, bg_color=[1.0,1.0,1.0])
  1455 +# #gt.graph_draw(G,pos=gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array())), node_weight=vbetweeness_centrality), vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title2, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000), vertex_font_size = 6)
  1456 +# #print(np.argmax(vbetweeness_centrality.get_array()))
  1457 +#
  1458 +# #np.savetxt("vbetweeness_centrality", vbetweeness_centrality.get_array())
  1459 +# gt.graph_draw(G,pos=gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array()))), edge_pen_width = 2.0, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1460 +# gt.graph_draw(G, pos=pos, edge_pen_width = 4.0, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1461 +#
  1462 +#
  1463 +# #gt.graph_draw(G,pos=vpos, efilt=mst, vertex_fill_color=vbetweeness_centrality, edge_pen_width=ebetweeness_centrality, output="mst.pdf")
  1464 +# #gt.graph_draw(G,pos=pos, efilt=mst, vertex_fill_color=vbetweeness_centrality, output="mst.pdf")
  1465 +# #need to create subgraphs!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1466 +# #rst_r = 0
  1467 +# #for i in range(1000):
  1468 +# # rst = gt.graph_tool.topology.random_spanning_tree(G)
  1469 +# # rst_r = rst_r + np.double(np.sum(rst.get_array()))/np.double(len(self.N))
  1470 +#
  1471 +# #rst_ratio[G] = rst_r/1000.0
  1472 +#
  1473 +#
  1474 +#
  1475 +# #G1 = gt.Graph(G)
  1476 +#
  1477 +# #G1.purge_edges()
  1478 +# #G1.purge_vertices(in_place = True)
  1479 +#
  1480 +#
  1481 +# title = "raw_full_cortical_7_6_after_delete.pdf"
  1482 +# title2 = "raw_full_radial_cordical_7_6_after_delete.pdf"
  1483 +#
  1484 +# G1 = self.filterFullGraph_gt(G,borders=False)
  1485 +#
  1486 +# #print(clusters.get_array()[:], clusters.get_array()[:])
  1487 +# #pos = gt.sfdp_layout(G)
  1488 +#
  1489 +# #gt.graph_draw(G,pos=vpos,edge_color=mst, vertex_fill_color=vbetweeness_centrality, edge_pen_width=ebetweeness_centrality, output="raw.pdf")
  1490 +#
  1491 +# gt.graph_draw(pos = gt.sfdp_layout(G1), output=title)
  1492 +# gt.graph_draw(G1, pos=G1.vertex_properties["pos"], edge_pen_width = 4.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1493 +# #gt.graph_draw(G1, pos=G1.vertex_properties["pos"], edge_pen_width = 4.0, vertex_size=degree, edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title)
  1494 +#
  1495 +#
  1496 +# G2 = self.filterFullGraph_gt(G1)
  1497 +# title = "raw_full_cortical_7_6_after_borders.pdf"
  1498 +# title2 = "raw_full_radial_cortical_7_6_after_borders.pdf"
  1499 +# gt.graph_draw(G2, pos=G2.vertex_properties["pos"], edge_pen_width = 4.0, vertex_size=G2.vertex_properties["degree"], edge_color=G2.edge_properties["mst"], vertex_fill_color=G2.vertex_properties["bc"], output=title, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G2.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1500 +# gt.graph_draw(G2, pos=gt.radial_tree_layout(G2, root=G2.vertex(np.argmax(G2.vertex_properties["bc"].get_array()))), edge_pen_width = 4.0, vertex_size=G2.vertex_properties["degree"], edge_color=G2.edge_properties["mst"], vertex_fill_color=G2.vertex_properties["bc"], output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G2.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1501 +# #gt.graph_draw(G,pos=pos, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title, bg_color=[1.0,1.0,1.0])
  1502 +#
  1503 +# #gt.graph_draw(G,pos=gt.radial_tree_layout(G, root=G.vertex(np.argmax(vbetweeness_centrality.get_array())), node_weight=vbetweeness_centrality), vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title2, bg_color=[1.0, 1.0,1.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000), vertex_font_size = 6)
  1504 +# #print(np.argmax(vbetweeness_centrality.get_array()))
  1505 +# #np.savetxt("vbetweeness_centrality", vbetweeness_centrality.get_array())
  1506 +# #gt.graph_draw(G1, pos=gt.radial_tree_layout(G1, root=G1.vertex(np.argmax(G1.vertex_properties["bc"].get_array()))), edge_pen_width = 2.0, vertex_size=G1.vertex_properties["degree"], edge_color=G1.edge_properties["mst"], vertex_fill_color=G1.vertex_properties["bc"], output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G1.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1507 +# #gt.graph_draw(G1, pos=gt.radial_tree_layout(G1, root=G1.vertex(np.argmax(G1.vertex_properties["bc"].get_array()))), edge_pen_width = 2.0, vertex_size=degree, edge_color=G1.edge_properties["mst"])
  1508 +# #gt.draw_hierarchy(gt.minimize_nested_blockmodel_dl(G1, deg_corr=False), output = "test.pdf")
  1509 +# #gt.graph_draw(G1,pos=gt.radial_tree_layout(G1, root=G1.vertex(np.argmax(G1.edge_properties["mst"].get_array()))), edge_pen_width = 2.0, vertex_size=degree, edge_color=mst, vertex_fill_color=vbetweeness_centrality, output=title2, bg_color=[0.0, 0.0,0.0,1.0], vertex_text=G.vertex_index, output_size=(1000,1000),vertex_font_size = 6)
  1510 +# print(gt.graph_tool.openmp_enabled(), gt.graph_tool.openmp_get_num_threads())
  1511 +
  1512 + #print(ratio, G2.graph_properties["mst_ratio"])
  1513 + #n, bins, patches = plt.hist(np.asarray(rst_dist), 50, normed=1, facecolor='green', alpha=0.75)
  1514 +
  1515 + # add a 'best fit' line
  1516 + #plt.grid(True)
  1517 +
  1518 + #plt.show()
  1519 + #self.partition(G, 5, 7, False)
  1520 + self.export_points(G)
  1521 + self.export_bb(G)
  1522 + return G
  1523 +
  1524 +
  1525 + def gen_new_fd_layout(G):
  1526 + pos = gt.sfdp_layout(G, C = 1.0, K = 10)
  1527 + G.vertex_properties["pos"] = pos
  1528 + return G
  1529 +
  1530 + def map_edges_to_range(G, rng, propertymap):
  1531 + def func(maximum, minimum, new_maximum, new_minimum, value):
  1532 + return ((value-minimum)/(maximum-minimum)*(new_maximum-new_minimum)+new_minimum)
  1533 +
  1534 + mx = max(G.edge_properties[propertymap].get_array())
  1535 + mn = min(G.edge_properties[propertymap].get_array())
  1536 + G.edge_properties["map"] = G.new_edge_property("float")
  1537 + gt.map_property_values(G.edge_properties[propertymap], G.edge_properties["map"], lambda x: func(mx, mn, rng[0], rng[1], x))
  1538 +
  1539 + return G.edge_properties["map"]
  1540 +
  1541 +
  1542 + def map_vertices_to_range(G, rng, propertymap):
  1543 + def func(maximum, minimum, new_maximum, new_minimum, value):
  1544 + return ((value-minimum)/(maximum-minimum)*(new_maximum-new_minimum)+new_minimum)
  1545 +
  1546 + mx = max(G.vertex_properties[propertymap].get_array())
  1547 + mn = min(G.vertex_properties[propertymap].get_array())
  1548 + G.vertex_properties["map"] = G.new_vertex_property("float")
  1549 + gt.map_property_values(G.vertex_properties[propertymap], G.vertex_properties["map"], lambda x: func(mx, mn, rng[0], rng[1], x))
  1550 +
  1551 + return G.vertex_properties["map"]
  1552 +
  1553 + '''
  1554 + G and propertymap in G are passed in.
  1555 + Maps select property from range to color, works for int's floats and vec3
  1556 + and vec4 values
  1557 + Returns a vector<double> property map that maps the property value to color.
  1558 + If passed a vertex propertymap, returns a vertex property map
  1559 + If passed an edge propertymap, returns an edge property map
  1560 + '''
  1561 + def map_property_to_color(G, propertymap, colormap = 'tab20'):
  1562 + key_type = ''
  1563 + if propertymap.key_type()=='e':
  1564 + key_type = 'e'
  1565 + elif propertymap.key_type()=='v':
  1566 + key_type = 'v'
  1567 + else:
  1568 + #TO DO: Write graph property type return.
  1569 + print("Graph_property passed")
  1570 + if G.properties[(key_type, 'RGBA')] == None:
  1571 + color = [0.0, 0.0, 0.0, 1.0]
  1572 + G.properties[(key_type, 'RGBA')] = G.new_property(key_type, "vector<double>", val=color)
  1573 +
  1574 + #This is when the property map is integers
  1575 + #int32_t when integer
  1576 + value_type = propertymap.value_type()
  1577 + print(value_type)
  1578 + if(value_type == "int32_t"):
  1579 + array = propertymap.get_array()
  1580 + colors = cm.get_cmap(colormap, len(np.unique(array)))
  1581 + if key_type == 'v':
  1582 + for v in G.vertices():
  1583 + G.vertex_properties["RGBA"][v] = colors(propertymap[v])
  1584 + return G.vertex_properties["RGBA"]
  1585 + elif(value_type == 'double' or value_type == 'float'):
  1586 + array = propertymap.get_array()
  1587 + norm = cm.colors.Normalize(vmin=min(array), vmax=max(array))
  1588 + #colors = cm.get_cmap(colormap, len(np.unique(array)))
  1589 + colors = cm.ScalarMappable(norm=norm, cmap=colormap)
  1590 + if key_type =='v':
  1591 + for v in G.vertices():
  1592 + print(colors.to_rgba(propertymap[v]))
  1593 + G.vertex_properties["RGBA"][v] = colors.to_rgba(propertymap[v])
  1594 + return G.vertex_properties["RGBA"]
  1595 + elif(value_type == 'vector<double>' or value_type == 'vector<float>'):
  1596 + print("detected a vector of doubles or floats")
  1597 +
  1598 +
  1599 +
  1600 + '''
  1601 + Attempts to partition the graph G into b_i = [0, n^3] sections, where each section is the connected componenets of
  1602 + m steps away from the source node (where the source node is the closest node to point n_i)
  1603 + '''
  1604 + def partition(self, G, n, m, is_dual, path, prefix, saveKey, lbl = "none"):
  1605 + bb = AABB(G, is_dual)
  1606 + #print("FLJKKHDFLKJFDLKJFDLKJ ", m)
  1607 + x, y, z = bb.project_grid(n)
  1608 + #cluster_belongance = G.new_vertex_property("vector<boolean>")
  1609 + #ecluster_belongance = G.new_edge_property("vector<boolean>")
  1610 + #X, Y, Z = np.meshgrid(x,y,z)
  1611 + array_cluster_bel = np.zeros([n**3, G.num_vertices()])
  1612 + array_ecluster_bel = np.zeros([n**3, G.num_edges()])
  1613 + c = 0
  1614 + for i in range(len(x)):
  1615 + for j in range(len(y)):
  1616 + for k in range(len(z)):
  1617 + #for i in range(1):
  1618 + # for j in range(1):
  1619 + # for k in range(1):
  1620 + p = G.vertex_properties["p"].get_2d_array(range(G.num_vertices()))
  1621 + if(p.shape[1] < 3):
  1622 + break
  1623 + #print("stuff", p)
  1624 + idx = -1
  1625 + dist = 100000000000
  1626 + for M in range(p.shape[1]):
  1627 + d = math.sqrt((x[i] - p[0][M])**2 + (y[j] - p[1][M])**2 + (z[k] - p[2][M])**2)
  1628 + if d < dist:
  1629 + idx = M
  1630 + dist = d
  1631 + print(idx, " vertex[",p[0][idx], p[1][idx], p[2][idx], "], point [", x[i], y[j], z[k], "]")
  1632 + clusters = G.new_vertex_property("int", vals=np.full((G.num_vertices(), 1), -1, dtype=int))
  1633 + #eclusters = G.new_edge_property("int", vals=np.full((G.num_edges(), 1), -1, dtype=int))
  1634 + dist = G.new_vertex_property("int", val = 100000)
  1635 + dist[G.vertex(idx)] = 0
  1636 + #run a bfs on the index node
  1637 + gt.bfs_search(G, idx, VisitorClassPartition(clusters, dist, c, m))
  1638 + #print(dist.get_array());
  1639 + temp_vertices = np.ma.masked_where(dist.get_array() <= m, dist.get_array()).mask
  1640 + #print(temp_vertices)
  1641 + temp_edges = np.zeros([G.num_edges()])
  1642 + for vertex in np.nditer(np.where(temp_vertices == True)):
  1643 + for edge in G.get_out_edges(vertex):
  1644 + #print(edge)
  1645 + temp_edges[edge[2]] = 1
  1646 + array_cluster_bel[c,:] = temp_vertices[:]
  1647 + array_ecluster_bel[c,:] = temp_edges[:]
  1648 + c = c + 1
  1649 + #print(G.num_vertices())
  1650 + #np.savetxt("clusters.txt", array_cluster_bel)
  1651 + G.vertex_properties["partition"] = G.new_vertex_property("vector<boolean>", array_cluster_bel)
  1652 + G.edge_properties["partition"] = G.new_edge_property("vector<boolean>", array_ecluster_bel)
  1653 + j = 0
  1654 + gt.graph_draw(G, pos = gt.sfdp_layout(G), output="Graph_full.pdf")
  1655 + for i in range(c):
  1656 + TFv = G.new_vertex_property("bool", vals=array_cluster_bel[i,:])
  1657 + TFe = G.new_edge_property("bool", vals=array_ecluster_bel[i,:])
  1658 + G.set_filters(TFe, TFv)
  1659 + G1 = gt.Graph(G, directed=False, prune=True)
  1660 + G.clear_filters()
  1661 + G1.clear_filters()
  1662 + iteration = 0
  1663 + title = "graph_" + str(iteration) + ".pdf"
  1664 + gt.graph_draw(G1, pos = gt.sfdp_layout(G1), output=title)
  1665 + while(np.any(G1.degree_property_map("total").get_array() == True)):
  1666 + #print(np.any(G1.degree_property_map("total").get_array()), " ", i)
  1667 + iteration = iteration + 1
  1668 + G1 = self.filterBorder(G1, is_dual)
  1669 + title = "graph_" + str(iteration) + ".pdf"
  1670 + gt.graph_draw(G1, pos = gt.sfdp_layout(G1), output=title)
  1671 + #G1 = self.filterDisconnected(G1, is_dual)
  1672 + G1 = self.recalculate_metrics(G1, is_dual)
  1673 +
  1674 + if(G1.num_edges() > 2):
  1675 + ppath = path + "/partial"+str(j)+".nwt"
  1676 + self.saveGraph_gt(G1, ppath)
  1677 + if(i == 0):
  1678 + self.saveSample_gt(G1, path, prefix, is_dual, writeKey=saveKey, label=lbl)
  1679 + else:
  1680 + self.saveSample_gt(G1, path, prefix, is_dual, label=lbl)
  1681 + j = j + 1
  1682 +
  1683 +
  1684 + #print(array_cluster_bel)
  1685 +
  1686 +
  1687 + def find_loops(self, G, path, is_dual, largest):
  1688 + iteration = 0
  1689 + if(largest):
  1690 + G = self.filterDisconnected(G)
  1691 + gt.graph_draw(G, pos = gt.sfdp_layout(G), output="./graph_full.pdf")
  1692 + while(np.any(G.degree_property_map("total").get_array() == True)):
  1693 + #print(np.any(G1.degree_property_map("total").get_array()), " ", i)
  1694 + iteration = iteration + 1
  1695 + G = self.filterBorder(G, is_dual)
  1696 + title = path +"graph_" + str(iteration) + ".pdf"
  1697 + gt.graph_draw(G, pos = gt.sfdp_layout(G), output=title)
  1698 + #G1 = self.filterDisconnected(G1, is_dual)
  1699 + G = self.recalculate_metrics(G, is_dual)
  1700 + ppath = path + "graph_full_mst.pdf"
  1701 + gt.graph_draw(G, pos = gt.sfdp_layout(G, C=0.4), output=ppath, edge_color=G.edge_properties["mst"], vertex_text=G.vertex_index,output_size=(1000,1000), vertex_font_size = 6, vertex_size = 3)
  1702 + paths = G.new_edge_property("int", vals=np.full((G.num_edges(), 1), 1, dtype=int))
  1703 + #values = np.full((G.num_vertices(),1), 0, dtype=int)
  1704 + #for i in range(G.num_vertices()):
  1705 + # values[i,0] = G.vertex_index[G.vertex(i)]
  1706 + #G.vertex_properties["original_index"] = values
  1707 +
  1708 + #add index, then use index to search for new vertecies.
  1709 + G.edge_properties["path_weights"] = paths
  1710 + cycle_list = []
  1711 + for e in G.edges():
  1712 + if G.edge_properties["mst"][e] == 0:
  1713 + cycle_list.append(e)
  1714 + # else:
  1715 + # G.edge_properties["path_weights"][e] = 1
  1716 +
  1717 +
  1718 + #for e in cycle_list:
  1719 + #print("Loop Detected between %i, %i", e.source(), e.target())
  1720 +
  1721 + G.set_edge_filter(G.edge_properties["mst"])
  1722 + iteration = 0
  1723 + G1 = gt.Graph(G, directed=False, prune=True)
  1724 + G.clear_filters()
  1725 + for e in cycle_list:
  1726 + edge = G1.add_edge(e.target(), e.source())
  1727 + #G.edge_properties["mst"][e] = 1
  1728 + G1.edge_properties["path_weights"][edge] = 1000
  1729 + #G.set_edge_filter(G.edge_properties["mst"])
  1730 + #G1 = gt.Graph(G, directed=False, prune=True)
  1731 + #s = -1
  1732 + #t = -1
  1733 + #for edge in G1.edges():
  1734 + # if(G1.vertex_properties["original_index"][edge.source()] == int(e.source())):
  1735 + # s = int(edge.source())
  1736 + # elif(G1.vertex_properties["original_index"][edge.target()] == int(e.target())):
  1737 + # t = int(edge.target())
  1738 +
  1739 + [Vl, El] = gt.shortest_path(G1, e.source(), e.target(), weights=G1.edge_properties["path_weights"])
  1740 + clusters = G1.new_vertex_property("int", vals=np.full((G1.num_vertices(), 1), 5, dtype=int))
  1741 + eclusters = G1.new_edge_property("int", vals = np.full((G1.num_edges(), 1), 3, dtype=int))
  1742 + G1.vertex_properties["cycle"] = clusters
  1743 + G1.edge_properties["cycle"] = eclusters
  1744 + print("Number of vertices in Path is:", len(Vl))
  1745 + for v in Vl:
  1746 + G1.vertex_properties["cycle"][G1.vertex(v)] = 10
  1747 + print(str(v))
  1748 + #Create the arrays to be histogrammed later regarding every loop
  1749 + length_total = 0
  1750 + volume_total = 0
  1751 + tortuosity_total = 0
  1752 + bc_total = 0
  1753 + bcl_total = 0
  1754 + num_edges = 1
  1755 + for v in range(len(Vl)-1):
  1756 + G1.edge_properties["cycle"][G1.edge(Vl[v], Vl[v+1])] = 10
  1757 + length_total = length_total + G1.edge_properties["length"][G1.edge(Vl[v], Vl[v+1])]
  1758 + volume_total = volume_total + G1.edge_properties["volume"][G1.edge(Vl[v], Vl[v+1])]
  1759 + tortuosity_total = tortuosity_total + G1.edge_properties["tortuosity"][G1.edge(Vl[v], Vl[v+1])]
  1760 + bc_total = bc_total + G1.edge_properties["bc"][G1.edge(Vl[v], Vl[v+1])]
  1761 + bcl_total = bcl_total + G1.edge_properties["bc*length"][G1.edge(Vl[v], Vl[v+1])]
  1762 + num_edges = num_edges + 1
  1763 + G1.edge_properties["cycle"][G1.edge(e.source(), e.target())] = 10
  1764 + length_total = length_total + G.edge_properties["length"][G.edge(e.source(), e.target())]
  1765 + volume_total = volume_total + G.edge_properties["volume"][G.edge(e.source(), e.target())]
  1766 + tortuosity_total = tortuosity_total + G.edge_properties["tortuosity"][G.edge(e.source(), e.target())]
  1767 + bc_total = bc_total + G.edge_properties["bc"][G.edge(e.source(), e.target())]
  1768 + bcl_total = bcl_total + G.edge_properties["bc*length"][G.edge(e.source(), e.target())]
  1769 + ppath = path + "Loop_Histograms.txt"
  1770 + f = open(ppath, "a+")
  1771 + f.write("%.15f\t" % length_total)
  1772 + f.write("%.15f\t" % volume_total)
  1773 + f.write("%.15f\t" % tortuosity_total)
  1774 + f.write("%.15f\t" % bc_total)
  1775 + f.write("%.15f\t" % bcl_total)
  1776 + f.write("%d\t\n" % num_edges)
  1777 + f.close()
  1778 +
  1779 + title = path+"cycle" + str(iteration)+".png"
  1780 + gt.graph_draw(G1, pos=G1.vertex_properties["pos"], output=title, edge_color='red', edge_pen_width=G1.edge_properties["cycle"], vertex_text=G1.vertex_index, output_size=(1920,1280), vertex_font_size = 4, vertex_fill_color = G1.vertex_properties["cycle"], vertex_size = 3)
  1781 + G1.remove_edge(edge)
  1782 + #G.clear_filters();
  1783 + #G.edge_properties["mst"][e] = 0
  1784 + #G.edge_properties["path_weights"][e] = 1000
  1785 + iteration = iteration + 1
  1786 + '''
  1787 + returns an affinity matrix based on the property edge property, given as a string.
  1788 + '''
  1789 + def get_affinity_matrix(G, edge_property, gaussian=False, sigma = None):
  1790 + affinity = gt.shortest_distance(G, weights=G.edge_properties[edge_property])
  1791 + affinity = affinity.get_2d_array(range(G.num_vertices()))
  1792 + if gaussian:
  1793 + if sigma == None:
  1794 + sig = 60.0
  1795 + else:
  1796 + sig = sigma
  1797 + for i in range(G.num_vertices()):
  1798 + for j in range(G.num_vertices()):
  1799 + affinity[i,j] = np.exp(-np.power(affinity[i, j], 2.) / (2 * np.power(sig, 2.)))
  1800 + return affinity
  1801 +
  1802 +
  1803 + '''
  1804 + Attempts to apporixmate the number of clusters in the graph by finding
  1805 + the approximate number of minimum graph cuts
  1806 +
  1807 + TO_DO: Program the gaussian and sigma parameters
  1808 + '''
  1809 + def approximate_n_cuts(G, affinity_property):
  1810 + #L = gt.laplacian(G, weight = G.edge_properties[affinity_property])
  1811 + G1 = gt.Graph(G, directed=False)
  1812 + G1 = Network.filterDisconnected(G1, G1)
  1813 + L = Network.get_affinity_matrix(G1, affinity_property, gaussian=True, sigma=157)
  1814 + L = sp.sparse.csgraph.laplacian(L, normed=True)
  1815 + n_components = G1.num_vertices()
  1816 + eigenvalues, eigenvectors = eigsh(L, k=n_components, which = "LM", sigma=1.0, maxiter = 5000)
  1817 +
  1818 + plt.figure()
  1819 + plt.scatter(np.arange(len(eigenvalues)), eigenvalues)
  1820 + plt.grid()
  1821 + plt.show()
  1822 +
  1823 + count = sum(eigenvalues > 1.01)
  1824 + return count
  1825 + '''
  1826 + Cluster the vectices in the graph based on a property passed to the clustering algorithm
  1827 +
  1828 + TO_DO: program the guassian and sigma parameters
  1829 + '''
  1830 + def spectral_clustering(G, affinity_property, gaussian = False, sigma = None, n_clusters = None, num_iters = 1000):
  1831 + if(n_clusters == None):
  1832 + n_c = Network.approximate_n_cuts(G, affinity_property);
  1833 + else:
  1834 + n_c = n_clusters
  1835 + sc = SpectralClustering(n_c, affinity='precomputed', n_init = num_iters)
  1836 + sc.fit(Network.get_affinity_matrix(G, affinity_property, gaussian = True, sigma=157))
  1837 +
  1838 + return sc.labels_
  1839 +
  1840 +
  1841 + def export_points(self, G):
  1842 + location = "./vertex_points.txt"
  1843 + f = open(location, "a+")
  1844 + for v in G.vertices():
  1845 + f.write("%.15f\t" % G.vertex_properties["p"][v][0])
  1846 + f.write("%.15f\t" % G.vertex_properties["p"][v][1])
  1847 + f.write("%.15f\n" % G.vertex_properties["p"][v][2])
  1848 +
  1849 + f.close()
  1850 +
  1851 + def export_bb(self, G):
  1852 + location = "./bb_points.txt"
  1853 + aabb = AABB(G)
  1854 + points = aabb.vertices()
  1855 + f = open(location, "a+")
  1856 + for i in points:
  1857 + f.write("%.15f\t" % i[0])
  1858 + f.write("%.15f\t" % i[1])
  1859 + f.write("%.15f\n" % i[2])
  1860 +
  1861 + f.close()
  1862 +
  1863 +
  1864 + def write_vtk(G, location, camera = None, binning = True):
  1865 + #location = "./nwt.vtk"
  1866 + f = open(location, "w+")
  1867 + f.write("# vtk DataFile Version 1.0\n")
  1868 + f.write("Data values\n")
  1869 + f.write("ASCII\n\n")
  1870 + f.write("DATASET POLYDATA\n")
  1871 + num_pts = 0
  1872 + for e in G.edges():
  1873 + X = G.edge_properties["x"][e]
  1874 + num_pts += len(X)
  1875 + f.write("POINTS %d float\n" % num_pts)
  1876 + for e in G.edges():
  1877 + X = G.edge_properties["x"][e]
  1878 + Y = G.edge_properties["y"][e]
  1879 + Z = G.edge_properties["z"][e]
  1880 + #pts = np.array([X,Y,Z]).T
  1881 + for p in range(len(X)):
  1882 + f.write("%.15f\t" % X[p])
  1883 + f.write("%.15f\t" % Y[p])
  1884 + f.write("%.15f\n" % Z[p])
  1885 + f.write("LINES " + str(G.num_edges()) + " " + str(G.num_edges()+num_pts) + "\n")
  1886 + num_pts = 0
  1887 + for e in G.edges():
  1888 + X = G.edge_properties["x"][e]
  1889 + indices = list(range(0, len(X)))
  1890 + indices = [x+num_pts for x in indices]
  1891 + f.write(str(len(X)) + " ")
  1892 + f.write(" ".join(str(x) for x in indices))
  1893 + f.write("\n")
  1894 + num_pts += len(X)
  1895 + f.write("POINT_DATA %d\n" % num_pts)
  1896 + f.write("SCALARS radius float 1\n")
  1897 + f.write("LOOKUP_TABLE radius_table\n")
  1898 + for e in G.edges():
  1899 + R = G.edge_properties["r"][e]
  1900 + #pts = np.array([X,Y,Z]).T
  1901 + for p in range(len(R)):
  1902 + f.write("%.15f\n" % R[p])
  1903 +
  1904 + f.write("COLOR_SCALARS color_table %d\n" % 4)
  1905 + bins = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
  1906 +
  1907 + if camera == None:
  1908 + for e in G.edges():
  1909 + X = G.edge_properties["x"][e]
  1910 + #RGBA = G.edge_properties["RGBA"].get_2d_array(range(4)).T
  1911 + for p in range(len(X)):
  1912 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][0])
  1913 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][1])
  1914 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][2])
  1915 + if binning:
  1916 + index = np.digitize(G.edge_properties["RGBA"][e][3], bins, right=True)
  1917 + if (index >= len(bins) or index < 0):
  1918 + print(G.edge_properties["RGBA"][e][3])
  1919 + print(index)
  1920 + f.write("%.15f\n" % bins[index])
  1921 + else:
  1922 + f.write("%.15f\n" % G.edge_properties["RGBA"][e][3])
  1923 + else:
  1924 + ptx = G.graph_properties["point_cloud_x"].get_array()
  1925 + pty = G.graph_properties["point_cloud_y"].get_array()
  1926 + ptz = G.graph_properties["point_cloud_z"].get_array()
  1927 + pts = np.array([ptx,pty,ptz]).T
  1928 + temp = AABB(G)
  1929 + bbl = temp.A
  1930 + bbu = temp.B
  1931 + tp = np.zeros((1,3), dtype = np.float64)
  1932 + tp[0][0] = (bbu[0]-bbl[0])/2
  1933 + tp[0][1] = (bbu[1]-bbl[1])/2
  1934 + tp[0][2] = (bbu[2]-bbl[2])/2
  1935 + pts[:] = pts[:] - camera - \
  1936 + tp
  1937 + distance = np.zeros(pts.shape[0], dtype = np.float32)
  1938 + for i in range(distance.shape[0]):
  1939 + distance[i] = np.sqrt(np.power(pts[i][0],2) + np.power(pts[i][1],2) + np.power(pts[i][2],2))
  1940 + mx = max(distance)
  1941 + mn = min(distance)
  1942 + distance = (distance - mx)/(mn-mx)
  1943 + idx = 0
  1944 + for e in G.edges():
  1945 + X = G.edge_properties["x"][e]
  1946 + for p in range(len(X)):
  1947 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][0])
  1948 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][1])
  1949 + f.write("%.15f\t" % G.edge_properties["RGBA"][e][2])
  1950 + if binning:
  1951 + index = np.digitize(distance[idx], bins, right=True)
  1952 + f.write("%.15f\n" % bins[index])
  1953 + else:
  1954 + f.write("%.15f\n" % distance[idx])
  1955 + idx += 1
  1956 +
  1957 + f.close()
  1958 +
  1959 +
  1960 + '''
  1961 + Creates a graph from a list of nodes and a list of edges.
  1962 + Uses edge turtuosity as weight.
  1963 + Returns a NetworkX Object.
  1964 + '''
  1965 + def createTortuosityGraph(self):
  1966 + G = nx.Graph()
  1967 + for i in range(len(self.N)):
  1968 + G.add_node(i, p=self.N[i].p)
  1969 + for i in range(len(self.F)):
  1970 + G.add_edge(self.F[i].v0, self.F[i].v1, weight = self.F[i].turtuosity())
  1971 + G[self.F[i].v0][self.F[i].v1]['pts'] = self.F[i].points
  1972 + G[self.F[i].v0][self.F[i].v1]['rads'] = self.F[i].radii
  1973 +
  1974 + return G
  1975 +
  1976 +
  1977 + '''
  1978 + Creates a graph from a list of nodes and a list of edges.
  1979 + Uses edge volume as weight.
  1980 + Returns a NetworkX Object.
  1981 + '''
  1982 + def createVolumeGraph(self):
  1983 + G = nx.Graph()
  1984 + for i in range(len(self.N)):
  1985 + G.add_node(i, p=self.N[i].p)
  1986 + for i in range(len(self.F)):
  1987 + G.add_edge(self.F[i].v0, self.F[i].v1, weight = self.F[i].volume())
  1988 + G[self.F[i].v0][self.F[i].v1]['pts'] = self.F[i].points
  1989 + G[self.F[i].v0][self.F[i].v1]['rads'] = self.F[i].radii
  1990 +
  1991 + return G
  1992 +#'''
  1993 +#Returns the positions dictionary for the Circular layout.
  1994 +#'''
  1995 +#def getCircularLayout(graph, dim, radius):
  1996 +# return nx.circular_layout(graph, dim, radius)
  1997 +#
  1998 +#'''
  1999 +#Return the positions dictionary for the Spring layout.
  2000 +#'''
  2001 +#def getSpringLayout(graph, pos, iterations, scale):
  2002 +# return nx.spring_layout(graph, 2, None, pos, iterations, 'weight', scale, None)
  2003 +#
  2004 +#'''
  2005 +#Draws the graph.
  2006 +#'''
  2007 +#def drawGraph(graph, pos):
  2008 +# nx.draw(graph, pos)
  2009 +# return
  2010 +
  2011 + def aabb(self):
  2012 +
  2013 + lower = self.N[0].p.copy()
  2014 + upper = lower.copy()
  2015 + for i in self.N:
  2016 + for c in range(len(lower)):
  2017 + if lower[c] > i.p[c]:
  2018 + lower[c] = i.p[c]
  2019 + if upper[c] < i.p[c]:
  2020 + upper[c] = i.p[c]
  2021 + return lower, upper
  2022 +
  2023 + #calculate the distance field at a given resolution
  2024 + # R (triple) resolution along each dimension
  2025 + def distancefield(self, R=(100, 100, 100)):
  2026 +
  2027 + #get a list of all node positions in the network
  2028 + P = []
  2029 + for e in self.F:
  2030 + for p in e.points:
  2031 + P.append(p)
  2032 +
  2033 + #turn that list into a Numpy array so that we can create a KD tree
  2034 + P = np.array(P)
  2035 +
  2036 + #generate a KD-Tree out of the network point array
  2037 + tree = sp.spatial.cKDTree(P)
  2038 +
  2039 + plt.scatter(P[:, 0], P[:, 1])
  2040 +
  2041 + #specify the resolution of the ouput grid
  2042 + R = (200, 200, 200)
  2043 +
  2044 + #generate a meshgrid of the appropriate size and resolution to surround the network
  2045 + lower, upper = self.aabb(self.N, self.F) #get the space occupied by the network
  2046 + x = np.linspace(lower[0], upper[0], R[0]) #get the grid points for uniform sampling of this space
  2047 + y = np.linspace(lower[1], upper[1], R[1])
  2048 + z = np.linspace(lower[2], upper[2], R[2])
  2049 + X, Y, Z = np.meshgrid(x, y, z)
  2050 + #Z = 150 * numpy.ones(X.shape)
  2051 +
  2052 +
  2053 + Q = np.stack((X, Y, Z), 3)
  2054 +
  2055 +
  2056 + D, I = tree.query(Q)
  2057 +
  2058 + return D
  2059 +
  2060 + #returns the number of points in the network
  2061 + def npoints(self):
  2062 + n = 0 #initialize the counter to zero
  2063 + for f in self.F: #for each fiber
  2064 + n = n + len(f.points) - 2 #count the number of points in the fiber - ignoring the end points
  2065 + n = n + len(self.N) #add the number of nodes (shared points) to the node count
  2066 + return n #return the number of nodes
  2067 +
  2068 + #returns all of the points in the network
  2069 + def points(self):
  2070 + k = self.npoints()
  2071 + P = np.zeros((3, k)) #allocate space for the point list
  2072 +
  2073 + idx = 0
  2074 + for f in self.F: #for each fiber in the network
  2075 + for ip in range(1, len(f.points)-1): #for each point in the network
  2076 + P[:, idx] = f.points[ip] #store the point in the raw point list
  2077 + idx = idx + 1
  2078 + return P #return the point array
  2079 +
  2080 + #returns the number of linear segments in the network
  2081 + def nsegments(self):
  2082 + n = 0 #initialize the segment counter to 0
  2083 + for f in self.F: #for each fiber
  2084 + n = n + len(f.points) - 1 #calculate the number of line segments in the fiber (points - 1)
  2085 + return n #return the number of line segments
  2086 +
  2087 + #return a list of line segments representing the network
  2088 + def segments(self, dtype=np.float32):
  2089 + k = self.nsegments() #get the number of line segments
  2090 + start = np.zeros((k, 3),dtype=dtype) #start points for the line segments
  2091 + end = np.zeros((k, 3), dtype=dtype) #end points for the line segments
  2092 +
  2093 + idx = 0 #initialize the index counter to zero
  2094 + for f in self.F: #for each fiber in the network
  2095 + for ip in range(0, len(f.points)-1): #for each point in the network
  2096 + start[idx, :] = f.points[ip] #store the point in the raw point list
  2097 + idx = idx + 1
  2098 +
  2099 + idx = 0
  2100 + for f in self.F: #for each fiber in the network
  2101 + for ip in range(1, len(f.points)): #for each point in the network
  2102 + end[idx, :] = f.points[ip] #store the point in the raw point list
  2103 + idx = idx + 1
  2104 +
  2105 + return start, end
  2106 +
  2107 + #returns the points inside the fiber.
  2108 + def fiber(self, idx):
  2109 + p = self.F[idx].points
  2110 + X = np.zeros(len(p))
  2111 + Y = np.zeros(len(p))
  2112 + Z = np.zeros(len(p))
  2113 + for i in range(len(p)):
  2114 + X[i] = p[i][0]
  2115 + Y[i] = p[i][1]
  2116 + Z[i] = p[i][2]
  2117 + return X, Y, Z
  2118 +
  2119 + #function returns the fiber associated with a given 1D line segment index
  2120 + def segment2fiber(self, idx):
  2121 + i = 0
  2122 + for f in range(len(self.F)): #for each fiber in the network
  2123 + i = i + len(self.F[f].points)-1 #add the number of points in the fiber to i
  2124 + if i > idx: #if we encounter idx in this fiber
  2125 + return self.F[f].points, f #return the fiber associated with idx and the index into the fiber array
  2126 +
  2127 + def vectors(self, clock=False, dtype=np.float32):
  2128 + if clock:
  2129 + start_time = time.time()
  2130 + start, end = self.segments(dtype) #retrieve all of the line segments
  2131 + v = end - start #calculate the resulting vectors
  2132 + l = np.sqrt(v[:, 0]**2 + v[:,1]**2 + v[:,2]**2) #calculate the fiber lengths
  2133 + z = l==0 #look for any zero values
  2134 + nz = z.sum()
  2135 + if nz > 0:
  2136 + print("WARNING: " + str(nz) + " line segment(s) of length zero were found in the network and will be removed" )
  2137 +
  2138 + if clock:
  2139 + print("Network::vectors: " + str(time.time() - start_time) + "s")
  2140 +
  2141 + return np.delete(v, np.where(z), 0)
  2142 +
  2143 + #scale all values in the network by tuple S = (sx, sy, sz)
  2144 + def scale(self, S):
  2145 + for f in self.F:
  2146 + for p in f.points:
  2147 + p[0] = p[0] * S[0]
  2148 + p[1] = p[1] * S[1]
  2149 + p[2] = p[2] * S[2]
  2150 +
  2151 + for n in self.N:
  2152 + n.p[0] = n.p[0] * S[0]
  2153 + n.p[1] = n.p[1] * S[1]
  2154 + n.p[2] = n.p[2] * S[2]
  2155 +
  2156 +
  2157 + #calculate the adjacency weighting function for the network given a set of vectors X = (x, y, z) and weight exponent k
  2158 + def adjacencyweight(self, P, k=200, length_threshold = 25, dtype=np.float32):
  2159 + V = self.vectors(dtype) #get the vectors representing each segment
  2160 + #V = V[0:n_vectors, :]
  2161 + L = np.expand_dims(np.sqrt((V**2).sum(1)), 1) #calculate the length of each vector
  2162 +
  2163 + outliers = L > length_threshold #remove outliers based on the length_threshold
  2164 + V = np.delete(V, np.where(outliers), 0)
  2165 + L = np.delete(L, np.where(outliers))
  2166 + V = V/L[:,None] #normalize the vectors
  2167 +
  2168 + P = np.stack(spharmonics.sph2cart(1, P[0], P[1]), P[0].ndim)
  2169 + PV = P[...,None,:] * V
  2170 + cos_alpha = PV.sum(PV.ndim-1)
  2171 + W = np.abs(cos_alpha) ** k
  2172 +
  2173 + return W, L
  2174 +
  2175 +
... ...
subgraph_shaders.py 0 → 100644
  1 +++ a/subgraph_shaders.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:35:04 2019
  5 +
  6 +@author: pavel
  7 +"""
  8 +
  9 +#shaders for drawing supernodes
  10 +vert_s = """
  11 +#version 120
  12 +// Uniforms
  13 +// ------------------------------------
  14 +uniform mat4 u_model;
  15 +uniform mat4 u_view;
  16 +uniform mat4 u_projection;
  17 +uniform vec3 u_graph_size;
  18 +uniform bool u_picking;
  19 +// Attributes
  20 +// ------------------------------------
  21 +attribute vec3 a_position;
  22 +attribute vec4 a_bg_color;
  23 +attribute vec4 a_cluster_color;
  24 +attribute vec2 a_value;
  25 +attribute float a_arc_length;
  26 +attribute vec4 a_outer_arc_length;
  27 +attribute vec4 a_unique_id;
  28 +
  29 +// Varyings
  30 +// ------------------------------------
  31 +varying vec4 v_bg_color;
  32 +varying vec4 v_cluster_color;
  33 +varying vec2 v_value;
  34 +varying float v_zoom_level;
  35 +varying float v_arc_length;
  36 +varying vec4 v_outer_arc_length;
  37 +varying vec4 v_unique_id;
  38 +
  39 +void main (void) {
  40 + v_value = a_value;
  41 + v_bg_color = a_bg_color;
  42 + v_cluster_color = a_cluster_color;
  43 + gl_Position = u_projection * u_view * u_model *
  44 + vec4(a_position, 1.0);
  45 + v_zoom_level = u_view[0][0];
  46 + v_arc_length = a_arc_length;
  47 + v_outer_arc_length = a_outer_arc_length;
  48 + v_unique_id = a_unique_id;
  49 +}
  50 +"""
  51 +
  52 +frag_s = """
  53 +#version 120
  54 +const float pi = 3.1415926535897932384626433832795;
  55 +// Varyings
  56 +// ------------------------------------
  57 +varying vec4 v_bg_color;
  58 +varying vec2 v_value;
  59 +varying float v_zoom_level;
  60 +varying vec4 v_cluster_color;
  61 +varying float v_arc_length;
  62 +varying vec4 v_outer_arc_length;
  63 +varying vec4 v_unique_id;
  64 +
  65 +uniform bool u_picking;
  66 +
  67 +
  68 +// Attributes
  69 +// ------------------------------------
  70 +
  71 +// Functions
  72 +// ------------------------------------
  73 +float new_alpha(float zoom_level);
  74 +float atan2(float y, float x);
  75 +// Main
  76 +// ------------------------------------
  77 +void main()
  78 +{
  79 + if(!u_picking)
  80 + {
  81 + //This is the color of the outer arc lengths
  82 + float R = 0.55;
  83 + float R2 = 0.3;
  84 + float dist = sqrt(dot(v_value, v_value));
  85 + float sm = smoothstep(R, R-0.0010, dist);
  86 + float sm2 = smoothstep(R2, R2+0.0010, dist);
  87 + float alpha = sm*sm2;
  88 +
  89 + float n_alpha = 1.0;
  90 + if(v_zoom_level < 0.0010)
  91 + {
  92 + n_alpha = new_alpha(v_zoom_level);
  93 + }
  94 + else
  95 + {
  96 + discard;
  97 + }
  98 + float angle = atan(v_value[1], v_value[0]);
  99 + if(dist < 0.25)
  100 + {
  101 + //gl_FragColor = v_cluster_color;
  102 + gl_FragColor = vec4(v_cluster_color.rgb, n_alpha);
  103 + }
  104 + if(dist > 0.3 && 0.55 > dist)
  105 + {
  106 + float angle2 = angle+pi;
  107 + if(angle2 > v_arc_length)
  108 + {
  109 + discard;
  110 + }
  111 + else
  112 + {
  113 + gl_FragColor = vec4(v_bg_color.rgb, n_alpha*alpha);
  114 + }
  115 + }
  116 + angle = atan(v_value[1]/dist, v_value[0]/dist);
  117 + //Need to add antialiasing to all other circles in the form of smoothstep.
  118 +
  119 + //THIS NEED TO BE FIXED
  120 + if(dist > 0.61 && 0.9 > dist)
  121 + {
  122 + if(angle < v_outer_arc_length[1])
  123 + {
  124 + gl_FragColor = vec4(0.32, 0.61, 0.93, n_alpha);
  125 + //gl_FragColor = vec4(1.0, 0.0, 0.0, n_alpha);
  126 + }
  127 + else if(angle > v_outer_arc_length[1] && angle < v_outer_arc_length[2])
  128 + {
  129 + gl_FragColor = vec4(0.337, 0.866, 0.415, n_alpha);
  130 + //gl_FragColor = vec4(0.0, 1.0, 0.0, n_alpha);
  131 + }
  132 + else if(angle > v_outer_arc_length[2] && angle < v_outer_arc_length[3])
  133 + {
  134 + gl_FragColor = vec4(0.988, 0.631, 0.058, n_alpha);
  135 + //gl_FragColor = vec4(0.0, 0.0, 1.0, n_alpha);
  136 + }
  137 + else //if(angle > v_outer_arc_length[3] && angle < pi)
  138 + {
  139 + gl_FragColor = vec4(0.93, 0.949, 0.329, n_alpha);
  140 + //gl_FragColor = vec4(1.0, 1.0, 0.0, n_alpha);
  141 + }
  142 + //else
  143 + //{
  144 + // discard;
  145 + //}
  146 +
  147 +// if(angle > -pi && angle < -pi/4.0)
  148 +// {
  149 +// //gl_FragColor = vec4(0.32, 0.61, 0.93, n_alpha);
  150 +// gl_FragColor = vec4(1.0, 0.0, 0.0, n_alpha);
  151 +// }
  152 +// else if(angle > -pi/4.0 && angle < 0.0)
  153 +// {
  154 +// gl_FragColor = vec4(0.0, 1.0, 0.0, n_alpha);
  155 +// }
  156 +// else if(angle > 0.0 && angle < pi/2.0)
  157 +// {
  158 +// gl_FragColor = vec4(0.0, 0.0, 1.0, n_alpha);
  159 +// }
  160 +// else if(angle > pi/2.0 && angle < pi)
  161 +// {
  162 +// gl_FragColor = vec4(1.0, 1.0, 0.0, n_alpha);
  163 +// }
  164 + }
  165 + }
  166 + else
  167 + {
  168 + float dist = sqrt(dot(v_value, v_value));
  169 + if (dist > 0.9)
  170 + discard;
  171 + else
  172 + gl_FragColor = v_unique_id;
  173 + }
  174 +}
  175 +
  176 +float atan2(float y, float x)
  177 +{
  178 + float s;
  179 + if(abs(x) > abs(y))
  180 + s = 1.0;
  181 + else
  182 + s = 0.0;
  183 + return (mix(pi/2.0 - atan(x,y), atan(y,x), s));
  184 +}
  185 +
  186 +//float atan2(in float y, in float x)
  187 +//{
  188 +// return x == 0.0 ? sign(y)*pi/2 : atan(y, x);
  189 +//}
  190 +
  191 +float new_alpha(float zoom_level)
  192 +{
  193 + float val = 1 - (zoom_level - 0.00075)/(0.0010-0.00075);
  194 + if(val > 1.0)
  195 + {
  196 + val = 1.0;
  197 + }
  198 + return val;
  199 +}
  200 +"""
  201 +
  202 +vs_s = """
  203 +#version 120
  204 +
  205 +// Uniforms
  206 +// ------------------------------------
  207 +uniform mat4 u_model;
  208 +uniform mat4 u_view;
  209 +uniform mat4 u_projection;
  210 +
  211 +// Attributes
  212 +// ------------------------------------
  213 +attribute vec3 a_position;
  214 +attribute vec2 a_normal;
  215 +attribute vec4 a_fg_color;
  216 +attribute float a_linewidth;
  217 +//attribute vec4 a_unique_id;
  218 +//attribute vec4 l_color;
  219 +
  220 +// Varyings
  221 +// ------------------------------------
  222 +varying vec4 v_fg_color;
  223 +varying float v_zoom_level;
  224 +varying vec2 v_normal;
  225 +varying float v_linewidth;
  226 +
  227 +void main() {
  228 + vec3 delta = vec3(a_normal*a_linewidth/((1-u_view[0][0])*0.1), 0);
  229 + //vec3 delta = vec3(a_normal*a_linewidth/(1-u_view[0][0]), 0);
  230 + gl_Position = u_model * u_view * u_projection * vec4(a_position+delta, 1.0);
  231 + //gl_Position = u_projection * u_view * u_model *
  232 + // vec4(a_position, 1.0);
  233 + v_zoom_level = u_view[0][0];
  234 + v_fg_color = a_fg_color;
  235 + v_normal = a_normal;
  236 + v_linewidth = a_linewidth;
  237 +
  238 +}
  239 +"""
  240 +
  241 +fs_s = """
  242 +#version 120
  243 +// Varying
  244 +// ------------------------------------
  245 +varying vec4 v_fg_color;
  246 +varying float v_zoom_level;
  247 +varying vec2 v_normal;
  248 +varying float v_linewidth;
  249 +
  250 +float new_alpha(float zoom_level);
  251 +
  252 +void main()
  253 +{
  254 + float l = length(v_normal);
  255 + float feather = 0.5;
  256 + float alpha = 1.0;
  257 + if(l > v_linewidth/2.0+feather)
  258 + discard;
  259 + else
  260 + alpha = 0.0;
  261 +
  262 + if(v_zoom_level < 0.0010)
  263 + alpha = new_alpha(v_zoom_level);
  264 + gl_FragColor = vec4(v_fg_color.rgb, alpha);
  265 +}
  266 +
  267 +float new_alpha(float zoom_level)
  268 +{
  269 + float val = 1 - (zoom_level - 0.00075)/(0.0010-0.00075);
  270 + if(val > 1.0)
  271 + {
  272 + val = 1.0;
  273 + }
  274 + return val;
  275 +}
  276 +"""
0 277 \ No newline at end of file
... ...
tube_shaders.py 0 → 100644
  1 +++ a/tube_shaders.py
  1 +#!/usr/bin/env python3
  2 +# -*- coding: utf-8 -*-
  3 +"""
  4 +Created on Mon Aug 5 15:58:30 2019
  5 +
  6 +@author: pavel
  7 +"""
  8 +
  9 +#vertex shader for the tube drawing
  10 +VERT_SHADER = """
  11 +// Uniforms
  12 +// ------------------------------------
  13 +uniform vec4 u_bb[26];
  14 +uniform vec3 u_LightPost;
  15 +uniform mat4 u_model;
  16 +//uniform mat4 u_view;
  17 +uniform mat4 u_projection;
  18 +
  19 +uniform vec3 u_eye;
  20 +uniform vec3 u_up;
  21 +uniform vec3 u_target;
  22 +
  23 +// Attributes
  24 +// ------------------------------------
  25 +attribute vec3 a_position;
  26 +attribute vec3 a_normal;
  27 +attribute vec4 a_fg_color;
  28 +
  29 +// Varyings
  30 +// ------------------------------------
  31 +varying vec3 v_normal;
  32 +varying vec4 v_fg_color;
  33 +varying vec3 v_position;
  34 +varying mat4 u_view;
  35 +varying vec3 vt_position;
  36 +
  37 +
  38 +// Functions
  39 +// ------------------------------------
  40 +
  41 +mat4 make_view(vec3 eye, vec3 target, vec3 up);
  42 +
  43 +void main (void) {
  44 + // Calculate position
  45 + mat4 u_view = make_view(u_eye, u_target, u_up);
  46 +
  47 + mat4 MVP = u_projection * u_view * u_model;
  48 + mat4 MV = u_view * u_model;
  49 + v_normal = vec3(MV * vec4(a_normal, 0.0));
  50 + v_position = vec3(MV*vec4(a_position, 1.0));
  51 + vt_position = vec3(a_position);
  52 + v_fg_color = a_fg_color;
  53 + gl_Position = MVP * vec4(a_position, 1.0);
  54 +}
  55 +
  56 +mat4 make_view(vec3 eye, vec3 target, vec3 up)
  57 +{
  58 + vec3 zaxis = normalize(eye - target);
  59 + vec3 xaxis = normalize(cross(up, zaxis));
  60 + vec3 yaxis = cross(zaxis, xaxis);
  61 +
  62 + mat4 viewMatrix = mat4(
  63 + vec4( xaxis.x, yaxis.x, zaxis.x, 0 ),
  64 + vec4( xaxis.y, yaxis.y, zaxis.y, 0 ),
  65 + vec4( xaxis.z, yaxis.z, zaxis.z, 0 ),
  66 + vec4(-dot( xaxis, eye ), -dot( yaxis, eye ), -dot( zaxis, eye ), 1 )
  67 + );
  68 +
  69 +// mat4 viewMatrix = mat4(
  70 +// vec4( xaxis.x, xaxis.y, xaxis.z, -dot( xaxis, eye ) ),
  71 +// vec4( yaxis.x, yaxis.y, yaxis.z, -dot( yaxis, eye ) ),
  72 +// vec4( zaxis.x, zaxis.y, zaxis.z, -dot( zaxis, eye ) ),
  73 +// vec4( 0, 0, 0, 1)
  74 +// );
  75 +
  76 +
  77 + return viewMatrix;
  78 +}
  79 +"""
  80 +
  81 +#Fragment shader for the tube drawing.
  82 +FRAG_SHADER = """
  83 +// Uniforms
  84 +// ------------------------------------
  85 +uniform vec3 u_bb[26];
  86 +uniform vec3 u_LightPos;
  87 +uniform mat4 u_model;
  88 +//uniform mat4 u_view;
  89 +uniform mat4 u_projection;
  90 +
  91 +uniform vec3 u_eye;
  92 +uniform vec3 u_up;
  93 +uniform vec3 u_target;
  94 +
  95 +// Varyings
  96 +// ------------------------------------
  97 +varying vec3 v_normal;
  98 +varying vec4 v_fg_color;
  99 +varying vec3 v_position;
  100 +varying mat4 u_view;
  101 +varying vec3 vt_position;
  102 +
  103 +float new_alpha();
  104 +float set_view();
  105 +float bin_alpha(float alpha);
  106 +
  107 +void main()
  108 +{
  109 + //Ambient Lighting
  110 + float ambientStrength = 0.5;
  111 + vec4 ambient_color = ambientStrength*v_fg_color;
  112 +
  113 +
  114 + mat4 MV = u_projection * u_view * u_model;
  115 + //vec3 u_LightP = vec3(0., 0., 0.);
  116 + vec3 u_LightP = vec3(u_eye);
  117 + //mat4 inv = inverse(u_view);
  118 +
  119 + //vec3 u_LightP = vec3(u_view[0][3], u_view[1][3], u_view[2][3]);
  120 +
  121 + //Diffuse Lighting
  122 + //float distance = length(u_LightP - v_position);
  123 + vec3 norm = normalize(v_normal);
  124 + vec3 lightVector = normalize(u_LightP - v_position);
  125 + float diffuse = max(dot(norm, lightVector), 0.0);
  126 +
  127 + //diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));
  128 +
  129 + vec4 color = (ambient_color + diffuse)*v_fg_color;
  130 + float alpha = new_alpha();
  131 + //if(alpha == 1.0)
  132 + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  133 + // gl_FragColor = vec4(color.rgb, alpha);
  134 + //else
  135 + gl_FragColor = vec4(color.rgb, alpha);
  136 + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  137 + //gl_FragColor = v_fg_color;
  138 +}
  139 +
  140 +float new_alpha()
  141 +{
  142 + float alpha = 0.0;
  143 +
  144 + //find the min and the max
  145 + float mx = -100000.0;
  146 + float mn = 100000.0;
  147 + vec3 u_light = vec3(u_eye);
  148 + for(int i = 0; i < 26; i++)
  149 + {
  150 + //vec3 rot_bb = vec3(u_projection * u_view * u_model * vec4(u_bb[i], 1.0));
  151 + vec3 rot_bb = u_bb[i];
  152 + if(length(rot_bb - u_light) < mn)
  153 + {
  154 + mn = length(rot_bb - u_light);
  155 + }
  156 + if(length(rot_bb - u_light) > mx)
  157 + {
  158 + mx = length(rot_bb - u_light);
  159 + }
  160 + }
  161 +
  162 + float l = length(u_light - vt_position);
  163 + alpha = (l-mn)/(mx-mn);
  164 + return bin_alpha(alpha);
  165 +// if(alpha > 0.9)
  166 +// return 1.0;
  167 +// else if(alpha > 0.8)
  168 +// return 0.9;
  169 +// else if(alpha > 0.7)
  170 +// return 0.8;
  171 +// else if (alpha > 0.6)
  172 +// return 0.7;
  173 +// else if (alpha > 0.5)
  174 +// return 0.6;
  175 +// else if (alpha > 0.4)
  176 +// return 0.5;
  177 +// else if (alpha > 0.3)
  178 +// return 0.4;
  179 +// else if (alpha > 0.2)
  180 +// return 0.3;
  181 +// else if (alpha > 0.1)
  182 +// return 0.2;
  183 +// else if (alpha > 0.0)
  184 +// return 0.0;
  185 +// else
  186 +// return alpha;
  187 +}
  188 +
  189 +float bin_alpha(float alpha)
  190 +{
  191 +
  192 +
  193 + if(alpha > 0.8)
  194 + return 0.0;
  195 + else if(alpha > 0.6)
  196 + return 0.1;
  197 + else if (alpha > 0.4)
  198 + return 0.4;
  199 + else if (alpha > 0.2)
  200 + return 0.8;
  201 + else if (alpha > 0.0)
  202 + return 1.0;
  203 + else
  204 + return 1.0;
  205 +
  206 +// if(alpha > 0.9)
  207 +// return 0.0;
  208 +// else if(alpha > 0.8)
  209 +// return 0.1;
  210 +// else if(alpha > 0.7)
  211 +// return 0.2;
  212 +// else if (alpha > 0.6)
  213 +// return 0.3;
  214 +// else if (alpha > 0.5)
  215 +// return 0.4;
  216 +// else if (alpha > 0.4)
  217 +// return 0.5;
  218 +// else if (alpha > 0.3)
  219 +// return 0.6;
  220 +// else if (alpha > 0.2)
  221 +// return 0.7;
  222 +// else if (alpha > 0.1)
  223 +// return 0.8;
  224 +// else if (alpha > 0.0)
  225 +// return 0.9;
  226 +// else
  227 +// return 1.0;
  228 +}
  229 +
  230 +"""
  231 +
  232 +
... ...