Commit df036f4ca570f117333f8a4551f8ba66b889fb42
1 parent
ac788020
added the network class, made relevant changes to the obj class to support it
Showing
2 changed files
with
375 additions
and
0 deletions
Show diff stats
1 | +#ifndef STIM_NETWORK_H | |
2 | +#define STIM_NETWORK_H | |
3 | + | |
4 | +#include <stim/math/mathvec.h> | |
5 | +#include <list> | |
6 | +#include <ANN/ANN.h> | |
7 | + | |
8 | +namespace stim{ | |
9 | + | |
10 | +/** This class provides an interface for dealing with biological networks. | |
11 | + * It takes the following aspects into account: | |
12 | + * 1) Network geometry and centerlines | |
13 | + * 2) Network connectivity (a graph structure can be extracted) | |
14 | + * 3) Network surface structure (the surface is represented as a triangular mesh and referenced to the centerline) | |
15 | + */ | |
16 | + | |
17 | +template<typename T> | |
18 | +class network{ | |
19 | + | |
20 | + //helper classes | |
21 | + /// Stores information about a geometric point on the network centerline (including point position and radius) | |
22 | + //template<typename T> | |
23 | + class point : public stim::vec<T>{ | |
24 | + | |
25 | + public: | |
26 | + T r; | |
27 | + | |
28 | + point() : stim::vec<T>(){} | |
29 | + | |
30 | + //casting constructor | |
31 | + point(stim::vec<T> rhs) : stim::vec<T>(rhs){} | |
32 | + }; | |
33 | + | |
34 | + //template<typename T> | |
35 | + class t_node; | |
36 | + class fiber; | |
37 | + | |
38 | + //create typedefs for the iterators to simplify the network code | |
39 | + typedef typename std::list< fiber >::iterator fiber_i; | |
40 | + typedef typename std::list< t_node >::iterator t_node_i; | |
41 | + | |
42 | + /// Stores information about a single capillary (a length of vessel between two branch or end points) | |
43 | + //template<typename T> | |
44 | + class fiber{ | |
45 | + | |
46 | + public: | |
47 | + std::list< point > P; //geometric point positions | |
48 | + | |
49 | + typename std::list< t_node >::iterator n[2]; //indices to terminal nodes | |
50 | + unsigned int id; | |
51 | + | |
52 | + public: | |
53 | + | |
54 | + /// Calculate the length of the fiber and return it. | |
55 | + T length(){ | |
56 | + | |
57 | + point p0, p1; | |
58 | + T l = 0; //initialize the length to zero | |
59 | + | |
60 | + //for each point | |
61 | + typename std::list< point >::iterator i; //create a point iterator | |
62 | + for(i = P.begin(); i != P.end(); i++){ //for each point in the fiber | |
63 | + | |
64 | + if(i == P.begin()) //if this is the first point, just store it | |
65 | + p1 = *i; | |
66 | + else{ //if this is any other point | |
67 | + p0 = p1; //shift p1->p0 | |
68 | + p1 = *i; //set p1 to the new point | |
69 | + l += (p1 - p0).len(); //add the length of p1 - p0 to the running sum | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + return l; //return the length | |
74 | + } | |
75 | + | |
76 | + T radius(T& length){ | |
77 | + | |
78 | + point p0, p1; //temporary variables to store point positions | |
79 | + T r0, r1; //temporary variables to store radii at points | |
80 | + T l, r; //temporary variable to store the length and average radius of a fiber segment | |
81 | + T length_sum = 0; //initialize the length to zero | |
82 | + T radius_sum = 0; //initialize the radius sum to zero | |
83 | + | |
84 | + //for each point | |
85 | + typename std::list< point >::iterator i; //create a point iterator | |
86 | + for(i = P.begin(); i != P.end(); i++){ //for each point in the fiber | |
87 | + | |
88 | + if(i == P.begin()){ //if this is the first point, just store it | |
89 | + p1 = *i; | |
90 | + r1 = i->r; | |
91 | + } | |
92 | + else{ //if this is any other point | |
93 | + p0 = p1; //shift p1->p0 and r1->r0 | |
94 | + r0 = r1; | |
95 | + p1 = *i; //set p1 to the new point | |
96 | + r1 = i->r; //and r1 | |
97 | + | |
98 | + l = (p1 - p0).len(); //calculate the length of the p0-p1 segment | |
99 | + r = (r0 + r1) / 2; //calculate the average radius of the segment | |
100 | + | |
101 | + radius_sum += r * l; //add the radius scaled by the length to a running sum | |
102 | + length_sum += l; //add the length of p1 - p0 to the running sum | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + length = length_sum; //store the total length | |
107 | + return radius_sum / length; //return the average radius of the fiber | |
108 | + } | |
109 | + | |
110 | + std::string str(){ | |
111 | + std::stringstream ss; | |
112 | + | |
113 | + //create an iterator for the point list | |
114 | + typename std::list<point>::iterator i; | |
115 | + for(i = P.begin(); i != P.end(); i++){ | |
116 | + ss<<i->str()<<" r = "<<i->r<<std::endl; | |
117 | + } | |
118 | + | |
119 | + return ss.str(); | |
120 | + } | |
121 | + }; | |
122 | + | |
123 | + /// Terminal node for a capillary. This is analogous to a graph vertex and contains a list of edge indices. | |
124 | + //template<typename T> | |
125 | + class t_node{ | |
126 | + | |
127 | + public: | |
128 | + | |
129 | + unsigned int id; | |
130 | + | |
131 | + //lists of edge indices for capillaries | |
132 | + //the "in" and "out" just indicate how the geometry is defined: | |
133 | + // edges in the "in" list are geometrically oriented such that the terminal node is last | |
134 | + // edges in the "out" list are geometrically oriented such that the terminal node is first | |
135 | + std::list< fiber_i > in; //edge indices for incoming capillaries | |
136 | + std::list< fiber_i > out; //edge indices for outgoing capillaries | |
137 | + | |
138 | + std::string str(){ | |
139 | + | |
140 | + std::stringstream ss; | |
141 | + | |
142 | + ss<<id<<": "; //output the node ID | |
143 | + | |
144 | + //output the IDs for both lists | |
145 | + typename std::list< fiber_i >::iterator f; | |
146 | + | |
147 | + for(f = in.begin(); f != in.end(); f++){ | |
148 | + | |
149 | + if(f != in.begin()) | |
150 | + ss<<", "; | |
151 | + ss<<(*f)->n[0]->id; | |
152 | + } | |
153 | + | |
154 | + //if there are nodes in both lists, separate them by a comma | |
155 | + if(out.size() > 0 && in.size() > 0) | |
156 | + ss<<", "; | |
157 | + | |
158 | + for(f = out.begin(); f != out.end(); f++){ | |
159 | + | |
160 | + if(f != out.begin()) | |
161 | + ss<<", "; | |
162 | + ss<<(*f)->n[1]->id; | |
163 | + } | |
164 | + | |
165 | + | |
166 | + return ss.str(); | |
167 | + | |
168 | + | |
169 | + | |
170 | + } | |
171 | + }; | |
172 | + | |
173 | + | |
174 | + | |
175 | + | |
176 | +protected: | |
177 | + | |
178 | + //list of terminal nodes | |
179 | + std::list<t_node> N; | |
180 | + | |
181 | + //list of fibers | |
182 | + std::list<fiber> F; | |
183 | + | |
184 | + /// Sets a unique ID for each terminal node and fiber | |
185 | + void set_names(){ | |
186 | + | |
187 | + unsigned int i; | |
188 | + | |
189 | + i = 0; | |
190 | + for(t_node_i ti = N.begin(); ti != N.end(); ti++) | |
191 | + ti->id = i++; | |
192 | + | |
193 | + i = 0; | |
194 | + for(fiber_i fi = F.begin(); fi != F.end(); fi++) | |
195 | + fi->id = i++; | |
196 | + } | |
197 | + | |
198 | +public: | |
199 | + | |
200 | + std::string str(){ | |
201 | + | |
202 | + | |
203 | + //assign names to elements of the network | |
204 | + set_names(); | |
205 | + | |
206 | + //create a stringstream for output | |
207 | + std::stringstream ss; | |
208 | + | |
209 | + //output the nodes | |
210 | + ss<<"Nodes----------------------------"<<std::endl; | |
211 | + for(t_node_i i = N.begin(); i != N.end(); i++){ | |
212 | + ss<<i->str()<<std::endl; | |
213 | + } | |
214 | + | |
215 | + //output the fibers | |
216 | + ss<<std::endl<<"Fibers---------------------------"<<std::endl; | |
217 | + | |
218 | + T length, radius; | |
219 | + //output every fiber | |
220 | + for(fiber_i f = F.begin(); f != F.end(); f++){ | |
221 | + | |
222 | + //calculate the length and average radius | |
223 | + radius = f->radius(length); | |
224 | + | |
225 | + //output the IDs of the terminal nodes | |
226 | + ss<<f->n[0]->id<<" -- "<<f->n[1]->id<<": length = "<<length<<", average radius = "<<radius<<std::endl; | |
227 | + } | |
228 | + | |
229 | + return ss.str(); | |
230 | + } | |
231 | + | |
232 | + /// Load a network from an OBJ object | |
233 | + void load( stim::obj<T> object){ | |
234 | + | |
235 | + //get the number of vertices in the object | |
236 | + unsigned int nV = object.numV(); | |
237 | + | |
238 | + //allocate an array of pointers to nodes, which will be used to preserve connectivity | |
239 | + //initiate all values to T.end() | |
240 | + std::vector< t_node_i > node_hash(nV, N.end()); | |
241 | + | |
242 | + unsigned int nL = object.numL(); //get the number of lines in the OBJ | |
243 | + | |
244 | + //for each line in the OBJ structure | |
245 | + for(unsigned int li = 0; li < nL; li++){ | |
246 | + | |
247 | + F.push_back(fiber()); //push a new fiber onto the fiber list | |
248 | + | |
249 | + fiber_i f = --(F.end()); //get an iterator to the new fiber | |
250 | + | |
251 | + //----------Handle the terminating nodes for the fiber | |
252 | + | |
253 | + //get the indices of the line vertices | |
254 | + std::vector< unsigned int > Li = object.getL_Vi(li); | |
255 | + unsigned int i0 = Li.front() - 1; | |
256 | + unsigned int i1 = Li.back() - 1; | |
257 | + | |
258 | + //deal with the first end point of the capillary | |
259 | + if(node_hash[i0] != N.end()){ //if the node has been used before | |
260 | + (*f).n[0] = node_hash[i0]; //assign the node to the new capillary | |
261 | + (*node_hash[i0]).out.push_back(f); //add an out pointer to the existing node | |
262 | + } | |
263 | + else{ //otherwise | |
264 | + N.push_back(t_node()); //create a new node and add it to the node list | |
265 | + t_node_i t = --(N.end()); //get an iterator to the new node | |
266 | + node_hash[i0] = t; //add a pointer to the new node to the hash list | |
267 | + (*f).n[0] = t; //add a pointer to the new node to the capillary | |
268 | + (*t).out.push_back(f); //add a pointer to the capillary to the new node | |
269 | + } | |
270 | + | |
271 | + //deal with the last end point of the capillary | |
272 | + if(node_hash[i1] != N.end()){ | |
273 | + (*f).n[1] = node_hash[i1]; | |
274 | + (*node_hash[i1]).in.push_back(f); | |
275 | + } | |
276 | + else{ | |
277 | + N.push_back(t_node()); | |
278 | + t_node_i t = --(N.end()); | |
279 | + node_hash[i1] = t; //add the new node to the hash list | |
280 | + (*f).n[1] = t; | |
281 | + (*t).in.push_back(f); | |
282 | + } | |
283 | + | |
284 | + //-------------Handle the geometric points for the fiber | |
285 | + std::vector< vec<T> > L = object.getL_V(li); | |
286 | + std::vector< vec<T> > R = object.getL_VT(li); | |
287 | + | |
288 | + unsigned int nP = L.size(); //get the number of geometric points in the fiber | |
289 | + //for each vertex in the fiber | |
290 | + for(unsigned int pi = 0; pi < nP; pi++){ | |
291 | + point p = (point)L[pi]; //move the geometric coordinates into a point structure | |
292 | + p.r = R[pi][0]; //store the radius | |
293 | + f->P.push_back(p); //push the point onto the current fiber | |
294 | + } | |
295 | + } | |
296 | + | |
297 | + } //end load() | |
298 | + | |
299 | + /// This function returns the information necessary for a simple graph-based physical (ex. fluid) simulation. | |
300 | + | |
301 | + /// @param n0 is a array which will contain the list of source nodes | |
302 | + /// @param n1 is a array which will contain the list of destination nodes | |
303 | + /// @param length is a array containing the lengths of fibers in the network | |
304 | + /// @param radius is a array containing the average radii of fibers in the network | |
305 | + void build_simgraph(std::vector<unsigned int>& n0, std::vector<unsigned int>& n1, std::vector<T>& length, std::vector<T>& radius){ | |
306 | + | |
307 | + //determine the number of fibers in the network | |
308 | + unsigned int nF = F.size(); | |
309 | + | |
310 | + //allocate the necessary space to store the fiber information | |
311 | + n0.resize(nF); | |
312 | + n1.resize(nF); | |
313 | + length.resize(nF); | |
314 | + radius.resize(nF); | |
315 | + | |
316 | + //assign names (identifiers) to the network components | |
317 | + set_names(); | |
318 | + | |
319 | + //fill the arrays | |
320 | + unsigned int i = 0; | |
321 | + T l, r; | |
322 | + for(fiber_i f = F.begin(); f != F.end(); f++){ | |
323 | + n0[i] = f->n[0]->id; //get the identifiers for the first and second nodes for the current fiber | |
324 | + n1[i] = f->n[1]->id; | |
325 | + | |
326 | + r = f->radius(l); //get the length and radius of the capillary (calculated at the same time) | |
327 | + | |
328 | + radius[i] = r; //store the radius in the output array | |
329 | + length[i] = l; //store the length in the output array | |
330 | + | |
331 | + i++; //increment the array index | |
332 | + } | |
333 | + | |
334 | + | |
335 | + } | |
336 | + | |
337 | +}; | |
338 | + | |
339 | +}; //end namespace stim | |
340 | + | |
341 | + | |
342 | +#endif | ... | ... |
stim/visualization/obj.h
... | ... | @@ -600,9 +600,42 @@ public: |
600 | 600 | } |
601 | 601 | |
602 | 602 | return l; |
603 | + } | |
604 | + | |
605 | + /// Returns a vector containing the list of texture coordinates associated with each point of a line. | |
606 | + | |
607 | + /// @param i is the index of the desired line | |
608 | + std::vector< stim::vec<T> > getL_VT(unsigned int i){ | |
609 | + | |
610 | + //get the number of points in the specified line | |
611 | + unsigned int nP = L[i].size(); | |
612 | + | |
613 | + //create a vector of points | |
614 | + std::vector< stim::vec<T> > l; | |
615 | + | |
616 | + //set the size of the vector | |
617 | + l.resize(nP); | |
618 | + | |
619 | + //copy the points from the point list to the stim vector | |
620 | + unsigned int pi; | |
621 | + for(unsigned int p = 0; p < nP; p++){ | |
622 | + | |
623 | + //get the index of the geometry point | |
624 | + pi = L[i][p][1] - 1; | |
625 | + | |
626 | + //get the coordinates of the current point | |
627 | + stim::vec<T> newP = VT[pi]; | |
628 | + | |
629 | + //copy the point into the vector | |
630 | + l[p] = newP; | |
631 | + } | |
632 | + | |
633 | + return l; | |
603 | 634 | |
604 | 635 | } |
605 | 636 | |
637 | + | |
638 | + | |
606 | 639 | }; |
607 | 640 | |
608 | 641 | ... | ... |