Commit e6712a7291e3ec5f3d3738c8a8b6985c460fbe32

Authored by sberisha
2 parents 35534291 176a9b1c

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

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