Commit f1bb77987f4b4912b5327d148963de4487a4f52b

Authored by David Mayerich
1 parent 9832489d

moved the static network class into the netmets repository from STIMLIB, where it is no longer used

centerline.h 0 → 100644
  1 +#ifndef STIM_CENTERLINE_H
  2 +#define STIM_CENTERLINE_H
  3 +
  4 +#include <vector>
  5 +#include <stim/math/vec3.h>
  6 +#include <stim/structures/kdtree.cuh>
  7 +
  8 +namespace stim{
  9 +
  10 +/** This class stores information about a single fiber represented as a set of geometric points
  11 + * between two branch or end points. This class is used as a fundamental component of the stim::network
  12 + * class to describe an interconnected (often biological) network.
  13 + */
  14 +template<typename T>
  15 +class centerline : public std::vector< stim::vec3<T> >{
  16 +
  17 +protected:
  18 +
  19 + std::vector<T> L; //stores the integrated length along the fiber (used for parameterization)
  20 +
  21 + ///Return the normalized direction vector at point i (average of the incoming and outgoing directions)
  22 + vec3<T> d(size_t i) {
  23 + if (size() <= 1) return vec3<T>(0, 0, 0); //if there is insufficient information to calculate the direction, return a null vector
  24 + 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
  25 + if (i == 0) return (at(1) - at(0)).norm(); //the first direction vector is oriented towards the first line segment
  26 + if (i == size() - 1) return (at(size() - 1) - at(size() - 2)).norm(); //the last direction vector is oriented towards the last line segment
  27 +
  28 + //all other direction vectors are the average direction of the two joined line segments
  29 + vec3<T> a = at(i) - at(i - 1);
  30 + vec3<T> b = at(i + 1) - at(i);
  31 + vec3<T> ab = a.norm() + b.norm();
  32 + return ab.norm();
  33 + }
  34 +
  35 + //initializes the integrated length vector to make parameterization easier, starting with index idx (all previous indices are assumed to be correct)
  36 + void update_L(size_t start = 0) {
  37 + L.resize(size()); //allocate space for the L array
  38 + if (start == 0) {
  39 + L[0] = 0; //initialize the length value for the first point to zero (0)
  40 + start++;
  41 + }
  42 +
  43 + stim::vec3<T> d;
  44 + for (size_t i = start; i < size(); i++) { //for each line segment in the centerline
  45 + d = at(i) - at(i - 1);
  46 + L[i] = L[i - 1] + d.len(); //calculate the running length total
  47 + }
  48 + }
  49 +
  50 + void init() {
  51 + if (size() == 0) return; //return if there aren't any points
  52 + update_L();
  53 + }
  54 +
  55 + /// Returns a stim::vec representing the point at index i
  56 +
  57 + /// @param i is an index of the desired centerline point
  58 + stim::vec<T> get_vec(unsigned i){
  59 + return std::vector< stim::vec3<T> >::at(i);
  60 + }
  61 +
  62 + ///finds the index of the point closest to the length l on the lower bound.
  63 + ///binary search.
  64 + size_t findIdx(T l) {
  65 + for (size_t i = 1; i < L.size(); i++) { //for each point in the centerline
  66 + if (L[i] > l) return i - 1; //if we have passed the desired length value, return i-1
  67 + }
  68 + return L.size() - 1;
  69 + /*size_t i = L.size() / 2;
  70 + size_t max = L.size() - 1;
  71 + size_t min = 0;
  72 + while (i < L.size() - 1){
  73 + if (l < L[i]) {
  74 + max = i;
  75 + i = min + (max - min) / 2;
  76 + }
  77 + else if (L[i] <= l && L[i + 1] >= l) {
  78 + break;
  79 + }
  80 + else {
  81 + min = i;
  82 + i = min + (max - min) / 2;
  83 + }
  84 + }
  85 + return i;*/
  86 + }
  87 +
  88 + ///Returns a position vector at the given length into the fiber (based on the pvalue).
  89 + ///Interpolates the radius along the line.
  90 + ///@param l: the location of the in the cylinder.
  91 + ///@param idx: integer location of the point closest to l but prior to it.
  92 + stim::vec3<T> p(T l, int idx) {
  93 + T rat = (l - L[idx]) / (L[idx + 1] - L[idx]);
  94 + stim::vec3<T> v1 = at(idx);
  95 + stim::vec3<T> v2 = at(idx + 1);
  96 + return(v1 + (v2 - v1)*rat);
  97 + }
  98 +
  99 +
  100 +public:
  101 +
  102 + using std::vector< stim::vec3<T> >::at;
  103 + using std::vector< stim::vec3<T> >::size;
  104 +
  105 + centerline() : std::vector< stim::vec3<T> >() {
  106 + init();
  107 + }
  108 + centerline(size_t n) : std::vector< stim::vec3<T> >(n){
  109 + init();
  110 + }
  111 + centerline(std::vector<stim::vec3<T> > pos) :
  112 + std::vector<stim::vec3<T> > (pos)
  113 + {
  114 + init();
  115 + }
  116 +
  117 + //overload the push_back function to update the length vector
  118 + void push_back(stim::vec3<T> p) {
  119 + std::vector< stim::vec3<T> >::push_back(p);
  120 + update_L(size() - 1);
  121 + }
  122 +
  123 + ///Returns a position vector at the given p-value (p value ranges from 0 to 1).
  124 + ///interpolates the position along the line.
  125 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  126 + stim::vec3<T> p(T pvalue) {
  127 + if (pvalue <= 0.0) return at(0); //return the first element
  128 + if (pvalue >= 1.0) return back(); //return the last element
  129 +
  130 + T l = pvalue*L[L.size() - 1];
  131 + int idx = findIdx(l);
  132 + return p(l, idx);
  133 + }
  134 +
  135 + ///Update centerline internal parameters (currently the L vector)
  136 + void update() {
  137 + init();
  138 + }
  139 + ///Return the length of the entire centerline
  140 + T length() {
  141 + return L.back();
  142 + }
  143 +
  144 +
  145 + /// stitch two centerlines
  146 + ///@param c1, c2: two centerlines
  147 + ///@param sigma: sample rate
  148 + static std::vector< stim::centerline<T> > stitch(stim::centerline<T> c1, stim::centerline<T> c2 = stim::centerline<T>()) {
  149 +
  150 + std::vector< stim::centerline<T> > result;
  151 + stim::centerline<T> new_centerline;
  152 + stim::vec3<T> new_vertex;
  153 +
  154 + // if only one centerline, stitch itself!
  155 + if (c2.size() == 0) {
  156 + size_t num = c1.size();
  157 + size_t id = 100000; // store the downsample start position
  158 + T threshold;
  159 + if (num < 4) { // if the number of vertex is less than 4, do nothing
  160 + result.push_back(c1);
  161 + return result;
  162 + }
  163 + else {
  164 + // test geometry start vertex
  165 + stim::vec3<T> v1 = c1[1] - c1[0]; // vector from c1[0] to c1[1]
  166 + for (size_t p = 2; p < num; p++) { // 90° standard???
  167 + stim::vec3<T> v2 = c1[p] - c1[0];
  168 + float cosine = v2.dot(v1);
  169 + if (cosine < 0) {
  170 + id = p;
  171 + threshold = v2.len();
  172 + break;
  173 + }
  174 + }
  175 + if (id != 100000) { // find a downsample position on the centerline
  176 + T* c;
  177 + c = (T*)malloc(sizeof(T) * (num - id) * 3);
  178 + for (size_t p = id; p < num; p++) {
  179 + for (size_t d = 0; d < 3; d++) {
  180 + c[p * 3 + d] = c1[p][d];
  181 + }
  182 + }
  183 + stim::kdtree<T, 3> kdt;
  184 + kdt.create(c, num - id, 5); // create tree
  185 +
  186 + T* query = (T*)malloc(sizeof(T) * 3);
  187 + for (size_t d = 0; d < 3; d++)
  188 + query[d] = c1[0][d];
  189 + size_t index;
  190 + T dist;
  191 +
  192 + kdt.search(query, 1, &index, &dist);
  193 +
  194 + free(query);
  195 + free(c);
  196 +
  197 + if (dist > threshold) {
  198 + result.push_back(c1);
  199 + }
  200 + else {
  201 + // the loop part
  202 + new_vertex = c1[index];
  203 + new_centerline.push_back(new_vertex);
  204 + for (size_t p = 0; p < index + 1; p++) {
  205 + new_vertex = c1[p];
  206 + new_centerline.push_back(new_vertex);
  207 + }
  208 + result.push_back(new_centerline);
  209 + new_centerline.clear();
  210 +
  211 + // the tail part
  212 + for (size_t p = index; p < num; p++) {
  213 + new_vertex = c1[p];
  214 + new_centerline.push_back(new_vertex);
  215 + }
  216 + result.push_back(new_centerline);
  217 + }
  218 + }
  219 + else { // there is one potential problem that two positions have to be stitched
  220 + // test geometry end vertex
  221 + stim::vec3<T> v1 = c1[num - 2] - c1[num - 1];
  222 + for (size_t p = num - 2; p > 0; p--) { // 90° standard
  223 + stim::vec3<T> v2 = c1[p - 1] - c1[num - 1];
  224 + float cosine = v2.dot(v1);
  225 + if (cosine < 0) {
  226 + id = p;
  227 + threshold = v2.len();
  228 + break;
  229 + }
  230 + }
  231 + if (id != 100000) { // find a downsample position
  232 + T* c;
  233 + c = (T*)malloc(sizeof(T) * (id + 1) * 3);
  234 + for (size_t p = 0; p < id + 1; p++) {
  235 + for (size_t d = 0; d < 3; d++) {
  236 + c[p * 3 + d] = c1[p][d];
  237 + }
  238 + }
  239 + stim::kdtree<T, 3> kdt;
  240 + kdt.create(c, id + 1, 5); // create tree
  241 +
  242 + T* query = (T*)malloc(sizeof(T) * 1 * 3);
  243 + for (size_t d = 0; d < 3; d++)
  244 + query[d] = c1[num - 1][d];
  245 + size_t index;
  246 + T dist;
  247 +
  248 + kdt.search(query, 1, &index, &dist);
  249 +
  250 + free(query);
  251 + free(c);
  252 +
  253 + if (dist > threshold) {
  254 + result.push_back(c1);
  255 + }
  256 + else {
  257 + // the tail part
  258 + for (size_t p = 0; p < index + 1; p++) {
  259 + new_vertex = c1[p];
  260 + new_centerline.push_back(new_vertex);
  261 + }
  262 + result.push_back(new_centerline);
  263 + new_centerline.clear();
  264 +
  265 + // the loop part
  266 + for (size_t p = index; p < num; p++) {
  267 + new_vertex = c1[p];
  268 + new_centerline.push_back(new_vertex);
  269 + }
  270 + new_vertex = c1[index];
  271 + new_centerline.push_back(new_vertex);
  272 + result.push_back(new_centerline);
  273 + }
  274 + }
  275 + else { // no stitch position
  276 + result.push_back(c1);
  277 + }
  278 + }
  279 + }
  280 + }
  281 +
  282 +
  283 + // two centerlines
  284 + else {
  285 + // find stitch position based on nearest neighbors
  286 + size_t num1 = c1.size();
  287 + T* c = (T*)malloc(sizeof(T) * num1 * 3); // c1 as reference point
  288 + for (size_t p = 0; p < num1; p++) // centerline to array
  289 + for (size_t d = 0; d < 3; d++) // because right now my kdtree code is a relatively close code, it has its own structure
  290 + c[p * 3 + d] = c1[p][d]; // I will merge it into stimlib totally in the near future
  291 +
  292 + stim::kdtree<T, 3> kdt; // kdtree object
  293 + kdt.create(c, num1, 5); // create tree
  294 +
  295 + size_t num2 = c2.size();
  296 + T* query = (T*)malloc(sizeof(T) * num2 * 3); // c2 as query point
  297 + for (size_t p = 0; p < num2; p++) {
  298 + for (size_t d = 0; d < 3; d++) {
  299 + query[p * 3 + d] = c2[p][d];
  300 + }
  301 + }
  302 + std::vector<size_t> index(num2);
  303 + std::vector<T> dist(num2);
  304 +
  305 + kdt.search(query, num2, &index[0], &dist[0]); // find the nearest neighbors in c1 for c2
  306 +
  307 + // clear up
  308 + free(query);
  309 + free(c);
  310 +
  311 + // find the average vertex distance of one centerline
  312 + T sigma1 = 0;
  313 + T sigma2 = 0;
  314 + for (size_t p = 0; p < num1 - 1; p++)
  315 + sigma1 += (c1[p] - c1[p + 1]).len();
  316 + for (size_t p = 0; p < num2 - 1; p++)
  317 + sigma2 += (c2[p] - c2[p + 1]).len();
  318 + sigma1 /= (num1 - 1);
  319 + sigma2 /= (num2 - 1);
  320 + float threshold = 4 * (sigma1 + sigma2) / 2; // better way to do this?
  321 +
  322 + T min_d = *std::min_element(dist.begin(), dist.end()); // find the minimum distance between c1 and c2
  323 +
  324 + if (min_d > threshold) { // if the minimum distance is too large
  325 + result.push_back(c1);
  326 + result.push_back(c2);
  327 +
  328 +#ifdef DEBUG
  329 + std::cout << "The distance between these two centerlines is too large" << std::endl;
  330 +#endif
  331 + }
  332 + else {
  333 + // auto smallest = std::min_element(dist.begin(), dist.end());
  334 + unsigned int smallest = std::min_element(dist.begin(), dist.end());
  335 + // auto i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
  336 + unsigned int i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
  337 +
  338 + // stitch position in c1 and c2
  339 + int id1 = index[i];
  340 + int id2 = i;
  341 +
  342 + // actually there are two cases
  343 + // first one inacceptable
  344 + // second one acceptable
  345 + if (id1 != 0 && id1 != num1 - 1 && id2 != 0 && id2 != num2 - 1) { // only stitch one end vertex to another centerline
  346 + result.push_back(c1);
  347 + result.push_back(c2);
  348 + }
  349 + else {
  350 + if (id1 == 0 || id1 == num1 - 1) { // if the stitch vertex is the first or last vertex of c1
  351 + // for c2, consider two cases(one degenerate case)
  352 + if (id2 == 0 || id2 == num2 - 1) { // case 1, if stitch position is also on the end of c2
  353 + // we have to decide which centerline get a new vertex, based on direction
  354 + // for c1, computer the direction change angle
  355 + stim::vec3<T> v1, v2;
  356 + float alpha1, alpha2; // direction change angle
  357 + if (id1 == 0)
  358 + v1 = (c1[1] - c1[0]).norm();
  359 + else
  360 + v1 = (c1[num1 - 2] - c1[num1 - 1]).norm();
  361 + v2 = (c2[id2] - c1[id1]).norm();
  362 + alpha1 = v1.dot(v2);
  363 + if (id2 == 0)
  364 + v1 = (c2[1] - c2[0]).norm();
  365 + else
  366 + v1 = (c2[num2 - 2] - c2[num2 - 1]).norm();
  367 + v2 = (c1[id1] - c2[id2]).norm();
  368 + alpha2 = v1.dot(v2);
  369 + if (abs(alpha1) > abs(alpha2)) { // add the vertex to c1 in order to get smooth connection
  370 + // push back c1
  371 + if (id1 == 0) { // keep geometry information
  372 + new_vertex = c2[id2];
  373 + new_centerline.push_back(new_vertex);
  374 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  375 + new_vertex = c1[p];
  376 + new_centerline.push_back(new_vertex);
  377 + }
  378 + }
  379 + else {
  380 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  381 + new_vertex = c1[p];
  382 + new_centerline.push_back(new_vertex);
  383 + }
  384 + new_vertex = c2[id2];
  385 + new_centerline.push_back(new_vertex);
  386 + }
  387 + result.push_back(new_centerline);
  388 + new_centerline.clear();
  389 +
  390 + // push back c2
  391 + for (size_t p = 0; p < num2; p++) {
  392 + new_vertex = c2[p];
  393 + new_centerline.push_back(new_vertex);
  394 + }
  395 + result.push_back(new_centerline);
  396 + }
  397 + else { // add the vertex to c2 in order to get smooth connection
  398 + // push back c1
  399 + for (size_t p = 0; p < num1; p++) {
  400 + new_vertex = c1[p];
  401 + new_centerline.push_back(new_vertex);
  402 + }
  403 + result.push_back(new_centerline);
  404 + new_centerline.clear();
  405 +
  406 + // push back c2
  407 + if (id2 == 0) { // keep geometry information
  408 + new_vertex = c1[id1];
  409 + new_centerline.push_back(new_vertex);
  410 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  411 + new_vertex = c2[p];
  412 + new_centerline.push_back(new_vertex);
  413 + }
  414 + }
  415 + else {
  416 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  417 + new_vertex = c2[p];
  418 + new_centerline.push_back(new_vertex);
  419 + }
  420 + new_vertex = c1[id1];
  421 + new_centerline.push_back(new_vertex);
  422 + }
  423 + result.push_back(new_centerline);
  424 + }
  425 + }
  426 + else { // case 2, the stitch position is on c2
  427 + // push back c1
  428 + if (id1 == 0) { // keep geometry information
  429 + new_vertex = c2[id2];
  430 + new_centerline.push_back(new_vertex);
  431 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  432 + new_vertex = c1[p];
  433 + new_centerline.push_back(new_vertex);
  434 + }
  435 + }
  436 + else {
  437 + for (size_t p = 0; p < num1; p++) { // geometry end vertex on c1 -> geometry start vertex on c1 -> stitch vertex on c2
  438 + new_vertex = c1[p];
  439 + new_centerline.push_back(new_vertex);
  440 + }
  441 + new_vertex = c2[id2];
  442 + new_centerline.push_back(new_vertex);
  443 + }
  444 + result.push_back(new_centerline);
  445 + new_centerline.clear();
  446 +
  447 + // push back c2
  448 + for (size_t p = 0; p < id2 + 1; p++) { // first part
  449 + new_vertex = c2[p];
  450 + new_centerline.push_back(new_vertex);
  451 + }
  452 + result.push_back(new_centerline);
  453 + new_centerline.clear();
  454 +
  455 + for (size_t p = id2; p < num2; p++) { // second part
  456 + new_vertex = c2[p];
  457 + new_centerline.push_back(new_vertex);
  458 + }
  459 + result.push_back(new_centerline);
  460 + }
  461 + }
  462 + else { // if the stitch vertex is the first or last vertex of c2
  463 + // push back c2
  464 + if (id2 == 0) { // keep geometry information
  465 + new_vertex = c1[id1];
  466 + new_centerline.push_back(new_vertex);
  467 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c1 -> geometry start vertex on c2 -> geometry end vertex on c2
  468 + new_vertex = c2[p];
  469 + new_centerline.push_back(new_vertex);
  470 + }
  471 + }
  472 + else {
  473 + for (size_t p = 0; p < num2; p++) { // geometry end vertex on c2 -> geometry start vertex on c2 -> stitch vertex on c1
  474 + new_vertex = c2[p];
  475 + new_centerline.push_back(new_vertex);
  476 + }
  477 + new_vertex = c1[id1];
  478 + new_centerline.push_back(new_vertex);
  479 + result.push_back(new_centerline);
  480 + new_centerline.clear();
  481 +
  482 + // push back c1
  483 + for (size_t p = 0; p < id1 + 1; p++) { // first part
  484 + new_vertex = c1[p];
  485 + new_centerline.push_back(new_vertex);
  486 + }
  487 + result.push_back(new_centerline);
  488 + new_centerline.clear();
  489 +
  490 + for (size_t p = id1; p < num1; p++) { // second part
  491 + new_vertex = c1[p];
  492 + new_centerline.push_back(new_vertex);
  493 + }
  494 + result.push_back(new_centerline);
  495 + }
  496 + }
  497 + }
  498 + }
  499 + }
  500 + return result;
  501 + }
  502 +
  503 + /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
  504 + std::vector< stim::centerline<T> > split(unsigned int idx){
  505 +
  506 + std::vector< stim::centerline<T> > fl; //create an array to store up to two fibers
  507 + size_t N = size();
  508 +
  509 + //if the index is an end point, only the existing fiber is returned
  510 + if(idx == 0 || idx == N-1){
  511 + fl.resize(1); //set the size of the fiber to 1
  512 + fl[0] = *this; //copy the current fiber
  513 + }
  514 +
  515 + //if the index is not an end point
  516 + else{
  517 +
  518 + unsigned int N1 = idx + 1; //calculate the size of both fibers
  519 + unsigned int N2 = N - idx;
  520 +
  521 + fl.resize(2); //set the array size to 2
  522 +
  523 + fl[0] = stim::centerline<T>(N1); //set the size of each fiber
  524 + fl[1] = stim::centerline<T>(N2);
  525 +
  526 + //copy both halves of the fiber
  527 + unsigned int i;
  528 +
  529 + //first half
  530 + for(i = 0; i < N1; i++) //for each centerline point
  531 + fl[0][i] = std::vector< stim::vec3<T> >::at(i);
  532 + fl[0].init(); //initialize the length vector
  533 +
  534 + //second half
  535 + for(i = 0; i < N2; i++)
  536 + fl[1][i] = std::vector< stim::vec3<T> >::at(idx+i);
  537 + fl[1].init(); //initialize the length vector
  538 + }
  539 +
  540 + return fl; //return the array
  541 +
  542 + }
  543 +
  544 + /// Outputs the fiber as a string
  545 + std::string str(){
  546 + std::stringstream ss;
  547 + size_t N = std::vector< stim::vec3<T> >::size();
  548 + ss << "---------[" << N << "]---------" << std::endl;
  549 + for (size_t i = 0; i < N; i++)
  550 + ss << std::vector< stim::vec3<T> >::at(i) << std::endl;
  551 + ss << "--------------------" << std::endl;
  552 +
  553 + return ss.str();
  554 + }
  555 +
  556 + /// Back method returns the last point in the fiber
  557 + stim::vec3<T> back(){
  558 + return std::vector< stim::vec3<T> >::back();
  559 + }
  560 +
  561 + ////resample a fiber in the network
  562 + stim::centerline<T> resample(T spacing)
  563 + {
  564 + //std::cout<<"fiber::resample()"<<std::endl;
  565 +
  566 + stim::vec3<T> v; //v-direction vector of the segment
  567 + stim::vec3<T> p; //- intermediate point to be added
  568 + stim::vec3<T> p1; // p1 - starting point of an segment on the fiber,
  569 + stim::vec3<T> p2; // p2 - ending point,
  570 + //double sum=0; //distance summation
  571 +
  572 + size_t N = size();
  573 +
  574 + centerline<T> new_c; // initialize list of new resampled points on the fiber
  575 + // for each point on the centerline (skip if it is the last point on centerline)
  576 + for(unsigned int f=0; f< N-1; f++)
  577 + {
  578 + p1 = at(f);
  579 + p2 = at(f+1);
  580 + v = p2 - p1;
  581 +
  582 + T lengthSegment = v.len(); //find Length of the segment as distance between the starting and ending points of the segment
  583 +
  584 + if(lengthSegment >= spacing){ // if length of the segment is greater than standard deviation resample
  585 +
  586 + // repeat resampling until accumulated stepsize is equsl to length of the segment
  587 + for(T step=0.0; step<lengthSegment; step+=spacing){
  588 + // calculate the resampled point by travelling step size in the direction of normalized gradient vector
  589 + p = p1 + v * (step / lengthSegment);
  590 +
  591 + // add this resampled points to the new fiber list
  592 + new_c.push_back(p);
  593 + }
  594 + }
  595 + 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
  596 + new_c.push_back(at(f));
  597 + }
  598 + new_c.push_back(at(N-1)); //add the last point on the fiber to the new fiber list
  599 + //centerline newFiber(newPointList);
  600 + return new_c;
  601 + }
  602 +
  603 +};
  604 +
  605 +
  606 +
  607 +} //end namespace stim
  608 +
  609 +
  610 +
  611 +#endif
