Commit 7f1ab3c201bf09a8ff02f8bc66109d5be3a5252d
1 parent
b7f8c759
fixed problems with dynamic network class
Showing
11 changed files
with
2327 additions
and
2782 deletions
Show diff stats
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 |