Commit 9f9f17883ce16db5a1cfa46f0dfa21853c0f4f60
0 parents
clead up version of the GUI submitted to vis 2019
Showing
9 changed files
with
5125 additions
and
0 deletions
Show diff stats
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() | ... | ... |
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 | + | ... | ... |
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 | + | ... | ... |
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 | ... | ... |
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 | + | ... | ... |
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 | ... | ... |
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 | + | ... | ... |
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 | ... | ... |
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 | + | ... | ... |