... ...
cylinder.h 0 → 100644
  1 +#ifndef STIM_CYLINDER_H
  2 +#define STIM_CYLINDER_H
  3 +#include <iostream>
  4 +#include <stim/math/circle.h>
  5 +#include "centerline.h"
  6 +#include <stim/visualization/obj.h>
  7 +
  8 +
  9 +namespace stim
  10 +{
  11 +template<typename T>
  12 +class cylinder : public centerline<T> {
  13 +protected:
  14 +
  15 + using stim::centerline<T>::d;
  16 +
  17 + std::vector< stim::vec3<T> > U; //stores the array of U vectors defining the Frenet frame
  18 + std::vector< T > R; //stores a list of magnitudes for each point in the centerline (assuming mags[0] is the radius)
  19 +
  20 + using stim::centerline<T>::findIdx;
  21 +
  22 + void init() {
  23 + U.resize(size()); //allocate space for the frenet frame vectors
  24 +// if (R.size() != 0)
  25 + R.resize(size());
  26 +
  27 + stim::circle<T> c; //create a circle
  28 + stim::vec3<T> d0, d1;
  29 + for (size_t i = 0; i < size() - 1; i++) { //for each line segment in the centerline
  30 + c.rotate(d(i)); //rotate the circle to match that normal
  31 + U[i] = c.U; //save the U vector from the circle
  32 + }
  33 + U[size() - 1] = c.U; //for the last point, duplicate the final frenet frame vector
  34 + }
  35 +
  36 + //calculates the U values for each point to initialize the frenet frame
  37 + // this function assumes that the centerline has already been set
  38 +
  39 +public:
  40 +
  41 + using stim::centerline<T>::size;
  42 + using stim::centerline<T>::at;
  43 + using stim::centerline<T>::L;
  44 + using stim::centerline<T>::length;
  45 +
  46 + cylinder() : centerline<T>(){}
  47 +
  48 + cylinder(centerline<T>c) : centerline<T>(c) {
  49 + init();
  50 + }
  51 +
  52 + cylinder(std::vector<stim::vec3<T> > p, std::vector<T> s)
  53 + : centerline<T>(p)
  54 + {
  55 + R = s;
  56 + init();
  57 + }
  58 +
  59 + cylinder(stim::centerline<T> p, std::vector<T> s)
  60 + {
  61 + //was d = s;
  62 + p = s;
  63 + init();
  64 + }
  65 +
  66 + //cylinder(centerline<T>c, T r) : centerline(c) {
  67 + // init();
  68 + // //add_mag(r);
  69 + //}
  70 +
  71 + //initialize a cylinder with a list of points and magnitude values
  72 + //cylinder(centerline<T>c, std::vector<T> r) : centerline(c){
  73 + // init();
  74 + // //add_mag(r);
  75 + //}
  76 +
  77 + //copy the original radius
  78 + void copy_r(std::vector<T> radius) {
  79 + for (unsigned i = 0; i < radius.size(); i++)
  80 + R[i] = radius[i];
  81 + }
  82 +
  83 + ///Returns magnitude i at the given length into the fiber (based on the pvalue).
  84 + ///Interpolates the position along the line.
  85 + ///@param l: the location of the in the cylinder.
  86 + ///@param idx: integer location of the point closest to l but prior to it.
  87 + T r(T l, int idx) {
  88 + T a = (l - L[idx]) / (L[idx + 1] - L[idx]);
  89 + T v2 = (R[idx] + (R[idx + 1] - R[idx])*a);
  90 +
  91 + return v2;
  92 + }
  93 +
  94 + ///Returns the ith magnitude at the given p-value (p value ranges from 0 to 1).
  95 + ///interpolates the radius along the line.
  96 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  97 + T rl(T pvalue) {
  98 + if (pvalue <= 0.0) return R[0];
  99 + if (pvalue >= 1.0) return R[size() - 1];
  100 +
  101 + T l = pvalue*L[L.size() - 1];
  102 + int idx = findIdx(l);
  103 + return r(l, idx);
  104 + }
  105 +
  106 + /// Returns the magnitude at the given index
  107 + /// @param i is the index of the desired point
  108 + /// @param r is the index of the magnitude value
  109 + T r(unsigned i) {
  110 + return R[i];
  111 + }
  112 +
  113 +
  114 + ///adds a magnitude to each point in the cylinder
  115 + /*void add_mag(V val = 0) {
  116 + if (M.size() == 0) M.resize(size()); //if the magnitude vector isn't initialized, resize it to match the centerline
  117 + for (size_t i = 0; i < size(); i++) //for each point
  118 + R[i].push_back(val); //add this value to the magnitude vector at each point
  119 + }*/
  120 +
  121 + //adds a magnitude based on a list of magnitudes for each point
  122 + /*void add_mag(std::vector<T> val) {
  123 + if (M.size() == 0) M.resize(size()); //if the magnitude vector isn't initialized, resize it to match the centerline
  124 + for (size_t i = 0; i < size(); i++) //for each point
  125 + R[i].push_back(val[i]); //add this value to the magnitude vector at each point
  126 + }*/
  127 +
  128 + //sets the value of magnitude m at point i
  129 + void set_r(size_t i, T r) {
  130 + R[i] = r;
  131 + }
  132 +
  133 + /*size_t nmags() {
  134 + if (M.size() == 0) return 0;
  135 + else return R[0].size();
  136 + }*/
  137 +
  138 + ///Returns a circle representing the cylinder cross section at point i
  139 + stim::circle<T> circ(size_t i) {
  140 + return stim::circle<T>(at(i), R[i], d(i), U[i]);
  141 + }
  142 +
  143 + ///Returns an OBJ object representing the cylinder with a radial tesselation value of N using magnitude m
  144 + stim::obj<T> OBJ(size_t N) {
  145 + stim::obj<T> out; //create an OBJ object
  146 + stim::circle<T> c0, c1;
  147 + std::vector< stim::vec3<T> > p0, p1; //allocate space for the point sets representing the circles bounding each cylinder segment
  148 + T u0, u1, v0, v1; //allocate variables to store running texture coordinates
  149 + for (size_t i = 1; i < size(); i++) { //for each line segment in the cylinder
  150 + c0 = circ(i - 1); //get the two circles bounding the line segment
  151 + c1 = circ(i);
  152 +
  153 + p0 = c0.points(N); //get t points for each of the end caps
  154 + p1 = c1.points(N);
  155 +
  156 + u0 = L[i - 1] / length(); //calculate the texture coordinate (u, v) where u runs along the cylinder length
  157 + u1 = L[i] / length();
  158 +
  159 + for (size_t n = 1; n < N; n++) { //for each point in the circle
  160 + v0 = (double)(n-1) / (double)(N - 1); //v texture coordinate runs around the cylinder
  161 + v1 = (double)(n) / (double)(N - 1);
  162 + out.Begin(OBJ_FACE); //start a face (quad)
  163 + out.TexCoord(u0, v0);
  164 + out.Vertex(p0[n - 1][0], p0[n - 1][1], p0[n - 1][2]); //output the points composing a strip of quads wrapping the cylinder segment
  165 + out.TexCoord(u1, v0);
  166 + out.Vertex(p1[n - 1][0], p1[n - 1][1], p1[n - 1][2]);
  167 +
  168 + out.TexCoord(u0, v1);
  169 + out.Vertex(p1[n][0], p1[n][1], p1[n][2]);
  170 + out.TexCoord(u1, v1);
  171 + out.Vertex(p0[n][0], p0[n][1], p0[n][2]);
  172 + out.End();
  173 + }
  174 + v0 = (double)(N - 2) / (double)(N - 1); //v texture coordinate runs around the cylinder
  175 + v1 = 1.0;
  176 + out.Begin(OBJ_FACE);
  177 + out.TexCoord(u0, v0);
  178 + out.Vertex(p0[N - 1][0], p0[N - 1][1], p0[N - 1][2]); //output the points composing a strip of quads wrapping the cylinder segment
  179 + out.TexCoord(u1, v0);
  180 + out.Vertex(p1[N - 1][0], p1[N - 1][1], p1[N - 1][2]);
  181 +
  182 + out.TexCoord(u0, v1);
  183 + out.Vertex(p1[0][0], p1[0][1], p1[0][2]);
  184 + out.TexCoord(u1, v1);
  185 + out.Vertex(p0[0][0], p0[0][1], p0[0][2]);
  186 + out.End();
  187 + }
  188 + return out;
  189 + }
  190 +
  191 + std::string str() {
  192 + std::stringstream ss;
  193 + size_t N = std::vector< stim::vec3<T> >::size();
  194 + ss << "---------[" << N << "]---------" << std::endl;
  195 + for (size_t i = 0; i < N; i++)
  196 + ss << std::vector< stim::vec3<T> >::at(i) << " r = " << R[i] << " u = " << U[i] << std::endl;
  197 + ss << "--------------------" << std::endl;
  198 +
  199 + return ss.str();
  200 + }
  201 +
  202 + /// Integrates a magnitude value along the cylinder.
  203 + /// @param m is the magnitude value to be integrated (this is usually the radius)
  204 + T integrate() {
  205 + T sum = 0; //initialize the integral to zero
  206 + if (L.size() == 1)
  207 + return sum;
  208 + else {
  209 + T m0, m1; //allocate space for both magnitudes in a single segment
  210 + m0 = R[0]; //initialize the first point and magnitude to the first point in the cylinder
  211 + T len = L[1]; //allocate space for the segment length
  212 +
  213 +
  214 + for (unsigned p = 1; p < size(); p++) { //for every consecutive point in the cylinder
  215 + m1 = R[p];
  216 + if (p > 1) len = (L[p] - L[p - 1]); //calculate the segment length using the L array
  217 + sum += (m0 + m1) / (T)2.0 * len; //add the average magnitude, weighted by the segment length
  218 + m0 = m1; //move to the next segment by shifting points
  219 + }
  220 + return sum; //return the integral
  221 + }
  222 + }
  223 +
  224 + /// Resamples the cylinder to provide a maximum distance of "spacing" between centerline points. All current
  225 + /// centerline points are guaranteed to exist in the new cylinder
  226 + /// @param spacing is the maximum spacing allowed between sample points
  227 + cylinder<T> resample(T spacing) {
  228 + cylinder<T> c = stim::centerline<T>::resample(spacing); //resample the centerline and use it to create a new cylinder
  229 +
  230 + //size_t nm = nmags(); //get the number of magnitude values in the current cylinder
  231 + //if (nm > 0) { //if there are magnitude values
  232 + // std::vector<T> magvec(nm, 0); //create a magnitude vector for a single point
  233 + // c.M.resize(c.size(), magvec); //allocate space for a magnitude vector at each point of the new cylinder
  234 + //}
  235 +
  236 + T l, t;
  237 + for (size_t i = 0; i < c.size(); i++) { //for each point in the new cylinder
  238 + l = c.L[i]; //get the length along the new cylinder
  239 + t = l / length(); //calculate the parameter value along the new cylinder
  240 + //for (size_t mag = 0; mag < nm; mag++) { //for each magnitude value
  241 + c.R[i] = r(t); //retrieve the interpolated magnitude from the current cylinder and store it in the new one
  242 + //}
  243 + }
  244 + return c;
  245 + }
  246 +
  247 + std::vector< stim::cylinder<T> > split(unsigned int idx) {
  248 +
  249 + unsigned N = size();
  250 + std::vector< stim::centerline<T> > LL;
  251 + LL.resize(2);
  252 + LL = (*this).centerline<T>::split(idx);
  253 + std::vector< stim::cylinder<T> > C(LL.size());
  254 + unsigned i = 0;
  255 + C[0] = LL[0];
  256 + //C[0].R.resize(idx);
  257 + for (; i < idx + 1; i++) {
  258 + //for(unsigned d = 0; d < 3; d++)
  259 + //C[0][i][d] = LL[0].c[i][d];
  260 + C[0].R[i] = R[i];
  261 + //C[0].R[i].resize(1);
  262 + }
  263 + if (C.size() == 2) {
  264 + C[1] = LL[1];
  265 + i--;
  266 + //C[1].M.resize(N - idx);
  267 + for (; i < N; i++) {
  268 + //for(unsigned d = 0; d < 3; d++)
  269 + //C[1][i - idx][d] = LL[1].c[i - idx][d];
  270 + C[1].R[i - idx] = R[i];
  271 + //C[1].M[i - idx].resize(1);
  272 + }
  273 + }
  274 +
  275 + return C;
  276 + }
  277 +
  278 +
  279 + /*
  280 + ///inits the cylinder from a list of points (std::vector of stim::vec3 --inP) and magnitudes (inM)
  281 + void
  282 + init(centerline inP, std::vector< std::vector<T> > inM) {
  283 + M = inM; //the magnitude vector can be copied directly
  284 + (*this) = inP; //the centerline can be copied to this class directly
  285 + stim::vec3<float> v1;
  286 + stim::vec3<float> v2;
  287 + e.resize(inP.size());
  288 +
  289 + norms.resize(inP.size());
  290 + Us.resize(inP.size());
  291 +
  292 + if(inP.size() < 2)
  293 + return;
  294 +
  295 + //calculate each L.
  296 + L.resize(inP.size()); //the number of precomputed lengths will equal the number of points
  297 + T temp = (T)0; //length up to that point
  298 + L[0] = temp;
  299 + for(size_t i = 1; i < L.size(); i++)
  300 + {
  301 + temp += (inP[i-1] - inP[i]).len();
  302 + L[i] = temp;
  303 + }
  304 +
  305 + stim::vec3<T> dr = (inP[1] - inP[0]).norm();
  306 + s = stim::circle<T>(inP[0], inR[0][0], dr, stim::vec3<T>(1,0,0));
  307 + norms[0] = s.N;
  308 + e[0] = s;
  309 + Us[0] = e[0].U;
  310 + for(size_t i = 1; i < inP.size()-1; i++)
  311 + {
  312 + s.center(inP[i]);
  313 + v1 = (inP[i] - inP[i-1]).norm();
  314 + v2 = (inP[i+1] - inP[i]).norm();
  315 + dr = (v1+v2).norm();
  316 + s.normal(dr);
  317 +
  318 + norms[i] = s.N;
  319 +
  320 + s.scale(inR[i][0]/inR[i-1][0]);
  321 + e[i] = s;
  322 + Us[i] = e[i].U;
  323 + }
  324 +
  325 + int j = inP.size()-1;
  326 + s.center(inP[j]);
  327 + dr = (inP[j] - inP[j-1]).norm();
  328 + s.normal(dr);
  329 +
  330 + norms[j] = s.N;
  331 +
  332 + s.scale(inR[j][0]/inR[j-1][0]);
  333 + e[j] = s;
  334 + Us[j] = e[j].U;
  335 + }
  336 +
  337 + ///returns the direction vector at point idx.
  338 + stim::vec3<T>
  339 + d(int idx)
  340 + {
  341 + if(idx == 0)
  342 + {
  343 + stim::vec3<T> temp(
  344 + c[idx+1][0]-c[idx][0],
  345 + c[idx+1][1]-c[idx][1],
  346 + c[idx+1][2]-c[idx][2]
  347 + );
  348 +// return (e[idx+1].P - e[idx].P).norm();
  349 + return (temp.norm());
  350 + }
  351 + else if(idx == N-1)
  352 + {
  353 + stim::vec3<T> temp(
  354 + c[idx][0]-c[idx+1][0],
  355 + c[idx][1]-c[idx+1][1],
  356 + c[idx][2]-c[idx+1][2]
  357 + );
  358 + // return (e[idx].P - e[idx-1].P).norm();
  359 + return (temp.norm());
  360 + }
  361 + else
  362 + {
  363 +// return (e[idx+1].P - e[idx].P).norm();
  364 +// stim::vec3<float> v1 = (e[idx].P-e[idx-1].P).norm();
  365 + stim::vec3<T> v1(
  366 + c[idx][0]-c[idx-1][0],
  367 + c[idx][1]-c[idx-1][1],
  368 + c[idx][2]-c[idx-1][2]
  369 + );
  370 +
  371 +// stim::vec3<float> v2 = (e[idx+1].P-e[idx].P).norm();
  372 + stim::vec3<T> v2(
  373 + c[idx+1][0]-c[idx][0],
  374 + c[idx+1][1]-c[idx][1],
  375 + c[idx+1][2]-c[idx][2]
  376 + );
  377 +
  378 + return (v1.norm()+v2.norm()).norm();
  379 + }
  380 + // return e[idx].N;
  381 +
  382 + }
  383 +
  384 + stim::vec3<T>
  385 + d(T l, int idx)
  386 + {
  387 + if(idx == 0 || idx == N-1)
  388 + {
  389 + return norms[idx];
  390 +// return e[idx].N;
  391 + }
  392 + else
  393 + {
  394 +
  395 + T rat = (l-L[idx])/(L[idx+1]-L[idx]);
  396 + return( norms[idx] + (norms[idx+1] - norms[idx])*rat);
  397 +// return( e[idx].N + (e[idx+1].N - e[idx].N)*rat);
  398 + }
  399 + }
  400 +
  401 +
  402 + ///finds the index of the point closest to the length l on the lower bound.
  403 + ///binary search.
  404 + int
  405 + findIdx(T l)
  406 + {
  407 + unsigned int i = L.size()/2;
  408 + unsigned int max = L.size()-1;
  409 + unsigned int min = 0;
  410 + while(i > 0 && i < L.size()-1)
  411 + {
  412 +// std::cerr << "Trying " << i << std::endl;
  413 +// std::cerr << "l is " << l << ", L[" << i << "]" << L[i] << std::endl;
  414 + if(l < L[i])
  415 + {
  416 + max = i;
  417 + i = min+(max-min)/2;
  418 + }
  419 + else if(L[i] <= l && L[i+1] >= l)
  420 + {
  421 + break;
  422 + }
  423 + else
  424 + {
  425 + min = i;
  426 + i = min+(max-min)/2;
  427 + }
  428 + }
  429 + return i;
  430 + }
  431 +
  432 + public:
  433 + ///default constructor
  434 + cylinder()
  435 + // : centerline<T>()
  436 + {
  437 +
  438 + }
  439 +
  440 + ///constructor to create a cylinder from a set of points, radii, and the number of sides for the cylinder.
  441 + ///@param inP: Vector of stim vec3 composing the points of the centerline.
  442 + ///@param inM: Vector of stim vecs composing the radii of the centerline.
  443 + cylinder(std::vector<stim::vec3<T> > inP, std::vector<stim::vec<T> > inM)
  444 + : centerline<T>(inP)
  445 + {
  446 + init(inP, inM);
  447 + }
  448 +
  449 + ///constructor to create a cylinder from a set of points, radii, and the number of sides for the cylinder.
  450 + ///@param inP: Vector of stim vec3 composing the points of the centerline.
  451 + ///@param inM: Vector of stim vecs composing the radii of the centerline.
  452 + cylinder(std::vector<stim::vec3<T> > inP, std::vector< T > inM)
  453 + : centerline<T>(inP)
  454 + {
  455 + std::vector<stim::vec<T> > temp;
  456 + stim::vec<T> zero(0.0,0.0);
  457 + temp.resize(inM.size(), zero);
  458 + for(int i = 0; i < inM.size(); i++)
  459 + temp[i][0] = inR[i];
  460 + init(inP, temp);
  461 + }
  462 +
  463 +
  464 + ///Constructor defines a cylinder with centerline inP and magnitudes of zero
  465 + ///@param inP: Vector of stim vec3 composing the points of the centerline
  466 + cylinder(std::vector< stim::vec3<T> > inP)
  467 + : centerline<T>(inP)
  468 + {
  469 + std::vector< stim::vec<T> > inM; //create an array of arbitrary magnitudes
  470 +
  471 + stim::vec<T> zero;
  472 + zero.push_back(0);
  473 +
  474 + inM.resize(inP.size(), zero); //initialize the magnitude values to zero
  475 + init(inP, inM);
  476 + }
  477 +
  478 + //assignment operator creates a cylinder from a centerline (default radius is zero)
  479 + cylinder& operator=(stim::centerline<T> c) {
  480 + init(c);
  481 + }
  482 +
  483 + ///Returns the number of points on the cylinder centerline
  484 +
  485 + unsigned int size(){
  486 + return N;
  487 + }
  488 +
  489 +
  490 + ///Returns a position vector at the given p-value (p value ranges from 0 to 1).
  491 + ///interpolates the position along the line.
  492 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  493 + stim::vec3<T>
  494 + p(T pvalue)
  495 + {
  496 + if(pvalue < 0.0 || pvalue > 1.0)
  497 + {
  498 + return stim::vec3<float>(-1,-1,-1);
  499 + }
  500 + T l = pvalue*L[L.size()-1];
  501 + int idx = findIdx(l);
  502 + return (p(l,idx));
  503 + }
  504 +
  505 + ///Returns a position vector at the given length into the fiber (based on the pvalue).
  506 + ///Interpolates the radius along the line.
  507 + ///@param l: the location of the in the cylinder.
  508 + ///@param idx: integer location of the point closest to l but prior to it.
  509 + stim::vec3<T>
  510 + p(T l, int idx)
  511 + {
  512 + T rat = (l-L[idx])/(L[idx+1]-L[idx]);
  513 + stim::vec3<T> v1(
  514 + c[idx][0],
  515 + c[idx][1],
  516 + c[idx][2]
  517 + );
  518 +
  519 + stim::vec3<T> v2(
  520 + c[idx+1][0],
  521 + c[idx+1][1],
  522 + c[idx+1][2]
  523 + );
  524 +// return( e[idx].P + (e[idx+1].P-e[idx].P)*rat);
  525 + return( v1 + (v2-v1)*rat);
  526 +// return(
  527 +// return (pos[idx] + (pos[idx+1]-pos[idx])*((l-L[idx])/(L[idx+1]- L[idx])));
  528 + }
  529 +
  530 + ///Returns a radius at the given p-value (p value ranges from 0 to 1).
  531 + ///interpolates the radius along the line.
  532 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  533 + T
  534 + r(T pvalue)
  535 + {
  536 + if(pvalue < 0.0 || pvalue > 1.0){
  537 + std::cerr<<"Error, value "<<pvalue<<" is outside of [0 1]."<<std::endl;
  538 + exit(1);
  539 + }
  540 + T l = pvalue*L[L.size()-1];
  541 + int idx = findIdx(l);
  542 + return (r(l,idx));
  543 + }
  544 +
  545 + ///Returns a radius at the given length into the fiber (based on the pvalue).
  546 + ///Interpolates the position along the line.
  547 + ///@param l: the location of the in the cylinder.
  548 + ///@param idx: integer location of the point closest to l but prior to it.
  549 + T
  550 + r(T l, int idx)
  551 + {
  552 + T rat = (l-L[idx])/(L[idx+1]-L[idx]);
  553 + T v1 = (e[idx].U.len() + (e[idx+1].U.len() - e[idx].U.len())*rat);
  554 + T v3 = (Us[idx].len() + (Us[idx+1].len() - Us[idx].len())*rat);
  555 + T v2 = (mags[idx][0] + (mags[idx+1][0]-mags[idx][0])*rat);
  556 +// std::cout << (float)v1 = (float) v2 << std::endl;
  557 + if(abs(v3 - v1) >= 10e-6)
  558 + {
  559 + std::cout << "-------------------------" << std::endl;
  560 + std::cout << e[idx].str() << std::endl << std::endl;
  561 + std::cout << Us[idx].str() << std::endl;
  562 + std::cout << (float)v1 - (float) v2 << std::endl;
  563 + std::cout << "failed" << std::endl;
  564 + }
  565 +// std::cout << e[idx].U.len() << " " << mags[idx][0] << std::endl;
  566 +// std::cout << v2 << std::endl;
  567 + return(v2);
  568 +// return (mags[idx][0] + (mags[idx+1][0]-mags[idx][0])*rat);
  569 + // (
  570 + }
  571 +
  572 + /// Returns the magnitude at the given index
  573 + /// @param i is the index of the desired point
  574 + /// @param m is the index of the magnitude value
  575 + T ri(unsigned i, unsigned m = 0){
  576 + return mags[i][m];
  577 + }
  578 +
  579 + /// Adds a new magnitude value to all points
  580 + /// @param m is the starting value for the new magnitude
  581 + void add_mag(T m = 0){
  582 + for(unsigned int p = 0; p < N; p++)
  583 + mags[p].push_back(m);
  584 + }
  585 +
  586 + /// Sets a magnitude value
  587 + /// @param val is the new value for the magnitude
  588 + /// @param p is the point index for the magnitude to be set
  589 + /// @param m is the index for the magnitude
  590 + void set_mag(T val, unsigned p, unsigned m = 0){
  591 + mags[p][m] = val;
  592 + }
  593 +
  594 + /// Returns the number of magnitude values at each point
  595 + unsigned nmags(){
  596 + return mags[0].size();
  597 + }
  598 +
  599 + ///returns the position of the point with a given pvalue and theta on the surface
  600 + ///in x, y, z coordinates. Theta is in degrees from 0 to 360.
  601 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  602 + ///@param theta: the angle to the point of a circle.
  603 + stim::vec3<T>
  604 + surf(T pvalue, T theta)
  605 + {
  606 + if(pvalue < 0.0 || pvalue > 1.0)
  607 + {
  608 + return stim::vec3<float>(-1,-1,-1);
  609 + } else {
  610 + T l = pvalue*L[L.size()-1];
  611 + int idx = findIdx(l);
  612 + stim::vec3<T> ps = p(l, idx);
  613 + T m = r(l, idx);
  614 + s = e[idx];
  615 + s.center(ps);
  616 + s.normal(d(l, idx));
  617 + s.scale(m/e[idx].U.len());
  618 + return(s.p(theta));
  619 + }
  620 + }
  621 +
  622 + ///returns a vector of points necessary to create a circle at every position in the fiber.
  623 + ///@param sides: the number of sides of each circle.
  624 + std::vector<std::vector<vec3<T> > >
  625 + getPoints(int sides)
  626 + {
  627 + std::vector<std::vector <vec3<T> > > points;
  628 + points.resize(N);
  629 + for(int i = 0; i < N; i++)
  630 + {
  631 + points[i] = e[i].getPoints(sides);
  632 + }
  633 + return points;
  634 + }
  635 +
  636 + ///returns the total length of the line at index j.
  637 + T
  638 + getl(int j)
  639 + {
  640 + return (L[j]);
  641 + }
  642 + /// Allows a point on the centerline to be accessed using bracket notation
  643 +
  644 + vec3<T> operator[](unsigned int i){
  645 + return e[i].P;
  646 + }
  647 +
  648 + /// Returns the total length of the cylinder centerline
  649 + T length(){
  650 + return L.back();
  651 + }
  652 +
  653 + /// Integrates a magnitude value along the cylinder.
  654 + /// @param m is the magnitude value to be integrated (this is usually the radius)
  655 + T integrate(unsigned m = 0){
  656 +
  657 + T M = 0; //initialize the integral to zero
  658 + T m0, m1; //allocate space for both magnitudes in a single segment
  659 +
  660 + //vec3<T> p0, p1; //allocate space for both points in a single segment
  661 +
  662 + m0 = mags[0][m]; //initialize the first point and magnitude to the first point in the cylinder
  663 + //p0 = pos[0];
  664 +
  665 + T len = L[0]; //allocate space for the segment length
  666 +
  667 + //for every consecutive point in the cylinder
  668 + for(unsigned p = 1; p < N; p++){
  669 +
  670 + //p1 = pos[p]; //get the position and magnitude for the next point
  671 + m1 = mags[p][m];
  672 +
  673 + if(p > 1) len = (L[p-1] - L[p-2]); //calculate the segment length using the L array
  674 +
  675 + //add the average magnitude, weighted by the segment length
  676 + M += (m0 + m1)/(T)2.0 * len;
  677 +
  678 + m0 = m1; //move to the next segment by shifting points
  679 + }
  680 + return M; //return the integral
  681 + }
  682 +
  683 + /// Averages a magnitude value across the cylinder
  684 + /// @param m is the magnitude value to be averaged (this is usually the radius)
  685 + T average(unsigned m = 0){
  686 +
  687 + //return the average magnitude
  688 + return integrate(m) / L.back();
  689 + }
  690 +
  691 + /// Resamples the cylinder to provide a maximum distance of "spacing" between centerline points. All current
  692 + /// centerline points are guaranteed to exist in the new cylinder
  693 + /// @param spacing is the maximum spacing allowed between sample points
  694 + cylinder<T> resample(T spacing){
  695 +
  696 + std::vector< vec3<T> > result;
  697 +
  698 + vec3<T> p0 = e[0].P; //initialize p0 to the first point on the centerline
  699 + vec3<T> p1;
  700 + unsigned N = size(); //number of points in the current centerline
  701 +
  702 + //for each line segment on the centerline
  703 + for(unsigned int i = 1; i < N; i++){
  704 + p1 = e[i].P; //get the second point in the line segment
  705 +
  706 + vec3<T> v = p1 - p0; //calculate the vector between these two points
  707 + T d = v.len(); //calculate the distance between these two points (length of the line segment)
  708 +
  709 + size_t nsteps = (size_t)std::ceil(d / spacing); //calculate the number of steps to take along the segment to meet the spacing criteria
  710 + T stepsize = (T)1.0 / nsteps; //calculate the parametric step size between new centerline points
  711 +
  712 + //for each step along the line segment
  713 + for(unsigned s = 0; s < nsteps; s++){
  714 + T alpha = stepsize * s; //calculate the fraction of the distance along the line segment covered
  715 + result.push_back(p0 + alpha * v); //push the point at alpha position along the line segment
  716 + }
  717 +
  718 + p0 = p1; //shift the points to move to the next line segment
  719 + }
  720 +
  721 + result.push_back(e[size() - 1].P); //push the last point in the centerline
  722 +
  723 + return cylinder<T>(result);
  724 +
  725 + }*/
  726 +
  727 +
  728 +};
  729 +
  730 +}
  731 +#endif
