Commit e6712a7291e3ec5f3d3738c8a8b6985c460fbe32

Authored by sberisha
2 parents 35534291 176a9b1c

Merge branch 'master' of git.stim.ee.uh.edu:codebase/stimlib

stim/biomodels/nwt_format.pptx renamed to docs/nwt_format.pptx
No preview for this file type
docs/test.nwt 0 → 100644
No preview for this file type
python/fibernet.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Mon Mar 19 14:45:39 2018
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import nwt
  9 +import matplotlib.pyplot as plt
  10 +import numpy
  11 +
  12 +
  13 +class fibernet:
  14 +
  15 + def __init__(self, filename):
  16 + self.nwt(filename)
  17 + self.buildgraph()
  18 +
  19 + #load a network from a NWT file
  20 + def nwt(self, filename):
  21 + net = nwt.NWT(filename)
  22 +
  23 + #first insert the points where fibers are connected
  24 + self.P = []
  25 + for vi in range(0, len(net.v)): #store each vertex in the point list
  26 + self.P.append( (net.v[vi].p[0], net.v[vi].p[1], net.v[vi].p[2]) )
  27 +
  28 + #now insert each fiber, connecting them appropriately using previously inserted points (vertices)
  29 + self.F = []
  30 + for ei in range(0, len(net.e)):
  31 + f = [numpy.int(net.e[ei].v[0])] #create a new fiber to store point indices, initialize with the first vertex ID
  32 + for pi in range(1, net.e[ei].p.shape[0]):
  33 + self.P.append( (net.e[ei].p[pi,0], net.e[ei].p[pi,1], net.e[ei].p[pi,2])) #append the current point to the global point list
  34 + f.append(len(self.P) - 1) #append the current point ID
  35 + f.append(numpy.int(net.e[ei].v[1])) #append the last vertex ID
  36 + self.F.append(f)
  37 +
  38 + #build a graph from the available geometry
  39 + def buildgraph(self):
  40 + p_to_v = numpy.ones((self.npoints()), numpy.int) * (-1) #create an array that maps a point ID to a vertex ID
  41 +
  42 + self.V = [] #create an empty vertex list
  43 + self.E = [] #create an empty edge list
  44 + for fi in range(0, self.nfibers()): #for each fiber in the network
  45 + if p_to_v[self.F[fi][0]] == -1: #if the first point hasn't been encountered
  46 + p_to_v[self.F[fi][0]] = len(self.V) #add it to the mapping
  47 + self.V.append( (self.F[fi][0], []) ) #add a new vertex representing this point
  48 + if p_to_v[self.F[fi][-1]] == -1:
  49 + p_to_v[self.F[fi][-1]] = len(self.V)
  50 + self.V.append( (self.F[fi][-1], []) )
  51 +
  52 + v0 = p_to_v[self.F[fi][0]] #get the two vertex indices corresponding to this edge
  53 + v1 = p_to_v[self.F[fi][-1]]
  54 +
  55 + self.E.append( (v0, v1) ) #create an edge representing this fiber and add it to the edge list
  56 + self.V[v0][1].append(fi) #add this edge ID to the corresponding vertices to update the adjacency list
  57 + self.V[v1][1].append(fi)
  58 +
  59 + #return the length of a fiber specified by fiber ID fid
  60 + def length(self, fid):
  61 + p = self.points(fid) #get the points corresponding to the fiber
  62 + l = 0 #initialize the length to zero
  63 + for pi in range(1, len(p)): #for each line segment
  64 + v = p[pi] - p[pi-1] #calculate the vector representing the line segment
  65 + l = l + numpy.linalg.norm(numpy.array(v)) #add the length of the line segment
  66 + return l #return the total length
  67 +
  68 + #return a list of vertices adjacent to the vertex specified by vid
  69 + def adjacent(self, vid):
  70 + adj = [] #initialize an empty adjacency list
  71 + for e in self.V[vid][1]: #for each edge connected to vid
  72 + if self.E[e][0] != vid:
  73 + adj.append(self.E[e][0])
  74 + else:
  75 + adj.append(self.E[e][1])
  76 + return adj
  77 +
  78 + #return the fiber network graph as an adjacency list
  79 + def adjacencylist(self):
  80 + A = []
  81 + for vi in range(0, len(self.V)): #for each vertex in the graph
  82 + A.append( self.adjacent(vi) ) #add the adjacency list for the vertex into the main adjacency list
  83 + return A
  84 +
  85 + #return the fiber network graph as an adjacency matrix
  86 + # possible values for the matrix include:
  87 + # binary - 1 when there is a connection, 0 otherwise
  88 + # edgeID - stores the edge ID corresponding to a connection, and -1 otherwise
  89 + # length - stores the length of the connecting fiber, and -1 if there is no connection
  90 + def adjacencymatrix(self, value="binary"):
  91 + if value == "binary":
  92 + M = numpy.ones( (len(self.V), len(self.V)), numpy.bool )
  93 + elif value == "edgeID":
  94 + M = numpy.ones( (len(self.V), len(self.V)), numpy.int ) * -1
  95 + elif value == "length":
  96 + M = numpy.ones( (len(self.V), len(self.V)) ) * -1
  97 + else:
  98 + return []
  99 +
  100 + for vi in range(0, len(self.V)): #for each vertex in the graph
  101 + A = self.adjacent(vi) #get the adjacency matrix
  102 + for a in A:
  103 + if value == "binary":
  104 + M[vi,a] = M[a,vi] = 1
  105 + if value == "edgeID":
  106 + M[vi,a] = M[a,vi] = self.findedge(a, vi)
  107 + if value == "length":
  108 + M[vi,a] = M[a,vi] = self.length(self.findedge(a,vi))
  109 + return M
  110 +
  111 + #find the edge ID corresponding to two vertices
  112 + def findedge(self, v0, v1):
  113 +
  114 + edges = self.V[v0][1] #get the list of candidate edges connected to v0
  115 + for e in edges: #for each edge
  116 + if self.E[e][0] == v1 or self.E[e][1] == v1:
  117 + return e
  118 +
  119 + return -1 #return -1 if the edge doesn't exist
  120 +
  121 + #get the points associated with a specified fiber ID fid
  122 + def points(self, fid): #get the points corresponding to a specified fiber index
  123 + f = self.F[fid] #get the list of point indices
  124 + return numpy.array(self.P)[f]
  125 +
  126 + #get the total number of points in the network
  127 + def npoints(self):
  128 + return len(self.P)
  129 +
  130 + #get the total number of fibers in the network
  131 + def nfibers(self):
  132 + return len(self.F)
  133 +
  134 + #plot the network as a 3D line plot
  135 + def plot(self):
  136 + fig = plt.figure()
  137 + ax = fig.add_subplot(111, projection='3d')
  138 +
  139 + for fi in range(0, self.nfibers()):
  140 + test = fibers.points(fi)
  141 + ax.plot(test[:, 0], test[:, 1], test[:, 2])
  142 + return fig
0 143 \ No newline at end of file
... ...
python/network.py renamed to python/network_dep.py
python/nwt.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Mon Mar 19 12:38:30 2018
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import numpy
  9 +
  10 +class Vertex:
  11 + def __init__(self, f): #load a vertex given a file ID at that vertex location
  12 + self.p = numpy.fromfile(f, dtype=numpy.float32, count=3) #load the vertex coordinates
  13 +
  14 + EO = int.from_bytes(f.read(4), "little") #get the number of edges moving out of and into this vertex
  15 + EI = int.from_bytes(f.read(4), "little")
  16 +
  17 + self.Eout = numpy.fromfile(f, dtype=numpy.uint32, count=EO) #get the incoming and outgoing edge lists
  18 + self.Ein = numpy.fromfile(f, dtype=numpy.uint32, count=EI)
  19 +
  20 + def __str__(self):
  21 + sp = "(" + str(self.p[0]) + ", " + str(self.p[1]) + ", " + str(self.p[2]) + ")"
  22 +
  23 + seo = "["
  24 + for e in self.Eout:
  25 + seo = seo + " " + str(e)
  26 + seo = seo + " ]"
  27 +
  28 + sei = "["
  29 + for e in self.Ein:
  30 + sei = sei + " " + str(e)
  31 + sei = sei + " ]"
  32 +
  33 + return sp + " out = " + seo + " in = " + sei
  34 +
  35 +class Edge:
  36 + def __init__(self, f):
  37 + self.v = numpy.fromfile(f, dtype=numpy.uint32, count=2)
  38 + P = int.from_bytes(f.read(4), "little")
  39 + p1d = numpy.fromfile(f, dtype=numpy.float32, count=4*P)
  40 + self.p = numpy.reshape(p1d, (P, 4))
  41 +
  42 + def __str__(self):
  43 + sv = "[ " + str(self.v[0]) + "---> " + str(self.v[1]) + " ]"
  44 + return sv + str(self.p)
  45 +
  46 +class NWT:
  47 + def __init__(self, fname):
  48 + f = open(fname, "rb") #open the NWT file for binary reading
  49 + self.filetype = f.read(14) #this should be "nwtfileformat "
  50 + self.desc = f.read(58) #NWT file description
  51 + V = int.from_bytes(f.read(4), "little") #retrieve the number of vertices (graph)
  52 + E = int.from_bytes(f.read(4), "little")
  53 +
  54 + self.v = []
  55 + for i in range(0, V):
  56 + self.v.append(Vertex(f))
  57 +
  58 + self.e = []
  59 + for i in range(1, E):
  60 + self.e.append(Edge(f))
