Commit bda1bd09c5453b550b854962041b66e87068d10e
1 parent
e98c381d
Added a network class implemented in python. Reads, writes NWT files, returns Ne…
…tworkX graph objects, and makes layouts.
Showing
1 changed file
with
280 additions
and
0 deletions
Show diff stats
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 | +import networkx as nx | ||
11 | +import matplotlib.pyplot as plt | ||
12 | +import math | ||
13 | + | ||
14 | +''' | ||
15 | + Definition of the Node class | ||
16 | + Duplicate of the node class in network | ||
17 | + Stores the physical position, outgoing edges list and incoming edges list. | ||
18 | +''' | ||
19 | +class Node: | ||
20 | + def __init__(self, point, outgoing, incoming): | ||
21 | + self.p = point | ||
22 | + self.o = outgoing | ||
23 | + self.i = incoming | ||
24 | + | ||
25 | +''' | ||
26 | + Definition of the Fiber class. | ||
27 | + Duplicate of the Node class in network | ||
28 | + Stores the starting vertex, the ending vertex, the points array and the radius array | ||
29 | +''' | ||
30 | +class Fiber: | ||
31 | + | ||
32 | + def __init__ (self): | ||
33 | + self.v0 = 0 | ||
34 | + self.v1 = 0 | ||
35 | + self.points = [] | ||
36 | + self.radii = [] | ||
37 | + | ||
38 | + def __init__ (self, p1, p2, pois, rads): | ||
39 | + self.v0 = p1 | ||
40 | + self.v1 = p2 | ||
41 | + self.points = pois | ||
42 | + self.radii = rads | ||
43 | + | ||
44 | + ''' | ||
45 | + return the length of the fiber. | ||
46 | + ''' | ||
47 | + def length(self): | ||
48 | + length = 0 | ||
49 | + for i in range(len(self.points)-1): | ||
50 | + 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)) | ||
51 | + | ||
52 | + return length | ||
53 | + | ||
54 | + ''' | ||
55 | + returns the turtuosity of the fiber. | ||
56 | + ''' | ||
57 | + def turtuosity(self): | ||
58 | + turtuosity = 0 | ||
59 | + 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)) | ||
60 | + turtuosity = self.length()/distance | ||
61 | + #print(turtuosity) | ||
62 | + | ||
63 | + return turtuosity | ||
64 | + | ||
65 | + ''' | ||
66 | + returns the volume of the fiber. | ||
67 | + ''' | ||
68 | + def volume(self): | ||
69 | + volume = 0 | ||
70 | + for i in range(len(self.points)-1): | ||
71 | + volume = volume + 1.0/3.0 * math.pi * (math.pow(self.radii[i],2) + math.pow(self.radii[i+1],2) + self.radii[i]*self.radii[i+1]) * 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)) | ||
72 | + | ||
73 | + #print(volume) | ||
74 | + return volume | ||
75 | +''' | ||
76 | + Writes the header given and open file descripion, number of verticies and number of edges. | ||
77 | +''' | ||
78 | +def writeHeader(open_file, numVerts, numEdges): | ||
79 | + txt = "nwtFileFormat fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata" | ||
80 | + b = bytearray() | ||
81 | + b.extend(txt.encode()) | ||
82 | + open_file.write(b) | ||
83 | + open_file.write(struct.pack('i', numVerts)) | ||
84 | + open_file.write(struct.pack('i', numEdges)) | ||
85 | + | ||
86 | + | ||
87 | +''' | ||
88 | + Writes a single vertex to a file. | ||
89 | +''' | ||
90 | +def writeVertex(open_file, vertex): | ||
91 | + open_file.write(struct.pack('<f',vertex.p[0])) | ||
92 | + open_file.write(struct.pack('<f',vertex.p[1])) | ||
93 | + open_file.write(struct.pack('<f',vertex.p[2])) | ||
94 | + open_file.write(struct.pack('i', len(vertex.o))) | ||
95 | + open_file.write(struct.pack('i', len(vertex.i))) | ||
96 | + for j in range(len(vertex.o)): | ||
97 | + open_file.write(struct.pack('i',vertex.o[j])) | ||
98 | + | ||
99 | + for j in range(len(vertex.i)): | ||
100 | + open_file.write(struct.pack('i', vertex.i[j])) | ||
101 | + | ||
102 | + return | ||
103 | + | ||
104 | +''' | ||
105 | + Writes a single fiber to a file. | ||
106 | +''' | ||
107 | +def writeFiber(open_file, edge): | ||
108 | + open_file.write(struct.pack('i',edge.v0)) | ||
109 | + open_file.write(struct.pack('i',edge.v1)) | ||
110 | + open_file.write(struct.pack('i',len(edge.points))) | ||
111 | + for j in range(len(edge.points)): | ||
112 | + open_file.write(struct.pack('<f', edge.points[j][0])) | ||
113 | + open_file.write(struct.pack('<f', edge.points[j][1])) | ||
114 | + open_file.write(struct.pack('<f', edge.points[j][2])) | ||
115 | + open_file.write(struct.pack('<f', edge.radii[j])) | ||
116 | + | ||
117 | + return | ||
118 | + | ||
119 | +''' | ||
120 | + Writes the entire network to a file in str given the vertices array and the edges array. | ||
121 | +''' | ||
122 | +def exportNWT(str, vertices, edges): | ||
123 | + with open(str, "wb") as file: | ||
124 | + writeHeader(file, len(vertices), len(edges)) | ||
125 | + for i in range(len(vertices)): | ||
126 | + writeVertex(file, vertices[i]) | ||
127 | + | ||
128 | + for i in range(len(edges)): | ||
129 | + writeFiber(file, edges[i]) | ||
130 | + | ||
131 | + return | ||
132 | + | ||
133 | + | ||
134 | +''' | ||
135 | + Reads a single vertex from an open file and returns a node Object. | ||
136 | +''' | ||
137 | +def readVertex(open_file): | ||
138 | + points = np.tile(0., 3) | ||
139 | + bytes = open_file.read(4) | ||
140 | + points[0] = struct.unpack('f', bytes)[0] | ||
141 | + bytes = open_file.read(4) | ||
142 | + points[1] = struct.unpack('f', bytes)[0] | ||
143 | + bytes = open_file.read(4) | ||
144 | + points[2] = struct.unpack('f', bytes)[0] | ||
145 | + bytes = open_file.read(4) | ||
146 | + | ||
147 | + numO = int.from_bytes(bytes, byteorder='little') | ||
148 | + outgoing = np.tile(0, numO) | ||
149 | + bts = open_file.read(4) | ||
150 | + numI = int.from_bytes(bts, byteorder='little') | ||
151 | + incoming = np.tile(0, numI) | ||
152 | + for j in range(numO): | ||
153 | + bytes = open_file.read(4) | ||
154 | + outgoing[j] = int.from_bytes(bytes, byteorder='little') | ||
155 | + | ||
156 | + for j in range(numI): | ||
157 | + bytes = open_file.read(4) | ||
158 | + incoming[j] = int.from_bytes(bytes, byteorder='little') | ||
159 | + | ||
160 | + node = Node(points, outgoing, incoming) | ||
161 | + return node | ||
162 | + | ||
163 | + | ||
164 | +''' | ||
165 | + Reads a single fiber from an open file and returns a Fiber object . | ||
166 | +''' | ||
167 | +def readFiber(open_file): | ||
168 | + bytes = open_file.read(4) | ||
169 | + vtx0 = int.from_bytes(bytes, byteorder = 'little') | ||
170 | + bytes = open_file.read(4) | ||
171 | + vtx1 = int.from_bytes(bytes, byteorder = 'little') | ||
172 | + bytes = open_file.read(4) | ||
173 | + numVerts = int.from_bytes(bytes, byteorder = 'little') | ||
174 | + pts = [] | ||
175 | + rads = [] | ||
176 | + | ||
177 | + for j in range(numVerts): | ||
178 | + point = np.tile(0., 3) | ||
179 | + bytes = open_file.read(4) | ||
180 | + point[0] = struct.unpack('f', bytes)[0] | ||
181 | + bytes = open_file.read(4) | ||
182 | + point[1] = struct.unpack('f', bytes)[0] | ||
183 | + bytes = open_file.read(4) | ||
184 | + point[2] = struct.unpack('f', bytes)[0] | ||
185 | + bytes = open_file.read(4) | ||
186 | + radius = struct.unpack('f', bytes)[0] | ||
187 | + pts.append(point) | ||
188 | + rads.append(radius) | ||
189 | + | ||
190 | + F = Fiber(vtx0, vtx1, pts, rads) | ||
191 | + | ||
192 | + return F | ||
193 | + | ||
194 | +''' | ||
195 | + Imports a NWT file at location str. | ||
196 | + Returns a list of Nodes objects and a list of Fiber objects. | ||
197 | +''' | ||
198 | +def importNWT(str): | ||
199 | + with open(str, "rb") as file: | ||
200 | + header = file.read(72) | ||
201 | + bytes = file.read(4) | ||
202 | + numVertex = int.from_bytes(bytes, byteorder='little') | ||
203 | + bytes = file.read(4) | ||
204 | + numEdges = int.from_bytes(bytes, byteorder='little') | ||
205 | + | ||
206 | + nodeList = [] | ||
207 | + fiberList = [] | ||
208 | + for i in range(numVertex): | ||
209 | + node = readVertex(file) | ||
210 | + nodeList.append(node) | ||
211 | + | ||
212 | + for i in range(numEdges): | ||
213 | + edge = readFiber(file) | ||
214 | + fiberList.append(edge) | ||
215 | + | ||
216 | + exportNWT("/home/pavel/Documents/Python/NetLayout/from_python_seg.nwt", nodeList, fiberList) | ||
217 | + print(str) | ||
218 | + return nodeList, fiberList; | ||
219 | + | ||
220 | +''' | ||
221 | +Creates a graph from a list of nodes and a list of edges. | ||
222 | +Uses edge length as weight. | ||
223 | +Returns a NetworkX Object. | ||
224 | +''' | ||
225 | + | ||
226 | +def createLengthGraph(nodeList, edgeList): | ||
227 | + G = nx.Graph() | ||
228 | + for i in range(len(nodeList)): | ||
229 | + G.add_node(i, p=V[i].p) | ||
230 | + for i in range(len(edgeList)): | ||
231 | + G.add_edge(edgeList[i].v0, edgeList[i].v1, weight = E[i].length()) | ||
232 | + | ||
233 | + return G | ||
234 | + | ||
235 | +''' | ||
236 | +Creates a graph from a list of nodes and a list of edges. | ||
237 | +Uses edge turtuosity as weight. | ||
238 | +Returns a NetworkX Object. | ||
239 | +''' | ||
240 | +def createTortuosityGraph(nodeList, edgeList): | ||
241 | + G = nx.Graph() | ||
242 | + for i in range(len(nodeList)): | ||
243 | + G.add_node(i, p=V[i].p) | ||
244 | + for i in range(len(edgeList)): | ||
245 | + G.add_edge(edgeList[i].v0, edgeList[i].v1, weight = E[i].turtuosity()) | ||
246 | + | ||
247 | + return G | ||
248 | + | ||
249 | + | ||
250 | +''' | ||
251 | +Creates a graph from a list of nodes and a list of edges. | ||
252 | +Uses edge volume as weight. | ||
253 | +Returns a NetworkX Object. | ||
254 | +''' | ||
255 | +def createVolumeGraph(nodeList, edgeList): | ||
256 | + G = nx.Graph() | ||
257 | + for i in range(len(nodeList)): | ||
258 | + G.add_node(i, p=V[i].p) | ||
259 | + for i in range(len(edgeList)): | ||
260 | + G.add_edge(edgeList[i].v0, edgeList[i].v1, weight = E[i].volume()) | ||
261 | + | ||
262 | + return G | ||
263 | +''' | ||
264 | +Returns the positions dictionary for the Circular layout. | ||
265 | +''' | ||
266 | +def getCircularLayout(graph, dim, radius): | ||
267 | + return nx.circular_layout(graph, dim, radius) | ||
268 | + | ||
269 | +''' | ||
270 | +Return the positions dictionary for the Spring layout. | ||
271 | +''' | ||
272 | +def getSpringLayout(graph, pos, iterations, scale): | ||
273 | + return nx.spring_layout(graph, 2, None, pos, iterations, weight='weight', scale, None) | ||
274 | + | ||
275 | +''' | ||
276 | +Draws the graph. | ||
277 | +''' | ||
278 | +def drawGraph(graph, pos): | ||
279 | + nx.draw(graph, pos) | ||
280 | + return | ||
0 | \ No newline at end of file | 281 | \ No newline at end of file |