... ...
gl_network.h 0 → 100644
  1 +#ifndef STIM_GL_NETWORK
  2 +#define STIM_GL_NETWORK
  3 +
  4 +#include <GL/glut.h>
  5 +#include "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(T scale = 1.0f, bool undo = false) {
  161 +
  162 + float r1, r2;
  163 + if (undo == true)
  164 + glDeleteLists(dlist, 1); // delete display list
  165 + if (!glIsList(dlist)) { // if dlist isn't a display list, create it
  166 + dlist = glGenLists(1); // generate a display list
  167 + glNewList(dlist, GL_COMPILE); // start a new display list
  168 + for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network
  169 + for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge
  170 + stim::circle<T> C1 = E[e].circ(p - 1);
  171 + stim::circle<T> C2 = E[e].circ(p);
  172 + r1 = E[e].r(p - 1);
  173 + r2 = E[e].r(p);
  174 + C1.set_R(scale * r1); // re-scale the circle to the same
  175 + C2.set_R(scale * r2);
  176 + std::vector< stim::vec3<T> > Cp1 = C1.glpoints(20); // get 20 points on the circle plane
  177 + std::vector< stim::vec3<T> > Cp2 = C2.glpoints(20);
  178 + glBegin(GL_QUAD_STRIP);
  179 + for (unsigned i = 0; i < Cp1.size(); i++) {
  180 + glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]);
  181 + glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]);
  182 + }
  183 + glEnd();
  184 + } // set the texture coordinate based on the specified magnitude index
  185 + }
  186 + for (unsigned n = 0; n < V.size(); n++) {
  187 + for (unsigned i = 0; i < E.size(); i++) {
  188 + if (E[i].v[0] == n) {
  189 + r1 = E[i].r(0) * scale;
  190 + break;
  191 + }
  192 + else if (E[i].v[1] == n) {
  193 + r1 = E[i].r(E[i].size() - 1) * scale;
  194 + break;
  195 + }
  196 + }
  197 + renderBall(V[n][0], V[n][1], V[n][2], r1, 20);
  198 + }
  199 + glEndList(); // end the display list
  200 + }
  201 + glCallList(dlist); // render the display list
  202 + }
  203 +
  204 + ///render the network cylinder as a series of tubes
  205 + ///colors are based on metric values
  206 + void glCylinder(float sigma, float radius) {
  207 +
  208 + if (radius != sigma) // if render radius was changed by user, create a new display list
  209 + glDeleteLists(dlist, 1);
  210 + if (!glIsList(dlist)) { // if dlist isn't a display list, create it
  211 + dlist = glGenLists(1); // generate a display list
  212 + glNewList(dlist, GL_COMPILE); // start a new display list
  213 + for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network
  214 + for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge
  215 + stim::circle<T> C1 = E[e].circ(p - 1);
  216 + stim::circle<T> C2 = E[e].circ(p);
  217 + C1.set_R(2*radius); // re-scale the circle to the same
  218 + C2.set_R(2*radius);
  219 + std::vector< stim::vec3<T> > Cp1 = C1.glpoints(20);// get 20 points on the circle plane
  220 + std::vector< stim::vec3<T> > Cp2 = C2.glpoints(20);
  221 + glBegin(GL_QUAD_STRIP);
  222 + for (unsigned i = 0; i < Cp1.size(); i++) {
  223 + stim::vec3<T> n1 = Cp1[i] - E[e][p - 1]; // set normal vector for every vertex on the quads
  224 + stim::vec3<T> n2 = Cp2[i] - E[e][p];
  225 + n1 = n1.norm();
  226 + n2 = n2.norm();
  227 +
  228 + glNormal3f(n1[0], n1[1], n1[2]);
  229 + glTexCoord1f(E[e].r(p - 1));
  230 + glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]);
  231 + glNormal3f(n2[0], n2[1], n2[2]);
  232 + glTexCoord1f(E[e].r(p));
  233 + glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]);
  234 + }
  235 + glEnd();
  236 + } // set the texture coordinate based on the specified magnitude index
  237 + }
  238 + for (unsigned n = 0; n < V.size(); n++) {
  239 + size_t num = V[n].e[0].size(); // store the number of outgoing edge of that vertex
  240 + if (num != 0) { // if it has outgoing edge
  241 + unsigned idx = V[n].e[0][0]; // find the index of first outgoing edge of that vertex
  242 + glTexCoord1f(E[idx].r(0)); // bind the texture as metric of first point on that edge
  243 + }
  244 + else {
  245 + unsigned idx = V[n].e[1][0]; // find the index of first incoming edge of that vertex
  246 + glTexCoord1f(E[idx].r(E[idx].size() - 1)); // bind the texture as metric of last point on that edge
  247 + }
  248 + renderBall(V[n][0], V[n][1], V[n][2], 2*radius, 20);
  249 + }
  250 + glEndList(); // end the display list
  251 + }
  252 + glCallList(dlist); // render the display list
  253 + }
  254 +
  255 + ///render a T as a adjoint network of GT in transparancy(darkgreen, overlaid)
  256 + void glAdjointCylinder(float sigma, float radius) {
  257 +
  258 + if (radius != sigma) // if render radius was changed by user, create a new display list
  259 + glDeleteLists(dlist + 4, 1);
  260 + if (!glIsList(dlist + 4)) {
  261 + glNewList(dlist + 4, GL_COMPILE);
  262 + for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network
  263 + for (unsigned p = 1; p < E[e].size(); p++) { // for each point on that edge
  264 + stim::circle<T> C1 = E[e].circ(p - 1);
  265 + stim::circle<T> C2 = E[e].circ(p);
  266 + C1.set_R(2 * radius); // scale the circle to the same
  267 + C2.set_R(2 * radius);
  268 + std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20);
  269 + std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20);
  270 + glBegin(GL_QUAD_STRIP);
  271 + for (unsigned i = 0; i < Cp1.size(); i++) { // for every point on the circle(+1 means closing the circle)
  272 + glVertex3f(Cp1[i][0], Cp1[i][1], Cp1[i][2]);
  273 + glVertex3f(Cp2[i][0], Cp2[i][1], Cp2[i][2]);
  274 + }
  275 + glEnd();
  276 + } // set the texture coordinate based on the specified magnitude index
  277 + }
  278 + for (unsigned n = 0; n < V.size(); n++) {
  279 + size_t num = V[n].e[0].size(); // store the number of outgoing edge of that vertex
  280 + if (num != 0) { // if it has outgoing edge
  281 + unsigned idx = V[n].e[0][0]; // find the index of first outgoing edge of that vertex
  282 + }
  283 + else {
  284 + unsigned idx = V[n].e[1][0]; // find the index of first incoming edge of that vertex
  285 + }
  286 + renderBall(V[n][0], V[n][1], V[n][2], 2 * radius, 20);
  287 + }
  288 + glEndList();
  289 + }
  290 + glCallList(dlist + 4);
  291 + }
  292 +
  293 + ///render the network cylinder as series of tubes
  294 + ///@param I is a indicator: 0 -> GT, 1 -> T
  295 + ///@param map is the mapping relationship between two networks
  296 + ///@param colormap is the random generated color set for render
  297 + void glRandColorCylinder(int I, std::vector<unsigned> map, std::vector<T> colormap, float sigma, float radius) {
  298 +
  299 + if (radius != sigma) // if render radius was changed by user, create a new display list
  300 + glDeleteLists(dlist + 2, 1);
  301 + if (!glIsList(dlist + 2)) { // if dlist isn't a display list, create it
  302 + glNewList(dlist + 2, GL_COMPILE); // start a new display list
  303 + for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network
  304 + if (map[e] != unsigned(-1)) {
  305 + if (I == 0) { // if it is to render GT
  306 + glColor3f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2]);
  307 + }
  308 + else { // if it is to render T
  309 + glColor3f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2]);
  310 + }
  311 +
  312 + for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge
  313 + stim::circle<T> C1 = E[e].circ(p - 1);
  314 + stim::circle<T> C2 = E[e].circ(p);
  315 + C1.set_R(2*radius); // re-scale the circle to the same
  316 + C2.set_R(2*radius);
  317 + std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20);
  318 + std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20);
  319 + renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]);
  320 + }
  321 + }
  322 + else {
  323 + glColor3f(1.0, 1.0, 1.0); // white color for the un-mapping edges
  324 + for (unsigned p = 1; p < E[e].size(); p++) {// for each point on that edge
  325 + stim::circle<T> C1 = E[e].circ(p - 1);
  326 + stim::circle<T> C2 = E[e].circ(p);
  327 + C1.set_R(2*radius); // scale the circle to the same
  328 + C2.set_R(2*radius);
  329 + std::vector< stim::vec3<T> >Cp1 = C1.glpoints(20);
  330 + std::vector< stim::vec3<T> >Cp2 = C2.glpoints(20);
  331 + renderCylinder(Cp1, Cp2, E[e][p - 1], E[e][p]);
  332 + }
  333 + }
  334 + }
  335 + for (unsigned n = 0; n < V.size(); n++) {
  336 + size_t num_edge = V[n].e[0].size() + V[n].e[1].size();
  337 + if (num_edge > 1) { // if it is the joint vertex
  338 + glColor3f(0.3, 0.3, 0.3); // gray
  339 + renderBall(V[n][0], V[n][1], V[n][2], 3*radius, 20);
  340 + }
  341 + else { // if it is the terminal vertex
  342 + glColor3f(0.6, 0.6, 0.6); // more white gray
  343 + renderBall(V[n][0], V[n][1], V[n][2], 3*radius, 20);
  344 + }
  345 + }
  346 + glEndList();
  347 + }
  348 + glCallList(dlist + 2);
  349 + }
  350 +
  351 + ///render the network centerline as a series of line strips in random different color
  352 + ///@param I is a indicator: 0 -> GT, 1 -> T
  353 + ///@param map is the mapping relationship between two networks
  354 + ///@param colormap is the random generated color set for render
  355 + void glRandColorCenterline(int I, std::vector<unsigned> map, std::vector<T> colormap) {
  356 + if (!glIsList(dlist + 2)) {
  357 + glNewList(dlist + 2, GL_COMPILE);
  358 + for (unsigned e = 0; e < E.size(); e++) {
  359 + if (map[e] != unsigned(-1)) { // if it has corresponding edge in another network
  360 + if (I == 0) // if it is to render GT
  361 + glColor3f(colormap[e * 3 + 0], colormap[e * 3 + 1], colormap[e * 3 + 2]);
  362 + else // if it is to render T
  363 + glColor3f(colormap[map[e] * 3 + 0], colormap[map[e] * 3 + 1], colormap[map[e] * 3 + 2]);
  364 +
  365 + glBegin(GL_LINE_STRIP);
  366 + for (unsigned p = 0; p < E[e].size(); p++) {
  367 + glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]);
  368 + }
  369 + glEnd();
  370 + }
  371 + else {
  372 + glColor3f(1.0, 1.0, 1.0); // white color for the un-mapping edges
  373 + glBegin(GL_LINE_STRIP);
  374 + for (unsigned p = 0; p < E[e].size(); p++) {
  375 + glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]);
  376 + }
  377 + glEnd();
  378 + }
  379 + }
  380 + glEndList();
  381 + }
  382 + glCallList(dlist + 2);
  383 + }
  384 +
  385 + void glAdjointCenterline() {
  386 + if (!glIsList(dlist + 4)) {
  387 + glNewList(dlist + 4, GL_COMPILE);
  388 + for (unsigned e = 0; e < E.size(); e++) { //for each edge in the network
  389 +
  390 + glBegin(GL_LINE_STRIP);
  391 + for (unsigned p = 0; p < E[e].size(); p++) { //for each point on that edge
  392 + glVertex3f(E[e][p][0], E[e][p][1], E[e][p][2]); //set the vertex position based on the current point
  393 + glTexCoord1f(E[e].r(p)); //set the texture coordinate based on the specified magnitude index
  394 + }
  395 + glEnd();
  396 + }
  397 + glEndList();
  398 + }
  399 + glCallList(dlist + 4);
  400 + }
  401 +
  402 + // highlight the difference part
  403 + void glDifferenceCylinder(int I, std::vector<unsigned> map, std::vector<T> colormap, float sigma, float radius) {
  404 +
  405 + if (radius != sigma) // if render radius was changed by user, create a new display list
  406 + glDeleteLists(dlist + 6, 1);
  407 + if (!glIsList(dlist + 6)) { // if dlist isn't a display list, create it
  408 + glNewList(dlist + 6, GL_COMPILE); // start a new display list
  409 + for (unsigned e = 0; e < E.size(); e++) { // for each edge in the network
  410 + if (map[e] != unsigned(-1)) {
  411 + glEnable(GL_BLEND); //enable color blend