0 61 \ No newline at end of file
... ...
stim/biomodels/centerline.h
1   -/*
2   -Copyright <2017> <David Mayerich>
3   -
4   -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5   -
6   -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7   -
8   -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9   -*/
10   -
11   -#ifndef STIM_CENTERLINE_H
12   -#define STIM_CENTERLINE_H
13   -
14   -#include <vector>
15   -#include <stim/math/vec3.h>
16   -#include <stim/structures/kdtree.cuh>
17   -
18   -namespace stim{
19   -
20   -/** This class stores information about a single fiber represented as a set of geometric points
21   - * between two branch or end points. This class is used as a fundamental component of the stim::network
22   - * class to describe an interconnected (often biological) network.
23   - */
24   -template<typename T>
25   -class centerline : public std::vector< stim::vec3<T> >{
26   -
27   -protected:
28   -
29   - std::vector<T> L; //stores the integrated length along the fiber (used for parameterization)
30   -
31   - ///Return the normalized direction vector at point i (average of the incoming and outgoing directions)
32   - vec3<T> d(size_t i) {
33   - if (size() <= 1) return vec3<T>(0, 0, 0); //if there is insufficient information to calculate the direction, return a null vector
34   - if (size() == 2) return (at(1) - at(0)).norm(); //if there are only two points, the direction vector at both is the direction of the line segment
35   - if (i == 0) return (at(1) - at(0)).norm(); //the first direction vector is oriented towards the first line segment
36   - if (i == size() - 1) return (at(size() - 1) - at(size() - 2)).norm(); //the last direction vector is oriented towards the last line segment
37   -
38   - //all other direction vectors are the average direction of the two joined line segments
39   - vec3<T> a = at(i) - at(i - 1);
40   - vec3<T> b = at(i + 1) - at(i);
41   - vec3<T> ab = a.norm() + b.norm();
42   - return ab.norm();
43   - }
44   -
45   - //initializes the integrated length vector to make parameterization easier, starting with index idx (all previous indices are assumed to be correct)
46   - void update_L(size_t start = 0) {
47   - L.resize(size()); //allocate space for the L array
48   - if (start == 0) {
49   - L[0] = 0; //initialize the length value for the first point to zero (0)
50   - start++;
51   - }
52   -
53   - stim::vec3<T> d;
54   - for (size_t i = start; i < size(); i++) { //for each line segment in the centerline
55   - d = at(i) - at(i - 1);
56   - L[i] = L[i - 1] + d.len(); //calculate the running length total
57   - }
58   - }
59   -
60   - void init() {
61   - if (size() == 0) return; //return if there aren't any points
62   - update_L();
63   - }
64   -
65   - /// Returns a stim::vec representing the point at index i
66   -
67   - /// @param i is an index of the desired centerline point
68   - stim::vec<T> get_vec(unsigned i){
69   - return std::vector< stim::vec3<T> >::at(i);
70   - }
71   -
72   - ///finds the index of the point closest to the length l on the lower bound.
73   - ///binary search.
74   - size_t findIdx(T l) {
75   - for (size_t i = 1; i < L.size(); i++) { //for each point in the centerline
76   - if (L[i] > l) return i - 1; //if we have passed the desired length value, return i-1
77   - }
78   - return L.size() - 1;
79   - /*size_t i = L.size() / 2;
80   - size_t max = L.size() - 1;
81   - size_t min = 0;
82   - while (i < L.size() - 1){
83   - if (l < L[i]) {
84   - max = i;
85   - i = min + (max - min) / 2;
86   - }
87   - else if (L[i] <= l && L[i + 1] >= l) {
88   - break;
89   - }
90   - else {
91   - min = i;
92   - i = min + (max - min) / 2;
93   - }
94   - }
95   - return i;*/
96   - }
97   -
98   - ///Returns a position vector at the given length into the fiber (based on the pvalue).
99   - ///Interpolates the radius along the line.
100   - ///@param l: the location of the in the cylinder.
101   - ///@param idx: integer location of the point closest to l but prior to it.
102   - stim::vec3<T> p(T l, int idx) {
103   - T rat = (l - L[idx]) / (L[idx + 1] - L[idx]);
104   - stim::vec3<T> v1 = at(idx);
105   - stim::vec3<T> v2 = at(idx + 1);
106   - return(v1 + (v2 - v1)*rat);
107   - }
108   -
109   -
110   -public:
111   -
112   - using std::vector< stim::vec3<T> >::at;
113   - using std::vector< stim::vec3<T> >::size;
114   -
115   - centerline() : std::vector< stim::vec3<T> >() {
116   - init();
117   - }
118   - centerline(size_t n) : std::vector< stim::vec3<T> >(n){
119   - init();
120   - }
121   - centerline(std::vector<stim::vec3<T> > pos) :
122   - std::vector<stim::vec3<T> > (pos)
123   - {
124   - init();
125   - }
126   -
127   - //overload the push_back function to update the length vector
128   - void push_back(stim::vec3<T> p) {
129   - std::vector< stim::vec3<T> >::push_back(p);
130   - update_L(size() - 1);
131   - }
132   -
133   - ///Returns a position vector at the given p-value (p value ranges from 0 to 1).
134   - ///interpolates the position along the line.
135   - ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
136   - stim::vec3<T> p(T pvalue) {
137   - if (pvalue <= 0.0) return at(0); //return the first element
138   - if (pvalue >= 1.0) return back(); //return the last element
139   -
140   - T l = pvalue*L[L.size() - 1];
141   - int idx = findIdx(l);
142   - return p(l, idx);
143   - }
144   -
145   - ///Update centerline internal parameters (currently the L vector)
146   - void update() {
147   - init();
148   - }
149   - ///Return the length of the entire centerline
150   - T length() {
151   - return L.back();
152   - }
153   -
154   -
155   - /// stitch two centerlines
156   - ///@param c1, c2: two centerlines
157   - ///@param sigma: sample rate
158   - static std::vector< stim::centerline<T> > stitch(stim::centerline<T> c1, stim::centerline<T> c2 = stim::centerline<T>()) {
159   -
160   - std::vector< stim::centerline<T> > result;
161   - stim::centerline<T> new_centerline;
162   - stim::vec3<T> new_vertex;
163   -
164   - // if only one centerline, stitch itself!
165   - if (c2.size() == 0) {
166   - size_t num = c1.size();
167   - size_t id = 100000; // store the downsample start position
168   - T threshold;
169   - if (num < 4) { // if the number of vertex is less than 4, do nothing
170   - result.push_back(c1);
171   - return result;
172   - }
173   - else {
174   - // test geometry start vertex
175   - stim::vec3<T> v1 = c1[1] - c1[0]; // vector from c1[0] to c1[1]
176   - for (size_t p = 2; p < num; p++) { // 90° standard???
177   - stim::vec3<T> v2 = c1[p] - c1[0];
178   - float cosine = v2.dot(v1);
179   - if (cosine < 0) {
180   - id = p;
181   - threshold = v2.len();
182   - break;
183   - }
184   - }
185   - if (id != 100000) { // find a downsample position on the centerline
186   - T* c;
187   - c = (T*)malloc(sizeof(T) * (num - id) * 3);
188   - for (size_t p = id; p < num; p++) {
189   - for (size_t d = 0; d < 3; d++) {
190   - c[p * 3 + d] = c1[p][d];
191   - }
192   - }
193   - stim::kdtree<T, 3> kdt;
194   - kdt.create(c, num - id, 5); // create tree
195   -
196   - T* query = (T*)malloc(sizeof(T) * 3);
197   - for (size_t d = 0; d < 3; d++)
198   - query[d] = c1[0][d];
199   - size_t index;
200   - T dist;
201   -
202   - kdt.search(query, 1, &index, &dist);
203   -
204   - free(query);
205   - free(c);
206   -
207   - if (dist > threshold) {
208   - result.push_back(c1);
209   - }
210   - else {
211   - // the loop part
212   - new_vertex = c1[index];
213   - new_centerline.push_back(new_vertex);
214   - for (size_t p = 0; p < index + 1; p++) {
215   - new_vertex = c1[p];
216   - new_centerline.push_back(new_vertex);
217   - }
218   - result.push_back(new_centerline);
219   - new_centerline.clear();
220   -
221   - // the tail part
222   - for (size_t p = index; p < num; p++) {
223   - new_vertex = c1[p];
224   - new_centerline.push_back(new_vertex);
225   - }
226   - result.push_back(new_centerline);
227   - }
228   - }
229   - else { // there is one potential problem that two positions have to be stitched
230   - // test geometry end vertex
231   - stim::vec3<T> v1 = c1[num - 2] - c1[num - 1];
232   - for (size_t p = num - 2; p > 0; p--) { // 90° standard
233   - stim::vec3<T> v2 = c1[p - 1] - c1[num - 1];
234   - float cosine = v2.dot(v1);
235   - if (cosine < 0) {
236   - id = p;
237   - threshold = v2.len();
238   - break;
239   - }
240   - }
241   - if (id != 100000) { // find a downsample position
242   - T* c;
243   - c = (T*)malloc(sizeof(T) * (id + 1) * 3);
244   - for (size_t p = 0; p < id + 1; p++) {
245   - for (size_t d = 0; d < 3; d++) {
246   - c[p * 3 + d] = c1[p][d];
247   - }
248   - }
249   - stim::kdtree<T, 3> kdt;
250   - kdt.create(c, id + 1, 5); // create tree
251   -
252   - T* query = (T*)malloc(sizeof(T) * 1 * 3);
253   - for (size_t d = 0; d < 3; d++)
254   - query[d] = c1[num - 1][d];
255   - size_t index;
256   - T dist;
257   -
258   - kdt.search(query, 1, &index, &dist);
259   -
260   - free(query);
261   - free(c);
262   -
263   - if (dist > threshold) {
264   - result.push_back(c1);
265   - }
266   - else {
267   - // the tail part
268   - for (size_t p = 0; p < index + 1; p++) {
269   - new_vertex = c1[p];
270   - new_centerline.push_back(new_vertex);
271   - }
272   - result.push_back(new_centerline);
273   - new_centerline.clear();
274   -
275   - // the loop part
276   - for (size_t p = index; p < num; p++) {
277   - new_vertex = c1[p];
278   - new_centerline.push_back(new_vertex);
279   - }
280   - new_vertex = c1[index];
281   - new_centerline.push_back(new_vertex);
282   - result.push_back(new_centerline);
283   - }
284   - }
285   - else { // no stitch position
286   - result.push_back(c1);
287   - }
288   - }
289   - }
290   - }
291   -
292   -
293   - // two centerlines
294   - else {
295   - // find stitch position based on nearest neighbors
296   - size_t num1 = c1.size();
297   - T* c = (T*)malloc(sizeof(T) * num1 * 3); // c1 as reference point
298   - for (size_t p = 0; p < num1; p++) // centerline to array
299   - for (size_t d = 0; d < 3; d++) // because right now my kdtree code is a relatively close code, it has its own structure
300   - c[p * 3 + d] = c1[p][d]; // I will merge it into stimlib totally in the near future
301   -
302   - stim::kdtree<T, 3> kdt; // kdtree object
303   - kdt.create(c, num1, 5); // create tree
304   -
305   - size_t num2 = c2.size();
306   - T* query = (T*)malloc(sizeof(T) * num2 * 3); // c2 as query point
307   - for (size_t p = 0; p < num2; p++) {
308   - for (size_t d = 0; d < 3; d++) {
309   - query[p * 3 + d] = c2[p][d];
310   - }
311   - }
312   - std::vector<size_t> index(num2);
313   - std::vector<T> dist(num2);
314   -
315   - kdt.search(query, num2, &index[0], &dist[0]); // find the nearest neighbors in c1 for c2
316   -
317   - // clear up
318   - free(query);
319   - free(c);
320   -
321   - // find the average vertex distance of one centerline
322   - T sigma1 = 0;
323   - T sigma2 = 0;
324   - for (size_t p = 0; p < num1 - 1; p++)
325   - sigma1 += (c1[p] - c1[p + 1]).len();
326   - for (size_t p = 0; p < num2 - 1; p++)
327   - sigma2 += (c2[p] - c2[p + 1]).len();
328   - sigma1 /= (num1 - 1);
329   - sigma2 /= (num2 - 1);
330   - float threshold = 4 * (sigma1 + sigma2) / 2; // better way to do this?
331   -
332   - T min_d = *std::min_element(dist.begin(), dist.end()); // find the minimum distance between c1 and c2
333   -
334   - if (min_d > threshold) { // if the minimum distance is too large
335   - result.push_back(c1);
336   - result.push_back(c2);
337   -
338   -#ifdef DEBUG
339   - std::cout << "The distance between these two centerlines is too large" << std::endl;
340   -#endif
341   - }
342   - else {
343   - // auto smallest = std::min_element(dist.begin(), dist.end());
344   - unsigned int smallest = std::min_element(dist.begin(), dist.end());
345   - // auto i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
346   - unsigned int i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
347   -
348   - // stitch position in c1 and c2
349   - int id1 = index[i];
350   - int id2 = i;
351   -
352   - // actually there are two cases
353   - // first one inacceptable
354   - // second one acceptable
355   - if (id1 != 0 && id1 != num1 - 1 && id2 != 0 && id2 != num2 - 1) { // only stitch one end vertex to another centerline
356   - result.push_back(c1);
357   - result.push_back(c2);
358   - }
359   - else {
360   - if (id1 == 0 || id1 == num1 - 1) { // if the stitch vertex is the first or last vertex of c1
361   - // for c2, consider two cases(one degenerate case)
362   - if (id2 == 0 || id2 == num2 - 1) { // case 1, if stitch position is also on the end of c2
363   - // we have to decide which centerline get a new vertex, based on direction
364   - // for c1, computer the direction change angle
365   - stim::vec3<T> v1, v2;
366   - float alpha1, alpha2; // direction change angle
367   - if (id1 == 0)
368   - v1 = (c1[1] - c1[0]).norm();
369   - else
370   - v1 = (c1[num1 - 2] - c1[num1 - 1]).norm();
371   - v2 = (c2[id2] - c1[id1]).norm();
372   - alpha1 = v1.dot(v2);
373   - if (id2 == 0)
374   - v1 = (c2[1] - c2[0]).norm();
375   - else
376   - v1 = (c2[num2 - 2] - c2[num2 - 1]).norm();
377   - v2 = (c1[id1] - c2[id2]).norm();
378   - alpha2 = v1.dot(v2);
379   - if (abs(alpha1) > abs(alpha2)) { // add the vertex to c1 in order to get smooth connection
380   - // push back c1
381   - if (id1 == 0) { // keep geometry information
382   - new_vertex = c2[id2];
383   - new_centerline.push_back(new_vertex);
384   - for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
385   - new_vertex = c1[p];
386   - new_centerline.push_back(new_vertex);
387   - }
388   - }
389   - else {
390   - for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
391   - new_vertex = c1[p];
392   - new_centerline.push_back(new_vertex);
393   - }
394   - new_vertex = c2[id2];
395   - new_centerline.push_back(new_vertex);
396   - }
397   - result.push_back(new_centerline);
398   - new_centerline.clear();
399   -
400   - // push back c2
401   - for (size_t p = 0; p < num2; p++) {
402   - new_vertex = c2[p];
403   - new_centerline.push_back(new_vertex);
404   - }
405   - result.push_back(new_centerline);
406   - }
407   - else { // add the vertex to c2 in order to get smooth connection
408   - // push back c1
409   - for (size_t p = 0; p < num1; p++) {
410   - new_vertex = c1[p];
411   - new_centerline.push_back(new_vertex);
412   - }
413   - result.push_back(new_centerline);
414   - new_centerline.clear();
415   -
416   - // push back c2
417   - if (id2 == 0) { // keep geometry information
418   - new_vertex = c1[id1];
419   - new_centerline.push_back(new_vertex);
420   - for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
421   - new_vertex = c2[p];
422   - new_centerline.push_back(new_vertex);
423   - }
424   - }
425   - else {
426   - for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
427   - new_vertex = c2[p];
428   - new_centerline.push_back(new_vertex);
429   - }
430   - new_vertex = c1[id1];
431   - new_centerline.push_back(new_vertex);
432   - }
433   - result.push_back(new_centerline);
434   - }
435   - }
436   - else { // case 2, the stitch position is on c2
437   - // push back c1
438   - if (id1 == 0) { // keep geometry information
439   - new_vertex = c2[id2];
440   - new_centerline.push_back(new_vertex);
441   - for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
442   - new_vertex = c1[p];
443   - new_centerline.push_back(new_vertex);
444   - }
445   - }
446   - else {
447   - for (size_t p = 0; p < num1; p++) { // geometry end vertex on c1 -> geometry start vertex on c1 -> stitch vertex on c2
448   - new_vertex = c1[p];
449   - new_centerline.push_back(new_vertex);
450   - }
451   - new_vertex = c2[id2];
452   - new_centerline.push_back(new_vertex);
453   - }
454   - result.push_back(new_centerline);
455   - new_centerline.clear();
456   -
457   - // push back c2
458   - for (size_t p = 0; p < id2 + 1; p++) { // first part
459   - new_vertex = c2[p];
460   - new_centerline.push_back(new_vertex);
461   - }
462   - result.push_back(new_centerline);
463   - new_centerline.clear();
464   -
465   - for (size_t p = id2; p < num2; p++) { // second part
466   - new_vertex = c2[p];
467   - new_centerline.push_back(new_vertex);
468   - }
469   - result.push_back(new_centerline);
470   - }
471   - }
472   - else { // if the stitch vertex is the first or last vertex of c2
473   - // push back c2
474   - if (id2 == 0) { // keep geometry information
475   - new_vertex = c1[id1];
476   - new_centerline.push_back(new_vertex);
477   - for (size_t p = 0; p < num2; p++) { // stitch vertex on c1 -> geometry start vertex on c2 -> geometry end vertex on c2
478   - new_vertex = c2[p];
479   - new_centerline.push_back(new_vertex);
480   - }
481   - }
482   - else {
483   - for (size_t p = 0; p < num2; p++) { // geometry end vertex on c2 -> geometry start vertex on c2 -> stitch vertex on c1
484   - new_vertex = c2[p];
485   - new_centerline.push_back(new_vertex);
486   - }
487   - new_vertex = c1[id1];
488   - new_centerline.push_back(new_vertex);
489   - result.push_back(new_centerline);
490   - new_centerline.clear();
491   -
492   - // push back c1
493   - for (size_t p = 0; p < id1 + 1; p++) { // first part
494   - new_vertex = c1[p];
495   - new_centerline.push_back(new_vertex);
496   - }
497   - result.push_back(new_centerline);
498   - new_centerline.clear();
499   -
500   - for (size_t p = id1; p < num1; p++) { // second part
501   - new_vertex = c1[p];
502   - new_centerline.push_back(new_vertex);
503   - }
504   - result.push_back(new_centerline);
505   - }
506   - }
507   - }
508   - }
509   - }
510   - return result;
511   - }
512   -
513   - /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
514   - std::vector< stim::centerline<T> > split(unsigned int idx){
515   -
516   - std::vector< stim::centerline<T> > fl; //create an array to store up to two fibers
517   - size_t N = size();
518   -
519   - //if the index is an end point, only the existing fiber is returned
520   - if(idx == 0 || idx == N-1){
521   - fl.resize(1); //set the size of the fiber to 1
522   - fl[0] = *this; //copy the current fiber
523   - }
524   -
525   - //if the index is not an end point
526   - else{
527   -
528   - unsigned int N1 = idx + 1; //calculate the size of both fibers
529   - unsigned int N2 = N - idx;
530   -
531   - fl.resize(2); //set the array size to 2
532   -
533   - fl[0] = stim::centerline<T>(N1); //set the size of each fiber
534   - fl[1] = stim::centerline<T>(N2);
535   -
536   - //copy both halves of the fiber
537   - unsigned int i;
538   -
539   - //first half
540   - for(i = 0; i < N1; i++) //for each centerline point
541   - fl[0][i] = std::vector< stim::vec3<T> >::at(i);
542   - fl[0].init(); //initialize the length vector
543   -
544   - //second half
545   - for(i = 0; i < N2; i++)
546   - fl[1][i] = std::vector< stim::vec3<T> >::at(idx+i);
547   - fl[1].init(); //initialize the length vector
548   - }
549   -
550   - return fl; //return the array
551   -
552   - }
553   -
554   - /// Outputs the fiber as a string
555   - std::string str(){
556   - std::stringstream ss;
557   - size_t N = std::vector< stim::vec3<T> >::size();
558   - ss << "---------[" << N << "]---------" << std::endl;
559   - for (size_t i = 0; i < N; i++)
560   - ss << std::vector< stim::vec3<T> >::at(i) << std::endl;
561   - ss << "--------------------" << std::endl;
562   -
563   - return ss.str();
564   - }
565   -
566   - /// Back method returns the last point in the fiber
567   - stim::vec3<T> back(){
568   - return std::vector< stim::vec3<T> >::back();
569   - }
570   -
571   - ////resample a fiber in the network
572   - stim::centerline<T> resample(T spacing)
573   - {
574   - //std::cout<<"fiber::resample()"<<std::endl;
575   -
576   - stim::vec3<T> v; //v-direction vector of the segment
577   - stim::vec3<T> p; //- intermediate point to be added
578   - stim::vec3<T> p1; // p1 - starting point of an segment on the fiber,
579   - stim::vec3<T> p2; // p2 - ending point,
580   - //double sum=0; //distance summation
581   -
582   - size_t N = size();
583   -
584   - centerline<T> new_c; // initialize list of new resampled points on the fiber
585   - // for each point on the centerline (skip if it is the last point on centerline)
586   - for(unsigned int f=0; f< N-1; f++)
587   - {
588   - p1 = at(f);
589   - p2 = at(f+1);
590   - v = p2 - p1;
591   -
592   - T lengthSegment = v.len(); //find Length of the segment as distance between the starting and ending points of the segment
593   -
594   - if(lengthSegment >= spacing){ // if length of the segment is greater than standard deviation resample
595   -
596   - // repeat resampling until accumulated stepsize is equsl to length of the segment
597   - for(T step=0.0; step<lengthSegment; step+=spacing){
598   - // calculate the resampled point by travelling step size in the direction of normalized gradient vector
599   - p = p1 + v * (step / lengthSegment);
600   -
601   - // add this resampled points to the new fiber list
602   - new_c.push_back(p);
603   - }
604   - }
605   - else // length of the segment is now less than standard deviation, push the ending point of the segment and proceed to the next point in the fiber
606   - new_c.push_back(at(f));
607   - }
608   - new_c.push_back(at(N-1)); //add the last point on the fiber to the new fiber list
609   - //centerline newFiber(newPointList);
610   - return new_c;
611   - }
612   -
613   -};
614   -
615   -
616   -
617   -} //end namespace stim
618   -
619   -
620   -
621   -#endif
  1 +#ifndef JACK_CENTERLINE_H
  2 +#define JACK_CENTERLINE_H
  3 +
  4 +#include <stdlib.h>
  5 +#include <vector>
  6 +#include <stim/math/vec3.h>
  7 +#include <stim/structures/kdtree.cuh>
  8 +
  9 +namespace stim {
  10 +
  11 + /// we always assume that one centerline has a flow direction even it actually does not have. Also, we allow loop centerline
  12 + /// NOTE: centerline is not derived from std::vector<stim::vec3<T>> class!!!
  13 + template<typename T>
  14 + class centerline {
  15 +
  16 + private:
  17 + size_t n; // number of points on the centerline, can be zero if NULL
  18 +
  19 + // update length information at each point (distance from starting point) starting from index "start"
  20 + void update_L(size_t start = 0) {
  21 + L.resize(n);
  22 +
  23 + if (start == 0) {
  24 + L[0] = 0.0f;
  25 + start++;
  26 + }
  27 +
  28 + stim::vec3<T> dir; // temp direction vector for calculating length between two points
  29 + for (size_t i = start; i < n; i++) {
  30 + dir = C[i] - C[i - 1]; // calculate the certerline extending direction
  31 + L[i] = L[i - 1] + dir.len(); // addition
  32 + }
  33 + }
  34 +
  35 + protected:
  36 + std::vector<stim::vec3<T> > C; // points on the centerline
  37 + std::vector<T> L; // stores the integrated length along the fiber
  38 +
  39 + public:
  40 + /// constructors
  41 + // empty constructor
  42 + centerline() {
  43 + n = 0;
  44 + }
  45 +
  46 + // constructor that allocate memory
  47 + centerline(size_t s) {
  48 + n = s;
  49 + C.resize(s); // allocate memory for points
  50 + L.resize(s); // allocate memory for lengths
  51 +
  52 + update_L();
  53 + }
  54 +
  55 + // constructor that constructs a centerline based on a list of points
  56 + centerline(std::vector<stim::vec3<T> > rhs) {
  57 + n = rhs.size(); // get the number of points
  58 + C.resize(n);
  59 + for (size_t i = 0; i < n; i++)
  60 + C[i] = rhs[i]; // copy data
  61 + update_L();
  62 + }
  63 +
  64 +
  65 + /// vector operations
  66 + // add a new point to current centerline
  67 + void push_back(stim::vec3<T> p) {
  68 + C.push_back(p);
  69 + n++; // increase the number of points
  70 + update_L(n - 1);
  71 + }
  72 +
  73 + // insert a new point at specific location to current centerline
  74 + void insert(typename std::vector<stim::vec3<T> >::iterator pos, stim::vec3<T> p) {
  75 + C.insert(pos, p); // insert a new point
  76 + n++;
  77 + size_t d = std::distance(C.begin(), pos); // get the index
  78 + update_L(d);
  79 + }
  80 + // insert a new point at C[idx]
  81 + void insert(size_t idx, stim::vec3<T> p) {
  82 + n++;
  83 + C.resize(n);
  84 + for (size_t i = n - 1; i > idx; i--) // move point location
  85 + C[i] = C[i - 1];
  86 + C[idx] = p;
  87 + update_L(idx);
  88 + }
  89 +
  90 + // assign a point at specific location to current centerline
  91 + void assign(size_t idx, stim::vec3<T> p) {
  92 + C[idx] = p;
  93 + update_L(idx);
  94 + }
  95 +
  96 + // erase a point at specific location on current centerline
  97 + void erase(typename std::vector<stim::vec3<T> >::iterator pos) {
  98 + C.erase(pos); // erase a point
  99 + n--;
  100 + size_t d = std::distance(C.begin(), pos); // get the index
  101 + update_L(d);
  102 + }
  103 + // erase a point at C[idx]
  104 + void erase(size_t idx) {
  105 + n--;
  106 + for (size_t i = idx; i < n; i++)
  107 + C[i] = C[i + 1];
  108 + C.resize(n);
  109 + update_L(idx);
  110 + }
  111 +
  112 + // clear up all the points
  113 + void clear() {
  114 + C.clear(); // clear list
  115 + L.clear(); // clear length information
  116 + n = 0; // set number to zero
  117 + }
  118 +
  119 + // reverse current centerline in terms of points order
  120 + centerline<T> reverse() {
  121 + centerline<T> result = *this;
  122 +
  123 + std::reverse(result.C.begin(), result.C.end());
  124 + result.update_L();
  125 +
  126 + return result;
  127 + }
  128 +
  129 + /// functions for reading centerline information
  130 + // return the number of points on current centerline
  131 + size_t size() {
  132 + return n;
  133 + }
  134 +
  135 + // return the length
  136 + T length() {
  137 + return L.back();
  138 + }
  139 +
  140 + // finds the index of the point closest to the length "l" on the lower bound
  141 + size_t findIdx(T l) {
  142 + for (size_t i = 1; i < L.size(); i++) {
  143 + if (L[i] > l)
  144 + return i - 1;
  145 + }
  146 +
  147 + return L.size() - 1;
  148 + }
  149 +
  150 + // get a position vector at the given length into the fiber (based on the pvalue), interpolate
  151 + stim::vec3<T> p(T l, size_t idx) {
  152 + T rate = (l - L[idx]) / (L[idx + 1] - L[idx]);
  153 + stim::vec3<T> v1 = C[idx];
  154 + stim::vec3<T> v2 = C[idx + 1];
  155 +
  156 + return (v1 + (v2 - v1) * rate);
  157 + }
  158 + // get a position vector at the given pvalue(pvalue[0.0f, 1.0f])
  159 + stim::vec3<T> p(T pvalue) {
  160 + // degenerated cases
  161 + if (pvalue <= 0.0f) return C[0];
  162 + if (pvalue >= 1.0f) return C.back();
  163 +
  164 + T l = pvalue * L.back(); // get the length based on the given pvalue
  165 + size_t idx = findIdx(l);
  166 +
  167 + return p(l, idx); // interpolation
  168 + }
  169 +
  170 + // get the normalized direction vector at point idx (average of the incoming and outgoing directions)
  171 + stim::vec3<T> d(size_t idx) {
  172 + if (n <= 1) return stim::vec3<T>(0.0f, 0.0f, 0.0f); // if there is insufficient information to calculate the direction, return null
  173 + if (n == 2) return (C[1] - C[0]).norm(); // if there are only two points, the direction vector at both is the direction of the line segment
  174 +
  175 + // degenerate cases at two ends
  176 + if (idx == 0) return (C[1] - C[0]).norm(); // the first direction vector is oriented towards the first line segment
  177 + if (idx == n - 1) return (C[n - 1] - C[n - 2]).norm(); // the last direction vector is oriented towards the last line segment
  178 +
  179 + // all other direction vectors are the average direction of the two joined line segments
  180 + stim::vec3<T> a = C[idx] - C[idx - 1];
  181 + stim::vec3<T> b = C[idx + 1] - C[idx];
  182 + stim::vec3<T> ab = a.norm() + b.norm();
  183 + return ab.norm();
  184 + }
  185 +
  186 +
  187 + /// arithmetic operations
  188 + // '=' operation
  189 + centerline<T> & operator=(centerline<T> rhs) {
  190 + n = rhs.n;
  191 + C = rhs.C;
  192 + L = rhs.L;
  193 +
  194 + return *this;
  195 + }
  196 +
  197 + // "[]" operation
  198 + stim::vec3<T> & operator[](size_t idx) {
  199 + return C[idx];
  200 + }
  201 +
  202 + // "==" operation
  203 + bool operator==(centerline<T> rhs) const {
  204 +
  205 + if (n != rhs.size())
  206 + return false;
  207 + else {
  208 + size_t num = rhs.size();
  209 + stim::vec3<T> tmp; // weird situation that I can only use tmp instead of C itself in comparison
  210 + for (size_t i = 0; i < num; i++) {
  211 + stim::vec3<T> tmp = C[i];
  212 + if (tmp[0] != rhs[i][0] || tmp[1] != rhs[i][1] || tmp[2] != rhs[i][2])
  213 + return false;
  214 + }
  215 + return true;
  216 + }
  217 + }
  218 +
  219 + // "+" operation
  220 + centerline<T> operator+(stim::vec3<T> p) const {
  221 + centerline<T> result(*this);
  222 + result.C.push_back(p);
  223 + result.n++;
  224 + result.update_L(n - 1);
  225 +
  226 + return result;
  227 + }
  228 + centerline<T> operator+(centerline<T> c) const {
  229 + centerline<T> result(*this);
  230 + size_t num1 = result.size();
  231 + size_t num2 = c.size();
  232 + for (size_t i = 0; i < num2; i++)
  233 + result.push_back(c[i]);
  234 + result.update_L(num1);
  235 +
  236 + return result;
  237 + }
  238 +
  239 +
  240 + /// advanced operation
  241 + // stitch two centerlines if possible (mutual-stitch and self-stitch)
  242 + static std::vector<centerline<T> > stitch(centerline<T> c1, centerline<T> c2, size_t end = 0) {
  243 + std::vector<centerline<T> > result;
  244 + centerline<T> new_centerline;
  245 + stim::vec3<T> new_vertex;
  246 + // ********** for Pavel **********
  247 + // ********** JACK thinks that ultimately we want it AUTOMATEDLY! **********
  248 +
  249 + // check stitch case
  250 + if (c1 == c2) { // self-stitch case
  251 + // ***** don't know how it works *****
  252 + }
  253 + else { // mutual-stitch case
  254 + size_t num1 = c1.size(); // get the numbers of two centerlines
  255 + size_t num2 = c2.size();
  256 +
  257 + T* reference = (T*)malloc(sizeof(T) * num1 * 3); // c1 as reference set
  258 + T* query = (T*)malloc(sizeof(T) * num2 * 3); // c2 as query set
  259 + for (size_t p = 0; p < num1; p++) // read points
  260 + for (size_t d = 0; d < 3; d++)
  261 + reference[p * 3 + d] = c1[p][d]; // KDTREE is stilla close code, it has its own structure
  262 + for (size_t p = 0; p < num2; p++) // read points
  263 + for (size_t d = 0; d < 3; d++)
  264 + query[p * 3 + d] = c2[p][d];
  265 +
  266 + stim::kdtree<T, 3> kdt;
  267 + kdt.create(reference, num1, 5); // create a tree based on reference set, max_tree_level is set to 5
  268 +
  269 + std::vector<size_t> index(num2); // stores index results
  270 + std::vector<float> dist(num2);
  271 +
  272 + kdt.search(query, num2, &index[0], &dist[0]); // find the nearest neighbor in c1 for c2
  273 +
  274 + // clear up
  275 + free(reference);
  276 + free(query);
  277 +
  278 + std::vector<float>::iterator sm = std::min_element(dist.begin(), dist.end()); // find smallest distance
  279 + size_t id = std::distance(dist.begin(), sm); // find the target index
  280 +
  281 + size_t id1 = index[id];
  282 + size_t id2 = id;
  283 +
  284 + // for centerline c1
  285 + bool flag = false; // flag indicates whether it has already added the bifurcation point to corresponding new centerline
  286 + if (id1 == 0 || id1 == num1 - 1) { // splitting bifurcation is on the end
  287 + new_centerline = c1;
  288 + new_vertex = c2[id2];
  289 + if (id1 == 0) {
  290 + new_centerline.insert(0, new_vertex);
  291 + flag = true;
  292 + }
  293 + if (id1 == num1 - 1) {
  294 + new_centerline.push_back(new_vertex);
  295 + flag = true;
  296 + }
  297 +
  298 + result.push_back(new_centerline);
  299 + }
  300 + else { // splitting bifurcation is on the centerline
  301 + std::vector<centerline<T> > tmp_centerline = c1.split(id1);
  302 + result = tmp_centerline;
  303 + }
  304 +
  305 + // for centerline c2
  306 + if (id2 == 0 || id2 == num2 - 1) { // splitting bidurcation is on the end
  307 + new_centerline = c2;
  308 + if (flag)
  309 + result.push_back(new_centerline);
  310 + else { // add the bifurcation point to this centerline
  311 + new_vertex = c1[id1];
  312 + if (id2 == 0) {
  313 + new_centerline.insert(0, new_vertex);
  314 + flag = true;
  315 + }
  316 + if (id2 == num2 - 1) {
  317 + new_centerline.push_back(new_vertex);
  318 + flag = true;
  319 + }
  320 +
  321 + result.push_back(new_centerline);
  322 + }
  323 + }
  324 + else { // splitting bifurcation is on the centerline
  325 + std::vector<centerline<T> > tmp_centerline = c2.split(id2);
  326 + result.push_back(tmp_centerline[0]);
  327 + result.push_back(tmp_centerline[1]);
  328 + }
  329 + }
  330 +
  331 + return result;
  332 + }
  333 +
  334 + // split current centerline at specific position
  335 + std::vector<centerline<T> > split(size_t idx) {
  336 + std::vector<centerline<T> > result;
  337 +
  338 + // won't split
  339 + if (idx <= 0 || idx >= n - 1) {
  340 + result.resize(1);
  341 + result[0] = *this; // return current centerline
  342 + }
  343 + // do split
  344 + else {
  345 + size_t n1 = idx + 1; // vertex idx would appear twice
  346 + size_t n2 = n - idx; // in total n + 1 points
  347 +
  348 + centerline<T> tmp; // temp centerline
  349 +
  350 + result.resize(2);
  351 +
  352 + for (size_t i = 0; i < n1; i++) // first half
  353 + tmp.push_back(C[i]);
  354 + tmp.update_L();
  355 + result[0] = tmp;
  356 + tmp.clear(); // clear up for next computation
  357 +
  358 + for (size_t i = 0; i < n2; i++) // second half
  359 + tmp.push_back(C[i + idx]);
  360 + tmp.update_L();
  361 + result[1] = tmp;
  362 + }
  363 +
  364 + return result;
  365 + }
  366 +
  367 + // resample current centerline
  368 + centerline<T> resample(T spacing) {
  369 +
  370 + stim::vec3<T> dir; // direction vector
  371 + stim::vec3<T> tmp; // intermiate point to be added
  372 + stim::vec3<T> p1; // starting point
  373 + stim::vec3<T> p2; // ending point
  374 +
  375 + centerline<T> result;
  376 +
  377 + for (size_t i = 0; i < n - 1; i++) {
  378 + p1 = C[i];
  379 + p2 = C[i + 1];
  380 +
  381 + dir = p2 - p1; // compute the direction of current segment
  382 + T seg_len = dir.len();
  383 +
  384 + if (seg_len > spacing) { // current segment can be sampled
  385 + for (T step = 0.0f; step < seg_len; step += spacing) {
  386 + tmp = p1 + dir * (step / seg_len); // add new point
  387 + result.push_back(tmp);
  388 + }
  389 + }
  390 + else
  391 + result.push_back(p1); // push back starting point
  392 + }
  393 + result.push_back(p2); // push back ending point
  394 +
  395 + return result;
  396 + }
  397 + };
  398 +}
  399 +
  400 +#endif
