Commit e6712a7291e3ec5f3d3738c8a8b6985c460fbe32
Merge branch 'master' of git.stim.ee.uh.edu:codebase/stimlib
Showing
22 changed files
with
2712 additions
and
2795 deletions
Show diff stats
stim/biomodels/nwt_format.pptx renamed to docs/nwt_format.pptx
No preview for this file type
No preview for this file type
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
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 | - std::vector< unsigned > id; //create an array to store the centerline point IDs | |
514 | - O.getLinei(l, id); //get the list of point IDs for the line | |
515 | - i[0] = id.front(); //get the OBJ ID for the first element of the line | |
516 | - i[1] = id.back(); //get the OBJ ID for the last element of the line | |
517 | - | |
518 | - std::vector<unsigned>::iterator it; //create an iterator for searching the id2vert array | |
519 | - unsigned it_idx; //create an integer for the id2vert entry index | |
520 | - | |
521 | - //find out if the nodes for this fiber have already been created | |
522 | - it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node | |
523 | - if(it == id2vert.end()){ //if i[0] hasn't already been used | |
524 | - vertex new_vertex = new_edge[0]; //create a new vertex, assign it a position | |
525 | - bool flag = false; | |
526 | - unsigned j = 0; | |
527 | - for (; j < V.size(); j++) { // check whether current vertex is already exist | |
528 | - if (new_vertex == V[j]) { | |
529 | - flag = true; | |
530 | - break; | |
531 | - } | |
532 | - } | |
533 | - if (!flag) { // unique one | |
534 | - new_vertex.e[0].push_back(E.size()); //add the current edge as outgoing | |
535 | - new_edge.v[0] = V.size(); //add the new edge to the edge | |
536 | - V.push_back(new_vertex); //add the new vertex to the vertex list | |
537 | - id2vert.push_back(i[0]); //add the ID to the ID->vertex conversion list | |
538 | - } | |
539 | - else { | |
540 | - V[j].e[0].push_back(E.size()); | |
541 | - new_edge.v[0] = j; | |
542 | - } | |
543 | - } | |
544 | - else{ //if the vertex already exists | |
545 | - it_idx = std::distance(id2vert.begin(), it); | |
546 | - V[it_idx].e[0].push_back(E.size()); //add the current edge as outgoing | |
547 | - new_edge.v[0] = it_idx; | |
548 | - } | |
549 | - | |
550 | - it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID | |
551 | - if(it == id2vert.end()){ //if i[1] hasn't already been used | |
552 | - vertex new_vertex = new_edge[I-1]; //create a new vertex, assign it a position | |
553 | - bool flag = false; | |
554 | - unsigned j = 0; | |
555 | - for (; j < V.size(); j++) { // check whether current vertex is already exist | |
556 | - if (new_vertex == V[j]) { | |
557 | - flag = true; | |
558 | - break; | |
559 | - } | |
560 | - } | |
561 | - if (!flag) { | |
562 | - new_vertex.e[1].push_back(E.size()); //add the current edge as incoming | |
563 | - new_edge.v[1] = V.size(); //add the new vertex to the edge | |
564 | - V.push_back(new_vertex); //add the new vertex to the vertex list | |
565 | - id2vert.push_back(i[1]); //add the ID to the ID->vertex conversion list | |
566 | - } | |
567 | - else { | |
568 | - V[j].e[1].push_back(E.size()); | |
569 | - new_edge.v[1] = j; | |
570 | - } | |
571 | - } | |
572 | - else{ //if the vertex already exists | |
573 | - it_idx = std::distance(id2vert.begin(), it); | |
574 | - V[it_idx].e[1].push_back(E.size()); //add the current edge as incoming | |
575 | - new_edge.v[1] = it_idx; | |
576 | - } | |
577 | - | |
578 | - E.push_back(new_edge); //push the edge to the list | |
579 | - | |
580 | - } | |
581 | - | |
582 | - // copy the radii information from OBJ | |
583 | - /*if (O.numVT()) { | |
584 | - unsigned k = 0; | |
585 | - for (unsigned i = 0; i < E.size(); i++) { | |
586 | - for (unsigned j = 0; j < E[i].size(); j++) { | |
587 | - E[i].cylinder<T>::set_r(j, O.getVT(k)[0] / 2); | |
588 | - k++; | |
589 | - } | |
590 | - } | |
591 | - }*/ | |
592 | - // OBJ class assumes that in L the two values are equal | |
593 | - if (O.numVT()) { | |
594 | - std::vector< unsigned > id; //create an array to store the centerline point IDs | |
595 | - for (unsigned i = 0; i < O.numL(); i++) { | |
596 | - id.clear(); | |
597 | - O.getLinei(i + 1, id); //get the list of point IDs for the line | |
598 | - for (unsigned j = 0; j < id.size(); j++) | |
599 | - E[i].cylinder<T>::set_r(j, O.getVT(id[j] - 1)[0] / 2); | |
600 | - } | |
601 | - } | |
602 | - } | |
603 | - | |
604 | - ///loads a .nwt file. Reads the header and loads the data into the network according to the header. | |
605 | - void | |
606 | - loadNwt(std::string filename) | |
607 | - { | |
608 | - int dims[2]; ///number of vertex, number of edges | |
609 | - readHeader(filename, &dims[0]); //read header | |
610 | - std::ifstream file; | |
611 | - file.open(filename.c_str(), std::ios::in | std::ios::binary); ///skip header information. | |
612 | - file.seekg(14+58+4+4, file.beg); | |
613 | - vertex v; | |
614 | - for(int i = 0; i < dims[0]; i++) ///for every vertex, read vertex, add to network. | |
615 | - { | |
616 | - file >> v; | |
617 | - V.push_back(v); | |
618 | -// std::cout << i << " " << v.str() << std::endl; | |
619 | - } | |
620 | - | |
621 | - std::cout << std::endl; | |
622 | - for(int i = 0; i < dims[1]; i++) ///for every edge, read edge, add to network. | |
623 | - { | |
624 | - edge e; | |
625 | - file >> e; | |
626 | - E.push_back(e); | |
627 | - //std::cout << i << " " << E[i].str() << std::endl; // not necessary? | |
628 | - } | |
629 | - file.close(); | |
630 | - } | |
631 | - | |
632 | - ///saves a .nwt file. Writes the header in raw text format, then saves the network as a binary file. | |
633 | - void | |
634 | - saveNwt(std::string filename) | |
635 | - { | |
636 | - writeHeader(filename); | |
637 | - std::ofstream file; | |
638 | - file.open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app); ///since we have written the header we are not appending. | |
639 | - for(int i = 0; i < V.size(); i++) ///look through the Vertices and write each one. | |
640 | - { | |
641 | -// std::cout << i << " " << V[i].str() << std::endl; | |
642 | - file << V[i]; | |
643 | - } | |
644 | - for(int i = 0; i < E.size(); i++) ///loop through the Edges and write each one. | |
645 | - { | |
646 | - //std::cout << i << " " << E[i].str() << std::endl; // not necesarry? | |
647 | - file << E[i]; | |
648 | - } | |
649 | - file.close(); | |
650 | - } | |
651 | - | |
652 | - | |
653 | - ///Writes the header information to a .nwt file. | |
654 | - void | |
655 | - writeHeader(std::string filename) | |
656 | - { | |
657 | - std::string magicString = "nwtFileFormat "; ///identifier for the file. | |
658 | - std::string desc = "fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata"; | |
659 | - int hNumVertices = V.size(); ///int byte header storing the number of vertices in the file | |
660 | - int hNumEdges = E.size(); ///int byte header storing the number of edges. | |
661 | - std::ofstream file; | |
662 | - file.open(filename.c_str(), std::ios::out | std::ios::binary); | |
663 | - std::cout << hNumVertices << " " << hNumEdges << std::endl; | |
664 | - file.write(reinterpret_cast<const char*>(&magicString.c_str()[0]), 14); //write the file id | |
665 | - file.write(reinterpret_cast<const char*>(&desc.c_str()[0]), 58); //write the description | |
666 | - file.write(reinterpret_cast<const char*>(&hNumVertices), sizeof(int)); //write #vert. | |
667 | - file.write(reinterpret_cast<const char*>(&hNumEdges), sizeof(int)); //write #edges | |
668 | -// file << magicString.c_str() << desc.c_str() << hNumVertices << hNumEdges; | |
669 | - file.close(); | |
670 | - | |
671 | - } | |
672 | - | |
673 | - ///Reads the header information from a .nwt file. | |
674 | - void | |
675 | - readHeader(std::string filename, int *dims) | |
676 | - { | |
677 | - char magicString[14]; ///id | |
678 | - char desc[58]; ///description | |
679 | - int hNumVertices; ///#vert | |
680 | - int hNumEdges; ///#edges | |
681 | - std::ifstream file; ////create stream | |
682 | - file.open(filename.c_str(), std::ios::in | std::ios::binary); | |
683 | - file.read(reinterpret_cast<char*>(&magicString[0]), 14); ///read the file id. | |
684 | - file.read(reinterpret_cast<char*>(&desc[0]), 58); ///read the description | |
685 | - file.read(reinterpret_cast<char*>(&hNumVertices), sizeof(int)); ///read the number of vertices | |
686 | - file.read(reinterpret_cast<char*>(&hNumEdges), sizeof(int)); ///read the number of edges | |
687 | -// std::cout << magicString << desc << hNumVertices << " " << hNumEdges << std::endl; | |
688 | - file.close(); ///close the file. | |
689 | - dims[0] = hNumVertices; ///fill the returned reference. | |
690 | - dims[1] = hNumEdges; | |
691 | - } | |
692 | - | |
693 | - //load a network from an SWC file | |
694 | - void load_swc(std::string filename) { | |
695 | - stim::swc<T> S; // create swc variable | |
696 | - S.load(filename); // load the node information | |
697 | - S.create_tree(); // link those node according to their linking relationships as a tree | |
698 | - S.resample(); | |
699 | - | |
700 | - //NT.push_back(S.node[0].type); // set the neuronal_type value to the first vertex in the network | |
701 | - std::vector<unsigned> id2vert; // this list stores the SWC vertex ID associated with each network vertex | |
702 | - unsigned i[2]; // temporary, IDs associated with the first and last points | |
703 | - | |
704 | - for (unsigned int l = 0; l < S.numE(); l++) { // for every edge | |
705 | - //NT.push_back(S.node[l].type); | |
706 | - | |
707 | - std::vector< stim::vec3<T> > c; | |
708 | - S.get_points(l, c); | |
709 | - | |
710 | - stim::centerline<T> c3(c.size()); // new fiber | |
711 | - | |
712 | - for (unsigned j = 0; j < c.size(); j++) | |
713 | - c3[j] = c[j]; // copy the points | |
714 | - | |
715 | - c3.update(); // update the L information | |
716 | - | |
717 | - stim::cylinder<T> C3(c3); // create a new cylinder in order to copy the origin radius information | |
718 | - // upadate the R information | |
719 | - std::vector<T> radius; | |
720 | - S.get_radius(l, radius); | |
721 | - | |
722 | - C3.copy_r(radius); | |
723 | - | |
724 | - edge new_edge(C3); // new edge | |
725 | - | |
726 | - //create an edge from the given centerline | |
727 | - unsigned int I = new_edge.size(); //calculate the number of points on the centerline | |
728 | - | |
729 | - //get the first and last vertex IDs for the line | |
730 | - i[0] = S.E[l].front(); | |
731 | - i[1] = S.E[l].back(); | |
732 | - | |
733 | - std::vector<unsigned>::iterator it; //create an iterator for searching the id2vert array | |
734 | - unsigned it_idx; //create an integer for the id2vert entry index | |
735 | - | |
736 | - //find out if the nodes for this fiber have already been created | |
737 | - it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node | |
738 | - if (it == id2vert.end()) { //if i[0] hasn't already been used | |
739 | - vertex new_vertex = new_edge[0]; //create a new vertex, assign it a position | |
740 | - new_vertex.e[0].push_back(E.size()); //add the current edge as outgoing | |
741 | - new_edge.v[0] = V.size(); //add the new edge to the edge | |
742 | - V.push_back(new_vertex); //add the new vertex to the vertex list | |
743 | - id2vert.push_back(i[0]); //add the ID to the ID->vertex conversion list | |
744 | - } | |
745 | - else { //if the vertex already exists | |
746 | - it_idx = std::distance(id2vert.begin(), it); | |
747 | - V[it_idx].e[0].push_back(E.size()); //add the current edge as outgoing | |
748 | - new_edge.v[0] = it_idx; | |
749 | - } | |
750 | - | |
751 | - it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID | |
752 | - if (it == id2vert.end()) { //if i[1] hasn't already been used | |
753 | - vertex new_vertex = new_edge[I - 1]; //create a new vertex, assign it a position | |
754 | - new_vertex.e[1].push_back(E.size()); //add the current edge as incoming | |
755 | - new_edge.v[1] = V.size(); //add the new vertex to the edge | |
756 | - V.push_back(new_vertex); //add the new vertex to the vertex list | |
757 | - id2vert.push_back(i[1]); //add the ID to the ID->vertex conversion list | |
758 | - } | |
759 | - else { //if the vertex already exists | |
760 | - it_idx = std::distance(id2vert.begin(), it); | |
761 | - V[it_idx].e[1].push_back(E.size()); //add the current edge as incoming | |
762 | - new_edge.v[1] = it_idx; | |
763 | - } | |
764 | - | |
765 | - E.push_back(new_edge); //push the edge to the list | |
766 | - } | |
767 | - } | |
768 | - | |
769 | - /// Get adjacency matrix of the network | |
770 | - std::vector< typename std::vector<int> > get_adj_mat() { | |
771 | - | |
772 | - unsigned n = V.size(); // get the number of vertices in the networks | |
773 | - | |
774 | - std::vector< typename std::vector<int> > result(n, std::vector<int>(n, 0)); // initialize every entry in the matrix to be 0 | |
775 | - result.resize(n); // resize rows | |
776 | - for (unsigned i = 0; i < n; i++) | |
777 | - result[i].resize(n); // resize columns | |
778 | - | |
779 | - for (unsigned i = 0; i < n; i++) { // for every vertex | |
780 | - unsigned num_out = V[i].e[0].size(); // number of outgoing edges of current vertex | |
781 | - if (num_out != 0) { | |
782 | - for (unsigned j = 0; j < num_out; j++) { | |
783 | - int edge_idx = V[i].e[0][j]; // get the jth out-going edge index of current vertex | |
784 | - int vertex_idx = E[edge_idx].v[1]; // get the ending vertex of specific out-going edge | |
785 | - result[i][vertex_idx] = 1; // can simply set to 1 if it is simple-graph | |
786 | - result[vertex_idx][i] = 1; // symmetric | |
787 | - } | |
788 | - } | |
789 | - } | |
790 | - | |
791 | - return result; | |
792 | - } | |
793 | - | |
794 | - /// Output the network as a string | |
795 | - std::string str(){ | |
796 | - | |
797 | - std::stringstream ss; | |
798 | - ss<<"Nodes ("<<V.size()<<")--------"<<std::endl; | |
799 | - for(unsigned int v = 0; v < V.size(); v++){ | |
800 | - ss<<"\t"<<v<<V[v].str()<<std::endl; | |
801 | - } | |
802 | - | |
803 | - ss<<"Edges ("<<E.size()<<")--------"<<std::endl; | |
804 | - for(unsigned e = 0; e < E.size(); e++){ | |
805 | - ss<<"\t"<<e<<E[e].str()<<std::endl; | |
806 | - } | |
807 | - | |
808 | - return ss.str(); | |
809 | - } | |
810 | - | |
811 | - /// This function resamples all fibers in a network given a desired minimum spacing | |
812 | - /// @param spacing is the minimum distance between two points on the network | |
813 | - stim::network<T> resample(T spacing){ | |
814 | - stim::network<T> n; //create a new network that will be an exact copy, with resampled fibers | |
815 | - n.V = V; //copy all vertices | |
816 | - //n.NT = NT; //copy all the neuronal type information | |
817 | - n.E.resize(edges()); //allocate space for the edge list | |
818 | - | |
819 | - //copy all fibers, resampling them in the process | |
820 | - for(unsigned e = 0; e < edges(); e++){ //for each edge in the edge list | |
821 | - n.E[e] = E[e].resample(spacing); //resample the edge and copy it to the new network | |
822 | - } | |
823 | - | |
824 | - return n; //return the resampled network | |
825 | - } | |
826 | - | |
827 | - /// Calculate the total number of points on all edges. | |
828 | - unsigned total_points(){ | |
829 | - unsigned n = 0; | |
830 | - for(unsigned e = 0; e < E.size(); e++) | |
831 | - n += E[e].size(); | |
832 | - return n; | |
833 | - } | |
834 | - | |
835 | - //Copy the point cloud representing the centerline for the network into an array | |
836 | - void centerline_cloud(T* dst) { | |
837 | - size_t p; //stores the current edge point | |
838 | - size_t P; //stores the number of points in an edge | |
839 | - size_t i = 0; //index into the output array of points | |
840 | - for (size_t e = 0; e < E.size(); e++) { //for each edge in the network | |
841 | - P = E[e].size(); //get the number of points in this edge | |
842 | - for (p = 0; p < P; p++) { | |
843 | - dst[i * 3 + 0] = E[e][p][0]; | |
844 | - dst[i * 3 + 1] = E[e][p][1]; | |
845 | - dst[i * 3 + 2] = E[e][p][2]; | |
846 | - i++; | |
847 | - } | |
848 | - } | |
849 | - } | |
850 | - | |
851 | - // convert vec3 to array | |
852 | - void stim2array(float *a, stim::vec3<T> b){ | |
853 | - a[0] = b[0]; | |
854 | - a[1] = b[1]; | |
855 | - a[2] = b[2]; | |
856 | - } | |
857 | - | |
858 | - // convert vec3 to array in bunch | |
859 | - void edge2array(T* a, edge b){ | |
860 | - size_t n = b.size(); | |
861 | - for(size_t i = 0; i < n; i++){ | |
862 | - a[i * 3 + 0] = b[i][0]; | |
863 | - a[i * 3 + 1] = b[i][1]; | |
864 | - a[i * 3 + 2] = b[i][2]; | |
865 | - } | |
866 | - } | |
867 | - | |
868 | - // get list of metric | |
869 | - std::vector<T> metric() { | |
870 | - std::vector<T> result; | |
871 | - T m; | |
872 | - for (size_t e = 0; e < E.size(); e++) { | |
873 | - for (size_t p = 0; p < E[e].size(); p++) { | |
874 | - m = E[e].r(p); | |
875 | - result.push_back(m); | |
876 | - } | |
877 | - } | |
878 | - return result; | |
879 | - } | |
880 | - | |
881 | - /// Calculate the average magnitude across the entire network. | |
882 | - /// @param m is the magnitude value to use. The default is 0 (usually radius). | |
883 | - T average(unsigned m = 0){ | |
884 | - | |
885 | - T M, L; //allocate space for the total magnitude and length | |
886 | - M = L = 0; //initialize both the initial magnitude and length to zero | |
887 | - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network | |
888 | - M += E[e].integrate(); //get the integrated magnitude | |
889 | - L += E[e].length(); //get the edge length | |
890 | - } | |
891 | - | |
892 | - return M / L; //return the average magnitude | |
893 | - } | |
894 | - | |
895 | - /// This function compares two networks and returns the percentage of the current network that is missing from A. | |
896 | - /// @param A is the network to compare to - the field is generated for A | |
897 | - /// @param sigma is the user-defined tolerance value - smaller values provide a stricter comparison | |
898 | - stim::network<T> compare(stim::network<T> A, float sigma, int device = -1){ | |
899 | - | |
900 | - stim::network<T> R; //generate a network storing the result of the comparison | |
901 | - R = (*this); //initialize the result with the current network | |
902 | - | |
903 | - T *c; // centerline (array of double pointers) - points on kdtree must be double | |
904 | - size_t n_data = A.total_points(); // set the number of points | |
905 | - c = (T*) malloc(sizeof(T) * n_data * 3); // allocate an array to store all points in the data set | |
906 | - | |
907 | - unsigned t = 0; | |
908 | - for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in the network | |
909 | - for(unsigned p = 0; p < A.E[e].size(); p++){ //for each point in the edge | |
910 | - for(unsigned d = 0; d < 3; d++){ //for each coordinate | |
911 | - | |
912 | - c[t * 3 + d] = A.E[e][p][d]; //copy the point into the array c | |
913 | - } | |
914 | - t++; | |
915 | - } | |
916 | - } | |
917 | - | |
918 | - //generate a KD-tree for network A | |
919 | - size_t MaxTreeLevels = 3; // max tree level | |
920 | - | |
921 | -#ifdef __CUDACC__ | |
922 | - cudaSetDevice(device); | |
923 | - stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree | |
924 | - | |
925 | - kdt.create(c, n_data, MaxTreeLevels); // build a KD tree | |
926 | - | |
927 | - for(unsigned e = 0; e < R.E.size(); e++){ //for each edge in A | |
928 | - //R.E[e].add_mag(0); //add a new magnitude for the metric | |
929 | - //size_t errormag_id = R.E[e].nmags() - 1; //get the id for the new magnitude | |
930 | - | |
931 | - size_t n = R.E[e].size(); // the number of points in current edge | |
932 | - T* queryPt = new T[3 * n]; | |
933 | - T* m1 = new T[n]; | |
934 | - T* dists = new T[n]; | |
935 | - size_t* nnIdx = new size_t[n]; | |
936 | - | |
937 | - T* d_dists; | |
938 | - T* d_m1; | |
939 | - cudaMalloc((void**)&d_dists, n * sizeof(T)); | |
940 | - cudaMalloc((void**)&d_m1, n * sizeof(T)); | |
941 | - | |
942 | - edge2array(queryPt, R.E[e]); | |
943 | - kdt.search(queryPt, n, nnIdx, dists); | |
944 | - | |
945 | - cudaMemcpy(d_dists, dists, n * sizeof(T), cudaMemcpyHostToDevice); // copy dists from host to device | |
946 | - | |
947 | - // configuration parameters | |
948 | - size_t threads = (1024>n)?n:1024; | |
949 | - size_t blocks = n/threads + (n%threads)?1:0; | |
950 | - | |
951 | - find_metric_parallel<<<blocks, threads>>>(d_m1, n, d_dists, sigma); //calculate the metric value based on the distance | |
952 | - | |
953 | - cudaMemcpy(m1, d_m1, n * sizeof(T), cudaMemcpyDeviceToHost); | |
954 | - | |
955 | - for(unsigned p = 0; p < n; p++){ | |
956 | - R.E[e].set_r(p, m1[p]); | |
957 | - } | |
958 | - | |
959 | - //d_set_mag<<<blocks, threads>>>(R.E[e].M, errormag_id, n, m1); | |
960 | - } | |
961 | - | |
962 | -#else | |
963 | - stim::kdtree<T, 3> kdt; | |
964 | - kdt.create(c, n_data, MaxTreeLevels); | |
965 | - | |
966 | - for(unsigned e = 0; e < R.E.size(); e++){ //for each edge in A | |
967 | - | |
968 | - size_t n = R.E[e].size(); // the number of points in current edge | |
969 | - T* query = new T[3 * n]; | |
970 | - T* m1 = new T[n]; | |
971 | - T* dists = new T[n]; | |
972 | - size_t* nnIdx = new size_t[n]; | |
973 | - | |
974 | - edge2array(query, R.E[e]); | |
975 | - | |
976 | - kdt.cpu_search(query, n, nnIdx, dists); //find the distance between A and the current network | |
977 | - | |
978 | - for(unsigned p = 0; p < R.E[e].size(); p++){ | |
979 | - m1[p] = 1.0f - gaussianFunction((T)dists[p], sigma); //calculate the metric value based on the distance | |
980 | - R.E[e].set_r(p, m1[p]); //set the error for the second point in the segment | |
981 | - } | |
982 | - } | |
983 | -#endif | |
984 | - return R; //return the resulting network | |
985 | - } | |
986 | - | |
987 | - /// This function compares two networks and split the current one according to the nearest neighbor of each point in each edge | |
988 | - /// @param A is the network to split | |
989 | - /// @param B is the corresponding mapping network | |
990 | - /// @param sigma is the user-defined tolerance value - smaller values provide a stricter comparison | |
991 | - /// @param device is the device that user want to use | |
992 | - void split(stim::network<T> A, stim::network<T> B, float sigma, int device, float threshold){ | |
993 | - | |
994 | - T *c; | |
995 | - size_t n_data = B.total_points(); | |
996 | - c = (T*) malloc(sizeof(T) * n_data * 3); | |
997 | - | |
998 | - size_t NB = B.E.size(); // the number of edges in B | |
999 | - unsigned t = 0; | |
1000 | - for(unsigned e = 0; e < NB; e++){ // for every edge in B | |
1001 | - for(unsigned p = 0; p < B.E[e].size(); p++){ // for every points in B.E[e] | |
1002 | - for(unsigned d = 0; d < 3; d++){ | |
1003 | - | |
1004 | - c[t * 3 + d] = B.E[e][p][d]; // convert to array | |
1005 | - } | |
1006 | - t++; | |
1007 | - } | |
1008 | - } | |
1009 | - size_t MaxTreeLevels = 3; // max tree level | |
1010 | - | |
1011 | -#ifdef __CUDACC__ | |
1012 | - cudaSetDevice(device); | |
1013 | - stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree | |
1014 | - | |
1015 | - //compare each point in the current network to the field produced by A | |
1016 | - kdt.create(c, n_data, MaxTreeLevels); // build a KD tree | |
1017 | - | |
1018 | - std::vector<std::vector<unsigned> > relation; // the relationship between GT and T corresponding to NN | |
1019 | - relation.resize(A.E.size()); | |
1020 | - | |
1021 | - for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in A | |
1022 | - //A.E[e].add_mag(0); //add a new magnitude for the metric | |
1023 | - //size_t errormag_id = A.E[e].nmags() - 1; | |
1024 | - | |
1025 | - size_t n = A.E[e].size(); // the number of edges in A | |
1026 | - | |
1027 | - T* queryPt = new T[3 * n]; // set of all the points in current edge | |
1028 | - T* m1 = new T[n]; // array of metrics for every point in current edge | |
1029 | - T* dists = new T[n]; // store the distances for every point in current edge | |
1030 | - size_t* nnIdx = new size_t[n]; // store the indices for every point in current edge | |
1031 | - | |
1032 | - // define pointers in device | |
1033 | - T* d_dists; | |
1034 | - T* d_m1; | |
1035 | - size_t* d_nnIdx; | |
1036 | - | |
1037 | - // allocate memory for defined pointers | |
1038 | - cudaMalloc((void**)&d_dists, n * sizeof(T)); | |
1039 | - cudaMalloc((void**)&d_m1, n * sizeof(T)); | |
1040 | - cudaMalloc((void**)&d_nnIdx, n * sizeof(size_t)); | |
1041 | - | |
1042 | - edge2array(queryPt, A.E[e]); // convert edge to array | |
1043 | - kdt.search(queryPt, n, nnIdx, dists); // search the tree to find the NN for every point in current edge | |
1044 | - | |
1045 | - cudaMemcpy(d_dists, dists, n * sizeof(T), cudaMemcpyHostToDevice); // copy dists from host to device | |
1046 | - cudaMemcpy(d_nnIdx, nnIdx, n * sizeof(size_t), cudaMemcpyHostToDevice); // copy Idx from host to device | |
1047 | - | |
1048 | - // configuration parameters | |
1049 | - size_t threads = (1024>n)?n:1024; // test to see whether the number of point in current edge is more than 1024 | |
1050 | - size_t blocks = n/threads + (n%threads)?1:0; | |
1051 | - | |
1052 | - find_metric_parallel<<<blocks, threads>>>(d_m1, n, d_dists, sigma); // calculate the metrics in parallel | |
1053 | - | |
1054 | - cudaMemcpy(m1, d_m1, n * sizeof(T), cudaMemcpyDeviceToHost); | |
1055 | - | |
1056 | - for(unsigned p = 0; p < n; p++){ | |
1057 | - A.E[e].set_r(p, m1[p]); // set the error(radius) value to every point in current edge | |
1058 | - } | |
1059 | - | |
1060 | - relation[e].resize(n); // resize every edge relation size | |
1061 | - | |
1062 | - unsigned* d_relation; | |
1063 | - cudaMalloc((void**)&d_relation, n * sizeof(unsigned)); // allocate memory | |
1064 | - | |
1065 | - std::vector<size_t> edge_point_num(NB); // %th element is the number of points that %th edge has | |
1066 | - for(unsigned ee = 0; ee < NB; ee++) | |
1067 | - edge_point_num[ee] = B.E[ee].size(); | |
1068 | - | |
1069 | - size_t* d_edge_point_num; | |
1070 | - cudaMalloc((void**)&d_edge_point_num, NB * sizeof(size_t)); | |
1071 | - cudaMemcpy(d_edge_point_num, &edge_point_num[0], NB * sizeof(size_t), cudaMemcpyHostToDevice); | |
1072 | - | |
1073 | - find_edge_index_parallel<<<blocks, threads>>>(d_nnIdx, n, d_relation, d_edge_point_num, NB); // find the edge corresponding to the array index in parallel | |
1074 | - | |
1075 | - cudaMemcpy(&relation[e][0], d_relation, n * sizeof(unsigned), cudaMemcpyDeviceToHost); //copy relationship from device to host | |
1076 | - } | |
1077 | -#else | |
1078 | - stim::kdtree<T, 3> kdt; | |
1079 | - kdt.create(c, n_data, MaxTreeLevels); | |
1080 | - | |
1081 | - std::vector<std::vector<unsigned>> relation; // the mapping relationship between two networks | |
1082 | - relation.resize(A.E.size()); | |
1083 | - for(unsigned i = 0; i < A.E.size(); i++) | |
1084 | - relation[i].resize(A.E[i].size()); | |
1085 | - | |
1086 | - std::vector<size_t> edge_point_num(NB); //%th element is the number of points that %th edge has | |
1087 | - for(unsigned ee = 0; ee < NB; ee++) | |
1088 | - edge_point_num[ee] = B.E[ee].size(); | |
1089 | - | |
1090 | - for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in A | |
1091 | - | |
1092 | - size_t n = A.E[e].size(); //the number of edges in A | |
1093 | - | |
1094 | - T* queryPt = new T[3 * n]; | |
1095 | - T* m1 = new T[n]; | |
1096 | - T* dists = new T[n]; //store the distances | |
1097 | - size_t* nnIdx = new size_t[n]; //store the indices | |
1098 | - | |
1099 | - edge2array(queryPt, A.E[e]); | |
1100 | - kdt.search(queryPt, n, nnIdx, dists); | |
1101 | - | |
1102 | - for(unsigned p = 0; p < A.E[e].size(); p++){ | |
1103 | - m1[p] = 1.0f - gaussianFunction((T)dists[p], sigma); //calculate the metric value based on the distance | |
1104 | - A.E[e].set_r(p, m1[p]); //set the error for the second point in the segment | |
1105 | - | |
1106 | - unsigned id = 0; //mapping edge's idx | |
1107 | - size_t num = 0; //total number of points before #th edge | |
1108 | - for(unsigned i = 0; i < NB; i++){ | |
1109 | - num += B.E[i].size(); | |
1110 | - if(nnIdx[p] < num){ //find the edge it belongs to | |
1111 | - relation[e][p] = id; | |
1112 | - break; | |
1113 | - } | |
1114 | - id++; //current edge won't be the one, move to next edge | |
1115 | - } | |
1116 | - } | |
1117 | - } | |
1118 | -#endif | |
1119 | - E = A.E; | |
1120 | - V = A.V; | |
1121 | - | |
1122 | - unsigned int id = 0; // split value | |
1123 | - for(unsigned e = 0; e < E.size(); e++){ // for every edge | |
1124 | - for(unsigned p = 0; p < E[e].size() - 1; p++){ // for every point in each edge | |
1125 | - int t = (int)(E[e].length() / sigma * 2); | |
1126 | - if (t <= 20) | |
1127 | - threshold_fac = E[e].size(); | |
1128 | - else | |
1129 | - threshold_fac = (E[e].length() / sigma * 2)/10; | |
1130 | - if(relation[e][p] != relation[e][p + 1]){ // find the nearest edge changing point | |
1131 | - id = p + 1; // if there is no change in NN | |
1132 | - if(id < threshold_fac || (E[e].size() - id) < threshold_fac) | |
1133 | - id = E[e].size() - 1; // extreme situation is not acceptable | |
1134 | - else | |
1135 | - break; | |
1136 | - } | |
1137 | - if(p == E[e].size() - 2) // if there is no splitting index, set the id to the last point index of current edge | |
1138 | - id = E[e].size() - 1; | |
1139 | - } | |
1140 | - //unsigned errormag_id = E[e].nmags() - 1; | |
1141 | - T G = 0; // test to see whether it has its nearest neighbor | |
1142 | - for(unsigned i = 0; i < E[e].size(); i++) | |
1143 | - G += E[e].r(i); // won't split special edges | |
1144 | - if(G / E[e].size() > threshold) // should based on the color map | |
1145 | - id = E[e].size() - 1; // set split idx to outgoing direction vertex | |
1146 | - | |
1147 | - std::vector<edge> tmpe; | |
1148 | - tmpe.resize(2); | |
1149 | - tmpe = E[e].split(id); | |
1150 | - vertex tmpv = stim::vec3<T>(-1, -1, 0); // store the split point as vertex | |
1151 | - if(tmpe.size() == 2){ | |
1152 | - relation.resize(relation.size() + 1); | |
1153 | - for(unsigned d = id; d < E[e].size(); d++) | |
1154 | - relation[relation.size() - 1].push_back(relation[e][d]); | |
1155 | - tmpe[0].v[0] = E[e].v[0]; // begining vertex of first half edge -> original begining vertex | |
1156 | - tmpe[1].v[1] = E[e].v[1]; // ending vertex of second half edge -> original ending vertex | |
1157 | - tmpv = E[e][id]; | |
1158 | - V.push_back(tmpv); | |
1159 | - tmpe[0].v[1] = (unsigned)V.size() - 1; // ending vertex of first half edge -> new vertex | |
1160 | - tmpe[1].v[0] = (unsigned)V.size() - 1; // begining vertex of second half edge -> new vertex | |
1161 | - edge tmp(E[e]); | |
1162 | - E[e] = tmpe[0]; // replace original edge by first half edge | |
1163 | - E.push_back(tmpe[1]); // push second half edge to the last | |
1164 | - V[V.size() - 1].e[1].push_back(e); // push first half edge to the incoming of new vertex | |
1165 | - V[V.size() - 1].e[0].push_back((unsigned)E.size() - 1); // push second half edge to the outgoing of new vertex | |
1166 | - for(unsigned i = 0; i < V[tmp.v[1]].e[1].size(); i++) // find the incoming edge of original ending vertex | |
1167 | - if(V[tmp.v[1]].e[1][i] == e) | |
1168 | - V[tmp.v[1]].e[1][i] = (unsigned)E.size() - 1; // set to new edge | |
1169 | - } | |
1170 | - } | |
1171 | - } | |
1172 | - | |
1173 | - /// This function compares two splitted networks and yields a mapping relationship between them according to NN | |
1174 | - /// @param B is the network that the current network is going to map to | |
1175 | - /// @param C is the mapping relationship: C[e1] = _e1 means e1 edge in current network is mapping the _e1 edge in B | |
1176 | - /// @param device is the device that user want to use | |
1177 | - void mapping(stim::network<T> B, std::vector<unsigned> &C, int device, float threshold){ | |
1178 | - stim::network<T> A; //generate a network storing the result of the comparison | |
1179 | - A = (*this); | |
1180 | - | |
1181 | - size_t n = A.E.size(); // the number of edges in A | |
1182 | - size_t NB = B.E.size(); // the number of edges in B | |
1183 | - | |
1184 | - C.resize(A.E.size()); | |
1185 | - | |
1186 | - T *c; // centerline (array of double pointers) - points on kdtree must be double | |
1187 | - size_t n_data = B.total_points(); // set the number of points | |
1188 | - c = (T*) malloc(sizeof(T) * n_data * 3); | |
1189 | - | |
1190 | - unsigned t = 0; | |
1191 | - for(unsigned e = 0; e < NB; e++){ // for each edge in the network | |
1192 | - for(unsigned p = 0; p < B.E[e].size(); p++){ // for each point in the edge | |
1193 | - for(unsigned d = 0; d < 3; d++){ // for each coordinate | |
1194 | - | |
1195 | - c[t * 3 + d] = B.E[e][p][d]; | |
1196 | - } | |
1197 | - t++; | |
1198 | - } | |
1199 | - } | |
1200 | - | |
1201 | - //generate a KD-tree for network A | |
1202 | - //float metric = 0.0; // initialize metric to be returned after comparing the network | |
1203 | - size_t MaxTreeLevels = 3; // max tree level | |
1204 | - | |
1205 | -#ifdef __CUDACC__ | |
1206 | - cudaSetDevice(device); | |
1207 | - stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree | |
1208 | - | |
1209 | - kdt.create(c, n_data, MaxTreeLevels); // build a KD tree | |
1210 | - | |
1211 | - for(unsigned e = 0; e < n; e++){ //for each edge in A | |
1212 | - //size_t errormag_id = A.E[e].nmags() - 1; //get the id for the new magnitude | |
1213 | - | |
1214 | - //pre-judge to get rid of impossibly mapping edges | |
1215 | - T M = 0; | |
1216 | - for(unsigned p = 0; p < A.E[e].size(); p++) | |
1217 | - M += A.E[e].r(p); | |
1218 | - M = M / A.E[e].size(); | |
1219 | - if(M > threshold) | |
1220 | - C[e] = (unsigned)-1; //set the nearest edge of impossibly mapping edges to maximum of unsigned | |
1221 | - else{ | |
1222 | - T* queryPt = new T[3]; | |
1223 | - T* dists = new T[1]; | |
1224 | - size_t* nnIdx = new size_t[1]; | |
1225 | - | |
1226 | - stim2array(queryPt, A.E[e][A.E[e].size()/2]); | |
1227 | - kdt.search(queryPt, 1, nnIdx, dists); | |
1228 | - | |
1229 | - unsigned id = 0; //mapping edge's idx | |
1230 | - size_t num = 0; //total number of points before #th edge | |
1231 | - for(unsigned i = 0; i < NB; i++){ | |
1232 | - num += B.E[i].size(); | |
1233 | - if(nnIdx[0] < num){ | |
1234 | - C[e] = id; | |
1235 | - break; | |
1236 | - } | |
1237 | - id++; | |
1238 | - } | |
1239 | - } | |
1240 | - } | |
1241 | -#else | |
1242 | - stim::kdtree<T, 3> kdt; | |
1243 | - kdt.create(c, n_data, MaxTreeLevels); | |
1244 | - T *dists = new T[1]; // near neighbor distances | |
1245 | - size_t *nnIdx = new size_t[1]; // near neighbor indices // allocate near neigh indices | |
1246 | - | |
1247 | - stim::vec3<T> p0, p1; | |
1248 | - T* queryPt = new T[3]; | |
1249 | - | |
1250 | - for(unsigned int e = 0; e < R.E.size(); e++){ // for each edge in A | |
1251 | - T M; // the sum of metrics of current edge | |
1252 | - for(unsigned p = 0; p < R.E[e].size(); p++) | |
1253 | - M += A.E[e].r(p); | |
1254 | - M = M / A.E[e].size(); | |
1255 | - if(M > threshold) | |
1256 | - C[e] = (unsigned)-1; | |
1257 | - else{ // if it should have corresponding edge in B, then... | |
1258 | - p1 = R.E[e][R.E[e].size()/2]; | |
1259 | - stim2array(queryPt, p1); | |
1260 | - kdt.cpu_search(queryPt, 1, nnIdx, dists); // search the tree | |
1261 | - | |
1262 | - unsigned id = 0; //mapping edge's idx | |
1263 | - size_t num = 0; //total number of points before #th edge | |
1264 | - for(unsigned i = 0; i < NB; i++){ | |
1265 | - num += B.E[i].size(); | |
1266 | - if(nnIdx[0] < num){ | |
1267 | - C[e] = id; | |
1268 | - break; | |
1269 | - } | |
1270 | - id++; | |
1271 | - } | |
1272 | - } | |
1273 | - } | |
1274 | -#endif | |
1275 | - } | |
1276 | - | |
1277 | - /// Returns the number of magnitude values stored in each edge. This should be uniform across the network. | |
1278 | - //unsigned nmags(){ | |
1279 | - // return E[0].nmags(); | |
1280 | - //} | |
1281 | - // split a string in text by the character sep | |
1282 | - stim::vec<T> split(std::string &text, char sep) | |
1283 | - { | |
1284 | - stim::vec<T> tokens; | |
1285 | - std::size_t start = 0, end = 0; | |
1286 | - while ((end = text.find(sep, start)) != std::string::npos) { | |
1287 | - tokens.push_back(atof(text.substr(start, end - start).c_str())); | |
1288 | - start = end + 1; | |
1289 | - } | |
1290 | - tokens.push_back(atof(text.substr(start).c_str())); | |
1291 | - return tokens; | |
1292 | - } | |
1293 | - // load a network in text file to a network class | |
1294 | - void load_txt(std::string filename) | |
1295 | - { | |
1296 | - std::vector <std::string> file_contents; | |
1297 | - std::ifstream file(filename.c_str()); | |
1298 | - std::string line; | |
1299 | - std::vector<unsigned> id2vert; //this list stores the vertex ID associated with each network vertex | |
1300 | - //for each line in the text file, store them as strings in file_contents | |
1301 | - while (std::getline(file, line)) | |
1302 | - { | |
1303 | - std::stringstream ss(line); | |
1304 | - file_contents.push_back(ss.str()); | |
1305 | - } | |
1306 | - unsigned int numEdges = atoi(file_contents[0].c_str()); //number of edges in the network | |
1307 | - unsigned int I = atoi(file_contents[1].c_str()) ; //calculate the number of points3d on the first edge | |
1308 | - unsigned int count = 1; unsigned int k = 2; // count is global counter through the file contents, k is for the vertices on the edges | |
1309 | - // for each edge in the network. | |
1310 | - for (unsigned int i = 0; i < numEdges; i ++ ) | |
1311 | - { | |
1312 | - // pre allocate a position vector p with number of points3d on the edge p | |
1313 | - std::vector< stim::vec<T> > p(0, I); | |
1314 | - // for each point on the nth edge | |
1315 | - for (unsigned int j = k; j < I + k; j++) | |
1316 | - { | |
1317 | - // split the points3d of floats with separator space and form a float3 position vector out of them | |
1318 | - p.push_back(split(file_contents[j], ' ')); | |
1319 | - } | |
1320 | - count += p.size() + 1; // increment count to point at the next edge in the network | |
1321 | - I = atoi(file_contents[count].c_str()); // read in the points3d at the next edge and convert it to an integer | |
1322 | - k = count + 1; | |
1323 | - edge new_edge = p; // create an edge with a vector of points3d on the edge | |
1324 | - E.push_back(new_edge); // push the edge into the network | |
1325 | - } | |
1326 | - unsigned int numVertices = atoi(file_contents[count].c_str()); // this line in the text file gives the number of distinct vertices | |
1327 | - count = count + 1; // this line of text file gives the first verrtex | |
1328 | - // push each vertex into V | |
1329 | - for (unsigned int i = 0; i < numVertices; i ++) | |
1330 | - { | |
1331 | - vertex new_vertex = split(file_contents[count], ' '); | |
1332 | - V.push_back(new_vertex); | |
1333 | - count += atoi(file_contents[count + 1].c_str()) + 2; // Skip number of edge ids + 2 to point to the next vertex | |
1334 | - } | |
1335 | - } // end load_txt function | |
1336 | - | |
1337 | - // strTxt returns a string of edges | |
1338 | - std::string | |
1339 | - strTxt(std::vector< stim::vec<T> > p) | |
1340 | - { | |
1341 | - std::stringstream ss; | |
1342 | - std::stringstream oss; | |
1343 | - for(unsigned int i = 0; i < p.size(); i++){ | |
1344 | - ss.str(std::string()); | |
1345 | - for(unsigned int d = 0; d < 3; d++){ | |
1346 | - ss<<p[i][d]; | |
1347 | - } | |
1348 | - ss << "\n"; | |
1349 | - } | |
1350 | - return ss.str(); | |
1351 | - } | |
1352 | - // removes specified character from string | |
1353 | - void removeCharsFromString(std::string &str, char* charsToRemove ) { | |
1354 | - for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) { | |
1355 | - str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() ); | |
1356 | - } | |
1357 | - } | |
1358 | - //exports network to txt file | |
1359 | - void | |
1360 | - to_txt(std::string filename) | |
1361 | - { | |
1362 | - std::ofstream ofs(filename.c_str(), std::ofstream::out | std::ofstream::app); | |
1363 | - //int num; | |
1364 | - ofs << (E.size()).str() << "\n"; | |
1365 | - for(unsigned int i = 0; i < E.size(); i++) | |
1366 | - { | |
1367 | - std::string str; | |
1368 | - ofs << (E[i].size()).str() << "\n"; | |
1369 | - str = E[i].strTxt(); | |
1370 | - ofs << str << "\n"; | |
1371 | - } | |
1372 | - for(int i = 0; i < V.size(); i++) | |
1373 | - { | |
1374 | - std::string str; | |
1375 | - str = V[i].str(); | |
1376 | - char temp[4] = "[],"; | |
1377 | - removeCharsFromString(str, temp); | |
1378 | - ofs << str << "\n"; | |
1379 | - } | |
1380 | - ofs.close(); | |
1381 | - } | |
1382 | -}; //end stim::network class | |
1383 | -}; //end stim namespace | |
1384 | -#endif | |
1 | +#ifndef JACK_NETWORK_H | |
2 | +#define JACK_NETWORK_H | |
3 | + | |
4 | +#include "centerline.h" | |
5 | +#include<stim/visualization/obj.h> | |
6 | +#include<stim/visualization/swc.h> | |
7 | +#include <stim/math/circle.h> | |
8 | +#include <stim/structures/kdtree.cuh> | |
9 | + | |
10 | + | |
11 | +// *****help function***** | |
12 | + | |
13 | +template<typename T> | |
14 | +CUDA_CALLABLE T gaussian(T x, T std = 25.0f) { | |
15 | + return exp(-x / (2.0f * std * std)); | |
16 | +} | |
17 | + | |
18 | +#ifdef __CUDACC__ | |
19 | +template<typename T> | |
20 | +__global__ void find_metric(T* M, size_t n, T* D, T sigma) { | |
21 | + size_t x = blockDim.x * blockIdx.x + threadIdx.x; | |
22 | + if (x >= n) return; // segfault | |
23 | + M[x] = 1.0f - gaussian<T>(D[x], sigma); | |
24 | +} | |
25 | +#endif | |
26 | + | |
27 | +namespace stim{ | |
28 | + | |
29 | + template<typename T> | |
30 | + class network { | |
31 | + public: | |
32 | + // define edge class that extends centerline class with radius information | |
33 | + class edge : public stim::centerline<T> { | |
34 | + protected: | |
35 | + std::vector<T> R; // radius at each point on current edge | |
36 | + | |
37 | + public: | |
38 | + unsigned int v[2]; // unique idx for starting and ending point | |
39 | + using stim::centerline<T>::d; | |
40 | + using stim::centerline<T>::C; | |
41 | + | |
42 | + | |
43 | + /// constructors | |
44 | + // empty constructor | |
45 | + edge() : stim::centerline<T>() { | |
46 | + v[0] = UINT_MAX; // set to default value, risky! | |
47 | + v[1] = UINT_MAX; | |
48 | + } | |
49 | + | |
50 | + // constructor that contructs an edge based on a centerline | |
51 | + edge(stim::centerline<T> c) : stim::centerline<T>(c) { | |
52 | + size_t num = c.size(); | |
53 | + R.resize(num); | |
54 | + } | |
55 | + | |
56 | + // constructor that constructs an edge based on a centerline and a list of radii | |
57 | + edge(stim::centerline<T> c, std::vector<T> r) : stim::centerline<T>(c) { | |
58 | + R = r; // copy radii | |
59 | + } | |
60 | + | |
61 | + // constructor that constructs an edge based on a list of points and a list of radii | |
62 | + edge(std::vector<stim::vec3<T> > c, std::vector<T> r) : stim::centerline<T>(c) { | |
63 | + R = r; // copy radii | |
64 | + } | |
65 | + | |
66 | + | |
67 | + /// basic operations | |
68 | + // get radius | |
69 | + T r(size_t idx) { | |
70 | + return R[idx]; | |
71 | + } | |
72 | + | |
73 | + // set radius | |
74 | + void set_r(T value) { | |
75 | + size_t num = R.size(); | |
76 | + for (size_t i = 0; i < num; i++) | |
77 | + R[i] = value; | |
78 | + } | |
79 | + void set_r(size_t idx, T value) { | |
80 | + R[idx] = value; | |
81 | + } | |
82 | + void set_r(std::vector<T> value) { | |
83 | + size_t num = value.size(); | |
84 | + for (size_t i = 0; i < num; i++) | |
85 | + R[i] = value[i]; | |
86 | + } | |
87 | + | |
88 | + // push back a new radius | |
89 | + void push_back_r(T value) { | |
90 | + R.push_back(value); | |
91 | + } | |
92 | + | |
93 | + | |
94 | + /// vector operation | |
95 | + // insert a new point and radius at specific location | |
96 | + void insert(size_t idx, stim::vec3<T> p, T r) { | |
97 | + centerline<T>::insert(idx, p); // insert a new point on current centerline | |
98 | + | |
99 | + R.insert(R.begin() + idx, r); // insert a new radius for that point | |
100 | + } | |
101 | + | |
102 | + // reverse the order of an edge | |
103 | + edge reverse() { | |
104 | + centerline<T> new_centerline = (*this).centerline<T>::reverse(); | |
105 | + std::vector<T> new_radius = R; | |
106 | + std::reverse(new_radius.begin(), new_radius.end()); | |
107 | + | |
108 | + edge result(new_centerline, new_radius); | |
109 | + | |
110 | + return result; | |
111 | + } | |
112 | + | |
113 | + // copy edge to array | |
114 | + void edge_to_array(T* a) { | |
115 | + edge b = (*this); | |
116 | + size_t n = b.size(); | |
117 | + for (size_t i = 0; i < n; i++) { | |
118 | + a[i * 3 + 0] = b[i][0]; | |
119 | + a[i * 3 + 1] = b[i][1]; | |
120 | + a[i * 3 + 2] = b[i][2]; | |
121 | + } | |
122 | + } | |
123 | + | |
124 | + | |
125 | + /// arithmetic operations | |
126 | + // '+' operation | |
127 | + edge operator+(edge e) const { | |
128 | + edge result(*this); | |
129 | + size_t num = e.size(); | |
130 | + for (size_t i = 0; i < num; i++) { | |
131 | + result.push_back(e[i]); | |
132 | + result.push_back_r(e.R[i]); | |
133 | + } | |
134 | + | |
135 | + return result; | |
136 | + } | |
137 | + | |
138 | + | |
139 | + /// advanced operations | |
140 | + // concatenate two edges from specific point, The deference between function "+" and "concatenate" is that this requires that new edges should share a joint point | |
141 | + edge concatenate(edge e2, size_t p1, size_t p2) { | |
142 | + edge e1 = *this; | |
143 | + size_t num1 = e1.size(); // get the number of points | |
144 | + size_t num2 = e2.size(); | |
145 | + centerline<T> new_centerline; | |
146 | + std::vector<T> new_radius; | |
147 | + | |
148 | + // four situations | |
149 | + if (p1 == 0) { | |
150 | + if (p2 == 0) | |
151 | + e2 = e2.reverse(); | |
152 | + for (size_t i = 0; i < num2 - 1; i++) { | |
153 | + new_centerline.push_back(e2[i]); | |
154 | + new_radius.push_back(e2.R[i]); | |
155 | + } | |
156 | + for (size_t i = 0; i < num1; i++) { | |
157 | + new_centerline.push_back(e1[i]); | |
158 | + new_radius.push_back(e1.R[i]); | |
159 | + } | |
160 | + } | |
161 | + else { | |
162 | + if (p2 != 0) | |
163 | + e2 = e2.reverse(); | |
164 | + for (size_t i = 0; i < num1 - 1; i++) { | |
165 | + new_centerline.push_back(e1[i]); | |
166 | + new_radius.push_back(e1.R[i]); | |
167 | + } | |
168 | + for (size_t i = 0; i < num2; i++) { | |
169 | + new_centerline.push_back(e2[i]); | |
170 | + new_radius.push_back(e2.R[i]); | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + edge result(new_centerline, new_radius); | |
175 | + | |
176 | + return result; | |
177 | + } | |
178 | + | |
179 | + // split current edge at specific position | |
180 | + std::vector<edge> split(size_t idx) { | |
181 | + | |
182 | + // can't update v!!! | |
183 | + std::vector<centerline<T> > tmp; | |
184 | + tmp = (*this).centerline<T>::split(idx); // split current edge in terms of centerline | |
185 | + size_t num = tmp.size(); | |
186 | + std::vector<edge> result(num); // construct a list of edges | |
187 | + for (size_t i = 0; i < num; i++) { | |
188 | + edge new_edge(tmp[i]); // construct new edge based on one centerline | |
189 | + result[i] = new_edge; | |
190 | + } | |
191 | + | |
192 | + for (size_t i = 0; i < num; i++) { // for every edge | |
193 | + for (size_t j = 0; j < result[i].size(); j++) { // for every point on that edge | |
194 | + result[i].R[j] = R[j + i * idx]; // update radius information | |
195 | + } | |
196 | + } | |
197 | + | |
198 | + return result; | |
199 | + } | |
200 | + | |
201 | + // resample current edge | |
202 | + edge resample(T spacing) { | |
203 | + edge result(centerline<T>::resample(spacing)); // resample current edge and output as a new edge | |
204 | + result.v[0] = v[0]; // updates unique indices | |
205 | + result.v[1] = v[1]; | |
206 | + | |
207 | + return result; | |
208 | + } | |
209 | + | |
210 | + // compute a circle that represent the shape of cylinder cross section at point idx, (TGC -> truncated generalized cones) | |
211 | + stim::circle<T> circ(size_t idx) { | |
212 | + | |
213 | + stim::circle<T> c; // create a circle to orient for finding the circle plane at point idx | |
214 | + c.rotate(d(idx)); // rotate the circle | |
215 | + stim::vec3<T> U = c.U; // copy the frenet frame vector | |
216 | + | |
217 | + return stim::circle<T>(C[idx], R[idx], d(idx), U); | |
218 | + } | |
219 | + | |
220 | + /// output operation | |
221 | + // output the edge information as a string | |
222 | + std::string str() { | |
223 | + std::stringstream ss; | |
224 | + ss << "(" << centerline<T>::size() << ")\tl = " << this->length() << "\t" << v[0] << "----" << v[1]; | |
225 | + return ss.str(); | |
226 | + } | |
227 | + | |
228 | + /// operator for writing the edge information into a binary .nwt file. | |
229 | + friend std::ofstream& operator<<(std::ofstream& out, edge& e) { | |
230 | + out.write(reinterpret_cast<const char*>(&e.v[0]), sizeof(unsigned int)); // write the starting point. | |
231 | + out.write(reinterpret_cast<const char*>(&e.v[1]), sizeof(unsigned int)); // write the ending point. | |
232 | + size_t sz = e.size(); // write the number of point in the edge. | |
233 | + out.write(reinterpret_cast<const char*>(&sz), sizeof(unsigned int)); | |
234 | + for (size_t i = 0; i < sz; i++) { // write each point | |
235 | + stim::vec3<T> point = e[i]; | |
236 | + out.write(reinterpret_cast<const char*>(&point[0]), 3 * sizeof(T)); | |
237 | + out.write(reinterpret_cast<const char*>(&e.R[i]), sizeof(T)); // write the radius | |
238 | + } | |
239 | + return out; // return stream | |
240 | + } | |
241 | + | |
242 | + /// operator for reading an edge from a binary .nwt file. | |
243 | + friend std::ifstream& operator>>(std::ifstream& in, edge& e) { | |
244 | + unsigned int v0, v1, sz; | |
245 | + in.read(reinterpret_cast<char*>(&v0), sizeof(unsigned int)); // read the staring point. | |
246 | + in.read(reinterpret_cast<char*>(&v1), sizeof(unsigned int)); // read the ending point | |
247 | + in.read(reinterpret_cast<char*>(&sz), sizeof(unsigned int)); // read the number of points in the edge | |
248 | + | |
249 | + std::vector<stim::vec3<T> > p(sz); | |
250 | + std::vector<T> r(sz); | |
251 | + for (size_t i = 0; i < sz; i++) { // set the points and radii to the newly read values | |
252 | + stim::vec3<T> point; | |
253 | + in.read(reinterpret_cast<char*>(&point[0]), 3 * sizeof(T)); | |
254 | + p[i] = point; | |
255 | + T mag; | |
256 | + in.read(reinterpret_cast<char*>(&mag), sizeof(T)); | |
257 | + r[i] = mag; | |
258 | + } | |
259 | + e = edge(p, r); | |
260 | + e.v[0] = v0; e.v[1] = v1; | |
261 | + return in; | |
262 | + } | |
263 | + }; | |
264 | + | |
265 | + // define vertex class that extends vec3 class with connectivity information | |
266 | + class vertex : public stim::vec3<T> { | |
267 | + public: | |
268 | + std::vector<unsigned int> e[2]; // incoming and outgoing edges of that vertex | |
269 | + using stim::vec3<T>::ptr; | |
270 | + | |
271 | + /// constructors | |
272 | + // empty constructor | |
273 | + vertex() : stim::vec3<T>() { | |
274 | + } | |
275 | + | |
276 | + // constructor that constructs a vertex based on a vec3 vector | |
277 | + vertex(stim::vec3<T> v) : stim::vec3<T>(v) { | |
278 | + } | |
279 | + | |
280 | + stim::vec3<T> | |
281 | + getPosition() | |
282 | + { | |
283 | + return stim::vec3<T>(ptr[0], ptr[1], ptr[2]); | |
284 | + } | |
285 | + | |
286 | + /// output operation | |
287 | + // output the vertex information as a string | |
288 | + std::string str() { | |
289 | + std::stringstream ss; | |
290 | + ss << "\t(x, y, z) = " << stim::vec3<T>::str(); | |
291 | + | |
292 | + if (e[0].size() > 0) { | |
293 | + ss << "\t> "; | |
294 | + for (size_t i = 0; i < e[0].size(); i++) | |
295 | + ss << e[0][i] << " "; | |
296 | + } | |
297 | + if (e[1].size() > 0) { | |
298 | + ss << "\t< "; | |
299 | + for (size_t i = 0; i < e[1].size(); i++) | |
300 | + ss << e[1][i] << " "; | |
301 | + } | |
302 | + | |
303 | + return ss.str(); | |
304 | + } | |
305 | + | |
306 | + /// operator for writing the vector into the stream; | |
307 | + friend std::ofstream& operator<<(std::ofstream& out, const vertex& v) { | |
308 | + unsigned int s0, s1; | |
309 | + s0 = v.e[0].size(); | |
310 | + s1 = v.e[1].size(); | |
311 | + out.write(reinterpret_cast<const char*>(&v.ptr[0]), 3 * sizeof(T)); // write physical vertex location | |
312 | + out.write(reinterpret_cast<const char*>(&s0), sizeof(unsigned int)); // write the number of "outgoing edges" | |
313 | + out.write(reinterpret_cast<const char*>(&s1), sizeof(unsigned int)); // write the number of "incoming edges" | |
314 | + if (s0 != 0) | |
315 | + out.write(reinterpret_cast<const char*>(&v.e[0][0]), sizeof(unsigned int)*v.e[0].size()); // write the "outgoing edges" | |
316 | + if (s1 != 0) | |
317 | + out.write(reinterpret_cast<const char*>(&v.e[1][0]), sizeof(unsigned int)*v.e[1].size()); // write the "incoming edges" | |
318 | + return out; | |
319 | + } | |
320 | + | |
321 | + /// operator for reading the vector out of the stream; | |
322 | + friend std::ifstream& operator >> (std::ifstream& in, vertex& v) { | |
323 | + in.read(reinterpret_cast<char*>(&v[0]), 3 * sizeof(T)); // read the physical position | |
324 | + unsigned int s[2]; | |
325 | + in.read(reinterpret_cast<char*>(&s[0]), 2 * sizeof(unsigned int)); // read the sizes of incoming and outgoing edge arrays | |
326 | + | |
327 | + std::vector<unsigned int> one(s[0]); | |
328 | + std::vector<unsigned int> two(s[1]); | |
329 | + v.e[0] = one; | |
330 | + v.e[1] = two; | |
331 | + if (one.size() != 0) | |
332 | + in.read(reinterpret_cast<char*>(&v.e[0][0]), s[0] * sizeof(unsigned int)); // read the arrays of "outgoing edges" | |
333 | + if (two.size() != 0) | |
334 | + in.read(reinterpret_cast<char*>(&v.e[1][0]), s[1] * sizeof(unsigned int)); // read the arrays of "incoming edges" | |
335 | + return in; | |
336 | + } | |
337 | + }; | |
338 | + | |
339 | + public: | |
340 | + | |
341 | + std::vector<edge> E; // list of edges | |
342 | + std::vector<vertex> V; // list of vertices | |
343 | + | |
344 | + /// constructors | |
345 | + // empty constructor | |
346 | + network() { | |
347 | + } | |
348 | + | |
349 | + // constructor with a file to load | |
350 | + network(std::string fileLocation) { | |
351 | + load_obj(fileLocation); | |
352 | + } | |
353 | + | |
354 | + // constructor that constructs a network based on lists of vertices and edges | |
355 | + network(std::vector<edge> nE, std::vector<vertex> nV) { | |
356 | + E = nE; | |
357 | + V = nV; | |
358 | + } | |
359 | + | |
360 | + | |
361 | + /// basic operations | |
362 | + // get the number of edges | |
363 | + size_t edges() { | |
364 | + return E.size(); | |
365 | + } | |
366 | + | |
367 | + // get the number of vertices | |
368 | + size_t vertices() { | |
369 | + return V.size(); | |
370 | + } | |
371 | + | |
372 | + // get the radius at specific point | |
373 | + T r(size_t f, size_t p) { // edge f, point p | |
374 | + return E[f].r(p); | |
375 | + } | |
376 | + T r(size_t c) { // vertex c | |
377 | + T result; | |
378 | + if (V[c].e[0].size()) { // if this vertex has outgoing edges | |
379 | + size_t f = V[c].e[0][0]; // get the index of first outgoing edge of this vertex | |
380 | + result = r(f, 0); // this vertex should be the starting point of that edge | |
381 | + } | |
382 | + else { // if this vertex only has incoming edges | |
383 | + size_t f = V[c].e[1][0]; // get the index of first incoming edge of this vertex | |
384 | + result = r(f, E[f].size() - 1); // this vertex should be the ending point of that edge | |
385 | + } | |
386 | + | |
387 | + return result; | |
388 | + } | |
389 | + | |
390 | + // get the average radius of one specific edge | |
391 | + T ar(size_t f) { | |
392 | + T result = 0.0f; | |
393 | + size_t num = E[f].size(); | |
394 | + for (size_t i = 0; i < num; i++) | |
395 | + result += E[f].R[i]; | |
396 | + result = result / num; | |
397 | + | |
398 | + return result; | |
399 | + } | |
400 | + | |
401 | + // get the length of edge "f" | |
402 | + T length(size_t f) { | |
403 | + return E[f].length(); | |
404 | + } | |
405 | + | |
406 | + // copy specific edge | |
407 | + edge get_edge(size_t f) { | |
408 | + return E[f]; | |
409 | + } | |
410 | + | |
411 | + // copy specific vertex | |
412 | + vertex get_vertex(size_t c) { | |
413 | + return V[c]; | |
414 | + } | |
415 | + | |
416 | + // get boundary/pendant vertices | |
417 | + std::vector<size_t> pendant() { | |
418 | + std::vector<size_t> result; | |
419 | + | |
420 | + for (size_t i = 0; i < V.size(); i++) | |
421 | + if (V[i].e[0].size() + V[i].e[1].size() == 1) | |
422 | + result.push_back(i); | |
423 | + | |
424 | + return result; | |
425 | + } | |
426 | + | |
427 | + // set radius for specific point on edge "f" | |
428 | + void set_r(size_t f, size_t p, T value) { | |
429 | + E[f].set_r(p, value); | |
430 | + } | |
431 | + void set_r(size_t f, T value) { | |
432 | + E[f].set_r(value); | |
433 | + } | |
434 | + void set_r(size_t f, std::vector<T> value) { | |
435 | + E[f].set_r(value); | |
436 | + } | |
437 | + | |
438 | + // copy all points (coordinates) to 1D array | |
439 | + void copy_to_array(T* dst) { | |
440 | + size_t t = 0; // indicator for points | |
441 | + for (size_t i = 0; i < E.size(); i++) { | |
442 | + for (size_t j = 0; j < E[i].size(); j++) { | |
443 | + for (size_t k = 0; k < 3; k++) { | |
444 | + dst[t * 3 + k] = E[i][j][k]; | |
445 | + } | |
446 | + t++; // next point | |
447 | + } | |
448 | + } | |
449 | + } | |
450 | + | |
451 | + // get an average of branching index in the network | |
452 | + T BranchingIndex() { | |
453 | + T B = 0.0f; | |
454 | + size_t num = V.size(); | |
455 | + for (size_t i = 0; i < num; i++) | |
456 | + B += (T)(V[i].e[0].size() + V[i].e[1].size()); | |
457 | + | |
458 | + B = B / (T)num; | |
459 | + | |
460 | + return B; | |
461 | + } | |
462 | + | |
463 | + // get the number of branching points in the network | |
464 | + size_t BranchP() { | |
465 | + size_t B = 0; | |
466 | + size_t c; | |
467 | + size_t num = V.size(); | |
468 | + for (size_t i = 0; i < num; i++) { | |
469 | + c = (V[i].e[0].size() + V[i].e[1].size()); | |
470 | + if (c > 2) | |
471 | + B += 1; | |
472 | + } | |
473 | + | |
474 | + return B; | |
475 | + } | |
476 | + | |
477 | + // get the number of starting or ending points in the network | |
478 | + size_t EndP() { | |
479 | + size_t B = 0; | |
480 | + size_t c; | |
481 | + size_t num = V.size(); | |
482 | + for (size_t i = 0; i < num; i++) { | |
483 | + c = (V[i].e[0].size() + V[i].e[1].size()); | |
484 | + if (c == 1) | |
485 | + B += 1; | |
486 | + } | |
487 | + | |
488 | + return B; | |
489 | + } | |
490 | + | |
491 | + // get an average of fiber length in the network | |
492 | + T Lengths() { | |
493 | + std::vector<T> L; | |
494 | + T sumLength = 0.0f; | |
495 | + size_t num = E.size(); | |
496 | + for (size_t i = 0; i < num; i++) { | |
497 | + L.push_back(E[i].length()); | |
498 | + sumLength += E(i).length(); | |
499 | + } | |
500 | + T avg = sumLength / (T)num; | |
501 | + | |
502 | + return avg; | |
503 | + } | |
504 | + | |
505 | + // get the total number of points in the network | |
506 | + size_t total_points() { | |
507 | + size_t n = 0; | |
508 | + size_t num = E.size(); | |
509 | + for (size_t i = 0; i < num; i++) | |
510 | + n += num; | |
511 | + | |
512 | + return n; | |
513 | + } | |
514 | + | |
515 | + // get an average of tortuosities in the network | |
516 | + T Tortuosities() { | |
517 | + std::vector<T> t; | |
518 | + std::vector<T> id1, id2; | |
519 | + T distance; | |
520 | + T tortuosity; | |
521 | + T sumTortuosity = 0.0f; | |
522 | + size_t num = E.size(); | |
523 | + for (size_t i = 0; i < num; i++) { | |
524 | + id1 = E[i][0]; // get the starting point | |
525 | + id2 = E[i][num - 1]; // get the ending point | |
526 | + distance = (id1 - id2).len(); | |
527 | + if (distance > 0) | |
528 | + tortuosity = E[i].length() / distance; // tortuosity = edge length / edge displacement | |
529 | + else | |
530 | + tortuosity = 0.0f; | |
531 | + t.push_back(tortuosity); | |
532 | + sumTortuosity += tortuosity; | |
533 | + } | |
534 | + T avg = sumTortuosity / (T)num; | |
535 | + | |
536 | + return avg; | |
537 | + } | |
538 | + | |
539 | + // get an average contraction of the network | |
540 | + T Contraction() { | |
541 | + std::vector<T> t; | |
542 | + std::vector<T> id1, id2; // starting and ending vertices of the edge | |
543 | + T distance; | |
544 | + T contraction; | |
545 | + T sumContraction = 0.0f; | |
546 | + size_t num = E.size(); | |
547 | + for (size_t i = 0; i < num; i++) { // for each edge in the network | |
548 | + id1 = E[i][0]; // get the edge starting point | |
549 | + id2 = E[i][num - 1]; // get the edge ending point | |
550 | + distance = (id1 - id2).len(); // displacement between the starting and ending points | |
551 | + contraction = distance / E[i].length(); // contraction = edge displacement / edge length | |
552 | + t.push_back(contraction); | |
553 | + sumContraction += contraction; | |
554 | + } | |
555 | + T avg = sumContraction / (T)num; | |
556 | + | |
557 | + return avg; | |
558 | + } | |
559 | + | |
560 | + // get an average fractal dimension of the branches of the network | |
561 | + T FractalDimensions() { | |
562 | + std::vector<T> t; | |
563 | + std::vector<T> id1, id2; // starting and ending vertices of the edge | |
564 | + T distance; | |
565 | + T fract; | |
566 | + T sumFractDim = 0.0f; | |
567 | + size_t num = E.size(); | |
568 | + for (size_t i = 0; i < num; i++) { // for each edge in the network | |
569 | + id1 = E[i][0]; // get the edge starting point | |
570 | + id2 = E[i][num - 1]; // get the edge ending point | |
571 | + distance = (id1 - id2).len(); // displacement between the starting and ending points | |
572 | + fract = std::log(distance) / std::log(E[i].length()); // fractal dimension = log(edge displacement) / log(edge length) | |
573 | + t.push_back(sumFractDim); | |
574 | + sumFractDim += fract; | |
575 | + } | |
576 | + T avg = sumFractDim / (T)num; | |
577 | + | |
578 | + return avg; | |
579 | + } | |
580 | + | |
581 | + | |
582 | + /// construct network from files | |
583 | + // load network from OBJ files | |
584 | + void load_obj(std::string filename) { | |
585 | + stim::obj<T> O; // create OBJ object | |
586 | + O.load(filename); // load OBJ file to an object | |
587 | + | |
588 | + size_t ii[2]; // starting/ending point index of one centerline/edge | |
589 | + std::vector<size_t> index; // added vertex index | |
590 | + std::vector<size_t>::iterator it; // iterator for searching | |
591 | + size_t pos; // position of added vertex | |
592 | + | |
593 | + // get the points | |
594 | + for (size_t l = 1; l <= O.numL(); l++) { // for every line of points | |
595 | + std::vector<stim::vec<T> > tmp; // temp centerline | |
596 | + O.getLine(l, tmp); // get points | |
597 | + size_t n = tmp.size(); | |
598 | + std::vector<stim::vec3<T> > c(n); | |
599 | + for (size_t i = 0; i < n; i++) { // switch from vec to vec3 | |
600 | + for (size_t j = 0; j < 3; j++) { | |
601 | + c[i][j] = tmp[i][j]; | |
602 | + } | |
603 | + } | |
604 | + | |
605 | + centerline<T> C(c); // construct centerline | |
606 | + edge new_edge(C); // construct edge without radii | |
607 | + | |
608 | + std::vector<unsigned> id; // temp point index | |
609 | + O.getLinei(l, id); // get point index | |
610 | + | |
611 | + ii[0] = (size_t)id.front(); | |
612 | + ii[1] = (size_t)id.back(); | |
613 | + | |
614 | + size_t num = new_edge.size(); // get the number of point on current edge | |
615 | + | |
616 | + // for starting point | |
617 | + it = std::find(index.begin(), index.end(), ii[0]); | |
618 | + if (it == index.end()) { // new vertex | |
619 | + vertex new_vertex = new_edge[0]; | |
620 | + new_vertex.e[0].push_back(E.size()); // push back the outgoing edge index | |
621 | + new_edge.v[0] = V.size(); // save the starting vertex index | |
622 | + V.push_back(new_vertex); | |
623 | + index.push_back(ii[0]); // save the point index | |
624 | + } | |
625 | + else { // already added vertex | |
626 | + pos = std::distance(index.begin(), it); // find the added vertex position | |
627 | + V[pos].e[0].push_back(E.size()); // push back the outgoing edge index | |
628 | + new_edge.v[0] = pos; | |
629 | + } | |
630 | + | |
631 | + // for ending point | |
632 | + it = std::find(index.begin(), index.end(), ii[1]); | |
633 | + if (it == index.end()) { // new vertex | |
634 | + vertex new_vertex = new_edge[num - 1]; | |
635 | + new_vertex.e[1].push_back(E.size()); // push back the incoming edge index | |
636 | + new_edge.v[1] = V.size(); // save the ending vertex index | |
637 | + V.push_back(new_vertex); | |
638 | + index.push_back(ii[1]); // save the point index | |
639 | + } | |
640 | + else { // already added vertex | |
641 | + pos = std::distance(index.begin(), it); // find the added vertex position | |
642 | + V[pos].e[1].push_back(E.size()); // push back the incoming edge index | |
643 | + new_edge.v[1] = pos; | |
644 | + } | |
645 | + | |
646 | + E.push_back(new_edge); | |
647 | + } | |
648 | + | |
649 | + // get the radii | |
650 | + if (O.numVT()) { // copy radii information if provided | |
651 | + std::vector<unsigned> id; // a list stores the indices of points | |
652 | + for (size_t i = 1; i <= O.numL(); i++) { | |
653 | + id.clear(); // clear up temp for current round computation | |
654 | + O.getLinei(i, id); | |
655 | + size_t num = id.size(); // get the number of points | |
656 | + T radius; | |
657 | + for (size_t j = 0; j < num; j++) { | |
658 | + radius = O.getVT(id[j] - 1)[0] / 2; // hard-coded: radius = diameter / 2 | |
659 | + set_r(i - 1, j, radius); // copy the radius | |
660 | + } | |
661 | + } | |
662 | + } | |
663 | + } | |
664 | + | |
665 | + // load network from SWC files (neuron) | |
666 | + void load_swc(std::string filename) { | |
667 | + stim::swc<T> S; // create a SWC object | |
668 | + S.load(filename); // load data from SWC file to an object | |
669 | + S.create_tree(); // link nodes according to connectivity as a tree | |
670 | + S.resample(); | |
671 | + | |
672 | + size_t i[2]; // starting/ending point index of one centerline/edge | |
673 | + std::vector<size_t> index; // added vertex index | |
674 | + std::vector<size_t>::iterator it; // iterator for searching | |
675 | + size_t pos; // position of added vertex | |
676 | + | |
677 | + for (size_t l = 0; l < S.numE; l++) { | |
678 | + std::vector<stim::vec<T> > c; // temp centerline | |
679 | + S.get_points(l, c); | |
680 | + | |
681 | + centerline<T> C(c); // construct centerline | |
682 | + | |
683 | + std::vector<T> radius; | |
684 | + S.get_radius(l, radius); // get radius | |
685 | + | |
686 | + edge new_edge(C, radius); // construct edge | |
687 | + size_t num = new_edge.size(); // get the number of point on current edge | |
688 | + | |
689 | + i[0] = S.E[l].front(); | |
690 | + i[1] = S.E[l].back(); | |
691 | + | |
692 | + // for starting point | |
693 | + it = std::find(index.begin(), index.end(), i[0]); | |
694 | + if (it == index.end()) { // new vertex | |
695 | + vertex new_vertex = new_edge[0]; | |
696 | + new_vertex.e[0].push_back(E.size()); // push back the outgoing edge index | |
697 | + new_edge.v[0] = V.size(); // save the starting vertex index | |
698 | + V.push_back(new_vertex); | |
699 | + index.push_back(i[0]); // save the point index | |
700 | + } | |
701 | + else { // already added vertex | |
702 | + pos = std::distance(index.begin(), it); // find the added vertex position | |
703 | + V[pos].e[0].push_back(E.size()); // push back the outgoing edge index | |
704 | + new_edge.v[0] = pos; | |
705 | + } | |
706 | + | |
707 | + // for ending point | |
708 | + it = std::find(index.begin(), index.end(), i[1]); | |
709 | + if (it == index.end()) { // new vertex | |
710 | + vertex new_vertex = new_edge[num - 1]; | |
711 | + new_vertex.e[1].push_back(E.size()); // push back the incoming edge index | |
712 | + new_edge.v[1] = V.size(); // save the ending vertex index | |
713 | + V.push_back(new_vertex); | |
714 | + index.push_back(i[1]); // save the point index | |
715 | + } | |
716 | + else { // already added vertex | |
717 | + pos = std::distance(index.begin(), it); // find the added vertex position | |
718 | + V[pos].e[1].push_back(E.size()); // push back the incoming edge index | |
719 | + new_edge.v[1] = pos; | |
720 | + } | |
721 | + | |
722 | + E.push_back(new_edge); | |
723 | + } | |
724 | + } | |
725 | + | |
726 | +/* | |
727 | + // load a network in text file to a network class | |
728 | + void load_txt(std::string filename) { | |
729 | + std::vector <std::string> file_contents; | |
730 | + std::ifstream file(filename.c_str()); | |
731 | + std::string line; | |
732 | + std::vector<size_t> id2vert; // this list stores the vertex ID associated with each network vertex | |
733 | + // for each line in the text file, store them as strings in file_contents | |
734 | + while (std::getline(file, line)) { | |
735 | + std::stringstream ss(line); | |
736 | + file_contents.push_back(ss.str()); | |
737 | + } | |
738 | + size_t numEdges = atoi(file_contents[0].c_str()); // number of edges in the network | |
739 | + size_t I = atoi(file_contents[1].c_str()); // calculate the number of points3d on the first edge | |
740 | + size_t count = 1; size_t k = 2; // count is global counter through the file contents, k is for the vertices on the edges | |
741 | + | |
742 | + for (size_t i = 0; i < numEdges; i++) { | |
743 | + // pre allocate a position vector p with number of points3d on the edge p | |
744 | + std::vector<stim::vec<T> > p(0, I); | |
745 | + // for each point on the nth edge | |
746 | + for (size_t j = k; j < I + k; j++) { | |
747 | + // split the points3d of floats with separator space and form a float3 position vector out of them | |
748 | + p.push_back(std::split(file_contents[j], ' ')); | |
749 | + } | |
750 | + count += p.size() + 1; // increment count to point at the next edge in the network | |
751 | + I = atoi(file_contents[count].c_str()); // read in the points3d at the next edge and convert it to an integer | |
752 | + k = count + 1; | |
753 | + edge new_edge = p; // create an edge with a vector of points3d on the edge | |
754 | + E.push_back(new_edge); // push the edge into the network | |
755 | + } | |
756 | + size_t numVertices = atoi(file_contents[count].c_str()); // this line in the text file gives the number of distinct vertices | |
757 | + count = count + 1; // this line of text file gives the first verrtex | |
758 | + | |
759 | + for (size_t i = 0; i < numVertices; i++) { | |
760 | + vertex new_vertex = std::split(file_contents[count], ' '); | |
761 | + V.push_back(new_vertex); | |
762 | + count += atoi(file_contents[count + 1].c_str()) + 2; // Skip number of edge ids + 2 to point to the next vertex | |
763 | + } | |
764 | + } | |
765 | +*/ | |
766 | + // load network from NWT files | |
767 | + void load_nwt(std::string filename) { | |
768 | + int dims[2]; // number of vertex, number of edges | |
769 | + read_nwt_header(filename, &dims[0]); // read header | |
770 | + std::ifstream file; | |
771 | + file.open(filename.c_str(), std::ios::in | std::ios::binary); // skip header information. | |
772 | + file.seekg(14 + 58 + 4 + 4, file.beg); | |
773 | + vertex v; | |
774 | + for (int i = 0; i < dims[0]; i++) { // for every vertex, read vertex, add to network. | |
775 | + file >> v; | |
776 | + std::cerr << v.str() << std::endl; | |
777 | + V.push_back(v); | |
778 | + } | |
779 | + | |
780 | + std::cout << std::endl; | |
781 | + for (int i = 0; i < dims[1]; i++) { // for every edge, read edge, add to network. | |
782 | + edge e; | |
783 | + file >> e; | |
784 | + std::cerr << e.str() << std::endl; | |
785 | + E.push_back(e); | |
786 | + } | |
787 | + file.close(); | |
788 | + } | |
789 | + | |
790 | + // save network to NWT files | |
791 | + void save_nwt(std::string filename) { | |
792 | + write_nwt_header(filename); | |
793 | + std::ofstream file; | |
794 | + file.open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app); ///since we have written the header we are not appending. | |
795 | + for (int i = 0; i < V.size(); i++) { // look through the Vertices and write each one. | |
796 | + file << V[i]; | |
797 | + } | |
798 | + for (int i = 0; i < E.size(); i++) { // loop through the Edges and write each one. | |
799 | + file << E[i]; | |
800 | + } | |
801 | + file.close(); | |
802 | + } | |
803 | + | |
804 | + /// NWT format functions | |
805 | + void read_nwt_header(std::string filename, int *dims) { | |
806 | + char magicString[14]; // id | |
807 | + char desc[58]; // description | |
808 | + int hNumVertices; // #vert | |
809 | + int hNumEdges; // #edges | |
810 | + std::ifstream file; // create stream | |
811 | + file.open(filename.c_str(), std::ios::in | std::ios::binary); | |
812 | + file.read(reinterpret_cast<char*>(&magicString[0]), 14); // read the file id. | |
813 | + file.read(reinterpret_cast<char*>(&desc[0]), 58); // read the description | |
814 | + file.read(reinterpret_cast<char*>(&hNumVertices), sizeof(int)); // read the number of vertices | |
815 | + file.read(reinterpret_cast<char*>(&hNumEdges), sizeof(int)); // read the number of edges | |
816 | + file.close(); // close the file. | |
817 | + dims[0] = hNumVertices; // fill the returned reference. | |
818 | + dims[1] = hNumEdges; | |
819 | + } | |
820 | + void write_nwt_header(std::string filename) { | |
821 | + std::string magicString = "nwtFileFormat "; // identifier for the file. | |
822 | + std::string desc = "fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata"; | |
823 | + int hNumVertices = V.size(); // int byte header storing the number of vertices in the file | |
824 | + int hNumEdges = E.size(); // int byte header storing the number of edges. | |
825 | + std::ofstream file; | |
826 | + file.open(filename.c_str(), std::ios::out | std::ios::binary); | |
827 | + std::cout << hNumVertices << " " << hNumEdges << std::endl; | |
828 | + file.write(reinterpret_cast<const char*>(&magicString.c_str()[0]), 14); // write the file id | |
829 | + file.write(reinterpret_cast<const char*>(&desc.c_str()[0]), 58); // write the description | |
830 | + file.write(reinterpret_cast<const char*>(&hNumVertices), sizeof(int)); // write #vert. | |
831 | + file.write(reinterpret_cast<const char*>(&hNumEdges), sizeof(int)); // write #edges | |
832 | + file.close(); | |
833 | + } | |
834 | + | |
835 | + // output the network as a string | |
836 | + std::string str() { | |
837 | + std::stringstream ss; | |
838 | + size_t nv = V.size(); | |
839 | + size_t ne = E.size(); | |
840 | + ss << "Node (" << nv << ")--------" << std::endl; | |
841 | + for (size_t i = 0; i < nv; i++) | |
842 | + ss << "\t" << i << V[i].str() << std::endl; | |
843 | + ss << "Edge (" << ne << ")--------" << std::endl; | |
844 | + for (size_t i = 0; i < ne; i++) | |
845 | + ss << "\t" << i << E[i].str() << std::endl; | |
846 | + | |
847 | + return ss.str(); | |
848 | + } | |
849 | + | |
850 | + // get a string of edges | |
851 | + std::string strTxt(std::vector<std::vector<T> > p) { | |
852 | + std::stringstream ss; | |
853 | + std::stringstream oss; | |
854 | + size_t num = p.size(); | |
855 | + for (size_t i = 0; i < p; i++) { | |
856 | + ss.str(std::string()); | |
857 | + for (size_t j = 0; j < 3; j++) | |
858 | + ss << p[i][j]; | |
859 | + ss << "\n"; | |
860 | + } | |
861 | + | |
862 | + return ss.str(); | |
863 | + } | |
864 | + | |
865 | + // removes specified character from string | |
866 | + void removeCharsFromString(std::string &str, char* charsToRemove) { | |
867 | + for (size_t i = 0; i < strlen(charsToRemove); i++) | |
868 | + str.erase((remove(str.begin(), str.end(), charsToRemove[i])), str.end()); | |
869 | + } | |
870 | + | |
871 | + // exports network to txt file | |
872 | + void to_txt(std::string filename) { | |
873 | + std::ofstream ofs(filename.c_str(), std::ofstream::out | std::ofstream::app); | |
874 | + | |
875 | + ofs << (E.size()).str() << "\n"; | |
876 | + for (size_t i = 0; i < E.size(); i++) { | |
877 | + std::string str; | |
878 | + ofs << (E[i].size()).str() << "\n"; | |
879 | + str = E[i].strTxt(); | |
880 | + ofs << str << "\n"; | |
881 | + } | |
882 | + for (size_t i = 0; i < V.size(); i++) { | |
883 | + std::string str; | |
884 | + str = V[i].str(); | |
885 | + char temp[4] = "[],"; | |
886 | + removeCharsFromString(str, temp); | |
887 | + ofs << str << "\n"; | |
888 | + } | |
889 | + ofs.close(); | |
890 | + } | |
891 | + | |
892 | + | |
893 | + /// advanced operations | |
894 | + // adding a fiber to current network | |
895 | + // prior information: attaching fiber "f" and point "p" information, if "order" = 0 means the first point on fiber "e" is the attaching one (others mean the last point) | |
896 | + // "add" = 0 means replace the point on fiber "e" which is to be attached to the point on current fiber, "add" = 1 means add that one. Default "add" = 0 | |
897 | + // ********** we don't accept that one fiber has only 3 points on it, reorder if this happens ********** | |
898 | + void add_fiber(edge e, size_t f, size_t p, size_t order, size_t add = 0) { | |
899 | + size_t num = E[f].size(); // get the number of points on this fiber | |
900 | + size_t num1 = p + 1; // first "half" | |
901 | + size_t num2 = num - p; // second "half" | |
902 | + size_t id = p; // split point on the fiber that attach to | |
903 | + size_t ne = e.size(); | |
904 | + | |
905 | + // if a new fiber only has points that less than 4, either add or change (default) an attaching point | |
906 | + if (num1 < 4) | |
907 | + id = 0; | |
908 | + else if (num2 < 4) | |
909 | + id = num - 1; | |
910 | + | |
911 | + // check to see whether it needs to replace/add the joint point | |
912 | + if (add == 0) { // change the point | |
913 | + if (order == 0) { | |
914 | + e[0] = E[f][id]; | |
915 | + e.set_r(0, r(f, id)); | |
916 | + } | |
917 | + else { | |
918 | + e[ne - 1] = E[f][id]; | |
919 | + e.set_r(ne - 1, r(f, id)); | |
920 | + } | |
921 | + } | |
922 | + else { // add a point if necessary | |
923 | + if (order == 0) { | |
924 | + if (e[0] == E[f][id]) // if they share the same joint point, make sure radii are consistent | |
925 | + e.set_r(0, r(f, id)); | |
926 | + else | |
927 | + e.insert(0, E[f][id], r(f, id)); | |
928 | + } | |
929 | + else { | |
930 | + if (e[ne - 1] == E[f][id]) | |
931 | + e.set_r(ne - 1, r(f, id)); | |
932 | + else | |
933 | + e.insert(ne - 1, E[f][id], r(f, id)); | |
934 | + } | |
935 | + } | |
936 | + | |
937 | + std::vector<edge> tmp_edge = E[f].split(id); // check and split | |
938 | + // current one hasn't been splitted | |
939 | + if (tmp_edge.size() == 1) { | |
940 | + if (id == 0) { // stitch location is the starting point of current edge | |
941 | + if (V[E[f].v[0]].e[0].size() + V[E[f].v[0]].e[1].size() > 1) { // branching point | |
942 | + if (order == 0) { | |
943 | + V[E[f].v[0]].e[0].push_back(E.size()); | |
944 | + edge new_edge(e); | |
945 | + new_edge.v[0] = E[f].v[0]; // set the starting and ending points for the new edge | |
946 | + new_edge.v[1] = V.size(); | |
947 | + E.push_back(new_edge); | |
948 | + vertex new_vertex = e[e.size() - 1]; | |
949 | + new_vertex.e[1].push_back(V.size()); // set the incoming edge for the new point | |
950 | + V.push_back(new_vertex); | |
951 | + } | |
952 | + else { | |
953 | + V[E[f].v[0]].e[1].push_back(E.size()); | |
954 | + edge new_edge(e); | |
955 | + new_edge.v[0] = V.size(); // set the starting and ending points for the new edge | |
956 | + new_edge.v[1] = E[f].v[0]; | |
957 | + E.push_back(new_edge); | |
958 | + vertex new_vertex = e[e.size() - 1]; | |
959 | + new_vertex.e[0].push_back(V.size()); // set the outgoing edge for the new point | |
960 | + V.push_back(new_vertex); | |
961 | + } | |
962 | + } | |
963 | + else { // not branching point | |
964 | + size_t k = E[f].v[0]; // get the index of the starting point on current edge | |
965 | + vertex new_vertex; | |
966 | + edge new_edge; | |
967 | + if (order == 0) { | |
968 | + new_vertex = e[e.size() - 1]; | |
969 | + new_edge = E[f].concatenate(e, 0, 0); | |
970 | + } | |
971 | + else { | |
972 | + new_vertex = e[0]; | |
973 | + new_edge = E[f].concatenate(e, 0, e.size() - 1); | |
974 | + } | |
975 | + new_vertex.e[0].push_back(f); | |
976 | + new_edge.v[1] = E[f].v[1]; // set starting and ending points for the new concatenated edge | |
977 | + new_edge.v[0] = E[f].v[0]; | |
978 | + V[k] = new_vertex; | |
979 | + E[f] = new_edge; | |
980 | + } | |
981 | + } | |
982 | + else { // stitch location is the ending point of current edge | |
983 | + if (V[E[f].v[1]].e[0].size() + V[E[f].v[1]].e[1].size() > 1) { // branching point | |
984 | + if (order == 0) { | |
985 | + V[E[f].v[1]].e[0].push_back(E.size()); | |
986 | + edge new_edge(e); | |
987 | + new_edge.v[0] = E[f].v[1]; // set the starting and ending points for the new edge | |
988 | + new_edge.v[1] = V.size(); | |
989 | + E.push_back(new_edge); | |
990 | + vertex new_vertex = e[e.size() - 1]; | |
991 | + new_vertex.e[1].push_back(V.size()); // set the incoming edge for the new point | |
992 | + V.push_back(new_vertex); | |
993 | + } | |
994 | + else { | |
995 | + V[E[f].v[1]].e[1].push_back(E.size()); | |
996 | + edge new_edge(e); | |
997 | + new_edge.v[0] = V.size(); // set the starting and ending points for the new edge | |
998 | + new_edge.v[1] = E[f].v[1]; | |
999 | + E.push_back(new_edge); | |
1000 | + vertex new_vertex = e[e.size() - 1]; | |
1001 | + new_vertex.e[0].push_back(V.size()); // set the outgoing edge for the new point | |
1002 | + V.push_back(new_vertex); | |
1003 | + } | |
1004 | + } | |
1005 | + else { // not branching point | |
1006 | + size_t k = E[f].v[1]; // get the index of the ending point on current edge | |
1007 | + vertex new_vertex; | |
1008 | + edge new_edge; | |
1009 | + if (order == 0) { | |
1010 | + new_vertex = e[e.size() - 1]; | |
1011 | + new_edge = E[f].concatenate(e, num - 1, 0); | |
1012 | + } | |
1013 | + else { | |
1014 | + new_vertex = e[0]; | |
1015 | + new_edge = E[f].concatenate(e, num - 1, e.size() - 1); | |
1016 | + } | |
1017 | + new_vertex.e[1].push_back(f); | |
1018 | + new_edge.v[1] = E[f].v[1]; // set starting and ending points for the new concatenated edge | |
1019 | + new_edge.v[0] = E[f].v[0]; | |
1020 | + V[k] = new_vertex; | |
1021 | + E[f] = new_edge; | |
1022 | + } | |
1023 | + } | |
1024 | + } | |
1025 | + // current one has been splitted | |
1026 | + else { | |
1027 | + vertex new_vertex = E[f][id]; | |
1028 | + V.push_back(new_vertex); | |
1029 | + tmp_edge[0].v[0] = E[f].v[0]; | |
1030 | + tmp_edge[0].v[1] = V.size() - 1; // set the ending point of the first half edge | |
1031 | + tmp_edge[1].v[0] = V.size() - 1; // set the starting point of the second half edge | |
1032 | + tmp_edge[1].v[1] = E[f].v[1]; | |
1033 | + edge tmp(E[f]); | |
1034 | + E[f] = tmp_edge[0]; // replace current edge by the first half edge | |
1035 | + E.push_back(tmp_edge[1]); | |
1036 | + V[V.size() - 1].e[0].push_back(E.size() - 1); // set the incoming and outgoing edges for the splitted point | |
1037 | + V[V.size() - 1].e[1].push_back(f); // push "f" fiber as an incoming edge for the splitted point | |
1038 | + for (size_t i = 0; i < V[tmp.v[1]].e[1].size(); i++) // set the incoming edge for the original ending vertex | |
1039 | + if (V[tmp.v[1]].e[1][i] == f) | |
1040 | + V[tmp.v[1]].e[1][i] = E.size() - 1; | |
1041 | + | |
1042 | + if (order == 0) { | |
1043 | + e.v[0] = V.size() - 1; // set the starting and ending points for the new edge | |
1044 | + e.v[1] = V.size(); | |
1045 | + V[V.size() - 1].e[0].push_back(E.size()); // we assume "flow" flows from starting point to ending point! | |
1046 | + new_vertex = e[e.size() - 1]; // get the ending point on the new edge | |
1047 | + E.push_back(e); | |
1048 | + V.push_back(new_vertex); | |
1049 | + V[V.size() - 1].e[1].push_back(E.size() - 1); | |
1050 | + } | |
1051 | + else { | |
1052 | + e.v[0] = V.size(); // set the starting and ending points for the new edge | |
1053 | + e.v[1] = V.size() - 1; | |
1054 | + V[V.size() - 1].e[1].push_back(E.size()); | |
1055 | + new_vertex = e[0]; // get the ending point on the new edge | |
1056 | + E.push_back(e); | |
1057 | + V.push_back(new_vertex); | |
1058 | + V[V.size() - 1].e[0].push_back(E.size() - 1); | |
1059 | + } | |
1060 | + } | |
1061 | + } | |
1062 | + | |
1063 | + // THIS IS FOR PAVEL | |
1064 | + // @param "e" is the edge that is to be stitched | |
1065 | + // @param "f" is the index of edge that is to be stiched to | |
1066 | + void stitch(edge e, size_t f) { | |
1067 | + network<T> A = (*this); // make a copy of current network | |
1068 | + | |
1069 | + T* query_point = new T[3]; | |
1070 | + for (size_t k = 0; k < 3; k++) | |
1071 | + query_point[k] = e[0][k]; // we assume the first one is the one to be stitched | |
1072 | + | |
1073 | + size_t num = A.E[f].size(); // get the number of points on edge "f" | |
1074 | + T* reference_point = (T*)malloc(sizeof(T) * num * 3); | |
1075 | + A.E[f].edge_to_array(reference_point); | |
1076 | + size_t max_tree_level = 3; | |
1077 | + | |
1078 | + stim::kdtree<T, 3> kdt; // initialize a tree | |
1079 | + kdt.create(reference_point, num, max_tree_level); // build a tree | |
1080 | + | |
1081 | + T* dist = new T[1]; | |
1082 | + size_t* nnIdx = new size_t[1]; | |
1083 | + | |
1084 | +#ifdef __CUDACC__ | |
1085 | + kdt.search(query_point, 1, nnIdx, dist); // search for nearest neighbor | |
1086 | +#else | |
1087 | + kdt.cpu_search(query_point, 1, nnIdx, dist);// search for nearest neighbor | |
1088 | +#endif | |
1089 | + add_fiber(e, f, nnIdx[0], 0, 1); | |
1090 | + | |
1091 | + free(reference_point); | |
1092 | + delete(dist); | |
1093 | + delete(nnIdx); | |
1094 | + } | |
1095 | + | |
1096 | + // split current network at "idx" location on edge "f" | |
1097 | + network<T> split(size_t f, size_t idx) { | |
1098 | + size_t num = E.size(); | |
1099 | + | |
1100 | + if (f >= num) { // outside vector size | |
1101 | + } | |
1102 | + else { | |
1103 | + if (idx <= 0 || idx >= num - 1) { // can't split at this position | |
1104 | + } | |
1105 | + else { | |
1106 | + std::vector<edge> list; // a list of edges | |
1107 | + list = E[f].split(idx); // split in tems of edges | |
1108 | + // first segment replaces original one | |
1109 | + edge new_edge = list[0]; // new edge | |
1110 | + edge tmp(E[f]); // temp edge | |
1111 | + new_edge.v[0] = E[f].v[0]; // copy starting point | |
1112 | + new_edge.v[1] = V.size(); // set ending point | |
1113 | + E[f] = new_edge; // replacement | |
1114 | + vertex new_vertex(new_edge[idx]); | |
1115 | + new_vertex.e[1].push_back(f); // incoming edge for the new vertex | |
1116 | + new_vertex.e[0].push_back(E.size()); // outgoing edge for the new vertex | |
1117 | + | |
1118 | + // second segment gets newly push back | |
1119 | + new_edge = list[1]; // new edge | |
1120 | + new_edge.v[1] = tmp.v[1]; // copy ending point | |
1121 | + new_edge.v[0] = V.size(); // set starting point | |
1122 | + size_t n = V[tmp.v[1]].e[1].size(); | |
1123 | + for (size_t i = 0; i < n; i++) { | |
1124 | + if (V[tmp.v[1]].e[1][i] == f) { | |
1125 | + V[tmp.v[1]].e[1][i] = E.size(); | |
1126 | + break; | |
1127 | + } | |
1128 | + } | |
1129 | + | |
1130 | + V.push_back(new_vertex); | |
1131 | + E.push_back(new_edge); | |
1132 | + } | |
1133 | + } | |
1134 | + | |
1135 | + return (*this); | |
1136 | + } | |
1137 | + | |
1138 | + // resample current network | |
1139 | + network<T> resample(T spacing) { | |
1140 | + stim::network<T> result; | |
1141 | + | |
1142 | + result.V = V; // copy vertices | |
1143 | + size_t num = E.size(); // get the number of edges | |
1144 | + result.E.resize(num); | |
1145 | + | |
1146 | + for (size_t i = 0; i < num; i++) | |
1147 | + result.E[i] = E[i].resample(spacing); | |
1148 | + | |
1149 | + return result; | |
1150 | + } | |
1151 | + | |
1152 | + // copy the point cload representing the centerline for the network into an array | |
1153 | + void centerline_cloud(T* dst) { | |
1154 | + size_t p; // store the current edge point | |
1155 | + size_t P; // store the number of points in an edge | |
1156 | + size_t t = 0; // index in to the output array of points | |
1157 | + size_t num = E.size(); | |
1158 | + for (size_t i = 0; i < num; i++) { | |
1159 | + P = E[i].size(); | |
1160 | + for (p = 0; p < P; p++) { | |
1161 | + dst[t * 3 + 0] = E[i][p][0]; | |
1162 | + dst[t * 3 + 1] = E[i][p][1]; | |
1163 | + dst[t * 3 + 2] = E[i][p][2]; | |
1164 | + t++; | |
1165 | + } | |
1166 | + } | |
1167 | + } | |
1168 | + | |
1169 | + // subdivide current network and represent as a explicit undirected graph | |
1170 | + network<T> to_graph() { | |
1171 | + std::vector<size_t> OI; // a list of original vertex index | |
1172 | + std::vector<size_t> NI; // a list of new vertex index | |
1173 | + std::vector<edge> nE; // a list of new edge | |
1174 | + std::vector<vertex> nV; // a list of new vector | |
1175 | + size_t id = 0; // temp vertex index | |
1176 | + size_t n = E.size(); | |
1177 | + | |
1178 | + for (size_t i = 0; i < n; i++) { | |
1179 | + if (E[i].size() == 2) { // if current edge only contain 2 points, can't be subdivided | |
1180 | + stim::centerline<T> line; | |
1181 | + for (size_t k = 0; k < 2; k++) // copy points on current network | |
1182 | + line.push_back(E[i][k]); | |
1183 | + | |
1184 | + edge new_edge(line); // construct edge based on current centerline | |
1185 | + | |
1186 | + for (size_t k = 0; k < 2; k++) { | |
1187 | + vertex new_vertex = new_edge[k]; | |
1188 | + id = E[i].v[k]; // get the starting/ending point index | |
1189 | + std::vector<size_t>::iterator pos = std::find(OI.begin(), OI.end(), id); // search this index through the list of new vertex index and see whether it appears | |
1190 | + if (pos == OI.end()) { // current vertex hasn't been pushed back | |
1191 | + OI.push_back(id); // add current vertex to the original list | |
1192 | + NI.push_back(nV.size()); // add new index | |
1193 | + | |
1194 | + new_vertex.e[k].push_back(nE.size()); // set outgoing/incoming edge for the new vertex | |
1195 | + new_edge.v[k] = nV.size(); // set the starting/ending vertex for the new edge | |
1196 | + nV.push_back(new_vertex); | |
1197 | + } | |
1198 | + else { // current vertex has been pushed back before | |
1199 | + int d = std::distance(OI.begin(), pos); | |
1200 | + new_edge.v[k] = NI[d]; | |
1201 | + nV[NI[d]].e[k].push_back(nE.size()); | |
1202 | + } | |
1203 | + } | |
1204 | + | |
1205 | + nE.push_back(new_edge); | |
1206 | + | |
1207 | + // copy radii information | |
1208 | + nE[nE.size() - 1].set_r(0, E[i].r(0)); | |
1209 | + nE[nE.size() - 1].set_r(1, E[i].r(1)); | |
1210 | + } | |
1211 | + else { // if current edge contain at least 3 points, can be subdivided | |
1212 | + size_t num = E[i].size(); | |
1213 | + for (size_t j = 0; j < num - 1; j++) { | |
1214 | + stim::centerline<T> line; | |
1215 | + for (size_t k = 0; k < 2; k++) // copy points on current network | |
1216 | + line.push_back(E[i][k]); | |
1217 | + | |
1218 | + edge new_edge(line); | |
1219 | + if (j == 0) { // for edge that contains original starting point | |
1220 | + vertex new_vertex = new_edge[0]; | |
1221 | + id = E[i].v[0]; | |
1222 | + std::vector<size_t>::iterator pos = std::find(OI.begin(), OI.end(), id); | |
1223 | + if (pos == OI.end()) { // new vertex | |
1224 | + OI.push_back(id); | |
1225 | + NI.push_back(nV.size()); | |
1226 | + | |
1227 | + new_vertex.e[0].push_back(nE.size()); | |
1228 | + new_edge.v[0] = nV.size(); | |
1229 | + nV.push_back(new_vertex); | |
1230 | + } | |
1231 | + else { | |
1232 | + int d = std::distance(OI.begin(), pos); | |
1233 | + new_edge.v[0] = NI[d]; | |
1234 | + nV[NI[d]].e[0].push_back(nE.size()); | |
1235 | + } | |
1236 | + | |
1237 | + new_vertex = new_edge[1]; | |
1238 | + new_vertex.e[1].push_back(nE.size()); | |
1239 | + new_edge.v[1] = nV.size(); | |
1240 | + nV.push_back(new_vertex); | |
1241 | + nE.push_back(new_edge); | |
1242 | + } | |
1243 | + | |
1244 | + else if (j == E[i].size() - 2) {// for edge that contains original ending point | |
1245 | + vertex new_vertex = new_edge[1]; | |
1246 | + nV[nV.size() - 1].e[0].push_back(nE.size()); | |
1247 | + new_edge.v[0] = nV.size() - 1; | |
1248 | + | |
1249 | + id = E[i].v[1]; // get ending vertex index | |
1250 | + std::vector<size_t>::iterator pos = std::find(OI.begin(), OI.end(), id); | |
1251 | + if (pos == OI.end()) { | |
1252 | + OI.push_back(id); | |
1253 | + NI.push_back(nV.size()); | |
1254 | + | |
1255 | + new_vertex.e[1].push_back(nE.size()); | |
1256 | + new_edge.v[1] = nV.size(); | |
1257 | + nV.push_back(new_vertex); | |
1258 | + } | |
1259 | + else { | |
1260 | + int d = std::distance(OI.begin(), pos); | |
1261 | + new_edge.v[1] = NI[d]; | |
1262 | + nV[NI[d]].e[1].push_back(nE.size()); | |
1263 | + } | |
1264 | + | |
1265 | + nE.push_back(new_edge); | |
1266 | + } | |
1267 | + | |
1268 | + else { // for edge that doesn't contain original ending point | |
1269 | + vertex new_vertex = new_edge[1]; | |
1270 | + | |
1271 | + // push back the latter one on that segment | |
1272 | + nV[nV.size() - 1].e[0].push_back(nE.size()); | |
1273 | + new_vertex.e[1].push_back(nE.size()); | |
1274 | + new_edge.v[0] = nV.size() - 1; | |
1275 | + new_edge.v[1] = nV.size(); | |
1276 | + nV.push_back(new_vertex); | |
1277 | + nE.push_back(new_edge); | |
1278 | + } | |
1279 | + | |
1280 | + // copy radii information | |
1281 | + nE[nE.size() - 1].set_r(0, E[i].r(j)); | |
1282 | + nE[nE.size() - 1].set_r(1, E[i].r(j + 1)); | |
1283 | + } | |
1284 | + } | |
1285 | + } | |
1286 | + | |
1287 | + stim::network<T> result(nE, nV); | |
1288 | + | |
1289 | + return result; | |
1290 | + } | |
1291 | + | |
1292 | + // this function compares two networks and returns the percentage of the current network that is missing from "A" | |
1293 | + // @param "A" is the network to compare to - the field is generated for A | |
1294 | + // @param "sigma" is the user-defined tolerance value - smaller values provide a stricter comparison | |
1295 | + // @param "device" is the GPU device to use - default no GPU provided | |
1296 | + network<T> compare(network<T> A, T sigma, int device = -1) { | |
1297 | + network<T> R; // generate a network storing the result of comparison | |
1298 | + R = (*this); // initialize to current network | |
1299 | + | |
1300 | + size_t num = A.total_points(); | |
1301 | + T* c = (T*)malloc(sizeof(T) * num * 3); // allocate memory for the centerline of A | |
1302 | + | |
1303 | + A.copy_to_array(c); // copy points in A to a 1D array | |
1304 | + | |
1305 | + size_t max_tree_level = 3; // set max tree level parameter to 3 | |
1306 | + | |
1307 | +#ifdef __CUDACC__ | |
1308 | + cudaSetDevice(device); | |
1309 | + stim::kdtree<T, 3> kdt; // initialize a tree object | |
1310 | + | |
1311 | + kdt.create(c, num, max_tree_level); // build tree | |
1312 | + | |
1313 | + for (size_t i = 0; i < R.E.size(); i++) { | |
1314 | + size_t n = R.E[i].size(); // the number of points in current edge | |
1315 | + T* query_point = new T[3 * n]; // allocate memory for points | |
1316 | + T* m1 = new T[n]; // allocate memory for metrics | |
1317 | + T* dists = new T[n]; // allocate memory for distances | |
1318 | + size_t* nnIdx = new size_t[n]; // allocate memory for indices | |
1319 | + | |
1320 | + T* d_dists; | |
1321 | + T* d_m1; | |
1322 | + cudaMalloc((void**)&d_dists, n * sizeof(T)); | |
1323 | + cudaMalloc((void**)&d_m1, n * sizeof(T)); | |
1324 | + | |
1325 | + edge_to_array(query_point, R.E[i]); | |
1326 | + kdt.search(query_point, n, nnIdx, dists); | |
1327 | + | |
1328 | + cudaMemcpy(d_dists, dists, n * sizeof(T), cudaMemcpyHostToDevice); // copy dists from host to device | |
1329 | + | |
1330 | + // configuration parameters | |
1331 | + size_t threads = (1024 > n) ? n : 1024; | |
1332 | + size_t blocks = n / threads + (n % threads) ? 1 : 0; | |
1333 | + | |
1334 | + find_metric<< <blocks, threads >> >(d_m1, n, d_dists, sigma); // calculate the metric value based on the distance | |
1335 | + | |
1336 | + cudaMemcpy(m1, d_m1, n * sizeof(T), cudaMemcpyDeviceToHost); | |
1337 | + | |
1338 | + for (size_t j = 0; j < n; j++) { | |
1339 | + R.E[i].set_r(j, m1[j]); | |
1340 | + } | |
1341 | + } | |
1342 | + | |
1343 | +#else // if there is any GPU device, use CPU - much slower | |
1344 | + stim::kdtree<T, 3> kdt; | |
1345 | + kdt.create(c, num, max_tree_level); | |
1346 | + | |
1347 | + for (size_t i = 0; i < R.E.size(); i++) { // for each edge in A | |
1348 | + | |
1349 | + size_t n = R.E[i].size(); // the number of points in current edge | |
1350 | + T* query = new T[3 * n]; | |
1351 | + T* m1 = new T[n]; | |
1352 | + T* dists = new T[n]; | |
1353 | + size_t* nnIdx = new size_t[n]; | |
1354 | + | |
1355 | + edge_to_array(query, R.E[i]); | |
1356 | + | |
1357 | + kdt.cpu_search(query, n, nnIdx, dists); // find the distance between A and the current network | |
1358 | + | |
1359 | + for (size_t j = 0; j < n; j++) { | |
1360 | + m1[j] = 1.0f - gaussian(dists[j], sigma); // calculate the metric value based on the distance | |
1361 | + R.E[i].set_r(j, m1[j]); // set the error for the second point in the segment | |
1362 | + } | |
1363 | + } | |
1364 | +#endif | |
1365 | + return R; | |
1366 | + } | |
1367 | + | |
1368 | + // this function compares two splitted networks to yield a mapping relationship between them according to nearest neighbor principle | |
1369 | + // @param "B" is the template network | |
1370 | + // @param "C" is the mapping relationship: C[e1] = _e1 means edge "e1" in current network is mapped to edge "_e1" in "B" | |
1371 | + // @param "device" is the GPU device that user want to use | |
1372 | + // @param "threshold" is to control mapping tolerance (threshold) | |
1373 | + void mapping(network<T> B, std::vector<size_t> &C, T threshold, int device = -1) { | |
1374 | + network<T> A; // generate a network storing the result of the comparison | |
1375 | + A = (*this); | |
1376 | + | |
1377 | + size_t nA = A.E.size(); // the number of edges in A | |
1378 | + size_t nB = B.E.size(); // the number of edges in B | |
1379 | + | |
1380 | + C.resize(A.E.size()); | |
1381 | + | |
1382 | + size_t num = B.total_points(); // set the number of points | |
1383 | + T* c = (T*)malloc(sizeof(T) * num * 3); | |
1384 | + | |
1385 | + B.copy_to_array(c); | |
1386 | + | |
1387 | + size_t max_tree_level = 3; | |
1388 | + | |
1389 | +#ifdef __CUDACC__ | |
1390 | + cudaSetDevice(device); | |
1391 | + stim::kdtree<T, 3> kdt; // initialize a tree | |
1392 | + | |
1393 | + kdt.create(c, num, max_tree_level); // build a tree | |
1394 | + | |
1395 | + T M = 0.0f; | |
1396 | + for (size_t i = 0; i < nA; i++) { | |
1397 | + M = A.ar(i); // get the average metric of edge "i" | |
1398 | + if (M > threshold) | |
1399 | + C[i] = UINT_MAX; // set to MAX | |
1400 | + else { | |
1401 | + T* query_point = new T[3]; | |
1402 | + T* dist = new T[1]; | |
1403 | + size_t* nnIdx = new size_t[1]; | |
1404 | + | |
1405 | + for (size_t k = 0; k < 3; k++) | |
1406 | + query_point[k] = A.E[i][A.E[i].size() / 2][k]; // search by the middle one, risky? | |
1407 | + kdt.search(query_point, 1, nnIdx, dist); | |
1408 | + | |
1409 | + size_t id = 0; | |
1410 | + size_t sum = 0; | |
1411 | + for (size_t j = 0; j < nB; j++) { // low_band | |
1412 | + sum += B.E[j].size(); | |
1413 | + if (nnIdx[0] < sum) { | |
1414 | + C[i] = id; | |
1415 | + break; | |
1416 | + } | |
1417 | + id++; | |
1418 | + } | |
1419 | + } | |
1420 | + } | |
1421 | + | |
1422 | +#else | |
1423 | + stim::kdtree<T, 3> kdt; | |
1424 | + kdt.create(c, num, max_tree_level); | |
1425 | + T* dist = new T[1]; | |
1426 | + size_t* nnIdx = new size_t[1]; | |
1427 | + | |
1428 | + stim::vec3<T> p; | |
1429 | + T* query_point = new T[3]; | |
1430 | + | |
1431 | + T M = 0.0f; | |
1432 | + for (size_t i = 0; i < nA; i++) { | |
1433 | + M = A.ar(i); | |
1434 | + if (M > threshold) | |
1435 | + C[i] = UINT_MAX; | |
1436 | + else { | |
1437 | + p = A.E[i][A.E[i].size() / 2]; | |
1438 | + for (size_t k = 0; k < 3; k++) | |
1439 | + query_point[k] = p[k]; | |
1440 | + kdt.cpu_search(query_point, 1, nnIdx, dist); | |
1441 | + | |
1442 | + size_t id = 0; | |
1443 | + size_t sum = 0; | |
1444 | + for (size_t j = 0; j < nB; j++) { | |
1445 | + sum += B.E[j].size(); | |
1446 | + if (nnIdx[0] < sum) { | |
1447 | + C[i] = id; | |
1448 | + break; | |
1449 | + } | |
1450 | + id++; | |
1451 | + } | |
1452 | + } | |
1453 | + } | |
1454 | +#endif | |
1455 | + } | |
1456 | + }; | |
1457 | + | |
1458 | +} | |
1459 | + | |
1460 | + | |
1461 | +#endif | ... | ... |
stim/biomodels/test.nwt deleted
No preview for this file type
stim/cuda/branch_detection.cuh
... | ... | @@ -15,14 +15,14 @@ find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y, i |
15 | 15 | { |
16 | 16 | float sigma = 2.0; |
17 | 17 | unsigned int conn = 7; |
18 | - float threshold = 40.0; | |
18 | +// float threshold = 40.0; | |
19 | 19 | float* cpuCenters = (float*) malloc(x*y*sizeof(float)); |
20 | 20 | int sizek = 7; |
21 | 21 | |
22 | - stringstream name; | |
22 | + std::stringstream name; | |
23 | 23 | |
24 | 24 | |
25 | - cpuCenters = stim::cuda::get_centers(texbufferID, texType, x, y, sizek, sigma, conn, threshold, iter); | |
25 | + cpuCenters = stim::cuda::get_centers(texbufferID, texType, x, y, sizek, sigma, conn, iter); | |
26 | 26 | cudaDeviceSynchronize(); |
27 | 27 | |
28 | 28 | ... | ... |
stim/cuda/filter.cuh
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | #include <stim/cuda/cudatools/devices.h> |
12 | 12 | #include <stim/cuda/cudatools/threads.h> |
13 | 13 | #include <stim/cuda/cuda_texture.cuh> |
14 | -#include <stim/cuda/ivote.cuh> | |
14 | +#include <stim/iVote/ivote2.cuh> | |
15 | 15 | #include <stim/cuda/arraymath.cuh> |
16 | 16 | |
17 | 17 | #define IMAD(a,b,c) ( __mul24((a), (b)) + (c) ) |
... | ... | @@ -156,7 +156,7 @@ namespace stim |
156 | 156 | |
157 | 157 | extern "C" |
158 | 158 | float * |
159 | - get_centers(GLint texbufferID, GLenum texType, int DIM_X, int DIM_Y, int sizeK, float sigma, float conn, float threshold, int iter = 0) | |
159 | + get_centers(GLint texbufferID, GLenum texType, int DIM_X, int DIM_Y, int sizeK, float sigma, float conn, int iter = 0) | |
160 | 160 | { |
161 | 161 | tx.SetTextureCoordinates(1); |
162 | 162 | tx.SetAddressMode(1, 3); |
... | ... | @@ -180,7 +180,7 @@ namespace stim |
180 | 180 | #endif |
181 | 181 | |
182 | 182 | |
183 | - stim::cuda::gpu_local_max<float>(centers, res, threshold, conn, DIM_X, DIM_Y); | |
183 | + stim::cuda::gpu_local_max<float>(centers, res, conn, DIM_X, DIM_Y); | |
184 | 184 | |
185 | 185 | cudaDeviceSynchronize(); |
186 | 186 | ... | ... |
stim/gl/gl_spider.h
... | ... | @@ -2,33 +2,46 @@ |
2 | 2 | #define STIM_GL_SPIDER_H |
3 | 3 | |
4 | 4 | //#include <GL/glew.h> |
5 | +///basic GL and Cuda libs | |
5 | 6 | #include <GL/glut.h> |
6 | 7 | #include <cuda.h> |
7 | 8 | #include <cuda_gl_interop.h> |
8 | 9 | #include <cudaGL.h> |
9 | -#include <math.h> | |
10 | + | |
11 | +///stim .*h for gl tracking and visualization | |
10 | 12 | #include <stim/gl/gl_texture.h> |
11 | -#include <stim/visualization/camera.h> | |
12 | 13 | #include <stim/gl/error.h> |
14 | +#include <stim/visualization/camera.h> | |
15 | +#include <stim/math/matrix_sq.h> | |
16 | +#include <stim/cuda/spider_cost.cuh> | |
17 | +#include <stim/visualization/glObj.h> | |
18 | +#include <stim/cuda/cudatools/glbind.h> | |
19 | + | |
20 | +///stim *.h for CUDA | |
21 | +#include <stim/cuda/cuda_texture.cuh> | |
22 | +#include <stim/cuda/cudatools.h> | |
23 | + | |
24 | +///stim *.h for branch detection | |
25 | +#include <stim/visualization/cylinder.h> | |
26 | +#include <stim/cuda/branch_detection.cuh> | |
27 | +#include <stim/visualization/gl_network.h> | |
28 | + | |
29 | +/// *.h for math | |
30 | +#include <math.h> | |
13 | 31 | #include <stim/math/vector.h> |
14 | 32 | #include <stim/math/vec3.h> |
15 | 33 | #include <stim/math/rect.h> |
16 | -#include <stim/math/matrix.h> | |
17 | 34 | #include <stim/math/constants.h> |
18 | -#include <stim/cuda/spider_cost.cuh> | |
19 | -#include <stim/cuda/cudatools/glbind.h> | |
20 | 35 | #include <stim/cuda/arraymath.cuh> |
21 | -#include <stim/cuda/cuda_texture.cuh> | |
22 | -#include <stim/cuda/cudatools.h> | |
23 | -#include <stim/cuda/ivote.cuh> | |
24 | -#include <stim/visualization/glObj.h> | |
36 | +#include <stim/math/random.h> | |
37 | + | |
38 | + | |
39 | +///c++ libs for everything | |
25 | 40 | #include <vector> |
26 | 41 | #include <stack> |
27 | -#include <stim/cuda/branch_detection.cuh> | |
28 | -#include "../../../volume-spider/glnetwork.h" | |
29 | -#include <stim/visualization/cylinder.h> | |
30 | 42 | #include <iostream> |
31 | 43 | #include <fstream> |
44 | + | |
32 | 45 | #ifdef TIMING |
33 | 46 | #include <ctime> |
34 | 47 | #include <cstdio> |
... | ... | @@ -72,7 +85,7 @@ class gl_spider // : public virtual gl_texture<T> |
72 | 85 | std::vector<float> mV; //A list of all the size vectors. |
73 | 86 | std::vector<float> lV; //A list of all the size vectors. |
74 | 87 | |
75 | - stim::matrix<float, 4> cT; //current Transformation matrix (tissue)->(texture) | |
88 | + stim::matrix_sq<float, 4> cT; //current Transformation matrix (tissue)->(texture) | |
76 | 89 | GLuint texID; //OpenGL ID for the texture to be traced |
77 | 90 | stim::vec3<float> S; //Size of a voxel in the volume. |
78 | 91 | stim::vec3<float> R; //Dimensions of the volume. |
... | ... | @@ -121,7 +134,7 @@ class gl_spider // : public virtual gl_texture<T> |
121 | 134 | std::vector< float > cM; //radius up to the current point |
122 | 135 | std::vector< float > cLen; //radius up to the current point |
123 | 136 | |
124 | - stim::glnetwork<float> nt; //network object holding the currently traced centerlines | |
137 | + stim::gl_network<float> nt; //network object holding the currently traced centerlines | |
125 | 138 | stim::glObj<float> sk; //OBJ file storing the network (identical to above) |
126 | 139 | |
127 | 140 | //consider replacing with two seed points facing opposite directions |
... | ... | @@ -1318,25 +1331,25 @@ class gl_spider // : public virtual gl_texture<T> |
1318 | 1331 | } |
1319 | 1332 | } |
1320 | 1333 | |
1321 | - ///Saves the network to a file. | |
1322 | - void | |
1323 | - saveNetwork(std::string name) | |
1324 | - { | |
1325 | - stim::glObj<float> sk1; | |
1326 | - for(int i = 0; i < nt.sizeE(); i++) | |
1327 | - { | |
1328 | - std::vector<float> cm = nt.getEdgeCenterLineMag(i); | |
1329 | - std::vector<stim::vec3< float > > ce = nt.getEdgeCenterLine(i); | |
1330 | - sk1.Begin(stim::OBJ_LINE); | |
1331 | - for(int j = 0; j < ce.size(); j++) | |
1332 | - { | |
1333 | - sk1.TexCoord(cm[j]); | |
1334 | - sk1.Vertex(ce[j][0], ce[j][1], ce[j][2]); | |
1335 | - } | |
1336 | - sk1.End(); | |
1337 | - } | |
1338 | - sk1.save(name); | |
1339 | - } | |
1334 | + ///Saves the network to a file. | |
1335 | + void | |
1336 | + saveNetwork(std::string name, int xoffset = 0, int yoffset = 0, int zoffset = 0) | |
1337 | + { | |
1338 | + stim::glObj<float> sk1; | |
1339 | + for(int i = 0; i < nt.sizeE(); i++) | |
1340 | + { | |
1341 | + std::vector<float> cm = nt.getEdgeCenterLineMag(i); | |
1342 | + std::vector<stim::vec3< float > > ce = nt.getEdgeCenterLine(i); | |
1343 | + sk1.Begin(stim::OBJ_LINE); | |
1344 | + for(int j = 0; j < ce.size(); j++) | |
1345 | + { | |
1346 | + sk1.TexCoord(cm[j]); | |
1347 | + sk1.Vertex(ce[j][0]+xoffset, ce[j][1]+yoffset, ce[j][2]+zoffset); | |
1348 | + } | |
1349 | + sk1.End(); | |
1350 | + } | |
1351 | + sk1.save(name); | |
1352 | + } | |
1340 | 1353 | |
1341 | 1354 | ///Depreciated, but might be reused later() |
1342 | 1355 | ///returns a COPY of the entire stim::glObj object. |
... | ... | @@ -1347,7 +1360,7 @@ class gl_spider // : public virtual gl_texture<T> |
1347 | 1360 | } |
1348 | 1361 | |
1349 | 1362 | ///returns a COPY of the entire stim::glnetwork object. |
1350 | - stim::glnetwork<T> | |
1363 | + stim::gl_network<T> | |
1351 | 1364 | getGLNetwork() |
1352 | 1365 | { |
1353 | 1366 | return nt; | ... | ... |
stim/iVote/ivote2.cuh
... | ... | @@ -152,13 +152,17 @@ namespace stim { |
152 | 152 | |
153 | 153 | |
154 | 154 | template<typename T> |
155 | - void cpu_ivote2(T* cpuI, unsigned int rmax, size_t x, size_t y, bool invert = false, T t = 0, int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) { | |
155 | + void cpu_ivote2(T* cpuI, unsigned int rmax, size_t x, size_t y, float &gpu_time, bool invert = false, T t = 0, int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) { | |
156 | 156 | size_t bytes = x*y * sizeof(T); |
157 | 157 | T* gpuI; //allocate space on the gpu to save the input image |
158 | + | |
159 | + gpuTimer_start(); | |
158 | 160 | HANDLE_ERROR(cudaMalloc(&gpuI, bytes)); |
159 | 161 | HANDLE_ERROR(cudaMemcpy(gpuI, cpuI, bytes, cudaMemcpyHostToDevice)); //copy the image to the gpu |
160 | 162 | stim::gpu_ivote2<T>(gpuI, rmax, x, y, invert, t, iter, phi, conn, debug); //call the gpu version of the ivote |
161 | 163 | HANDLE_ERROR(cudaMemcpy(cpuI, gpuI, bytes, cudaMemcpyDeviceToHost)); //copy the output to the cpu |
164 | + | |
165 | + gpu_time = gpuTimer_end(); | |
162 | 166 | } |
163 | 167 | } |
164 | 168 | #endif |
165 | 169 | \ No newline at end of file | ... | ... |
stim/iVote/ivote2/local_max.cuh
... | ... | @@ -10,7 +10,7 @@ namespace stim{ |
10 | 10 | |
11 | 11 | // this kernel calculates the local maximum for finding the cell centers |
12 | 12 | template<typename T> |
13 | - __global__ void cuda_local_max(T* gpuCenters, T* gpuVote, int conn, int x, int y){ | |
13 | + __global__ void cuda_local_max(T* gpuCenters, T* gpuVote, int conn, int x, int y){ | |
14 | 14 | |
15 | 15 | // calculate the 2D coordinates for this current thread. |
16 | 16 | int xi = blockIdx.x * blockDim.x + threadIdx.x; |
... | ... | @@ -20,16 +20,16 @@ namespace stim{ |
20 | 20 | return; |
21 | 21 | |
22 | 22 | // convert 2D coordinates to 1D |
23 | - int i = yi * x + xi; | |
23 | + int i = yi * x + xi; | |
24 | 24 | |
25 | 25 | gpuCenters[i] = 0; //initialize the value at this location to zero |
26 | 26 | |
27 | 27 | T val = gpuVote[i]; |
28 | 28 | |
29 | - for(int xl = xi - conn; xl < xi + conn; xl++){ | |
30 | - for(int yl = yi - conn; yl < yi + conn; yl++){ | |
29 | + for(unsigned int xl = xi - conn; xl < xi + conn; xl++){ | |
30 | + for(unsigned int yl = yi - conn; yl < yi + conn; yl++){ | |
31 | 31 | if(xl >= 0 && xl < x && yl >= 0 && yl < y){ |
32 | - int il = yl * x + xl; | |
32 | + unsigned int il = yl * x + xl; | |
33 | 33 | if(gpuVote[il] > val){ |
34 | 34 | return; |
35 | 35 | } |
... | ... | @@ -47,7 +47,7 @@ namespace stim{ |
47 | 47 | } |
48 | 48 | |
49 | 49 | template<typename T> |
50 | - void gpu_local_max(T* gpuCenters, T* gpuVote, unsigned int conn, size_t x, size_t y){ | |
50 | + void gpu_local_max(T* gpuCenters, T* gpuVote, unsigned int conn, unsigned int x, unsigned int y){ | |
51 | 51 | |
52 | 52 | unsigned int max_threads = stim::maxThreadsPerBlock(); |
53 | 53 | /*dim3 threads(max_threads, 1); | ... | ... |
stim/image/image.h
... | ... | @@ -275,10 +275,10 @@ public: |
275 | 275 | int channels = cvImage.channels(); |
276 | 276 | allocate(cols, rows, channels); //allocate space for the image |
277 | 277 | size_t img_bytes = bytes(); |
278 | - unsigned char* cv_ptr = (unsigned char*)cvImage.data; | |
279 | - //if (C() == 1) //if this is a single-color image, just copy the data | |
280 | - // type_copy<T, T>(cv_ptr, img, size()); | |
281 | - memcpy(img, cv_ptr, bytes()); | |
278 | + unsigned char* cv_ptr = (unsigned char*)cvImage.data; | |
279 | + if (C() == 1) //if this is a single-color image, just copy the data | |
280 | + type_copy<unsigned char, T>(cv_ptr, img, size()); | |
281 | + //memcpy(img, cv_ptr, bytes()); | |
282 | 282 | if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving |
283 | 283 | from_opencv(cv_ptr, X(), Y()); |
284 | 284 | #else | ... | ... |
1 | +#ifndef STIM_CUDA_MEDIAN2_H | |
2 | +#define STIM_CUDA_MEDIAN2_H | |
3 | + | |
4 | +#include <iostream> | |
5 | +#include <cuda.h> | |
6 | +#include <cmath> | |
7 | +#include <algorithm> | |
8 | + | |
9 | +#ifdef USING_CUDA | |
10 | +#include "cuda_runtime.h" | |
11 | +#include "device_launch_parameters.h" | |
12 | +#include <stim/cuda/cudatools.h> | |
13 | +#endif | |
14 | + | |
15 | + | |
16 | +//#include <thrust/sort.h> | |
17 | +//#include <thrust/execution_policy.h> | |
18 | +//#include <thrust/binary_search.h> | |
19 | +//#include <thrust/device_ptr.h> | |
20 | + | |
21 | +template <typename T> | |
22 | +__device__ void cuswap ( T& a, T& b ){ | |
23 | + T c(a); | |
24 | + a=b; | |
25 | + b=c; | |
26 | +} | |
27 | + | |
28 | +namespace stim{ | |
29 | + namespace cuda{ | |
30 | + | |
31 | + template<typename T> | |
32 | + __global__ void cuda_median2(T* in, T* out, T* kernel, size_t X, size_t Y, size_t K){ | |
33 | + | |
34 | + int xi = blockIdx.x * blockDim.x + threadIdx.x; | |
35 | + int yi = blockIdx.y * blockDim.y + threadIdx.y; | |
36 | + | |
37 | + size_t Xout = X - K + 1; | |
38 | + size_t Yout = Y - K + 1; | |
39 | + int i = yi * Xout + xi; | |
40 | + | |
41 | + //return if (i,j) is outside the matrix | |
42 | + if(xi >= Xout || yi >= Yout) return; | |
43 | + | |
44 | + //scan the image, copy data from input to 2D window | |
45 | + size_t kxi, kyi; | |
46 | + for(kyi = 0; kyi < K; kyi++){ | |
47 | + for(kxi = 0; kxi < K; kxi++){ | |
48 | + kernel[i * K * K + kyi * K + kxi] = in[(yi + kyi)* X + xi + kxi]; | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + //calculate kernel radius 4 | |
53 | + int r = (K*K)/2; | |
54 | + | |
55 | + //sort the smallest half pixel values inside the window, calculate the middle one | |
56 | + size_t Ki = i * K * K; | |
57 | + | |
58 | + //sort the smallest half pixel values inside the window, calculate the middle one | |
59 | + for (int p = 0; p < r+1; p++){ | |
60 | + for(int kk = p + 1; kk < K*K; kk++){ | |
61 | + if (kernel[Ki + kk] < kernel[Ki + p]){ | |
62 | + cuswap<T>(kernel[Ki + kk], kernel[Ki + p]); | |
63 | + } | |
64 | + } | |
65 | + } | |
66 | + | |
67 | + //copy the middle pixel value inside the window to output | |
68 | + out[i] = kernel[Ki + r]; | |
69 | + | |
70 | + } | |
71 | + | |
72 | + | |
73 | + template<typename T> | |
74 | + void gpu_median2(T* gpu_in, T* gpu_out, T* gpu_kernel, size_t X, size_t Y, size_t K){ | |
75 | + | |
76 | + //get the maximum number of threads per block for the CUDA device | |
77 | + int threads_total = stim::maxThreadsPerBlock(); | |
78 | + | |
79 | + //set threads in each block | |
80 | + dim3 threads(sqrt(threads_total), sqrt(threads_total)); | |
81 | + | |
82 | + //calculate the number of blocks | |
83 | + dim3 blocks(( (X - K + 1) / threads.x) + 1, ((Y - K + 1) / threads.y) + 1); | |
84 | + | |
85 | + //call the kernel to perform median filter function | |
86 | + cuda_median2 <<< blocks, threads >>>( gpu_in, gpu_out, gpu_kernel, X, Y, K); | |
87 | + | |
88 | + } | |
89 | + | |
90 | + template<typename T> | |
91 | + void cpu_median2(T* cpu_in, T* cpu_out, size_t X, size_t Y, size_t K){ | |
92 | + | |
93 | + #ifndef USING_CUDA | |
94 | + | |
95 | + //output image width and height | |
96 | + size_t X_out = X + K -1; | |
97 | + size_t Y_out = Y + K -1; | |
98 | + | |
99 | + float* window = (float*)malloc(K * k *sizeof(float)); | |
100 | + | |
101 | + for(int i = 0; i< Y; i++){ | |
102 | + for(int j =0; j< X; j++){ | |
103 | + | |
104 | + //Pick up window elements | |
105 | + int k = 0; | |
106 | + for (int m=0; m< kernel_width; m++){ | |
107 | + for(int n = 0; n < kernel_width; n++){ | |
108 | + window[k] = in_image.at<float>(m+i, n+j); | |
109 | + k++; | |
110 | + } | |
111 | + } | |
112 | + | |
113 | + //calculate kernel radius 4 | |
114 | + r_ker = K * K/2; | |
115 | + //Order elements (only half of them) | |
116 | + for(int p = 0; p<r_ker+1; p++){ | |
117 | + //Find position of minimum element | |
118 | + for (int l = p + 1; l < size_kernel; l++){ | |
119 | + | |
120 | + if(window[l] < window[p]){ | |
121 | + float t = window[p]; | |
122 | + window[p] = window[l]; | |
123 | + window[l] = t; } | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + //Get result - the middle element | |
128 | + cpu_out[i * X_out + j] = window[r_ker]; | |
129 | + } | |
130 | + } | |
131 | + #else | |
132 | + //calculate input and out image pixels & calculate kernel size | |
133 | + size_t N_in = X * Y; //number of pixels in the input image | |
134 | + size_t N_out = (X - K + 1) * (Y - K + 1); //number of pixels in the output image | |
135 | + //size_t kernel_size = kernel_width*kernel_width; //total number of pixels in the kernel | |
136 | + | |
137 | + //allocate memory on the GPU for the array | |
138 | + T* gpu_in; //allocate device memory for the input image | |
139 | + HANDLE_ERROR( cudaMalloc( &gpu_in, N_in * sizeof(T) ) ); | |
140 | + T* gpu_kernel; //allocate device memory for the kernel | |
141 | + HANDLE_ERROR( cudaMalloc( &gpu_kernel, K * K * N_out * sizeof(T) ) ); | |
142 | + T* gpu_out; //allocate device memory for the output image | |
143 | + HANDLE_ERROR( cudaMalloc( &gpu_out, N_out * sizeof(T) ) ); | |
144 | + | |
145 | + //copy the array to the GPU | |
146 | + HANDLE_ERROR( cudaMemcpy( gpu_in, cpu_in, N_in * sizeof(T), cudaMemcpyHostToDevice) ); | |
147 | + | |
148 | + //call the GPU version of this function | |
149 | + gpu_median2<T>(gpu_in, gpu_out, gpu_kernel, X, Y, K); | |
150 | + | |
151 | + //copy the array back to the CPU | |
152 | + HANDLE_ERROR( cudaMemcpy( cpu_out, gpu_out, N_out * sizeof(T), cudaMemcpyDeviceToHost) ); | |
153 | + | |
154 | + //free allocated memory | |
155 | + cudaFree(gpu_in); | |
156 | + cudaFree(gpu_kernel); | |
157 | + cudaFree(gpu_out); | |
158 | + | |
159 | + } | |
160 | + | |
161 | + } | |
162 | + #endif | |
163 | +} | |
164 | + | |
165 | + | |
166 | +#endif | ... | ... |
stim/math/matrix.h
stim/math/matrix_sq.h
... | ... | @@ -74,6 +74,15 @@ struct matrix_sq |
74 | 74 | M[i*N+j] -= rhs; |
75 | 75 | return *this; |
76 | 76 | } |
77 | + | |
78 | + // element wise divide | |
79 | + CUDA_CALLABLE matrix_sq<T, N> operator/(T rhs) | |
80 | + { | |
81 | + for(int i=0; i<N; i++) | |
82 | + for(int j=0; j<N; j++) | |
83 | + M[i*N+j] = M[i*N+j]/rhs; | |
84 | + return *this; | |
85 | + } | |
77 | 86 | |
78 | 87 | template<typename Y> |
79 | 88 | vec<Y> operator*(vec<Y> rhs){ |
... | ... | @@ -116,6 +125,21 @@ struct matrix_sq |
116 | 125 | return ss.str(); |
117 | 126 | } |
118 | 127 | |
128 | + std::string toTensor() | |
129 | + { | |
130 | + std::stringstream ss; | |
131 | + | |
132 | + ss << (*this)(0, 0) << " "; | |
133 | + ss << (*this)(0, 1) << " "; | |
134 | + ss << (*this)(1, 1) << " "; | |
135 | + ss << (*this)(2, 0) << " "; | |
136 | + ss << (*this)(2, 1) << " "; | |
137 | + ss << (*this)(2, 2); | |
138 | + | |
139 | + return ss.str(); | |
140 | + } | |
141 | + | |
142 | + | |
119 | 143 | static matrix_sq<T, N> identity() { |
120 | 144 | matrix_sq<T, N> I; |
121 | 145 | I = 0; | ... | ... |
stim/math/spharmonics.h
... | ... | @@ -43,7 +43,7 @@ namespace stim { |
43 | 43 | } |
44 | 44 | |
45 | 45 | void setc(unsigned int l, int m, T value) { |
46 | - unsigned int c = coeff_1d(l, m); | |
46 | + u44nsigned int c = coeff_1d(l, m); | |
47 | 47 | C[c] = value; |
48 | 48 | } |
49 | 49 | |
... | ... | @@ -173,57 +173,57 @@ namespace stim { |
173 | 173 | project(sph_pts, l, m); |
174 | 174 | } |
175 | 175 | |
176 | - /// Generates a PDF describing the density distribution of points on a sphere | |
177 | - /// @param sph_pts is a list of points in cartesian coordinates | |
178 | - /// @param l is the maximum degree of the spherical harmonic function | |
179 | - /// @param m is the maximum order | |
180 | - /// @param c is the centroid of the points in sph_pts. DEFAULT 0,0,0 | |
181 | - /// @param n is the number of points of the surface of the sphere used to create the PDF. DEFAULT 1000 | |
182 | - /// @param norm, a boolean that sets where the output vectors will be normalized between 0 and 1. | |
183 | - /// @param | |
184 | - /*void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m, stim::vec3<T> c = stim::vec3<T>(0, 0, 0), unsigned int n = 1000, bool norm = true, std::vector<T> w = std::vector<T>()) | |
185 | - { | |
186 | - std::vector<double> weights; ///the weight at each point on the surface of the sphere. | |
187 | - // weights.resize(n); | |
188 | - unsigned int nP = sph_pts.size(); | |
189 | - std::vector<stim::vec3<T> > sphere = stim::Random<T>::sample_sphere(n, 1.0, stim::TAU); | |
190 | - if (w.size() < nP) | |
191 | - w = std::vector<T>(nP, 1.0); | |
192 | - | |
193 | - for (int i = 0; i < n; i++) | |
194 | - { | |
195 | - T val = 0; | |
196 | - for (int j = 0; j < nP; j++) | |
197 | - { | |
198 | - stim::vec3<T> temp = sph_pts[j] - c; | |
199 | - if (temp.dot(sphere[i]) > 0) | |
200 | - val += pow(temp.dot(sphere[i]), 4)*w[j]; | |
201 | - } | |
202 | - weights.push_back(val); | |
203 | - } | |
204 | - | |
205 | - mcBegin(l, m); //begin spherical harmonic sampling | |
206 | - | |
207 | - if (norm) | |
208 | - { | |
209 | - T min = *std::min_element(weights.begin(), weights.end()); | |
210 | - T max = *std::max_element(weights.begin(), weights.end()); | |
211 | - for (unsigned int i = 0; i < n; i++) | |
212 | - { | |
213 | - stim::vec3<T> sph = sphere[i].cart2sph(); | |
214 | - mcSample(sph[1], sph[2], (weights[i] - min) / (max - min)); | |
215 | - } | |
216 | - | |
217 | - } | |
218 | - else { | |
219 | - for (unsigned int i = 0; i < n; i++) | |
220 | - { | |
221 | - stim::vec3<T> sph = sphere[i].cart2sph(); | |
222 | - mcSample(sph[1], sph[2], weights[i]); | |
223 | - } | |
224 | - } | |
225 | - mcEnd(); | |
226 | - }*/ | |
176 | + /// Generates a PDF describing the density distribution of points on a sphere | |
177 | + /// @param sph_pts is a list of points in cartesian coordinates | |
178 | + /// @param l is the maximum degree of the spherical harmonic function | |
179 | + /// @param m is the maximum order | |
180 | + /// @param c is the centroid of the points in sph_pts. DEFAULT 0,0,0 | |
181 | + /// @param n is the number of points of the surface of the sphere used to create the PDF. DEFAULT 1000 | |
182 | + /// @param norm, a boolean that sets where the output vectors will be normalized between 0 and 1. | |
183 | + /// @param | |
184 | + void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m, stim::vec3<T> c = stim::vec3<T>(0, 0, 0), unsigned int n = 1000, bool norm = true, std::vector<T> w = std::vector<T>(), int normfactor = 1) | |
185 | + { | |
186 | + std::vector<double> weights; ///the weight at each point on the surface of the sphere. | |
187 | + // weights.resize(n); | |
188 | + unsigned int nP = sph_pts.size(); ///sph_pts is the vectors we want to fit a spehrical harmonic to. | |
189 | + std::vector<stim::vec3<T> > sphere = stim::Random<T>::sample_sphere(n, 1.0, stim::TAU); ///randomsample a sphere of radius 1 and return the sampled vectors. | |
190 | + if (w.size() < nP) | |
191 | + w = std::vector<T>(nP, 1.0); //1 weight per each m. | |
192 | + | |
193 | + for (int i = 0; i < n; i++) //for each weight | |
194 | + { | |
195 | + T val = 0; | |
196 | + for (int j = 0; j < nP; j++) //for each vector | |
197 | + { | |
198 | + stim::vec3<T> temp = sph_pts[j] - c; | |
199 | + if (temp.dot(sphere[i]) > 0) | |
200 | + val += pow(temp.dot(sphere[i]), normfactor)*w[j]; | |
201 | + } | |
202 | + weights.push_back(val); | |
203 | + } | |
204 | + | |
205 | + mcBegin(l, m); //begin spherical harmonic sampling | |
206 | + | |
207 | + if (norm) | |
208 | + { | |
209 | + T min = *std::min_element(weights.begin(), weights.end()); | |
210 | + T max = *std::max_element(weights.begin(), weights.end()); | |
211 | + for (unsigned int i = 0; i < n; i++) | |
212 | + { | |
213 | + stim::vec3<T> sph = sphere[i].cart2sph(); | |
214 | + mcSample(sph[1], sph[2], (weights[i] - min) / (max - min)); | |
215 | + } | |
216 | + | |
217 | + } | |
218 | + else { | |
219 | + for (unsigned int i = 0; i < n; i++) | |
220 | + { | |
221 | + stim::vec3<T> sph = sphere[i].cart2sph(); | |
222 | + mcSample(sph[1], sph[2], weights[i]); | |
223 | + } | |
224 | + } | |
225 | + mcEnd(); | |
226 | + } | |
227 | 227 | |
228 | 228 | std::string str() { |
229 | 229 | |
... | ... | @@ -390,6 +390,29 @@ namespace stim { |
390 | 390 | } |
391 | 391 | } |
392 | 392 | |
393 | + ///imperpolates a spherical harmonic linearly from itself to the spherical harmonic passed as an input. | |
394 | + ///@param target, spharmonic class to which we are interpolating. | |
395 | + ///@param dist float value representing the distance we are interpolating [0,1] | |
396 | + stim::spharmonics<T> | |
397 | + interp(stim::spharmonics<T> target, float dist) | |
398 | + { | |
399 | + stim::spharmonics<T> ret(std::max(target.C.size(), C.size())); | |
400 | + for(int i = 0; i < target.C.size(); i++) | |
401 | + { | |
402 | + if(i < C.size()) | |
403 | + { | |
404 | + T slope = target.C[i] - C[i]; | |
405 | + ret.C[i] = C[i] + dist * slope; | |
406 | + } | |
407 | + else | |
408 | + { | |
409 | + T slope = target.C[i]; | |
410 | + ret.C[i] = dist * slope; | |
411 | + } | |
412 | + } | |
413 | + return ret; | |
414 | + } | |
415 | + | |
393 | 416 | /// Generate spherical harmonic coefficients based on a set of N samples |
394 | 417 | /*void fit(std::vector<stim::vec3<T> > sph_pts, unsigned int L, bool norm = true) |
395 | 418 | { | ... | ... |
stim/optics/scalarmie.h
... | ... | @@ -389,7 +389,7 @@ void cpu_scalar_mie_scatter(stim::complex<T>* E, size_t N, T* x, T* y, T* z, std |
389 | 389 | r = p.len(); |
390 | 390 | if(r >= a){ |
391 | 391 | for(size_t w = 0; w < W.size(); w++){ |
392 | - Ew = W[w].E() * exp(stim::complex<double>(0, W[w].kvec().dot(c))); | |
392 | + Ew = W[w].E() * exp(stim::complex<float>(0, W[w].kvec().dot(c))); | |
393 | 393 | kr = p.len() * W[w].kmag(); //calculate k*r |
394 | 394 | stim::bessjyv_sph<double>(Nl, kr, vm, j_kr, y_kr, dj_kr, dy_kr); |
395 | 395 | cos_phi = p.norm().dot(W[w].kvec().norm()); //calculate the cosine of the angle from the propagating direction |
... | ... | @@ -649,7 +649,7 @@ void cpu_scalar_mie_internal(stim::complex<T>* E, size_t N, T* x, T* y, T* z, st |
649 | 649 | if(r < a){ |
650 | 650 | E[i] = 0; |
651 | 651 | for(size_t w = 0; w < W.size(); w++){ |
652 | - Ew = W[w].E() * exp(stim::complex<double>(0, W[w].kvec().dot(c))); | |
652 | + Ew = W[w].E() * exp(stim::complex<float>(0, W[w].kvec().dot(c))); | |
653 | 653 | knr = (stim::complex<double>)n * p.len() * W[w].kmag(); //calculate k*n*r |
654 | 654 | |
655 | 655 | stim::cbessjyva_sph<double>(Nl, knr, vm, j_knr, y_knr, dj_knr, dy_knr); | ... | ... |
stim/visualization/gl_network.h
1 | -#ifndef STIM_GL_NETWORK | |
2 | -#define STIM_GL_NETWORK | |
3 | - | |
4 | -#include <GL/glut.h> | |
5 | -#include <stim/biomodels/network.h> | |
6 | -#include <stim/visualization/aaboundingbox.h> | |
7 | - | |
8 | -namespace stim{ | |
9 | - | |
10 | -template <typename T> | |
11 | -class gl_network : public stim::network<T>{ | |
12 | - | |
13 | -protected: | |
14 | - using stim::network<T>::E; | |
15 | - using stim::network<T>::V; | |
16 | - | |
17 | - GLuint dlist; | |
18 | - | |
19 | -public: | |
20 | - | |
21 | - /// Default constructor | |
22 | - gl_network() : stim::network<T>(){ | |
23 | - dlist = 0; | |
24 | - } | |
25 | - | |
26 | - /// Constructor creates a gl_network from a stim::network | |
27 | - gl_network(stim::network<T> N) : stim::network<T>(N){ | |
28 | - dlist = 0; | |
29 | - } | |
30 | - | |
31 | - /// Fills the parameters with the minimum and maximum spatial positions in the network, | |
32 | - /// specifying a bounding box for the network geometry | |
33 | - aaboundingbox<T> boundingbox(){ | |
34 | - | |
35 | - aaboundingbox<T> bb; //create a bounding box | |
36 | - | |
37 | - //loop through every edge | |
38 | - for(unsigned e = 0; e < E.size(); e++){ | |
39 | - //loop through every point | |
40 | - for(unsigned p = 0; p < E[e].size(); p++) | |
41 | - bb.expand(E[e][p]); //expand the bounding box to include the point | |
42 | - } | |
43 | - | |
44 | - return bb; //return the bounding box | |
45 | - } | |
46 | - | |
47 | - ///render cylinder based on points from the top/bottom hat | |
48 | - ///@param C1 set of points from one of the hat | |
49 | - void renderCylinder(std::vector< stim::vec3<T> > C1, std::vector< stim::vec3<T> > C2, stim::vec3<T> P1, stim::vec3<T> P2) { | |
50 | - glBegin(GL_QUAD_STRIP); | |
51 | - for (unsigned i = 0; i < C1.size(); i++) { // for every point on the circle | |
52 | - stim::vec3<T> n1 = C1[i] - P1; // set normal vector for every vertex on the quads | |
53 | - stim::vec3<T> n2 = C2[i] - P2; | |
54 | - n1 = n1.norm(); | |
55 | - n2 = n2.norm(); | |
56 | - glNormal3f(n1[0], n1[1], n1[2]); | |
57 | - glVertex3f(C1[i][0], C1[i][1], C1[i][2]); | |
58 | - glNormal3f(n2[0], n2[1], n2[2]); | |
59 | - glVertex3f(C2[i][0], C2[i][1], C2[i][2]); | |
60 | - } | |
61 | - glEnd(); | |
62 | - } | |
63 | - | |
64 | - ///render the vertex as sphere based on glut build-in function | |
65 | - ///@param x, y, z are the three coordinates of the center point | |
66 | - ///@param radius is the radius of the sphere | |
67 | - ///@param subdivisions is the slice/stride along/around z-axis | |
68 | - void renderBall(T x, T y, T z, T radius, int subdivisions) { | |
69 | - glPushMatrix(); | |
70 | - glTranslatef(x, y, z); | |
71 | - glutSolidSphere(radius, subdivisions, subdivisions); | |
72 | - glPopMatrix(); | |
73 | - } | |
74 | - | |
75 | - ///render the vertex as sphere based on transformation | |
76 | - ///@param x, y, z are the three coordinates of the center point | |
77 | - ///@param radius is the radius of the sphere | |
78 | - ///@param slice is the number of subdivisions around the z-axis | |
79 | - ///@param stack is the number of subdivisions along the z-axis | |
80 | - void renderBall(T x, T y, T z, T radius, T slice, T stack) { | |
81 | - T step_z = stim::PI / slice; // step angle along z-axis | |
82 | - T step_xy = 2 * stim::PI / stack; // step angle in xy-plane | |
83 | - T xx[4], yy[4], zz[4]; // store coordinates | |
84 | - | |
85 | - T angle_z = 0.0; // start angle | |
86 | - T angle_xy = 0.0; | |
87 | - | |
88 | - glBegin(GL_QUADS); | |
89 | - for (unsigned i = 0; i < slice; i++) { // around the z-axis | |
90 | - angle_z = i * step_z; // step step_z each time | |
91 | - | |
92 | - for (unsigned j = 0; j < stack; j++) { // along the z-axis | |
93 | - angle_xy = j * step_xy; // step step_xy each time, draw floor by floor | |
94 | - | |
95 | - xx[0] = radius * std::sin(angle_z) * std::cos(angle_xy); // four vertices | |
96 | - yy[0] = radius * std::sin(angle_z) * std::sin(angle_xy); | |
97 | - zz[0] = radius * std::cos(angle_z); | |
98 | - | |
99 | - xx[1] = radius * std::sin(angle_z + step_z) * std::cos(angle_xy); | |
100 | - yy[1] = radius * std::sin(angle_z + step_z) * std::sin(angle_xy); | |
101 | - zz[1] = radius * std::cos(angle_z + step_z); | |
102 | - | |
103 | - xx[2] = radius * std::sin(angle_z + step_z) * std::cos(angle_xy + step_xy); | |
104 | - yy[2] = radius * std::sin(angle_z + step_z) * std::sin(angle_xy + step_xy); | |
105 | - zz[2] = radius * std::cos(angle_z + step_z); | |
106 | - | |
107 | - xx[3] = radius * std::sin(angle_z) * std::cos(angle_xy + step_xy); | |
108 | - yy[3] = radius * std::sin(angle_z) * std::sin(angle_xy + step_xy); | |
109 | - zz[3] = radius * std::cos(angle_z); | |
110 | - | |
111 | - for (unsigned k = 0; k < 4; k++) { | |
112 | - glVertex3f(x + xx[k], y + yy[k], z + zz[k]); // draw the floor plane | |
113 | - } | |
114 | - } | |
115 | - } | |
116 | - glEnd(); | |
117 | - } | |
118 | - | |
119 | - /// Render the network centerline as a series of line strips. | |
120 | - /// glCenterline0 is for only one input | |
121 | - void glCenterline0(){ | |
122 | - if (!glIsList(dlist)) { //if dlist isn't a display list, create it | |
123 | - dlist = glGenLists(1); //generate a display list | |
124 | - glNewList(dlist, GL_COMPILE); //start a new display list | |
125 | - for (unsigned e = 0; e < E.size(); e++) { //for each edge in the network | |
126 | - glBegin(GL_LINE_STRIP); | |
127 | - for (unsigned p = 0; p < E[e].size(); p++) { //for each point on that edge | |
128 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); //set the vertex position based on the current point | |
129 | - glTexCoord1f(0); //set white color | |
130 | - } | |
131 | - glEnd(); | |
132 | - } | |
133 | - glEndList(); //end the display list | |
134 | - } | |
135 | - glCallList(dlist); // render the display list | |
136 | - } | |
137 | - | |
138 | - ///render the network centerline as a series of line strips(when loading at least two networks, otherwise using glCenterline0()) | |
139 | - ///colors are based on metric values | |
140 | - void glCenterline(){ | |
141 | - | |
142 | - if(!glIsList(dlist)){ //if dlist isn't a display list, create it | |
143 | - dlist = glGenLists(1); //generate a display list | |
144 | - glNewList(dlist, GL_COMPILE); //start a new display list | |
145 | - for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network | |
146 | - //unsigned errormag_id = E[e].nmags() - 1; | |
147 | - glBegin(GL_LINE_STRIP); | |
148 | - for(unsigned p = 0; p < E[e].size(); p++){ //for each point on that edge | |
149 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); //set the vertex position based on the current point | |
150 | - glTexCoord1f(E[e].r(p)); //set the texture coordinate based on the specified magnitude index | |
151 | - } | |
152 | - glEnd(); | |
153 | - } | |
154 | - glEndList(); //end the display list | |
155 | - } | |
156 | - glCallList(dlist); //render the display list | |
157 | - } | |
158 | - | |
159 | - ///render the network cylinder as a series of tubes(when only one network loaded) | |
160 | - void glCylinder0() { | |
161 | - | |
162 | - float r1, r2; | |
163 | - if (!glIsList(dlist)) { // if dlist isn't a display list, create it | |
164 | - dlist = glGenLists(1); // generate a display list | |
165 | - glNewList(dlist, GL_COMPILE); // start a new display list | |
166 | - for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network | |
167 | - for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge | |
168 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
169 | - stim::circle<T> C2 = E[e].circ(p); | |
170 | - r1 = E[e].r(p - 1); | |
171 | - r2 = E[e].r(p); | |
172 | - C1.set_R(r1); // re-scale the circle to the same | |
173 | - C2.set_R(r2); | |
174 | - std::vector< stim::vec3<T> > Cp1 = C1.glpoints(20);// get 20 points on the circle plane | |
175 | - std::vector< stim::vec3<T> > Cp2 = C2.glpoints(20); | |
176 | - glBegin(GL_QUAD_STRIP); | |
177 | - for (unsigned i = 0; i < Cp1.size(); i++) { | |
178 | - glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]); | |
179 | - glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]); | |
180 | - } | |
181 | - glEnd(); | |
182 | - } // set the texture coordinate based on the specified magnitude index | |
183 | - } | |
184 | - for (unsigned n = 0; n < V.size(); n++) { | |
185 | - for (unsigned i = 0; i < E.size(); i++) { | |
186 | - if (E[i].v[0] == n) { | |
187 | - r1 = E[i].r(0); | |
188 | - break; | |
189 | - } | |
190 | - else if (E[i].v[1] == n) { | |
191 | - r1 = E[i].r(E[i].size() - 1); | |
192 | - break; | |
193 | - } | |
194 | - } | |
195 | - renderBall(V[n][0], V[n][1], V[n][2], r1, 20); | |
196 | - } | |
197 | - glEndList(); // end the display list | |
198 | - } | |
199 | - glCallList(dlist); // render the display list | |
200 | - } | |
201 | - | |
202 | - ///render the network cylinder as a series of tubes | |
203 | - ///colors are based on metric values | |
204 | - void glCylinder(float sigma, float radius) { | |
205 | - | |
206 | - if (radius != sigma) // if render radius was changed by user, create a new display list | |
207 | - glDeleteLists(dlist, 1); | |
208 | - if (!glIsList(dlist)) { // if dlist isn't a display list, create it | |
209 | - dlist = glGenLists(1); // generate a display list | |
210 | - glNewList(dlist, GL_COMPILE); // start a new display list | |
211 | - for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network | |
212 | - for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge | |
213 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
214 | - stim::circle<T> C2 = E[e].circ(p); | |
215 | - C1.set_R(2*radius); // re-scale the circle to the same | |
216 | - C2.set_R(2*radius); | |
217 | - std::vector< stim::vec3<T> > Cp1 = C1.glpoints(20);// get 20 points on the circle plane | |
218 | - std::vector< stim::vec3<T> > Cp2 = C2.glpoints(20); | |
219 | - glBegin(GL_QUAD_STRIP); | |
220 | - for (unsigned i = 0; i < Cp1.size(); i++) { | |
221 | - stim::vec3<T> n1 = Cp1[i] - E[e][p - 1]; // set normal vector for every vertex on the quads | |
222 | - stim::vec3<T> n2 = Cp2[i] - E[e][p]; | |
223 | - n1 = n1.norm(); | |
224 | - n2 = n2.norm(); | |
225 | - | |
226 | - glNormal3f(n1[0], n1[1], n1[2]); | |
227 | - glTexCoord1f(E[e].r(p - 1)); | |
228 | - glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]); | |
229 | - glNormal3f(n2[0], n2[1], n2[2]); | |
230 | - glTexCoord1f(E[e].r(p)); | |
231 | - glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]); | |
232 | - } | |
233 | - glEnd(); | |
234 | - } // set the texture coordinate based on the specified magnitude index | |
235 | - } | |
236 | - for (unsigned n = 0; n < V.size(); n++) { | |
237 | - size_t num = V[n].e[0].size(); // store the number of outgoing edge of that vertex | |
238 | - if (num != 0) { // if it has outgoing edge | |
239 | - unsigned idx = V[n].e[0][0]; // find the index of first outgoing edge of that vertex | |
240 | - glTexCoord1f(E[idx].r(0)); // bind the texture as metric of first point on that edge | |
241 | - } | |
242 | - else { | |
243 | - unsigned idx = V[n].e[1][0]; // find the index of first incoming edge of that vertex | |
244 | - glTexCoord1f(E[idx].r(E[idx].size() - 1)); // bind the texture as metric of last point on that edge | |
245 | - } | |
246 | - renderBall(V[n][0], V[n][1], V[n][2], 2*radius, 20); | |
247 | - } | |
248 | - glEndList(); // end the display list | |
249 | - } | |
250 | - glCallList(dlist); // render the display list | |
251 | - } | |
252 | - | |
253 | - ///render a T as a adjoint network of GT in transparancy(darkgreen, overlaid) | |
254 | - void glAdjointCylinder(float sigma, float radius) { | |
255 | - | |
256 | - if (radius != sigma) // if render radius was changed by user, create a new display list | |
257 | - glDeleteLists(dlist + 4, 1); | |
258 | - if (!glIsList(dlist + 4)) { | |
259 | - glNewList(dlist + 4, GL_COMPILE); | |
260 | - for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network | |
261 | - for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge | |
262 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
263 | - stim::circle<T> C2 = E[e].circ(p); | |
264 | - C1.set_R(2 * radius); // scale the circle to the same | |
265 | - C2.set_R(2 * radius); | |
266 | - std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
267 | - std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
268 | - glBegin(GL_QUAD_STRIP); | |
269 | - for (unsigned i = 0; i < Cp1.size(); i++) { // for every point on the circle(+1 means closing the circle) | |
270 | - glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]); | |
271 | - glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]); | |
272 | - } | |
273 | - glEnd(); | |
274 | - } // set the texture coordinate based on the specified magnitude index | |
275 | - } | |
276 | - for (unsigned n = 0; n < V.size(); n++) { | |
277 | - size_t num = V[n].e[0].size(); // store the number of outgoing edge of that vertex | |
278 | - if (num != 0) { // if it has outgoing edge | |
279 | - unsigned idx = V[n].e[0][0]; // find the index of first outgoing edge of that vertex | |
280 | - } | |
281 | - else { | |
282 | - unsigned idx = V[n].e[1][0]; // find the index of first incoming edge of that vertex | |
283 | - } | |
284 | - renderBall(V[n][0], V[n][1], V[n][2], 2 * radius, 20); | |
285 | - } | |
286 | - glEndList(); | |
287 | - } | |
288 | - glCallList(dlist + 4); | |
289 | - } | |
290 | - | |
291 | - ///render the network cylinder as series of tubes | |
292 | - ///@param I is a indicator: 0 -> GT, 1 -> T | |
293 | - ///@param map is the mapping relationship between two networks | |
294 | - ///@param colormap is the random generated color set for render | |
295 | - void glRandColorCylinder(int I, std::vector<unsigned> map, std::vector<T> colormap, float sigma, float radius) { | |
296 | - | |
297 | - if (radius != sigma) // if render radius was changed by user, create a new display list | |
298 | - glDeleteLists(dlist + 2, 1); | |
299 | - if (!glIsList(dlist + 2)) { // if dlist isn't a display list, create it | |
300 | - glNewList(dlist + 2, GL_COMPILE); // start a new display list | |
301 | - for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network | |
302 | - if (map[e] != unsigned(-1)) { | |
303 | - if (I == 0) { // if it is to render GT | |
304 | - glColor3f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2]); | |
305 | - } | |
306 | - else { // if it is to render T | |
307 | - glColor3f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2]); | |
308 | - } | |
309 | - | |
310 | - for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge | |
311 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
312 | - stim::circle<T> C2 = E[e].circ(p); | |
313 | - C1.set_R(2*radius); // re-scale the circle to the same | |
314 | - C2.set_R(2*radius); | |
315 | - std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
316 | - std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
317 | - renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]); | |
318 | - } | |
319 | - } | |
320 | - else { | |
321 | - glColor3f(1.0, 1.0, 1.0); // white color for the un-mapping edges | |
322 | - for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge | |
323 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
324 | - stim::circle<T> C2 = E[e].circ(p); | |
325 | - C1.set_R(2*radius); // scale the circle to the same | |
326 | - C2.set_R(2*radius); | |
327 | - std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
328 | - std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
329 | - renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]); | |
330 | - } | |
331 | - } | |
332 | - } | |
333 | - for (unsigned n = 0; n < V.size(); n++) { | |
334 | - size_t num_edge = V[n].e[0].size() + V[n].e[1].size(); | |
335 | - if (num_edge > 1) { // if it is the joint vertex | |
336 | - glColor3f(0.3, 0.3, 0.3); // gray | |
337 | - renderBall(V[n][0], V[n][1], V[n][2], 3*radius, 20); | |
338 | - } | |
339 | - else { // if it is the terminal vertex | |
340 | - glColor3f(0.6, 0.6, 0.6); // more white gray | |
341 | - renderBall(V[n][0], V[n][1], V[n][2], 3*radius, 20); | |
342 | - } | |
343 | - } | |
344 | - glEndList(); | |
345 | - } | |
346 | - glCallList(dlist + 2); | |
347 | - } | |
348 | - | |
349 | - ///render the network centerline as a series of line strips in random different color | |
350 | - ///@param I is a indicator: 0 -> GT, 1 -> T | |
351 | - ///@param map is the mapping relationship between two networks | |
352 | - ///@param colormap is the random generated color set for render | |
353 | - void glRandColorCenterline(int I, std::vector<unsigned> map, std::vector<T> colormap) { | |
354 | - if (!glIsList(dlist + 2)) { | |
355 | - glNewList(dlist + 2, GL_COMPILE); | |
356 | - for (unsigned e = 0; e < E.size(); e++) { | |
357 | - if (map[e] != unsigned(-1)) { // if it has corresponding edge in another network | |
358 | - if (I == 0) // if it is to render GT | |
359 | - glColor3f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2]); | |
360 | - else // if it is to render T | |
361 | - glColor3f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2]); | |
362 | - | |
363 | - glBegin(GL_LINE_STRIP); | |
364 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
365 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
366 | - } | |
367 | - glEnd(); | |
368 | - } | |
369 | - else { | |
370 | - glColor3f(1.0, 1.0, 1.0); // white color for the un-mapping edges | |
371 | - glBegin(GL_LINE_STRIP); | |
372 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
373 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
374 | - } | |
375 | - glEnd(); | |
376 | - } | |
377 | - } | |
378 | - glEndList(); | |
379 | - } | |
380 | - glCallList(dlist + 2); | |
381 | - } | |
382 | - | |
383 | - void glAdjointCenterline() { | |
384 | - if (!glIsList(dlist + 4)) { | |
385 | - glNewList(dlist + 4, GL_COMPILE); | |
386 | - for (unsigned e = 0; e < E.size(); e++) { //for each edge in the network | |
387 | - | |
388 | - glBegin(GL_LINE_STRIP); | |
389 | - for (unsigned p = 0; p < E[e].size(); p++) { //for each point on that edge | |
390 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); //set the vertex position based on the current point | |
391 | - glTexCoord1f(E[e].r(p)); //set the texture coordinate based on the specified magnitude index | |
392 | - } | |
393 | - glEnd(); | |
394 | - } | |
395 | - glEndList(); | |
396 | - } | |
397 | - glCallList(dlist + 4); | |
398 | - } | |
399 | - | |
400 | - // highlight the difference part | |
401 | - void glDifferenceCylinder(int I, std::vector<unsigned> map, std::vector<T> colormap, float sigma, float radius) { | |
402 | - | |
403 | - if (radius != sigma) // if render radius was changed by user, create a new display list | |
404 | - glDeleteLists(dlist + 6, 1); | |
405 | - if (!glIsList(dlist + 6)) { // if dlist isn't a display list, create it | |
406 | - glNewList(dlist + 6, GL_COMPILE); // start a new display list | |
407 | - for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network | |
408 | - if (map[e] != unsigned(-1)) { | |
409 | - glEnable(GL_BLEND); //enable color blend | |
410 | - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //set blend function | |
411 | - glDisable(GL_DEPTH_TEST); //should disable depth | |
412 | - | |
413 | - if (I == 0) { // if it is to render GT | |
414 | - glColor4f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2], 0.1); | |
415 | - } | |
416 | - else { // if it is to render T | |
417 | - glColor4f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2], 0.1); | |
418 | - } | |
419 | - | |
420 | - for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge | |
421 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
422 | - stim::circle<T> C2 = E[e].circ(p); | |
423 | - C1.set_R(2 * radius); // re-scale the circle to the same | |
424 | - C2.set_R(2 * radius); | |
425 | - std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
426 | - std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
427 | - renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]); | |
428 | - } | |
429 | - glDisable(GL_BLEND); | |
430 | - glEnable(GL_DEPTH_TEST); | |
431 | - } | |
432 | - else { | |
433 | - glColor3f(1.0, 1.0, 1.0); // white color for the un-mapping edges | |
434 | - for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge | |
435 | - stim::circle<T> C1 = E[e].circ(p - 1); | |
436 | - stim::circle<T> C2 = E[e].circ(p); | |
437 | - C1.set_R(2 * radius); // scale the circle to the same | |
438 | - C2.set_R(2 * radius); | |
439 | - std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
440 | - std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
441 | - renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]); | |
442 | - } | |
443 | - } | |
444 | - } | |
445 | - for (unsigned n = 0; n < V.size(); n++) { | |
446 | - | |
447 | - size_t num_edge = V[n].e[0].size() + V[n].e[1].size(); | |
448 | - if (num_edge > 1) { // if it is the joint vertex | |
449 | - glColor4f(0.3, 0.3, 0.3, 0.1); // gray | |
450 | - renderBall(V[n][0], V[n][1], V[n][2], 3 * radius, 20); | |
451 | - } | |
452 | - else { // if it is the terminal vertex | |
453 | - glColor4f(0.6, 0.6, 0.6, 0.1); // more white gray | |
454 | - renderBall(V[n][0], V[n][1], V[n][2], 3 * radius, 20); | |
455 | - } | |
456 | - } | |
457 | - glEndList(); | |
458 | - } | |
459 | - glCallList(dlist + 6); | |
460 | - } | |
461 | - | |
462 | - //void glRandColorCenterlineGT(GLuint &dlist1, std::vector<unsigned> map, std::vector<T> colormap){ | |
463 | - // if(!glIsList(dlist1)){ | |
464 | - // dlist1 = glGenLists(1); | |
465 | - // glNewList(dlist1, GL_COMPILE); | |
466 | - // for(unsigned e = 0; e < E.size(); e++){ | |
467 | - // if(map[e] != unsigned(-1)){ | |
468 | - // glColor3f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2]); | |
469 | - // glBegin(GL_LINE_STRIP); | |
470 | - // for(unsigned p = 0; p < E[e].size(); p++){ | |
471 | - // glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
472 | - // } | |
473 | - // glEnd(); | |
474 | - // for (unsigned p = 0; p < E[e].size() - 1; p++) { | |
475 | - // renderCylinder(E[e][p][0], E[e][p][1], E[e][p][2], E[e][p + 1][0], E[e][p + 1][1], E[e][p + 1][2], 10, 20); | |
476 | - // } | |
477 | - // } | |
478 | - // else{ | |
479 | - // glColor3f(1.0, 1.0, 1.0); | |
480 | - // glBegin(GL_LINE_STRIP); | |
481 | - // for(unsigned p = 0; p < E[e].size(); p++){ | |
482 | - // glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
483 | - // } | |
484 | - // glEnd(); | |
485 | - // } | |
486 | - // } | |
487 | - // for (unsigned v = 0; v < V.size(); v++) { | |
488 | - // size_t num_edge = V[v].e[0].size() + V[v].e[1].size(); | |
489 | - // if (num_edge > 1) { | |
490 | - // glColor3f(0.3, 0.3, 0.3); // gray color for vertex | |
491 | - // renderBall(V[v][0], V[v][1], V[v][2], 20, 20); | |
492 | - // } | |
493 | - // } | |
494 | - // glEndList(); | |
495 | - // } | |
496 | - // glCallList(dlist1); | |
497 | - //} | |
498 | - | |
499 | - //void glRandColorCenterlineT(GLuint &dlist2, std::vector<unsigned> map, std::vector<T> colormap){ | |
500 | - // if(!glIsList(dlist2)){ | |
501 | - // dlist2 = glGenLists(1); | |
502 | - // glNewList(dlist2, GL_COMPILE); | |
503 | - // for(unsigned e = 0; e < E.size(); e++){ | |
504 | - // if(map[e] != unsigned(-1)){ | |
505 | - // glColor3f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2]); | |
506 | - // glBegin(GL_LINE_STRIP); | |
507 | - // for(unsigned p = 0; p < E[e].size(); p++){ | |
508 | - // glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
509 | - // } | |
510 | - // glEnd(); | |
511 | - // for (unsigned p = 0; p < E[e].size() - 1; p++) { | |
512 | - // renderCylinder(E[e][p][0], E[e][p][1], E[e][p][2], E[e][p + 1][0], E[e][p + 1][1], E[e][p + 1][2], 10, 20); | |
513 | - // } | |
514 | - // } | |
515 | - // else{ | |
516 | - // glColor3f(1.0, 1.0, 1.0); | |
517 | - // glBegin(GL_LINE_STRIP); | |
518 | - // for(unsigned p = 0; p < E[e].size(); p++){ | |
519 | - // glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
520 | - // } | |
521 | - // glEnd(); | |
522 | - // } | |
523 | - // } | |
524 | - // for (unsigned v = 0; v < V.size(); v++) { | |
525 | - // size_t num_edge = V[v].e[0].size() + V[v].e[1].size(); | |
526 | - // if (num_edge > 1) { | |
527 | - // glColor3f(0.3, 0.3, 0.3); // gray color for vertex | |
528 | - // renderBall(V[v][0], V[v][1], V[v][2], 20, 20); | |
529 | - // } | |
530 | - // } | |
531 | - // glEndList(); | |
532 | - // } | |
533 | - // glCallList(dlist2); | |
534 | - //} | |
535 | - | |
536 | - | |
537 | - //void renderCylinder(T x1, T y1, T z1, T x2, T y2, T z2, T radius, int subdivisions) { | |
538 | - // T dx = x2 - x1; | |
539 | - // T dy = y2 - y1; | |
540 | - // T dz = z2 - z1; | |
541 | - // /// handle the degenerate case with an approximation | |
542 | - // if (dz == 0) | |
543 | - // dz = .00000001; | |
544 | - // T d = sqrt(dx*dx + dy*dy + dz*dz); | |
545 | - // T ax = 57.2957795*acos(dz / d); // 180°/pi | |
546 | - // if (dz < 0.0) | |
547 | - // ax = -ax; | |
548 | - // T rx = -dy*dz; | |
549 | - // T ry = dx*dz; | |
550 | - | |
551 | - // glPushMatrix(); | |
552 | - // glTranslatef(x1, y1, z1); | |
553 | - // glRotatef(ax, rx, ry, 0.0); | |
554 | - | |
555 | - // glutSolidCylinder(radius, d, subdivisions, 1); | |
556 | - // glPopMatrix(); | |
557 | - //} | |
558 | - | |
559 | - | |
560 | - /// render the network centerline from swc file as a series of strips in different colors based on the neuronal type | |
561 | - /// glCenterline0_swc is for only one input | |
562 | - /*void glCenterline0_swc() { | |
563 | - if (!glIsList(dlist)) { // if dlist isn't a display list, create it | |
564 | - dlist = glGenLists(1); // generate a display list | |
565 | - glNewList(dlist, GL_COMPILE); // start a new display list | |
566 | - for (unsigned e = 0; e < E.size(); e++) { | |
567 | - int type = NT[e]; // get the neuronal type | |
568 | - switch (type) { | |
569 | - case 0: | |
570 | - glColor3f(1.0f, 1.0f, 1.0f); // white for undefined | |
571 | - glBegin(GL_LINE_STRIP); | |
572 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
573 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
574 | - } | |
575 | - glEnd(); | |
576 | - break; | |
577 | - case 1: | |
578 | - glColor3f(1.0f, 0.0f, 0.0f); // red for soma | |
579 | - glBegin(GL_LINE_STRIP); | |
580 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
581 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
582 | - } | |
583 | - glEnd(); | |
584 | - break; | |
585 | - case 2: | |
586 | - glColor3f(1.0f, 0.5f, 0.0f); // orange for axon | |
587 | - glBegin(GL_LINE_STRIP); | |
588 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
589 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
590 | - } | |
591 | - glEnd(); | |
592 | - break; | |
593 | - case 3: | |
594 | - glColor3f(1.0f, 1.0f, 0.0f); // yellow for undefined | |
595 | - glBegin(GL_LINE_STRIP); | |
596 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
597 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
598 | - } | |
599 | - glEnd(); | |
600 | - break; | |
601 | - case 4: | |
602 | - glColor3f(0.0f, 1.0f, 0.0f); // green for undefined | |
603 | - glBegin(GL_LINE_STRIP); | |
604 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
605 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
606 | - } | |
607 | - glEnd(); | |
608 | - break; | |
609 | - case 5: | |
610 | - glColor3f(0.0f, 1.0f, 1.0f); // verdant for undefined | |
611 | - glBegin(GL_LINE_STRIP); | |
612 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
613 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
614 | - } | |
615 | - glEnd(); | |
616 | - break; | |
617 | - case 6: | |
618 | - glColor3f(0.0f, 0.0f, 1.0f); // blue for undefined | |
619 | - glBegin(GL_LINE_STRIP); | |
620 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
621 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
622 | - } | |
623 | - glEnd(); | |
624 | - break; | |
625 | - case 7: | |
626 | - glColor3f(0.5f, 0.0f, 1.0f); // purple for undefined | |
627 | - glBegin(GL_LINE_STRIP); | |
628 | - for (unsigned p = 0; p < E[e].size(); p++) { | |
629 | - glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); | |
630 | - } | |
631 | - glEnd(); | |
632 | - break; | |
633 | - } | |
634 | - } | |
635 | - glEndList(); //end the display list | |
636 | - } | |
637 | - glCallList(dlist); // render the display list | |
638 | - }*/ | |
639 | - | |
640 | - ///render the network cylinder as a series of tubes | |
641 | - ///colors are based on metric values | |
642 | - //void glCylinder(float sigma) { | |
643 | - // if (!glIsList(dlist)) { //if dlist isn't a display list, create it | |
644 | - // dlist = glGenLists(1); //generate a display list | |
645 | - // glNewList(dlist, GL_COMPILE); //start a new display list | |
646 | - // for (unsigned e = 0; e < E.size(); e++) { //for each edge in the network | |
647 | - // for (unsigned p = 1; p < E[e].size() - 1; p++) { // for each point on that edge | |
648 | - // stim::circle<T> C1 = E[e].circ(p - 1); | |
649 | - // stim::circle<T> C2 = E[e].circ(p); | |
650 | - // C1.set_R(2.5*sigma); // scale the circle to the same | |
651 | - // C2.set_R(2.5*sigma); | |
652 | - // std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20); | |
653 | - // std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20); | |
654 | - // glBegin(GL_QUAD_STRIP); | |
655 | - // for (unsigned i = 0; i < Cp1.size(); i++) { // for every point on the circle(+1 means closing the circle) | |
656 | - // glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]); | |
657 | - // glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]); | |
658 | - // glTexCoord1f(E[e].r(p)); | |
659 | - // } | |
660 | - // glEnd(); | |
661 | - // } //set the texture coordinate based on the specified magnitude index | |
662 | - // } | |
663 | - // for (unsigned n = 0; n < V.size(); n++) { | |
664 | - // size_t num = V[n].e[0].size(); //store the number of outgoing edge of that vertex | |
665 | - // if (num != 0) { //if it has outgoing edge | |
666 | - // unsigned idx = V[n].e[0][0]; //find the index of first outgoing edge of that vertex | |
667 | - // glTexCoord1f(E[idx].r(0)); //bind the texture as metric of first point on that edge | |
668 | - // } | |
669 | - // else { | |
670 | - // unsigned idx = V[n].e[1][0]; //find the index of first incoming edge of that vertex | |
671 | - // glTexCoord1f(E[idx].r(E[idx].size() - 1)); //bind the texture as metric of last point on that edge | |
672 | - // } | |
673 | - // renderBall(V[n][0], V[n][1], V[n][2], 2.5*sigma, 20); | |
674 | - // } | |
675 | - // glEndList(); //end the display list | |
676 | - // } | |
677 | - // glCallList(dlist); //render the display list | |
678 | - //} | |
679 | - | |
680 | -}; //end stim::gl_network class | |
681 | -}; //end stim namespace | |
682 | - | |
683 | - | |
684 | - | |
1 | +#ifndef JACK_GL_NETWORK | |
2 | +#define JACK_GL_NETWORK | |
3 | + | |
4 | +#include"network.h" | |
5 | +#include<GL/glut.h> | |
6 | +#include<stim/visualization/aaboundingbox.h> | |
7 | + | |
8 | +namespace jack { | |
9 | + | |
10 | + template<typename T> | |
11 | + class gl_network : public jack::network<T> { | |
12 | + private: | |
13 | + std::vector<T> ecolor; // colormap for edges | |
14 | + std::vector<T> vcolor; // colormap for vertices | |
15 | + GLint subdivision; // rendering subdivision | |
16 | + | |
17 | + /// basic geometry rendering | |
18 | + // main sphere rendering function | |
19 | + void sphere(T x, T y, T z, T radius) { | |
20 | + glPushMatrix(); | |
21 | + glTranslatef((GLfloat)x, (GLfloat)y, (GLfloat)z); | |
22 | + glutSolidSphere((double)radius, subdivision, subdivision); | |
23 | + glPopMatrix(); | |
24 | + } | |
25 | + // rendering sphere using glut function | |
26 | + void draw_sphere(T x, T y, T z, T radius, T alpha = 1.0f) { | |
27 | + if (alpha != 1.0f) { | |
28 | + glEnable(GL_BLEND); // enable color blend | |
29 | + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // set blend function | |
30 | + glDisable(GL_DEPTH_TEST); // disable depth buffer | |
31 | + glColor4f(vcolor[0], vcolor[1], vcolor[2], alpha); // set color | |
32 | + sphere(x, y, z, radius, subdivision); | |
33 | + glDisable(GL_BLEND); // disable color blend | |
34 | + glEnable(GL_DEPTH_TEST); // enbale depth buffer again | |
35 | + } | |
36 | + else { | |
37 | + glColor3f(vcolor[0], vcolor[1], vcolor[2]); | |
38 | + sphere(x, y, z, radius, subdivision); | |
39 | + } | |
40 | + } | |
41 | + // rendering sphere using quads | |
42 | + void draw_sphere(T x, T y, T z, T radius, T alpha = 1.0f) { | |
43 | + GLint stack = subdivision; | |
44 | + GLint slice = subdivision; | |
45 | + | |
46 | + if (alpha != 1.0f) { | |
47 | + glEnable(GL_BLEND); // enable color blend | |
48 | + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // set blend function | |
49 | + glDisable(GL_DEPTH_TEST); // disable depth buffer | |
50 | + } | |
51 | + glColor4f(vcolor[0], vcolor[1], vcolor[2], alpha); // set color | |
52 | + | |
53 | + T step_z = stim::PI / slice; // step angle along z-axis | |
54 | + T step_xy = 2 * stim::PI / stack; // step angle in xy-plane | |
55 | + T xx[4], yy[4], zz[4]; // store coordinates | |
56 | + | |
57 | + T angle_z = 0.0; // start angle | |
58 | + T angle_xy = 0.0; | |
59 | + | |
60 | + glBegin(GL_QUADS); | |
61 | + for (unsigned i = 0; i < slice; i++) { // around the z-axis | |
62 | + angle_z = i * step_z; // step step_z each time | |
63 | + | |
64 | + for (unsigned j = 0; j < stack; j++) { // along the z-axis | |
65 | + angle_xy = j * step_xy; // step step_xy each time, draw floor by floor | |
66 | + | |
67 | + xx[0] = radius * std::sin(angle_z) * std::cos(angle_xy); // four vertices | |
68 | + yy[0] = radius * std::sin(angle_z) * std::sin(angle_xy); | |
69 | + zz[0] = radius * std::cos(angle_z); | |
70 | + | |
71 | + xx[1] = radius * std::sin(angle_z + step_z) * std::cos(angle_xy); | |
72 | + yy[1] = radius * std::sin(angle_z + step_z) * std::sin(angle_xy); | |
73 | + zz[1] = radius * std::cos(angle_z + step_z); | |
74 | + | |
75 | + xx[2] = radius * std::sin(angle_z + step_z) * std::cos(angle_xy + step_xy); | |
76 | + yy[2] = radius * std::sin(angle_z + step_z) * std::sin(angle_xy + step_xy); | |
77 | + zz[2] = radius * std::cos(angle_z + step_z); | |
78 | + | |
79 | + xx[3] = radius * std::sin(angle_z) * std::cos(angle_xy + step_xy); | |
80 | + yy[3] = radius * std::sin(angle_z) * std::sin(angle_xy + step_xy); | |
81 | + zz[3] = radius * std::cos(angle_z); | |
82 | + | |
83 | + for (unsigned k = 0; k < 4; k++) { | |
84 | + glVertex3f(x + xx[k], y + yy[k], z + zz[k]); // draw the floor plane | |
85 | + } | |
86 | + } | |
87 | + } | |
88 | + glEnd(); | |
89 | + | |
90 | + if (alpha != 1.0f) { | |
91 | + glDisable(GL_BLEND); // disable color blend | |
92 | + glEnable(GL_DEPTH_TEST); // enbale depth buffer again | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + // render edge as cylinders | |
97 | + void draw_cylinder(T scale = 1.0f) { | |
98 | + stim::circle<T> C1, C2; // temp circles | |
99 | + std::vector<stim::vec3<T> > Cp1, Cp2; // temp lists for storing points on those circles | |
100 | + T r1, r2; // temp radii | |
101 | + | |
102 | + size_t num_edge = E.size(); | |
103 | + size_t num_vertex = V.size(); | |
104 | + | |
105 | + for (size_t i = 0; i < num_edge; i++) { // for every edge | |
106 | + glColor3f(ecolor[i * 3 + 0], ecolor[i * 3 + 1], ecolor[i * 3 + 2]); | |
107 | + for (size_t j = 1; j < num_vertex; j++) { // for every vertex except first one | |
108 | + C1 = E[i].circ(j - 1); // get the first circle plane of this segment | |
109 | + C2 = E[i].circ(j); // get the second circle plane of this segment | |
110 | + r1 = E[i].r(j - 1); // get the radius at first point | |
111 | + r2 = E[i].r(j); // get the radius at second point | |
112 | + C1.set_R(scale * r1); // rescale | |
113 | + C2.set_R(scale * r2); | |
114 | + Cp1 = C1.glpoints((unsigned)subdivision); // get 20 points on first circle plane | |
115 | + Cp2 = C2.glpoints((unsigned)subdivision); // get 20 points on second circle plane | |
116 | + | |
117 | + glBegin(GL_QUAD_STRIP); | |
118 | + for (size_t k = 0; k < subdivision + 1; k++) { | |
119 | + glVertex3f(Cp1[k][0], Cp1[k][1], Cp1[k][2]); | |
120 | + glVertex3f(Cp2[k][0], Cp2[k][1], Cp2[k][2]); | |
121 | + } | |
122 | + glEnd(); | |
123 | + } | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + | |
128 | + protected: | |
129 | + using jack::network<T>::E; | |
130 | + using jack::network<T>::T; | |
131 | + | |
132 | + GLuint dlist; | |
133 | + | |
134 | + public: | |
135 | + | |
136 | + /// constructors | |
137 | + // empty constructor | |
138 | + gl_network() : jack::network<T>() { | |
139 | + dlist = 0; | |
140 | + subdivision = 20; | |
141 | + } | |
142 | + | |
143 | + gl_network(jack::network<T> N) : jack::network<T>(N) { | |
144 | + dlist = 0; | |
145 | + subdivision = 20; | |
146 | + ecolor.resize(N.edges * 3, 0.0f); // default black color | |
147 | + vcolor.resize(N.vertices * 3, 0.0f); | |
148 | + } | |
149 | + | |
150 | + // compute the smallest bounding box of current network | |
151 | + aabboundingbox<T> boundingbox() { | |
152 | + aaboundingbox<T> bb; // create a bounding box object | |
153 | + | |
154 | + for (size_t i = 0; i < E.size(); i++) // for every edge | |
155 | + for (size_t j = 0; j < E[i].size(); j++) // for every point on that edge | |
156 | + bb.expand(E[i][j]); // expand the bounding box to include that point | |
157 | + | |
158 | + return bb; | |
159 | + } | |
160 | + | |
161 | + // change subdivision | |
162 | + void change_subdivision(GLint value) { | |
163 | + subdivision = value; | |
164 | + } | |
165 | + | |
166 | + | |
167 | + /// rendering functions | |
168 | + // render centerline | |
169 | + void centerline() { | |
170 | + size_t num = E.size(); // get the number of edges | |
171 | + for (size_t i = 0; i < num; i++) { | |
172 | + glColor3f(colormap[i * 3 + 0], colormap[i * 3 + 1], colormap[i * 3 + 2]); | |
173 | + glBegin(GL_LINE_STRIP); | |
174 | + for (size_t j = 0; j < E[i].size(); j++) | |
175 | + glVertex3f(E[i][j][0], E[i][j][1], E[i][j][2]); | |
176 | + glEnd(); | |
177 | + } | |
178 | + } | |
179 | + | |
180 | + | |
181 | + // render network as bunch of spheres and cylinders | |
182 | + void network(T scale = 1.0f) { | |
183 | + stim::vec3<T> v1, v2; // temp vertices for rendering | |
184 | + T r1, r2; // temp radii for rendering | |
185 | + | |
186 | + if (!glIsList(dlist)) { // if dlist is not a display list, create one | |
187 | + dlist = glGenLists(1); // generate a display list | |
188 | + glNewList(dlist, GL_COMPILE); // start a new display list | |
189 | + | |
190 | + // render every vertex as a sphere | |
191 | + size_t num = V.size(); | |
192 | + for (size_t i = 0; i < num; i++) { | |
193 | + v1 = stim::vec3<T>(V[i][0], V[i][1], V[i][2]); // get the vertex for rendering | |
194 | + r1 = (*this).network<T>::r(i); | |
195 | + draw_sphere(v1[0], v1[1], v1[2], r1 * scale); | |
196 | + } | |
197 | + | |
198 | + // render every edge as a cylinder | |
199 | + draw_cylinder(scale); | |
200 | + | |
201 | + glEndList(); // end the display list | |
202 | + } | |
203 | + glCallList(dlist); | |
204 | + } | |
205 | + }; | |
206 | +} | |
207 | + | |
208 | + | |
209 | + | |
210 | + | |
211 | + | |
212 | + | |
213 | + | |
214 | + | |
215 | + | |
216 | + | |
217 | + | |
218 | + | |
219 | + | |
220 | + | |
221 | + | |
222 | + | |
223 | + | |
685 | 224 | #endif |
686 | 225 | \ No newline at end of file | ... | ... |
stim/visualization/gl_spharmonics.h
... | ... | @@ -62,6 +62,95 @@ namespace stim { |
62 | 62 | if (dlist) glDeleteLists(dlist, 1); //delete the display list when the object is destroyed |
63 | 63 | } |
64 | 64 | |
65 | + ///saves the coeffients of a spherical harmonics in a text documents. | |
66 | + void save_coeffs(std::string filename) | |
67 | + { | |
68 | + std::ofstream myfile; | |
69 | + myfile.open(filename.c_str()); | |
70 | + for(int i = 0; i < Sd.C.size()-1; i++) | |
71 | + { | |
72 | + myfile << Sd.C[i] << ","; | |
73 | + } | |
74 | + myfile << Sd.C[Sd.C.size()-1] << std::endl; | |
75 | + for(int i = 0; i < Sc.C.size()-1; i++) | |
76 | + { | |
77 | + myfile << Sc.C[i] << ","; | |
78 | + } | |
79 | + myfile << Sc.C[Sc.C.size()-1] << std::endl; | |
80 | + myfile.close(); | |
81 | + } | |
82 | + | |
83 | + void save(std::string filename) | |
84 | + { | |
85 | + | |
86 | + if (!tex) { | |
87 | + init_tex(); | |
88 | + } | |
89 | + | |
90 | + stim::obj<T> object; | |
91 | + size_t theta_i, phi_i; | |
92 | + T d_theta = (T)stim::TAU / (T)N; | |
93 | + T d_phi = (T)stim::PI / (T)(N-1); | |
94 | + object.matKd("sfunc.jpg"); | |
95 | + for (phi_i = 1; phi_i < N; phi_i++) { | |
96 | + T phi = phi_i * d_phi; | |
97 | + object.Begin(OBJ_TRIANGLE_STRIP); | |
98 | + for (theta_i = 0; theta_i <= N; theta_i++) { | |
99 | + T theta = (N - theta_i) * d_theta; | |
100 | + float theta_t = 1 - (float)theta_i / (float)N; | |
101 | + | |
102 | + T r; | |
103 | + if (!displacement) r = 1; //if no displacement, set the r value to 1 (renders a unit sphere) | |
104 | + else r = Sd(theta, phi); //otherwise calculate the displacement value | |
105 | + | |
106 | + glColor3f(1.0f, 1.0f, 1.0f); | |
107 | + if (!colormap) { //if no colormap is being rendered | |
108 | + if (r < 0) glColor3f(1.0, 0.0, 0.0); //if r is negative, render it red | |
109 | + else glColor3f(0.0, 1.0, 0.0); //otherwise render in green | |
110 | + } | |
111 | + if (magnitude) { //if the magnitude is being displaced, calculate the magnitude of r | |
112 | + if (r < 0) r = -r; | |
113 | + } | |
114 | + stim::vec3<T> s(r, theta, phi); | |
115 | + stim::vec3<T> c = s.sph2cart(); | |
116 | + stim::vec3<T> n; //allocate a value to store the normal | |
117 | + if (!displacement) n = c; //if there is no displacement, the normal is spherical | |
118 | + else n = Sd.dphi(theta, phi).cross(Sd.dtheta(theta, phi)); //otherwise calculate the normal as the cross product of derivatives | |
119 | + | |
120 | + object.TexCoord(theta_t, (float)phi_i / (float)N); | |
121 | + //std::cout << theta_t <<" "<<(float)phi_i / (float)N << "----------------"; | |
122 | + object.Normal(n[0], n[1], n[2]); | |
123 | + object.Vertex(c[0], c[1], c[2]); | |
124 | + | |
125 | + T r1; | |
126 | + if (!displacement) r1 = 1; | |
127 | + else r1 = Sd(theta, phi - d_phi); | |
128 | + if (!colormap) { //if no colormap is being rendered | |
129 | + if (r1 < 0) glColor3f(1.0, 0.0, 0.0); //if r1 is negative, render it red | |
130 | + else glColor3f(0.0, 1.0, 0.0); //otherwise render in green | |
131 | + } | |
132 | + if (magnitude) { //if the magnitude is being rendered, calculate the magnitude of r | |
133 | + if (r1 < 0) r1 = -r1; | |
134 | + } | |
135 | + stim::vec3<T> s1(r1, theta, phi - d_phi); | |
136 | + stim::vec3<T> c1 = s1.sph2cart(); | |
137 | + stim::vec3<T> n1; | |
138 | + if (!displacement) n1 = c1; | |
139 | + else n1 = Sd.dphi(theta, phi - d_phi).cross(Sd.dtheta(theta, phi - d_phi)); | |
140 | + | |
141 | + //std::cout << theta_t << " " << (float)(phi_i - 1) / (float)N << std::endl; | |
142 | + object.TexCoord(theta_t, 1.0/(2*(N)) + (float)(phi_i-1) / (float)N); | |
143 | + object.Normal(n1[0], n1[1], n1[2]); | |
144 | + object.Vertex(c1[0], c1[1], c1[2]); | |
145 | + } | |
146 | + object.End(); | |
147 | + } | |
148 | + object.matKd(); | |
149 | + object.save(filename); | |
150 | + } | |
151 | + | |
152 | + | |
153 | + | |
65 | 154 | /// This function renders the spherical harmonic to the current OpenGL context |
66 | 155 | void render() { |
67 | 156 | //glShadeModel(GL_FLAT); | ... | ... |