Commit df036f4ca570f117333f8a4551f8ba66b889fb42

Authored by David Mayerich
1 parent ac788020

added the network class, made relevant changes to the obj class to support it

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