... ...
stim/biomodels/network.h
1   -/*
2   -Copyright <2017> <David Mayerich>
3   -
4   -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5   -
6   -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7   -
8   -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9   -*/
10   -#ifndef STIM_NETWORK_H
11   -#define STIM_NETWORK_H
12   -
13   -#include <stdlib.h>
14   -#include <assert.h>
15   -#include <sstream>
16   -#include <fstream>
17   -#include <algorithm>
18   -#include <string.h>
19   -#include <math.h>
20   -#include <stim/math/vec3.h>
21   -#include <stim/visualization/obj.h>
22   -#include <stim/visualization/swc.h>
23   -#include <stim/visualization/cylinder.h>
24   -#include <stim/cuda/cudatools/timer.h>
25   -#include <stim/cuda/cudatools/callable.h>
26   -#include <stim/structures/kdtree.cuh>
27   -//********************help function********************
28   -// gaussian_function
29   -CUDA_CALLABLE float gaussianFunction(float x, float std = 25) { return exp(-x / (2 * std*std)); } // std default sigma value is 25
30   -
31   -// compute metric in parallel
32   -#ifdef __CUDACC__
33   -template <typename T>
34   -__global__ void find_metric_parallel(T* M, size_t n, T* D, float sigma){
35   - size_t x = blockDim.x * blockIdx.x + threadIdx.x;
36   - if(x >= n) return;
37   - M[x] = 1.0f - gaussianFunction(D[x], sigma);
38   -}
39   -
40   -//find the corresponding edge index from array index
41   -__global__ void find_edge_index_parallel(size_t* I, size_t n, unsigned* R, size_t* E, size_t ne){
42   - size_t x = blockDim.x * blockIdx.x + threadIdx.x;
43   - if(x >= n) return;
44   - unsigned i = 0;
45   - size_t N = 0;
46   - for(unsigned e = 0; e < ne; e++){
47   - N += E[e];
48   - if(I[x] < N){
49   - R[x] = i;
50   - break;
51   - }
52   - i++;
53   - }
54   -}
55   -#endif
56   -
57   -//hard-coded factor
58   -int threshold_fac;
59   -
60   -namespace stim{
61   -/** This is the a class that interfaces with gl_spider in order to store the currently
62   - * segmented network. The following data is stored and can be extracted:
63   - * 1)Network geometry and centerline.
64   - * 2)Network connectivity (a graph of nodes and edges), reconstructed using kdtree.
65   -*/
66   -
67   -template<typename T>
68   -class network{
69   -
70   - ///Each edge is a fiber with two nodes.
71   - ///Each node is an in index to the endpoint of the fiber in the nodes array.
72   - class edge : public cylinder<T>
73   - {
74   - public:
75   -
76   - unsigned int v[2]; //unique id's designating the starting and ending
77   - // default constructor
78   - edge() : cylinder<T>() {
79   - v[1] = (unsigned)(-1); v[0] = (unsigned)(-1);
80   - }
81   - /// Constructor - creates an edge from a list of points by calling the stim::fiber constructor
82   -/*
83   - ///@param v0, the starting index.
84   - ///@param v1, the ending index.
85   - ///@param sz, the number of point in the fiber.
86   - edge(unsigned int v0, unsigned int v1, unsigned int sz) : cylinder<T>(
87   - {
88   -
89   - }
90   -*/
91   - edge(std::vector<stim::vec3<T> > p, std::vector<T> s)
92   - : cylinder<T>(p,s)
93   - {
94   - }
95   - ///@param p is an array of positions in space
96   - edge(stim::centerline<T> p) : cylinder<T>(p){}
97   -
98   - /// Copy constructor creates an edge from a cylinder
99   - edge(stim::cylinder<T> f) : cylinder<T>(f) {}
100   -
101   - /// Resamples an edge by calling the fiber resampling function
102   - edge resample(T spacing){
103   - edge e(cylinder<T>::resample(spacing)); //call the fiber->edge constructor
104   - e.v[0] = v[0]; //copy the vertex data
105   - e.v[1] = v[1];
106   -
107   - return e; //return the new edge
108   - }
109   -
110   - /// Output the edge information as a string
111   - std::string str(){
112   - std::stringstream ss;
113   - ss<<"("<<cylinder<T>::size()<<")\tl = "<<this->length()<<"\t"<<v[0]<<"----"<<v[1];
114   - return ss.str();
115   - }
116   -
117   - std::vector<edge> split(unsigned int idx){
118   -
119   - std::vector< stim::cylinder<T> > C;
120   - C.resize(2);
121   - C = (*this).cylinder<T>::split(idx);
122   - std::vector<edge> E(C.size());
123   -
124   - for(unsigned e = 0; e < E.size(); e++){
125   - E[e] = C[e];
126   - }
127   - return E;
128   - }
129   -
130   - /// operator for writing the edge information into a binary .nwt file.
131   - friend std::ofstream& operator<<(std::ofstream& out, const edge& e)
132   - {
133   - out.write(reinterpret_cast<const char*>(&e.v[0]), sizeof(unsigned int)); ///write the starting point.
134   - out.write(reinterpret_cast<const char*>(&e.v[1]), sizeof(unsigned int)); ///write the ending point.
135   - unsigned int sz = e.size(); ///write the number of point in the edge.
136   - out.write(reinterpret_cast<const char*>(&sz), sizeof(unsigned int));
137   - for(int i = 0; i < sz; i++) ///write each point
138   - {
139   - stim::vec3<T> point = e[i];
140   - out.write(reinterpret_cast<const char*>(&point[0]), 3*sizeof(T));
141   - // for(int j = 0; j < nmags(); j++) //future code for multiple mags
142   - // {
143   - out.write(reinterpret_cast<const char*>(&e.R[i]), sizeof(T)); ///write the radius
144   - //std::cout << point.str() << " " << e.R[i] << std::endl;
145   - // }
146   - }
147   - return out; //return stream
148   - }
149   -
150   - /// operator for reading an edge from a binary .nwt file.
151   - friend std::ifstream& operator>>(std::ifstream& in, edge& e)
152   - {
153   - unsigned int v0, v1, sz;
154   - in.read(reinterpret_cast<char*>(&v0), sizeof(unsigned int)); //read the staring point.
155   - in.read(reinterpret_cast<char*>(&v1), sizeof(unsigned int)); //read the ending point
156   - in.read(reinterpret_cast<char*>(&sz), sizeof(unsigned int)); //read the number of points in the edge
157   -// stim::centerline<T> temp = stim::centerline<T>(sz); //allocate the new edge
158   -// e = edge(temp);
159   - std::vector<stim::vec3<T> > p(sz);
160   - std::vector<T> r(sz);
161   - for(int i = 0; i < sz; i++) //set the points and radii to the newly read values
162   - {
163   - stim::vec3<T> point;
164   - in.read(reinterpret_cast<char*>(&point[0]), 3*sizeof(T));
165   - p[i] = point;
166   - T mag;
167   - // for(int j = 0; j < nmags(); j++) ///future code for mags
168   - // {
169   - in.read(reinterpret_cast<char*>(&mag), sizeof(T));
170   - r[i] = mag;
171   - //std::cout << point.str() << " " << mag << std::endl;
172   - // }
173   - }
174   - e = edge(p,r);
175   - e.v[0] = v0; e.v[1] = v1;
176   - return in;
177   - }
178   - };
179   -
180   - ///Node class that stores the physical position of the node as well as the edges it is connected to (edges that connect to it), As well as any additional data necessary.
181   - class vertex : public stim::vec3<T>
182   - {
183   - public:
184   - //std::vector<unsigned int> edges; //indices of edges connected to this node.
185   - std::vector<unsigned int> e[2]; //indices of edges going out (e[0]) and coming in (e[1])
186   - //stim::vec3<T> p; //position of this node in physical space.
187   - //default constructor
188   - vertex() : stim::vec3<T>()
189   - {
190   - }
191   - //constructor takes a stim::vec
192   - vertex(stim::vec3<T> p) : stim::vec3<T>(p){}
193   -
194   - /// Output the vertex information as a string
195   - std::string
196   - str(){
197   - std::stringstream ss;
198   - ss<<"\t(x, y, z) = "<<stim::vec3<T>::str();
199   -
200   - if(e[0].size() > 0){
201   - ss<<"\t> ";
202   - for(unsigned int o = 0; o < e[0].size(); o++)
203   - ss<<e[0][o]<<" ";
204   - }
205   - if(e[1].size() > 0){
206   - ss<<"\t< ";
207   - for(unsigned int i = 0; i < e[1].size(); i++)
208   - ss<<e[1][i]<<" ";
209   - }
210   -
211   - return ss.str();
212   - }
213   - ///operator for writing the vector into the stream;
214   - friend std::ofstream& operator<<(std::ofstream& out, const vertex& v)
215   - {
216   - unsigned int s0, s1;
217   - s0 = v.e[0].size();
218   - s1 = v.e[1].size();
219   - out.write(reinterpret_cast<const char*>(&v.ptr[0]), 3*sizeof(T)); ///write physical vertex location
220   - out.write(reinterpret_cast<const char*>(&s0), sizeof(unsigned int)); ///write the number of "outgoing edges"
221   - out.write(reinterpret_cast<const char*>(&s1), sizeof(unsigned int)); ///write the number of "incoming edges"
222   - if (s0 != 0)
223   - out.write(reinterpret_cast<const char*>(&v.e[0][0]), sizeof(unsigned int)*v.e[0].size()); ///write the "outgoing edges"
224   - if (s1 != 0)
225   - out.write(reinterpret_cast<const char*>(&v.e[1][0]), sizeof(unsigned int)*v.e[1].size()); ///write the "incoming edges"
226   - return out;
227   - }
228   -
229   - ///operator for reading the vector out of the stream;
230   - friend std::ifstream& operator>>(std::ifstream& in, vertex& v)
231   - {
232   - in.read(reinterpret_cast<char*>(&v[0]), 3*sizeof(T)); ///read the physical position
233   - unsigned int s[2];
234   - in.read(reinterpret_cast<char*>(&s[0]), 2*sizeof(unsigned int)); ///read the sizes of incoming and outgoing edge arrays
235   -
236   - std::vector<unsigned int> one(s[0]);
237   - std::vector<unsigned int> two(s[1]);
238   - v.e[0] = one;
239   - v.e[1] = two;
240   - if (one.size() != 0)
241   - in.read(reinterpret_cast<char*>(&v.e[0][0]), s[0] * sizeof(unsigned int)); ///read the arrays of "outgoing edges"
242   - if (two.size() != 0)
243   - in.read(reinterpret_cast<char*>(&v.e[1][0]), s[1] * sizeof(unsigned int)); ///read the arrays of "incoming edges"
244   - return in;
245   - }
246   -
247   - };
248   -
249   -protected:
250   -
251   - std::vector<edge> E; //list of edges
252   - std::vector<vertex> V; //list of vertices.
253   -
254   -public:
255   -
256   - ///default constructor
257   - network()
258   - {
259   -
260   - }
261   -
262   - ///constructor with a file to load.
263   - network(std::string fileLocation)
264   - {
265   - load_obj(fileLocation);
266   - }
267   -
268   - ///Returns the number of edges in the network.
269   - unsigned int edges(){
270   - return E.size();
271   - }
272   -
273   - ///Returns the number of nodes in the network.
274   - unsigned int vertices(){
275   - return V.size();
276   - }
277   -
278   - ///Returns the radius at specific point in the edge
279   - T get_r(unsigned e, unsigned i) {
280   - return E[e].r(i);
281   - }
282   -
283   - ///Returns the average radius of specific edge
284   - T get_average_r(unsigned e) {
285   - T result = 0.0;
286   - unsigned n = E[e].size();
287   - for (unsigned p = 0; p < n; p++)
288   - result += E[e].r(p);
289   -
290   - return (T)result / n;
291   - }
292   -
293   - ///Returns the length of current edge
294   - T get_l(unsigned e) {
295   - return E[e].length();
296   - }
297   -
298   - ///Returns the start vertex of current edge
299   - size_t get_start_vertex(unsigned e) {
300   - return E[e].v[0];
301   - }
302   -
303   - ///Returns the end vertex of current edge
304   - size_t get_end_vertex(unsigned e) {
305   - return E[e].v[1];
306   - }
307   -
308   - ///Returns one vertex
309   - stim::vec3<T> get_vertex(unsigned i) {
310   - return V[i];
311   - }
312   -
313   - ///Returns the boundary vertices' indices
314   - std::vector<unsigned> get_boundary_vertex() {
315   - std::vector<unsigned> result;
316   -
317   - for (unsigned v = 0; v < V.size(); v++) {
318   - if (V[v].e[0].size() + V[v].e[1].size() == 1) { // boundary vertex
319   - result.push_back(v);
320   - }
321   - }
322   -
323   - return result;
324   - }
325   -
326   - ///Set radius
327   - void set_r(unsigned e, std::vector<T> radius) {
328   - E[e].cylinder<T>::copy_r(radius);
329   - }
330   -
331   - void set_r(unsigned e, T radius) {
332   - for (size_t i = 0; i < E[e].size(); i++)
333   - E[e].cylinder<T>::set_r(i, radius);
334   - }
335   - //scale the network by some constant value
336   - // I don't think these work??????
337   - /*std::vector<vertex> operator*(T s){
338   - for (unsigned i=0; i< vertices; i ++ ){
339   - V[i] = V[i] * s;
340   - }
341   - return V;
342   - }
343   -
344   - std::vector<vertex> operator*(vec<T> s){
345   - for (unsigned i=0; i< vertices; i ++ ){
346   - for (unsigned dim = 0 ; dim< 3; dim ++){
347   - V[i][dim] = V[i][dim] * s[dim];
348   - }
349   - }
350   - return V;
351   - }*/
352   -
353   - // Returns an average of branching index in the network
354   - double BranchingIndex(){
355   - double B=0;
356   - for(unsigned v=0; v < V.size(); v ++){
357   - B += ((V[v].e[0].size()) + (V[v].e[1].size()));
358   - }
359   - B = B / V.size();
360   - return B;
361   -
362   - }
363   -
364   - // Returns number of branch points in thenetwork
365   - unsigned int BranchP(){
366   - unsigned int B=0;
367   - unsigned int c;
368   - for(unsigned v=0; v < V.size(); v ++){
369   - c = ((V[v].e[0].size()) + (V[v].e[1].size()));
370   - if (c > 2){
371   - B += 1;}
372   - }
373   - return B;
374   -
375   - }
376   -
377   - // Returns number of end points (tips) in thenetwork
378   - unsigned int EndP(){
379   - unsigned int B=0;
380   - unsigned int c;
381   - for(unsigned v=0; v < V.size(); v ++){
382   - c = ((V[v].e[0].size()) + (V[v].e[1].size()));
383   - if (c == 1){
384   - B += 1;}
385   - }
386   - return B;
387   -
388   - }
389   -
390   - //// Returns a dictionary with the key as the vertex
391   - //std::map<std::vector<vertex>,unsigned int> DegreeDict(){
392   - // std::map<std::vector<vertex>,unsigned int> dd;
393   - // unsigned int c = 0;
394   - // for(unsigned v=0; v < V.size(); v ++){
395   - // c = ((V[v].e[0].size()) + (V[v].e[1].size()));
396   - // dd[V[v]] = c;
397   - // }
398   - // return dd;
399   - //}
400   -
401   - //// Return number of branching stems
402   - //unsigned int Stems(){
403   - // unsigned int s = 0;
404   - // std::map<std::vector<vertex>,unsigned int> dd;
405   - // dd = DegreeDict();
406   - // //for(unsigned v=0; v < V.size(); v ++){
407   - // // V[v].e[0].
408   - // return s;
409   - //}
410   -
411   - //Calculate Metrics---------------------------------------------------
412   - // Returns an average of fiber/edge lengths in the network
413   - double Lengths(){
414   - stim::vec<T> L;
415   - double sumLength = 0;
416   - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network
417   - L.push_back(E[e].length()); //append the edge length
418   - sumLength = sumLength + E[e].length();
419   - }
420   - double avg = sumLength / E.size();
421   - return avg;
422   - }
423   -
424   -
425   - // Returns an average of tortuosities in the network
426   - double Tortuosities(){
427   - stim::vec<T> t;
428   - stim::vec<T> id1, id2; // starting and ending vertices of the edge
429   - double distance;double tortuosity;double sumTortuosity = 0;
430   - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network
431   - id1 = E[e][0]; //get the edge starting point
432   - id2 = E[e][E[e].size() - 1]; //get the edge ending point
433   - distance = (id1 - id2).len(); //displacement between the starting and ending points
434   - if(distance > 0){
435   - tortuosity = E[e].length()/ distance ; // tortuoisty = edge length / edge displacement
436   - }
437   - else{
438   - tortuosity = 0;}
439   - t.push_back(tortuosity);
440   - sumTortuosity += tortuosity;
441   - }
442   - double avg = sumTortuosity / E.size();
443   - return avg;
444   - }
445   -
446   - // Returns average contraction of the network
447   - double Contractions(){
448   - stim::vec<T> t;
449   - stim::vec<T> id1, id2; // starting and ending vertices of the edge
450   - double distance;double contraction;double sumContraction = 0;
451   - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network
452   - id1 = E[e][0]; //get the edge starting point
453   - id2 = E[e][E[e].size() - 1]; //get the edge ending point
454   - distance = (id1 - id2).len(); //displacement between the starting and ending points
455   - contraction = distance / E[e].length(); // tortuoisty = edge length / edge displacement
456   - t.push_back(contraction);
457   - sumContraction += contraction;
458   - }
459   - double avg = sumContraction / E.size();
460   - return avg;
461   - }
462   -
463   - // returns average fractal dimension of the branches of the network
464   - double FractalDimensions(){
465   - stim::vec<T> t;
466   - stim::vec<T> id1, id2; // starting and ending vertices of the edge
467   - double distance;double fract;double sumFractDim = 0;
468   - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network
469   - id1 = E[e][0]; //get the edge starting point
470   - id2 = E[e][E[e].size() - 1]; //get the edge ending point
471   - distance = (id1 - id2).len(); //displacement between the starting and ending points
472   - fract = std::log(distance) / std::log(E[e].length()); // tortuoisty = edge length / edge displacement
473   - t.push_back(sumFractDim);
474   - sumFractDim += fract;
475   - }
476   - double avg = sumFractDim / E.size();
477   - return avg;
478   - }
479   -
480   - //returns a cylinder represented a given fiber (based on edge index)
481   - stim::cylinder<T> get_cylinder(unsigned e){
482   - return E[e]; //return the specified edge (casting it to a fiber)
483   - }
484   -
485   - //load a network from an OBJ file
486   - void load_obj(std::string filename){
487   -
488   - stim::obj<T> O; //create an OBJ object
489   - O.load(filename); //load the OBJ file as an object
490   -
491   - std::vector<unsigned> id2vert; //this list stores the OBJ vertex ID associated with each network vertex
492   -
493   - unsigned i[2]; //temporary, IDs associated with the first and last points in an OBJ line
494   -
495   - //for each line in the OBJ object
496   - for(unsigned int l = 1; l <= O.numL(); l++){
497   -
498   - std::vector< stim::vec<T> > c; //allocate an array of points for the vessel centerline
499   - O.getLine(l, c); //get the fiber centerline
500   -
501   - stim::centerline<T> c3(c.size());
502   - for(size_t j = 0; j < c.size(); j++)
503   - c3[j] = c[j];
504   - c3.update();
505   -
506   - // edge new_edge = c3; ///This is dangerous.
507   - edge new_edge(c3);
508   -
509   - //create an edge from the given centerline
510   - unsigned int I = new_edge.size(); //calculate the number of points on the centerline
511   -
512   - //get the first and last vertex IDs for the line
513